about summary refs log tree commit diff
path: root/src/comp/middle
diff options
context:
space:
mode:
authorJosh Matthews <josh@joshmatthews.net>2011-11-15 21:11:22 -0500
committerJosh Matthews <josh@joshmatthews.net>2011-12-18 23:41:03 -0500
commite383004985a5d725cec91cc48793284eedbcdbce (patch)
treeb336f479402c26e0776e213dd0ba153cb392e18f /src/comp/middle
parent10030a37d50f5c3ae681709598be201153e0b1e5 (diff)
Add debug info for local vars, basic fundamental types, and lexical blocks, along with source line information generation for individual instructions.
Diffstat (limited to 'src/comp/middle')
-rw-r--r--src/comp/middle/debuginfo.rs300
-rw-r--r--src/comp/middle/trans.rs41
-rw-r--r--src/comp/middle/trans_build.rs355
-rw-r--r--src/comp/middle/trans_common.rs9
4 files changed, 553 insertions, 152 deletions
diff --git a/src/comp/middle/debuginfo.rs b/src/comp/middle/debuginfo.rs
index 72390a23383..9379c42623c 100644
--- a/src/comp/middle/debuginfo.rs
+++ b/src/comp/middle/debuginfo.rs
@@ -1,12 +1,13 @@
-import std::{vec, str, map, option, unsafe};
-import std::vec::to_ptr;
+import std::{vec, str, option, unsafe, fs};
 import std::map::hashmap;
 import lib::llvm::llvm;
-import lib::llvm::llvm::{ModuleRef, ValueRef};
+import lib::llvm::llvm::ValueRef;
 import middle::trans_common::*;
+import middle::ty;
+import ast::ty;
 import syntax::{ast, codemap};
 
-const LLVMDebugVersion: int = 0x80000;
+const LLVMDebugVersion: int = (9 << 16); //& 0xffff0000; // 0x80000 ?
 
 const DW_LANG_RUST: int = 0x9000;
 const DW_VIRTUALITY_none: int = 0;
@@ -14,6 +15,18 @@ const DW_VIRTUALITY_none: int = 0;
 const CompileUnitTag: int = 17;
 const FileDescriptorTag: int = 41;
 const SubprogramTag: int = 46;
