diff --git a/src/comp/lib/llvm.rs b/src/comp/lib/llvm.rs index 9ff014699abc..b090fb74644a 100644 --- a/src/comp/lib/llvm.rs +++ b/src/comp/lib/llvm.rs @@ -256,6 +256,7 @@ native mod llvm { /* Operations on Users */ fn LLVMGetOperand(Val: ValueRef, Index: uint) -> ValueRef; + fn LLVMSetOperand(Val: ValueRef, Index: uint, Op: ValueRef); /* Operations on constants of any type */ fn LLVMConstNull(Ty: TypeRef) -> ValueRef; @@ -275,6 +276,8 @@ native mod llvm { fn LLVMMDNodeInContext(C: ContextRef, Vals: *ValueRef, Count: uint) -> ValueRef; fn LLVMMDNode(Vals: *ValueRef, Count: uint) -> ValueRef; + fn LLVMAddNamedMetadataOperand(M: ModuleRef, Str: sbuf, SLen: uint, + Val: ValueRef); /* Operations on scalar constants */ fn LLVMConstInt(IntTy: TypeRef, N: ULongLong, SignExtend: Bool) -> diff --git a/src/comp/middle/debuginfo.rs b/src/comp/middle/debuginfo.rs new file mode 100644 index 000000000000..72390a23383d --- /dev/null +++ b/src/comp/middle/debuginfo.rs @@ -0,0 +1,192 @@ +import std::{vec, str, map, option, unsafe}; +import std::vec::to_ptr; +import std::map::hashmap; +import lib::llvm::llvm; +import lib::llvm::llvm::{ModuleRef, ValueRef}; +import middle::trans_common::*; +import syntax::{ast, codemap}; + +const LLVMDebugVersion: int = 0x80000; + +const DW_LANG_RUST: int = 0x9000; +const DW_VIRTUALITY_none: int = 0; + +const CompileUnitTag: int = 17; +const FileDescriptorTag: int = 41; +const SubprogramTag: int = 46; + +fn as_buf(s: str) -> str::sbuf { + str::as_buf(s, {|sbuf| sbuf}) +} +fn llstr(s: str) -> ValueRef { + llvm::LLVMMDString(as_buf(s), str::byte_len(s)) +} + +fn lltag(lltag: int) -> ValueRef { + lli32(0x80000 + lltag) +} +fn lli32(val: int) -> ValueRef { + C_i32(val as i32) +} +fn lli1(bval: bool) -> ValueRef { + C_bool(bval) +} +fn llmdnode(elems: [ValueRef]) -> ValueRef unsafe { + llvm::LLVMMDNode(vec::unsafe::to_ptr(elems), + vec::len(elems)) +} +fn llunused() -> ValueRef { + lli32(0x0) +} + +fn update_cache(cache: metadata_cache, mdtag: int, val: debug_metadata) { + let existing = if cache.contains_key(mdtag) { + cache.get(mdtag) + } else { + [] + }; + cache.insert(mdtag, existing + [val]); +} + +//////////////// + +type metadata = {node: ValueRef, data: T}; + +type file_md = {path: str}; +type compile_unit_md = {path: str}; +type subprogram_md = {name: str, file: str}; + +type metadata_cache = hashmap; + +tag debug_metadata { + file_metadata(@metadata); + compile_unit_metadata(@metadata); + subprogram_metadata(@metadata); +} + +fn md_from_metadata(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) } + } +} + +fn cached_metadata(cache: metadata_cache, mdtag: int, + eq: block(md: T) -> bool) -> option::t { + if cache.contains_key(mdtag) { + let items = cache.get(mdtag); + for item in items { + let md: T = md_from_metadata::(item); + if eq(md) { + ret option::some(md); + } + } + } + ret option::none; +} + +fn get_compile_unit_metadata(cx: @crate_ctxt, full_path: str) + -> @metadata { + let cache = cx.llmetadata; + alt cached_metadata::<@metadata>(cache, CompileUnitTag, + {|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_metadata = [lltag(CompileUnitTag), + llunused(), + lli32(DW_LANG_RUST), + llstr(fname), + llstr(path), + llstr(#env["CFG_VERSION"]), + lli1(false), // main compile unit + lli1(cx.sess.get_opts().optimize != 0u), + llstr(""), // flags (???) + lli32(0) // runtime version (???) + // list of enum types + // list of retained values + // list of subprograms + // list of global variables + ]; + let unit_node = llmdnode(unit_metadata); + llvm::LLVMAddNamedMetadataOperand(cx.llmod, as_buf("llvm.dbg.cu"), + str::byte_len("llvm.dbg.cu"), + unit_node); + let mdval = @{node: unit_node, data: {path: full_path}}; + update_cache(cache, CompileUnitTag, compile_unit_metadata(mdval)); + 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 { + let cache = cx.llmetadata; + alt cached_metadata::<@metadata>( + 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 file_md = [lltag(FileDescriptorTag), + llstr(fname), + llstr(path), + unit_node]; + let val = llmdnode(file_md); + let mdval = @{node: val, data: {path: full_path}}; + update_cache(cache, FileDescriptorTag, file_metadata(mdval)); + ret mdval; +} + +fn get_function_metadata(cx: @crate_ctxt, item: @ast::item, + llfndecl: ValueRef) -> @metadata { + let cache = cx.llmetadata; + alt cached_metadata::<@metadata>( + cache, SubprogramTag, {|md| md.data.name == item.ident && + /*sub.path == ??*/ true}) { + option::some(md) { ret md; } + option::none. {} + } + 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; +} \ No newline at end of file diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index acec464e6f8e..6985ef974e60 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -20,7 +20,7 @@ import std::map::{new_int_hash, new_str_hash}; import option::{some, none}; import driver::session; import front::attr; -import middle::{ty, gc, resolve}; +import middle::{ty, gc, resolve, debuginfo}; import middle::freevars::*; import back::{link, abi, upcall}; import syntax::{ast, ast_util}; @@ -4543,6 +4543,12 @@ fn trans_fn(cx: @local_ctxt, sp: span, f: ast::_fn, llfndecl: ValueRef, let do_time = cx.ccx.sess.get_opts().stats; let start = do_time ? time::get_time() : {sec: 0u32, usec: 0u32}; trans_closure(cx, sp, f, llfndecl, ty_self, ty_params, id, {|_fcx|}); + if cx.ccx.sess.get_opts().debuginfo { + let item = alt option::get(cx.ccx.ast_map.find(id)) { + ast_map::node_item(item) { item } + }; + debuginfo::get_function_metadata(cx.ccx, item, llfndecl); + } if do_time { let end = time::get_time(); log_fn_time(cx.ccx, str::connect(cx.path, "::"), start, end); @@ -5659,7 +5665,8 @@ fn trans_crate(sess: session::session, crate: @ast::crate, tcx: ty::ctxt, builder: BuilderRef_res(llvm::LLVMCreateBuilder()), shape_cx: shape::mk_ctxt(llmod), gc_cx: gc::mk_ctxt(), - crate_map: crate_map}; + crate_map: crate_map, + llmetadata: map::new_int_hash()}; let cx = new_local_ctxt(ccx); collect_items(ccx, crate); collect_tag_ctors(ccx, crate); diff --git a/src/comp/middle/trans_common.rs b/src/comp/middle/trans_common.rs index 19f439235e42..a4f0a6dfe3de 100644 --- a/src/comp/middle/trans_common.rs +++ b/src/comp/middle/trans_common.rs @@ -116,7 +116,8 @@ type crate_ctxt = builder: BuilderRef_res, shape_cx: shape::ctxt, gc_cx: gc::ctxt, - crate_map: ValueRef}; + crate_map: ValueRef, + llmetadata: debuginfo::metadata_cache}; type local_ctxt = {path: [str], diff --git a/src/comp/rustc.rc b/src/comp/rustc.rc index 51a01378fb3a..9f88b369749a 100644 --- a/src/comp/rustc.rc +++ b/src/comp/rustc.rc @@ -37,6 +37,7 @@ mod middle { mod freevars; mod shape; mod gc; + mod debuginfo; mod tstate { mod ck; diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index d50cc527dfd1..ad7fec1586b4 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -165,3 +165,17 @@ extern "C" LLVMValueRef LLVMGetOrInsertFunction(LLVMModuleRef M, return wrap(unwrap(M)->getOrInsertFunction(Name, unwrap(FunctionTy))); } + +extern "C" LLVMTypeRef LLVMMetadataTypeInContext(LLVMContextRef C) { + return wrap(Type::getMetadataTy(*unwrap(C))); +} +extern "C" LLVMTypeRef LLVMMetadataType(void) { + return LLVMMetadataTypeInContext(LLVMGetGlobalContext()); +} + +extern "C" void LLVMAddNamedMetadataOperand(LLVMModuleRef M, const char *Str, + unsigned SLen, LLVMValueRef Val) +{ + NamedMDNode *N = unwrap(M)->getOrInsertNamedMetadata(StringRef(Str, SLen)); + N->addOperand(unwrap(Val)); +} diff --git a/src/rustllvm/rustllvm.def.in b/src/rustllvm/rustllvm.def.in index 381c5293eb73..1a34f62f7a06 100644 --- a/src/rustllvm/rustllvm.def.in +++ b/src/rustllvm/rustllvm.def.in @@ -487,6 +487,7 @@ LLVMMDNode LLVMMDNodeInContext LLVMMDString LLVMMDStringInContext +LLVMAddNamedMetadataOperand LLVMModuleCreateWithName LLVMModuleCreateWithNameInContext LLVMMoveBasicBlockAfter