+const BasicTypeDescriptorTag: int = 36;
+const AutoVariableTag: int = 256;
+const ArgVariableTag: int = 257;
+const ReturnVariableTag: int = 258;
+const LexicalBlockTag: int = 11;
+
+const DW_ATE_boolean: int = 0x02;
+const DW_ATE_float: int = 0x04;
+const DW_ATE_signed: int = 0x05;
+const DW_ATE_signed_char: int = 0x06;
+const DW_ATE_unsigned: int = 0x07;
+const DW_ATE_unsigned_char: int = 0x08;
 
 fn as_buf(s: str) -> str::sbuf {
     str::as_buf(s, {|sbuf| sbuf})
@@ -23,11 +36,14 @@ fn llstr(s: str) -> ValueRef {
 }
 
 fn lltag(lltag: int) -> ValueRef {
-    lli32(0x80000 + lltag)
+    lli32(LLVMDebugVersion | lltag)
 }
 fn lli32(val: int) -> ValueRef {
     C_i32(val as i32)
 }
+fn lli64(val: int) -> ValueRef {
+    C_i64(val as i64)
+}
 fn lli1(bval: bool) -> ValueRef {
     C_bool(bval)
 }
@@ -38,6 +54,9 @@ fn llmdnode(elems: [ValueRef]) -> ValueRef unsafe {
 fn llunused() -> ValueRef {
     lli32(0x0)
 }
+fn llnull() -> ValueRef {
+    C_null(T_ptr(T_nil()))
+}
 
 fn update_cache(cache: metadata_cache, mdtag: int, val: debug_metadata) {
     let existing = if cache.contains_key(mdtag) {
@@ -55,6 +74,9 @@ type metadata<T> = {node: ValueRef, data: T};
 type file_md = {path: str};
 type compile_unit_md = {path: str};
 type subprogram_md = {name: str, file: str};
+type local_var_md = {id: ast::node_id};
+type tydesc_md = {hash: uint};
+type block_md = {start: codemap::loc, end: codemap::loc};
 
 type metadata_cache = hashmap<int, [debug_metadata]>;
 
@@ -62,18 +84,31 @@ tag debug_metadata {
     file_metadata(@metadata<file_md>);
     compile_unit_metadata(@metadata<compile_unit_md>);
     subprogram_metadata(@metadata<subprogram_md>);
+    local_var_metadata(@metadata<local_var_md>);
+    tydesc_metadata(@metadata<tydesc_md>);
+    block_metadata(@metadata<block_md>);
+}
+
+fn cast_safely<T, U>(val: T) -> U unsafe {
+    let val2 = val;
+    let val3 = unsafe::reinterpret_cast(val2);
+    unsafe::leak(val2);
+    ret val3;
 }
 
 fn md_from_metadata<T>(val: debug_metadata) -> T unsafe {
     alt val {
-      file_metadata(md) { unsafe::reinterpret_cast(md) }
-      compile_unit_metadata(md) { unsafe::reinterpret_cast(md) }
-      subprogram_metadata(md) { unsafe::reinterpret_cast(md) }
+      file_metadata(md) { cast_safely(md) }
+      compile_unit_metadata(md) { cast_safely(md) }
+      subprogram_metadata(md) { cast_safely(md) }
+      local_var_metadata(md) { cast_safely(md) }
+      tydesc_metadata(md) { cast_safely(md) }
+      block_metadata(md) { cast_safely(md) }
     }
 }
 
 fn cached_metadata<T>(cache: metadata_cache, mdtag: int,
-                      eq: block(md: T) -> bool) -> option::t<T> {
+                      eq: block(md: T) -> bool) -> option::t<T> unsafe {
     if cache.contains_key(mdtag) {
         let items = cache.get(mdtag);
         for item in items {
@@ -94,10 +129,8 @@ fn get_compile_unit_metadata(cx: @crate_ctxt, full_path: str)
       option::some(md) { ret md; }
       option::none. {}
     }
-    let sep = str::rindex(full_path, '/' as u8) as uint;
-    let fname = str::slice(full_path, sep + 1u,
-                           str::byte_len(full_path));
-    let path = str::slice(full_path, 0u, sep + 1u);
+    let fname = fs::basename(full_path);
+    let path = fs::dirname(full_path);
     let unit_metadata = [lltag(CompileUnitTag),
                          llunused(),
                          lli32(DW_LANG_RUST),
@@ -122,22 +155,18 @@ fn get_compile_unit_metadata(cx: @crate_ctxt, full_path: str)
     ret mdval;
 }
 
-//        let kind_id = llvm::LLVMGetMDKindID(as_buf("dbg"),
-//                                            str::byte_len("dbg"));
-
-
 fn get_file_metadata(cx: @crate_ctxt, full_path: str) -> @metadata<file_md> {
     let cache = cx.llmetadata;
     alt cached_metadata::<@metadata<file_md>>(
-        cache, FileDescriptorTag, {|md| md.data.path == full_path}) {
+        cache, FileDescriptorTag,
+        {|md|
+         md.data.path == full_path}) {
       option::some(md) { ret md; }
       option::none. {}
     }
-    let sep = str::rindex(full_path, '/' as u8) as uint;
-    let fname = str::slice(full_path, sep + 1u,
-                           str::byte_len(full_path));
-    let path = str::slice(full_path, 0u, sep + 1u);
-    let unit_node = get_compile_unit_metadata(cx, path).node;
+    let fname = fs::basename(full_path);
+    let path = fs::dirname(full_path);
+    let unit_node = get_compile_unit_metadata(cx, full_path).node;
     let file_md = [lltag(FileDescriptorTag),
                    llstr(fname),
                    llstr(path),
@@ -148,6 +177,162 @@ fn get_file_metadata(cx: @crate_ctxt, full_path: str) -> @metadata<file_md> {
     ret mdval;
 }
 
+fn get_block_metadata(cx: @block_ctxt) -> @metadata<block_md> {
+    let cache = bcx_ccx(cx).llmetadata;
+    let start = codemap::lookup_char_pos(bcx_ccx(cx).sess.get_codemap(),
+                                         cx.sp.lo);
+    let fname = start.filename;
+    let end = codemap::lookup_char_pos(bcx_ccx(cx).sess.get_codemap(),
+                                       cx.sp.hi);
+    alt cached_metadata::<@metadata<block_md>>(
+        cache, LexicalBlockTag,
+        {|md| start == md.data.start && end == md.data.end}) {
+      option::some(md) { ret md; }
+      option::none. {}
+    }
+    let parent = alt cx.parent {
+      trans_common::parent_none. { llnull() }
+      trans_common::parent_some(bcx) {
+        get_block_metadata(bcx).node
+      }
+    };
+    let file_node = get_file_metadata(bcx_ccx(cx), fname);
+    let unique_id = alt cache.find(LexicalBlockTag) {
+      option::some(v) { vec::len(v) as int }
+      option::none. { 0 }
+    };
+    let lldata = [lltag(LexicalBlockTag),
+                  parent,
+                  lli32(start.line as int),
+                  lli32(start.col as int),
+                  file_node.node,
+                  lli32(unique_id)
+                 ];
+      let val = llmdnode(lldata);
+      let mdval = @{node: val, data: {start: start, end: end}};
+      update_cache(cache, LexicalBlockTag, block_metadata(mdval));
+      ret mdval;
+}
+
+fn get_ty_metadata(cx: @crate_ctxt, t: ty::t, ty: @ast::ty) -> @metadata<tydesc_md> {
+    let cache = cx.llmetadata;
+    alt cached_metadata::<@metadata<tydesc_md>>(
+        cache, BasicTypeDescriptorTag,
+        {|md| ty::hash_ty(t) == ty::hash_ty(md.data.hash)}) {
+      option::some(md) { ret md; }
+      option::none. {}
+    }
+    let (name, size, flags) = alt ty.node {
+      ast::ty_bool. { ("bool", 1, DW_ATE_boolean) }
+      ast::ty_int. { ("int", 32, DW_ATE_signed) } //XXX machine-dependent?
+      ast::ty_uint. { ("uint", 32, DW_ATE_unsigned) } //XXX machine-dependent?
+      ast::ty_float. { ("float", 32, DW_ATE_float) } //XXX machine-dependent?
+      ast::ty_machine(m) { alt m {
+        ast::ty_i8. { ("i8", 1, DW_ATE_signed_char) }
+        ast::ty_i16. { ("i16", 16, DW_ATE_signed) }
+        ast::ty_i32. { ("i32", 32, DW_ATE_signed) }
+        ast::ty_i64. { ("i64", 64, DW_ATE_signed) }
+        ast::ty_u8. { ("u8", 8, DW_ATE_unsigned_char) }
+        ast::ty_u16. { ("u16", 16, DW_ATE_unsigned) }
+        ast::ty_u32. { ("u32", 32, DW_ATE_unsigned) }
+        ast::ty_u64. { ("u64", 64, DW_ATE_unsigned) }
+        ast::ty_f32. { ("f32", 32, DW_ATE_float) }
+        ast::ty_f64. { ("f64", 64, DW_ATE_float) }
+      } }
+      ast::ty_char. { ("char", 32, DW_ATE_unsigned) }
+    };
+    let lldata = [lltag(BasicTypeDescriptorTag),
+                  llunused(), //XXX scope context
+                  llstr(name),
+                  llnull(), //XXX basic types only
+                  lli32(0), //XXX basic types only
+                  lli64(size),
+                  lli64(32), //XXX alignment?
+                  lli64(0), //XXX offset?
+                  lli32(flags)];
+    let llnode = llmdnode(lldata);
+    let mdval = @{node: llnode, data: {hash: ty::hash_ty(t)}};
+    update_cache(cache, BasicTypeDescriptorTag, tydesc_metadata(mdval));
+    ret mdval;
+}
+
+fn get_local_var_metadata(bcx: @block_ctxt, local: @ast::local)
+    -> @metadata<local_var_md> unsafe {
+    let cx = bcx_ccx(bcx);
+    let cache = cx.llmetadata;
+    alt cached_metadata::<@metadata<local_var_md>>(
+        cache, AutoVariableTag, {|md| md.data.id == local.node.id}) {
+      option::some(md) { ret md; }
+      option::none. {}
+    }
+    let name = alt local.node.pat.node {
+      ast::pat_bind(ident) { ident }
+    };
+    let loc = codemap::lookup_char_pos(cx.sess.get_codemap(),
+                                       local.span.lo);
+    let ty = trans::node_id_type(cx, local.node.id);
+    let tymd = get_ty_metadata(cx, ty, local.node.ty);
+    let filemd = get_file_metadata(cx, loc.filename);
+    let blockmd = get_block_metadata(bcx);
+    let lldata = [lltag(AutoVariableTag),
+                  blockmd.node, //XXX block context (maybe subprogram if possible?)
+                  llstr(name), // name
+                  filemd.node,
+                  lli32(loc.line as int), // line
+                  tymd.node,
+                  lli32(0), //XXX flags
+                  llnull() // inline loc reference
+                 ];
+    let mdnode = llmdnode(lldata);
+    let mdval = @{node: mdnode, data: {id: local.node.id}};
+    update_cache(cache, AutoVariableTag, local_var_metadata(mdval));
+    let llptr = alt bcx.fcx.lllocals.find(local.node.id) {
+      option::some(local_mem(v)) { v }
+      option::none. {
+        alt bcx.fcx.lllocals.get(local.node.pat.id) {
+          local_imm(v) { v }
+        }
+      }
+    };
+    let declargs = [llmdnode([llptr]), mdnode];
+    let instr = trans_build::Call(bcx, cx.intrinsics.get("llvm.dbg.declare"),
+                                  declargs);
+    llvm::LLVMAddNamedMetadataOperand(cx.llmod, as_buf("llvm.dbg.vars"),
+                                      str::byte_len("llvm.dbg.vars"),
+                                      mdnode);
+    ret mdval;
+}
+
+fn update_source_pos<T>(cx: @block_ctxt, s: T) {
+    if !bcx_ccx(cx).sess.get_opts().debuginfo {
+        ret;
+    }
+    cx.source_pos = option::some(
+        codemap::lookup_char_pos(bcx_ccx(cx).sess.get_codemap(),
+                                 s.span.lo)); //XXX maybe hi
+
+}
+
+fn reset_source_pos(cx: @block_ctxt) {
+    cx.source_pos = option::none;
+}
+
+fn add_line_info(cx: @block_ctxt, llinstr: ValueRef) {
+    if !bcx_ccx(cx).sess.get_opts().debuginfo ||
+        option::is_none(cx.source_pos) {
+        ret;
+    }
+    let loc = option::get(cx.source_pos);
+    let blockmd = get_block_metadata(cx);
+    let kind_id = llvm::LLVMGetMDKindID(as_buf("dbg"), str::byte_len("dbg"));
+    let scopedata = [lli32(loc.line as int),
+                     lli32(loc.col as int),
+                     blockmd.node,
+                     llnull()];
+    let dbgscope = llmdnode(scopedata);
+    llvm::LLVMSetMetadata(llinstr, kind_id, dbgscope);    
+}
+
 fn get_function_metadata(cx: @crate_ctxt, item: @ast::item,
                          llfndecl: ValueRef) -> @metadata<subprogram_md> {
     let cache = cx.llmetadata;
@@ -159,34 +344,43 @@ fn get_function_metadata(cx: @crate_ctxt, item: @ast::item,
     }
     let loc = codemap::lookup_char_pos(cx.sess.get_codemap(),
                                            item.span.lo);
-        let file_node = get_file_metadata(cx, loc.filename).node;
-        let fn_metadata = [lltag(SubprogramTag),
-                           llunused(),
-                           file_node,
-                           llstr(item.ident),
-                           llstr(item.ident), //XXX fully-qualified C++ name
-                           llstr(item.ident), //XXX MIPS name?????
-                           file_node,
-                           lli32(loc.line as int),
-                           C_null(T_ptr(T_nil())), // XXX reference to tydesc
-                           lli1(false), //XXX static
-                           lli1(true), // not extern
-                           lli32(DW_VIRTUALITY_none), // virtual-ness
-                           lli32(0i), //index into virt func
-                           C_null(T_ptr(T_nil())), // base type with vtbl
-                           lli1(false), // artificial
-                           lli1(cx.sess.get_opts().optimize != 0u),
-                           llfndecl
-                           //list of template params
-                           //func decl descriptor
-                           //list of func vars
-                          ];
-        let val = llmdnode(fn_metadata);
-        llvm::LLVMAddNamedMetadataOperand(cx.llmod, as_buf("llvm.dbg.sp"),
-                                          str::byte_len("llvm.dbg.sp"),
-                                          val);
-        let mdval = @{node: val, data: {name: item.ident,
-                                        file: loc.filename}};
-        update_cache(cache, SubprogramTag, subprogram_metadata(mdval));
-        ret mdval;
+    let file_node = get_file_metadata(cx, loc.filename).node;
+    let mangled = cx.item_symbols.get(item.id);
+    let ret_ty = alt item.node {
+      ast::item_fn(f, _) { f.decl.output }
+    };
+    let ty_node = alt ret_ty.node {
+      ast::ty_nil. { llnull() }
+      _ { get_ty_metadata(cx, ty::node_id_to_type(ccx_tcx(cx), item.id),
+                          ret_ty).node }
+    };
+    let fn_metadata = [lltag(SubprogramTag),
+                       llunused(),
+                       file_node,
+                       llstr(item.ident),
+                       llstr(item.ident), //XXX fully-qualified C++ name
+                       llstr(mangled), //XXX MIPS name?????
+                       file_node,
+                       lli32(loc.line as int),
+                       ty_node,
+                       lli1(false), //XXX static (check export)
+                       lli1(true), // not extern
+                       lli32(DW_VIRTUALITY_none), // virtual-ness
+                       lli32(0i), //index into virt func
+                       llnull(), // base type with vtbl
+                       lli1(false), // artificial
+                       lli1(cx.sess.get_opts().optimize != 0u),
+                       llfndecl
+                       //list of template params
+                       //func decl descriptor
+                       //list of func vars
+                      ];
+    let val = llmdnode(fn_metadata);
+    llvm::LLVMAddNamedMetadataOperand(cx.llmod, as_buf("llvm.dbg.sp"),
+                                      str::byte_len("llvm.dbg.sp"),
+                                      val);
+    let mdval = @{node: val, data: {name: item.ident,
+                                    file: loc.filename}};
+    update_cache(cache, SubprogramTag, subprogram_metadata(mdval));
+    ret mdval;
 }
\ No newline at end of file
diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs
index 6985ef974e6..551ac18a1b2 100644
--- a/src/comp/middle/trans.rs
+++ b/src/comp/middle/trans.rs
@@ -3519,6 +3519,8 @@ fn trans_temp_expr(bcx: @block_ctxt, e: @ast::expr) -> result {
 // - exprs with non-immediate type never get dest=by_val
 fn trans_expr(bcx: @block_ctxt, e: @ast::expr, dest: dest) -> @block_ctxt {
     let tcx = bcx_tcx(bcx);
+    debuginfo::update_source_pos(bcx, e);
+
     if expr_is_lval(bcx, e) {
         ret lval_to_dps(bcx, e, dest);
     }
@@ -4012,6 +4014,8 @@ fn trans_stmt(cx: @block_ctxt, s: ast::stmt) -> @block_ctxt {
     }
 
     let bcx = cx;
+    debuginfo::update_source_pos(cx, s);
+    
     alt s.node {
       ast::stmt_expr(e, _) { bcx = trans_expr(cx, e, ignore); }
       ast::stmt_decl(d, _) {
@@ -4023,6 +4027,9 @@ fn trans_stmt(cx: @block_ctxt, s: ast::stmt) -> @block_ctxt {
                 } else {
                     bcx = init_ref_local(bcx, local);
                 }
+                if bcx_ccx(cx).sess.get_opts().debuginfo {
+                    debuginfo::get_local_var_metadata(bcx, local);
+                }
             }
           }
           ast::decl_item(i) { trans_item(cx.fcx.lcx, *i); }
@@ -4030,6 +4037,8 @@ fn trans_stmt(cx: @block_ctxt, s: ast::stmt) -> @block_ctxt {
       }
       _ { bcx_ccx(cx).sess.unimpl("stmt variant"); }
     }
+
+    debuginfo::reset_source_pos(cx);
     ret bcx;
 }
 
@@ -4053,7 +4062,8 @@ fn new_block_ctxt(cx: @fn_ctxt, parent: block_parent, kind: block_kind,
                 mutable lpad_dirty: true,
                 mutable lpad: option::none,
                 sp: cx.sp,
-                fcx: cx};
+                fcx: cx,
+                mutable source_pos: option::none};
     alt parent {
       parent_some(cx) {
         if cx.unreachable { Unreachable(bcx); }
@@ -4097,7 +4107,8 @@ fn new_raw_block_ctxt(fcx: @fn_ctxt, llbb: BasicBlockRef) -> @block_ctxt {
           mutable lpad_dirty: true,
           mutable lpad: option::none,
           sp: fcx.sp,
-          fcx: fcx};
+          fcx: fcx,
+          mutable source_pos: option::none};
 }
 
 
@@ -4164,7 +4175,8 @@ fn llstaticallocas_block_ctxt(fcx: @fn_ctxt) -> @block_ctxt {
           mutable lpad_dirty: true,
           mutable lpad: option::none,
           sp: fcx.sp,
-          fcx: fcx};
+          fcx: fcx,
+          mutable source_pos: option::none};
 }
 
 fn llderivedtydescs_block_ctxt(fcx: @fn_ctxt) -> @block_ctxt {
@@ -4177,7 +4189,8 @@ fn llderivedtydescs_block_ctxt(fcx: @fn_ctxt) -> @block_ctxt {
           mutable lpad_dirty: true,
           mutable lpad: option::none,
           sp: fcx.sp,
-          fcx: fcx};
+          fcx: fcx,
+          mutable source_pos: option::none};
 }
 
 
@@ -4250,6 +4263,7 @@ fn trans_block(bcx: @block_ctxt, b: ast::blk) -> @block_ctxt {
 fn trans_block_dps(bcx: @block_ctxt, b: ast::blk, dest: dest)
     -> @block_ctxt {
     let bcx = bcx;
+    debuginfo::update_source_pos(bcx, b);
     block_locals(b) {|local| bcx = alloc_local(bcx, local); };
     for s: @ast::stmt in b.node.stmts {
         bcx = trans_stmt(bcx, *s);
@@ -4261,7 +4275,9 @@ fn trans_block_dps(bcx: @block_ctxt, b: ast::blk, dest: dest)
       }
       _ { assert dest == ignore || bcx.unreachable; }
     }
-    ret trans_block_cleanups(bcx, find_scope_cx(bcx));
+    let rv = trans_block_cleanups(bcx, find_scope_cx(bcx));
+    debuginfo::reset_source_pos(bcx);
+    ret rv;
 }
 
 fn new_local_ctxt(ccx: @crate_ctxt) -> @local_ctxt {
@@ -5465,6 +5481,18 @@ fn declare_intrinsics(llmod: ModuleRef) -> hashmap<str, ValueRef> {
     ret intrinsics;
 }
 
+fn declare_dbg_intrinsics(llmod: ModuleRef,
+                          intrinsics: hashmap<str, ValueRef>) {
+    let declare =
+        decl_cdecl_fn(llmod, "llvm.dbg.declare",
+                      T_fn([T_metadata(), T_metadata()], T_void()));
+    let value =
+        decl_cdecl_fn(llmod, "llvm.dbg.value",
+                      T_fn([T_metadata(), T_i64(), T_metadata()], T_void()));
+    intrinsics.insert("llvm.dbg.declare", declare);
+    intrinsics.insert("llvm.dbg.value", value);
+}
+
 fn trap(bcx: @block_ctxt) {
     let v: [ValueRef] = [];
     alt bcx_ccx(bcx).intrinsics.find("llvm.trap") {
@@ -5601,6 +5629,9 @@ fn trans_crate(sess: session::session, crate: @ast::crate, tcx: ty::ctxt,
     let td = mk_target_data(sess.get_targ_cfg().target_strs.data_layout);
     let tn = mk_type_names();
     let intrinsics = declare_intrinsics(llmod);
+    if sess.get_opts().debuginfo {
+        declare_dbg_intrinsics(llmod, intrinsics);
+    }
     let int_type = T_int(targ_cfg);
     let float_type = T_float(targ_cfg);
     let task_type = T_task(targ_cfg);
diff --git a/src/comp/middle/trans_build.rs b/src/comp/middle/trans_build.rs
index 210626deffb..26c63290405 100644
--- a/src/comp/middle/trans_build.rs
+++ b/src/comp/middle/trans_build.rs
@@ -25,14 +25,16 @@ fn RetVoid(cx: @block_ctxt) {
     if cx.unreachable { ret; }
     assert (!cx.terminated);
     cx.terminated = true;
-    llvm::LLVMBuildRetVoid(B(cx));
+    let instr = llvm::LLVMBuildRetVoid(B(cx));
+    debuginfo::add_line_info(cx, instr);
 }
 
 fn Ret(cx: @block_ctxt, V: ValueRef) {
     if cx.unreachable { ret; }
     assert (!cx.terminated);
     cx.terminated = true;
-    llvm::LLVMBuildRet(B(cx), V);
+    let instr = llvm::LLVMBuildRet(B(cx), V);
+    debuginfo::add_line_info(cx, instr);
 }
 
 fn AggregateRet(cx: @block_ctxt, RetVals: [ValueRef]) {
@@ -40,8 +42,9 @@ fn AggregateRet(cx: @block_ctxt, RetVals: [ValueRef]) {
     assert (!cx.terminated);
     cx.terminated = true;
     unsafe {
-        llvm::LLVMBuildAggregateRet(B(cx), vec::to_ptr(RetVals),
-                                    vec::len(RetVals));
+        let instr = llvm::LLVMBuildAggregateRet(B(cx), vec::to_ptr(RetVals),
+                                                vec::len(RetVals));
+        debuginfo::add_line_info(cx, instr);
     }
 }
 
@@ -49,7 +52,8 @@ fn Br(cx: @block_ctxt, Dest: BasicBlockRef) {
     if cx.unreachable { ret; }
     assert (!cx.terminated);
     cx.terminated = true;
-    llvm::LLVMBuildBr(B(cx), Dest);
+    let instr = llvm::LLVMBuildBr(B(cx), Dest);
+    debuginfo::add_line_info(cx, instr);
 }
 
 fn CondBr(cx: @block_ctxt, If: ValueRef, Then: BasicBlockRef,
@@ -57,7 +61,8 @@ fn CondBr(cx: @block_ctxt, If: ValueRef, Then: BasicBlockRef,
     if cx.unreachable { ret; }
     assert (!cx.terminated);
     cx.terminated = true;
-    llvm::LLVMBuildCondBr(B(cx), If, Then, Else);
+    let instr = llvm::LLVMBuildCondBr(B(cx), If, Then, Else);
+    debuginfo::add_line_info(cx, instr);
 }
 
 fn Switch(cx: @block_ctxt, V: ValueRef, Else: BasicBlockRef, NumCases: uint)
@@ -65,7 +70,9 @@ fn Switch(cx: @block_ctxt, V: ValueRef, Else: BasicBlockRef, NumCases: uint)
     if cx.unreachable { ret _Undef(V); }
     assert !cx.terminated;
     cx.terminated = true;
-    ret llvm::LLVMBuildSwitch(B(cx), V, Else, NumCases);
+    let instr = llvm::LLVMBuildSwitch(B(cx), V, Else, NumCases);
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn AddCase(S: ValueRef, OnVal: ValueRef, Dest: BasicBlockRef) {
@@ -77,7 +84,8 @@ fn IndirectBr(cx: @block_ctxt, Addr: ValueRef, NumDests: uint) {
     if cx.unreachable { ret; }
     assert (!cx.terminated);
     cx.terminated = true;
-    llvm::LLVMBuildIndirectBr(B(cx), Addr, NumDests);
+    let instr = llvm::LLVMBuildIndirectBr(B(cx), Addr, NumDests);
+    debuginfo::add_line_info(cx, instr);
 }
 
 // This is a really awful way to get a zero-length c-string, but better (and a
@@ -93,8 +101,10 @@ fn Invoke(cx: @block_ctxt, Fn: ValueRef, Args: [ValueRef],
     assert (!cx.terminated);
     cx.terminated = true;
     unsafe {
-        llvm::LLVMBuildInvoke(B(cx), Fn, vec::to_ptr(Args),
-                              vec::len(Args), Then, Catch, noname());
+        let instr = llvm::LLVMBuildInvoke(B(cx), Fn, vec::to_ptr(Args),
+                                          vec::len(Args), Then, Catch,
+                                          noname());
+        debuginfo::add_line_info(cx, instr);
     }
 }
 
@@ -107,6 +117,7 @@ fn FastInvoke(cx: @block_ctxt, Fn: ValueRef, Args: [ValueRef],
         let v = llvm::LLVMBuildInvoke(B(cx), Fn, vec::to_ptr(Args),
                                       vec::len(Args), Then, Catch, noname());
         llvm::LLVMSetInstructionCallConv(v, lib::llvm::LLVMFastCallConv);
+        debuginfo::add_line_info(cx, v);
     }
 }
 
@@ -123,183 +134,254 @@ fn _Undef(val: ValueRef) -> ValueRef {
 /* Arithmetic */
 fn Add(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildAdd(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildAdd(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn NSWAdd(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildNSWAdd(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildNSWAdd(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn NUWAdd(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildNUWAdd(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildNUWAdd(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn FAdd(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildFAdd(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildFAdd(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn Sub(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildSub(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildSub(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn NSWSub(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildNSWSub(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildNSWSub(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn NUWSub(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildNUWSub(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildNUWSub(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn FSub(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildFSub(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildFSub(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn Mul(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildMul(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildMul(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn NSWMul(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildNSWMul(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildNSWMul(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn NUWMul(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildNUWMul(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildNUWMul(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn FMul(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildFMul(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildFMul(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn UDiv(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildUDiv(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildUDiv(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn SDiv(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildSDiv(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildSDiv(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn ExactSDiv(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildExactSDiv(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildExactSDiv(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn FDiv(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildFDiv(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildFDiv(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn URem(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildURem(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildURem(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn SRem(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildSRem(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildSRem(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn FRem(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildFRem(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildFRem(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn Shl(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildShl(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildShl(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn LShr(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildLShr(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildLShr(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn AShr(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildAShr(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildAShr(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn And(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildAnd(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildAnd(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn Or(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildOr(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildOr(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn Xor(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildXor(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildXor(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn BinOp(cx: @block_ctxt, Op: Opcode, LHS: ValueRef, RHS: ValueRef) ->
    ValueRef {
     if cx.unreachable { ret _Undef(LHS); }
-    ret llvm::LLVMBuildBinOp(B(cx), Op, LHS, RHS, noname());
+    let instr = llvm::LLVMBuildBinOp(B(cx), Op, LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn Neg(cx: @block_ctxt, V: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(V); }
-    ret llvm::LLVMBuildNeg(B(cx), V, noname());
+    let instr = llvm::LLVMBuildNeg(B(cx), V, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn NSWNeg(cx: @block_ctxt, V: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(V); }
-    ret llvm::LLVMBuildNSWNeg(B(cx), V, noname());
+    let instr = llvm::LLVMBuildNSWNeg(B(cx), V, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn NUWNeg(cx: @block_ctxt, V: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(V); }
-    ret llvm::LLVMBuildNUWNeg(B(cx), V, noname());
+    let instr = llvm::LLVMBuildNUWNeg(B(cx), V, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 fn FNeg(cx: @block_ctxt, V: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(V); }
-    ret llvm::LLVMBuildFNeg(B(cx), V, noname());
+    let instr = llvm::LLVMBuildFNeg(B(cx), V, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn Not(cx: @block_ctxt, V: ValueRef) -> ValueRef {
     if cx.unreachable { ret _Undef(V); }
-    ret llvm::LLVMBuildNot(B(cx), V, noname());
+    let instr = llvm::LLVMBuildNot(B(cx), V, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 /* Memory */
 fn Malloc(cx: @block_ctxt, Ty: TypeRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(T_i8())); }
-    ret llvm::LLVMBuildMalloc(B(cx), Ty, noname());
+    let instr = llvm::LLVMBuildMalloc(B(cx), Ty, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn ArrayMalloc(cx: @block_ctxt, Ty: TypeRef, Val: ValueRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(T_i8())); }
-    ret llvm::LLVMBuildArrayMalloc(B(cx), Ty, Val, noname());
+    let instr = llvm::LLVMBuildArrayMalloc(B(cx), Ty, Val, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn Alloca(cx: @block_ctxt, Ty: TypeRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(Ty)); }
-    ret llvm::LLVMBuildAlloca(B(cx), Ty, noname());
+    let instr = llvm::LLVMBuildAlloca(B(cx), Ty, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn ArrayAlloca(cx: @block_ctxt, Ty: TypeRef, Val: ValueRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(Ty)); }
-    ret llvm::LLVMBuildArrayAlloca(B(cx), Ty, Val, noname());
+    let instr = llvm::LLVMBuildArrayAlloca(B(cx), Ty, Val, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn Free(cx: @block_ctxt, PointerVal: ValueRef) {
     if cx.unreachable { ret; }
-    llvm::LLVMBuildFree(B(cx), PointerVal);
+    let instr = llvm::LLVMBuildFree(B(cx), PointerVal);
+    debuginfo::add_line_info(cx, instr);
 }
 
 fn Load(cx: @block_ctxt, PointerVal: ValueRef) -> ValueRef {
@@ -310,19 +392,24 @@ fn Load(cx: @block_ctxt, PointerVal: ValueRef) -> ValueRef {
             llvm::LLVMGetElementType(ty) } else { ccx.int_type };
         ret llvm::LLVMGetUndef(eltty);
     }
-    ret llvm::LLVMBuildLoad(B(cx), PointerVal, noname());
+    let instr = llvm::LLVMBuildLoad(B(cx), PointerVal, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn Store(cx: @block_ctxt, Val: ValueRef, Ptr: ValueRef) {
     if cx.unreachable { ret; }
-    llvm::LLVMBuildStore(B(cx), Val, Ptr);
+    let instr = llvm::LLVMBuildStore(B(cx), Val, Ptr);
+    debuginfo::add_line_info(cx, instr);
 }
 
 fn GEP(cx: @block_ctxt, Pointer: ValueRef, Indices: [ValueRef]) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(T_nil())); }
     unsafe {
-        ret llvm::LLVMBuildGEP(B(cx), Pointer, vec::to_ptr(Indices),
-                               vec::len(Indices), noname());
+        let instr = llvm::LLVMBuildGEP(B(cx), Pointer, vec::to_ptr(Indices),
+                                       vec::len(Indices), noname());
+        debuginfo::add_line_info(cx, instr);
+        ret instr;
     }
 }
 
@@ -338,142 +425,195 @@ fn InBoundsGEP(cx: @block_ctxt, Pointer: ValueRef, Indices: [ValueRef]) ->
    ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(T_nil())); }
     unsafe {
-        ret llvm::LLVMBuildInBoundsGEP(B(cx), Pointer, vec::to_ptr(Indices),
-                                       vec::len(Indices), noname());
+        let v = llvm::LLVMBuildInBoundsGEP(B(cx), Pointer,
+                                           vec::to_ptr(Indices),
+                                           vec::len(Indices), noname());
+        debuginfo::add_line_info(cx, v);
+        ret v;
     }
 }
 
 fn StructGEP(cx: @block_ctxt, Pointer: ValueRef, Idx: uint) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(T_nil())); }
-    ret llvm::LLVMBuildStructGEP(B(cx), Pointer, Idx, noname());
+    let instr = llvm::LLVMBuildStructGEP(B(cx), Pointer, Idx, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn GlobalString(cx: @block_ctxt, _Str: sbuf) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(T_i8())); }
-    ret llvm::LLVMBuildGlobalString(B(cx), _Str, noname());
+    let instr = llvm::LLVMBuildGlobalString(B(cx), _Str, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn GlobalStringPtr(cx: @block_ctxt, _Str: sbuf) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(T_i8())); }
-    ret llvm::LLVMBuildGlobalStringPtr(B(cx), _Str, noname());
+    let instr = llvm::LLVMBuildGlobalStringPtr(B(cx), _Str, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 /* Casts */
 fn Trunc(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
-    ret llvm::LLVMBuildTrunc(B(cx), Val, DestTy, noname());
+    let instr = llvm::LLVMBuildTrunc(B(cx), Val, DestTy, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn ZExt(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
-    ret llvm::LLVMBuildZExt(B(cx), Val, DestTy, noname());
+    let instr = llvm::LLVMBuildZExt(B(cx), Val, DestTy, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn SExt(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
-    ret llvm::LLVMBuildSExt(B(cx), Val, DestTy, noname());
+    let instr = llvm::LLVMBuildSExt(B(cx), Val, DestTy, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn FPToUI(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
-    ret llvm::LLVMBuildFPToUI(B(cx), Val, DestTy, noname());
+    let instr = llvm::LLVMBuildFPToUI(B(cx), Val, DestTy, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn FPToSI(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
-    ret llvm::LLVMBuildFPToSI(B(cx), Val, DestTy, noname());
+    let instr = llvm::LLVMBuildFPToSI(B(cx), Val, DestTy, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn UIToFP(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
-    ret llvm::LLVMBuildUIToFP(B(cx), Val, DestTy, noname());
+    let instr = llvm::LLVMBuildUIToFP(B(cx), Val, DestTy, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn SIToFP(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
-    ret llvm::LLVMBuildSIToFP(B(cx), Val, DestTy, noname());
+    let instr = llvm::LLVMBuildSIToFP(B(cx), Val, DestTy, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn FPTrunc(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
-    ret llvm::LLVMBuildFPTrunc(B(cx), Val, DestTy, noname());
+    let instr = llvm::LLVMBuildFPTrunc(B(cx), Val, DestTy, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn FPExt(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
-    ret llvm::LLVMBuildFPExt(B(cx), Val, DestTy, noname());
+    let instr = llvm::LLVMBuildFPExt(B(cx), Val, DestTy, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn PtrToInt(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
-    ret llvm::LLVMBuildPtrToInt(B(cx), Val, DestTy, noname());
+    let instr = llvm::LLVMBuildPtrToInt(B(cx), Val, DestTy, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn IntToPtr(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
-    ret llvm::LLVMBuildIntToPtr(B(cx), Val, DestTy, noname());
+    let instr = llvm::LLVMBuildIntToPtr(B(cx), Val, DestTy, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn BitCast(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
-    ret llvm::LLVMBuildBitCast(B(cx), Val, DestTy, noname());
+    let instr = llvm::LLVMBuildBitCast(B(cx), Val, DestTy, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn ZExtOrBitCast(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) ->
    ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
-    ret llvm::LLVMBuildZExtOrBitCast(B(cx), Val, DestTy, noname());
+    let instr = llvm::LLVMBuildZExtOrBitCast(B(cx), Val, DestTy, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn SExtOrBitCast(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) ->
    ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
-    ret llvm::LLVMBuildSExtOrBitCast(B(cx), Val, DestTy, noname());
+    let instr = llvm::LLVMBuildSExtOrBitCast(B(cx), Val, DestTy, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn TruncOrBitCast(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) ->
    ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
-    ret llvm::LLVMBuildTruncOrBitCast(B(cx), Val, DestTy, noname());
+    let instr = llvm::LLVMBuildTruncOrBitCast(B(cx), Val, DestTy, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn Cast(cx: @block_ctxt, Op: Opcode, Val: ValueRef, DestTy: TypeRef,
         _Name: sbuf) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
-    ret llvm::LLVMBuildCast(B(cx), Op, Val, DestTy, noname());
+    let instr = llvm::LLVMBuildCast(B(cx), Op, Val, DestTy, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn PointerCast(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
-    ret llvm::LLVMBuildPointerCast(B(cx), Val, DestTy, noname());
+    let instr = llvm::LLVMBuildPointerCast(B(cx), Val, DestTy, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn IntCast(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
-    ret llvm::LLVMBuildIntCast(B(cx), Val, DestTy, noname());
+    let instr = llvm::LLVMBuildIntCast(B(cx), Val, DestTy, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn FPCast(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
-    ret llvm::LLVMBuildFPCast(B(cx), Val, DestTy, noname());
+    let instr = llvm::LLVMBuildFPCast(B(cx), Val, DestTy, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 
 /* Comparisons */
 fn ICmp(cx: @block_ctxt, Op: uint, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(T_i1()); }
-    ret llvm::LLVMBuildICmp(B(cx), Op, LHS, RHS, noname());
+    let instr = llvm::LLVMBuildICmp(B(cx), Op, LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn FCmp(cx: @block_ctxt, Op: uint, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(T_i1()); }
-    ret llvm::LLVMBuildFCmp(B(cx), Op, LHS, RHS, noname());
+    let instr = llvm::LLVMBuildFCmp(B(cx), Op, LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 /* Miscellaneous instructions */
 fn EmptyPhi(cx: @block_ctxt, Ty: TypeRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(Ty); }
-    ret llvm::LLVMBuildPhi(B(cx), Ty, noname());
+    let instr = llvm::LLVMBuildPhi(B(cx), Ty, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn Phi(cx: @block_ctxt, Ty: TypeRef, vals: [ValueRef], bbs: [BasicBlockRef])
@@ -528,8 +668,10 @@ fn add_comment(bcx: @block_ctxt, text: str) {
 fn Call(cx: @block_ctxt, Fn: ValueRef, Args: [ValueRef]) -> ValueRef {
     if cx.unreachable { ret _UndefReturn(cx, Fn); }
     unsafe {
-        ret llvm::LLVMBuildCall(B(cx), Fn, vec::to_ptr(Args),
-                                vec::len(Args), noname());
+        let instr = llvm::LLVMBuildCall(B(cx), Fn, vec::to_ptr(Args),
+                                        vec::len(Args), noname());
+        debuginfo::add_line_info(cx, instr);
+        ret instr;
     }
 }
 
@@ -539,6 +681,7 @@ fn FastCall(cx: @block_ctxt, Fn: ValueRef, Args: [ValueRef]) -> ValueRef {
         let v = llvm::LLVMBuildCall(B(cx), Fn, vec::to_ptr(Args),
                                     vec::len(Args), noname());
         llvm::LLVMSetInstructionCallConv(v, lib::llvm::LLVMFastCallConv);
+        debuginfo::add_line_info(cx, v);
         ret v;
     }
 }
@@ -550,6 +693,7 @@ fn CallWithConv(cx: @block_ctxt, Fn: ValueRef, Args: [ValueRef], Conv: uint)
         let v = llvm::LLVMBuildCall(B(cx), Fn, vec::to_ptr(Args),
                                     vec::len(Args), noname());
         llvm::LLVMSetInstructionCallConv(v, Conv);
+        debuginfo::add_line_info(cx, v);
         ret v;
     }
 }
@@ -557,57 +701,76 @@ fn CallWithConv(cx: @block_ctxt, Fn: ValueRef, Args: [ValueRef], Conv: uint)
 fn Select(cx: @block_ctxt, If: ValueRef, Then: ValueRef, Else: ValueRef) ->
    ValueRef {
     if cx.unreachable { ret _Undef(Then); }
-    ret llvm::LLVMBuildSelect(B(cx), If, Then, Else, noname());
+    let instr = llvm::LLVMBuildSelect(B(cx), If, Then, Else, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn VAArg(cx: @block_ctxt, list: ValueRef, Ty: TypeRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(Ty); }
-    ret llvm::LLVMBuildVAArg(B(cx), list, Ty, noname());
+    let instr = llvm::LLVMBuildVAArg(B(cx), list, Ty, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn ExtractElement(cx: @block_ctxt, VecVal: ValueRef, Index: ValueRef) ->
    ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(T_nil()); }
-    ret llvm::LLVMBuildExtractElement(B(cx), VecVal, Index, noname());
+    let instr = llvm::LLVMBuildExtractElement(B(cx), VecVal, Index, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn InsertElement(cx: @block_ctxt, VecVal: ValueRef, EltVal: ValueRef,
                  Index: ValueRef) {
     if cx.unreachable { ret; }
-    llvm::LLVMBuildInsertElement(B(cx), VecVal, EltVal, Index, noname());
+    let instr = llvm::LLVMBuildInsertElement(B(cx), VecVal, EltVal, Index,
+                                             noname());
+    debuginfo::add_line_info(cx, instr);
 }
 
 fn ShuffleVector(cx: @block_ctxt, V1: ValueRef, V2: ValueRef,
                  Mask: ValueRef) {
     if cx.unreachable { ret; }
-    llvm::LLVMBuildShuffleVector(B(cx), V1, V2, Mask, noname());
+    let instr = llvm::LLVMBuildShuffleVector(B(cx), V1, V2, Mask, noname());
+    debuginfo::add_line_info(cx, instr);
 }
 
 fn ExtractValue(cx: @block_ctxt, AggVal: ValueRef, Index: uint) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(T_nil()); }
-    ret llvm::LLVMBuildExtractValue(B(cx), AggVal, Index, noname());
+    let instr = llvm::LLVMBuildExtractValue(B(cx), AggVal, Index, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn InsertValue(cx: @block_ctxt, AggVal: ValueRef, EltVal: ValueRef,
                Index: uint) {
     if cx.unreachable { ret; }
-    llvm::LLVMBuildInsertValue(B(cx), AggVal, EltVal, Index, noname());
+    let instr = llvm::LLVMBuildInsertValue(B(cx), AggVal, EltVal, Index,
+                                           noname());
+    debuginfo::add_line_info(cx, instr);
 }
 
 fn IsNull(cx: @block_ctxt, Val: ValueRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(T_i1()); }
-    ret llvm::LLVMBuildIsNull(B(cx), Val, noname());
+    let instr = llvm::LLVMBuildIsNull(B(cx), Val, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn IsNotNull(cx: @block_ctxt, Val: ValueRef) -> ValueRef {
     if cx.unreachable { ret llvm::LLVMGetUndef(T_i1()); }
-    ret llvm::LLVMBuildIsNotNull(B(cx), Val, noname());
+    let instr = llvm::LLVMBuildIsNotNull(B(cx), Val, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn PtrDiff(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
     let ccx = cx.fcx.lcx.ccx;
     if cx.unreachable { ret llvm::LLVMGetUndef(ccx.int_type); }
-    ret llvm::LLVMBuildPtrDiff(B(cx), LHS, RHS, noname());
+    let instr = llvm::LLVMBuildPtrDiff(B(cx), LHS, RHS, noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn Trap(cx: @block_ctxt) {
@@ -622,15 +785,19 @@ fn Trap(cx: @block_ctxt) {
     assert (T as int != 0);
     let Args: [ValueRef] = [];
     unsafe {
-        llvm::LLVMBuildCall(b, T, vec::to_ptr(Args),
-                            vec::len(Args), noname());
+        let instr = llvm::LLVMBuildCall(b, T, vec::to_ptr(Args),
+                                        vec::len(Args), noname());
+        debuginfo::add_line_info(cx, instr);
     }
 }
 
 fn LandingPad(cx: @block_ctxt, Ty: TypeRef, PersFn: ValueRef,
               NumClauses: uint) -> ValueRef {
     assert !cx.terminated && !cx.unreachable;
-    ret llvm::LLVMBuildLandingPad(B(cx), Ty, PersFn, NumClauses, noname());
+    let instr = llvm::LLVMBuildLandingPad(B(cx), Ty, PersFn, NumClauses,
+                                          noname());
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 fn SetCleanup(_cx: @block_ctxt, LandingPad: ValueRef) {
@@ -640,7 +807,9 @@ fn SetCleanup(_cx: @block_ctxt, LandingPad: ValueRef) {
 fn Resume(cx: @block_ctxt, Exn: ValueRef) -> ValueRef {
     assert (!cx.terminated);
     cx.terminated = true;
-    ret llvm::LLVMBuildResume(B(cx), Exn);
+    let instr = llvm::LLVMBuildResume(B(cx), Exn);
+    debuginfo::add_line_info(cx, instr);
+    ret instr;
 }
 
 //
diff --git a/src/comp/middle/trans_common.rs b/src/comp/middle/trans_common.rs
index a4f0a6dfe3d..98c4b497ab5 100644
--- a/src/comp/middle/trans_common.rs
+++ b/src/comp/middle/trans_common.rs
@@ -385,7 +385,8 @@ type block_ctxt =
      mutable lpad_dirty: bool,
      mutable lpad: option::t<BasicBlockRef>,
      sp: span,
-     fcx: @fn_ctxt};
+     fcx: @fn_ctxt,
+     mutable source_pos: option::t<syntax::codemap::loc>};
 
 // FIXME: we should be able to use option::t<@block_parent> here but
 // the infinite-tag check in rustboot gets upset.
@@ -464,6 +465,8 @@ fn T_nil() -> TypeRef {
     ret llvm::LLVMInt1Type();
 }
 
+fn T_metadata() -> TypeRef { ret llvm::LLVMMetadataType(); }
+
 fn T_i1() -> TypeRef { ret llvm::LLVMInt1Type(); }
 
 fn T_i8() -> TypeRef { ret llvm::LLVMInt8Type(); }
@@ -801,6 +804,10 @@ fn C_i32(i: i32) -> ValueRef {
     ret C_integral(T_i32(), i as u64, True);
 }
 
+fn C_i64(i: i64) -> ValueRef {
+    ret C_integral(T_i64(), i as uint, True);
+}
+
 fn C_int(cx: @crate_ctxt, i: int) -> ValueRef {
     ret C_integral(cx.int_type, i as u64, True);
 }