diff --git a/src/comp/driver/rustc.rs b/src/comp/driver/rustc.rs index 5bb7e60a6849c..8e293e98f0d04 100644 --- a/src/comp/driver/rustc.rs +++ b/src/comp/driver/rustc.rs @@ -421,7 +421,8 @@ fn build_session_options(match: getopts::match) let libcore = !opt_present(match, "no-core"); let verify = !opt_present(match, "no-verify"); let save_temps = opt_present(match, "save-temps"); - let debuginfo = opt_present(match, "g"); + let extra_debuginfo = opt_present(match, "xg"); + let debuginfo = opt_present(match, "g") || extra_debuginfo; let stats = opt_present(match, "stats"); let time_passes = opt_present(match, "time-passes"); let time_llvm_passes = opt_present(match, "time-llvm-passes"); @@ -468,6 +469,7 @@ fn build_session_options(match: getopts::match) libcore: libcore, optimize: opt_level, debuginfo: debuginfo, + extra_debuginfo: extra_debuginfo, verify: verify, save_temps: save_temps, stats: stats, @@ -487,7 +489,7 @@ fn build_session_options(match: getopts::match) ret sopts; } -fn build_session(sopts: @session::options) -> session::session { +fn build_session(sopts: @session::options, input: str) -> session::session { let target_cfg = build_target_config(sopts); let cstore = cstore::mk_cstore(); let filesearch = filesearch::mk_filesearch( @@ -496,7 +498,7 @@ fn build_session(sopts: @session::options) -> session::session { sopts.addl_lib_search_paths); ret session::session(target_cfg, sopts, cstore, @{cm: codemap::new_codemap(), mutable next_id: 1}, - none, 0u, filesearch, false); + none, 0u, filesearch, false, fs::dirname(input)); } fn parse_pretty(sess: session::session, &&name: str) -> pp_mode { @@ -516,7 +518,7 @@ fn opts() -> [getopts::opt] { optflag("emit-llvm"), optflagopt("pretty"), optflag("ls"), optflag("parse-only"), optflag("no-trans"), optflag("O"), optopt("opt-level"), optmulti("L"), optflag("S"), - optopt("o"), optopt("out-dir"), + optopt("o"), optopt("out-dir"), optflag("xg"), optflag("c"), optflag("g"), optflag("save-temps"), optopt("sysroot"), optopt("target"), optflag("stats"), optflag("time-passes"), optflag("time-llvm-passes"), @@ -642,7 +644,7 @@ fn main(args: [str]) { }; let sopts = build_session_options(match); - let sess = build_session(sopts); + let sess = build_session(sopts, ifile); let odir = getopts::opt_maybe_str(match, "out-dir"); let ofile = getopts::opt_maybe_str(match, "o"); let cfg = build_configuration(sess, binary, ifile); @@ -674,7 +676,7 @@ mod test { ok(m) { m } }; let sessopts = build_session_options(match); - let sess = build_session(sessopts); + let sess = build_session(sessopts, ""); let cfg = build_configuration(sess, "whatever", "whatever"); assert (attr::contains_name(cfg, "test")); } @@ -688,7 +690,7 @@ mod test { ok(m) { m } }; let sessopts = build_session_options(match); - let sess = build_session(sessopts); + let sess = build_session(sessopts, ""); let cfg = build_configuration(sess, "whatever", "whatever"); let test_items = attr::find_meta_items_by_name(cfg, "test"); assert (vec::len(test_items) == 1u); diff --git a/src/comp/driver/session.rs b/src/comp/driver/session.rs index 586dce38df969..1bedbf87f7eab 100644 --- a/src/comp/driver/session.rs +++ b/src/comp/driver/session.rs @@ -31,6 +31,7 @@ type options = libcore: bool, optimize: uint, debuginfo: bool, + extra_debuginfo: bool, verify: bool, save_temps: bool, stats: bool, @@ -59,7 +60,8 @@ obj session(targ_cfg: @config, mutable main_fn: option::t, mutable err_count: uint, filesearch: filesearch::filesearch, - mutable building_library: bool) { + mutable building_library: bool, + working_dir: str) { fn get_targ_cfg() -> @config { ret targ_cfg; } fn get_opts() -> @options { ret opts; } fn get_cstore() -> metadata::cstore::cstore { cstore } @@ -122,6 +124,9 @@ obj session(targ_cfg: @config, fn set_building_library(crate: @ast::crate) { building_library = session::building_library(opts.crate_type, crate); } + fn get_working_dir() -> str { + ret working_dir; + } } fn building_library(req_crate_type: crate_type, crate: @ast::crate) -> bool { diff --git a/src/comp/lib/llvm.rs b/src/comp/lib/llvm.rs index 9ff014699abcc..c0fe027f39b4f 100644 --- a/src/comp/lib/llvm.rs +++ b/src/comp/lib/llvm.rs @@ -234,9 +234,11 @@ native mod llvm { /* Operations on other types */ fn LLVMVoidTypeInContext(C: ContextRef) -> TypeRef; fn LLVMLabelTypeInContext(C: ContextRef) -> TypeRef; + fn LLVMMetadataTypeInContext(C: ContextRef) -> TypeRef; fn LLVMVoidType() -> TypeRef; fn LLVMLabelType() -> TypeRef; + fn LLVMMetadataType() -> TypeRef; /* Operations on all values */ fn LLVMTypeOf(Val: ValueRef) -> TypeRef; @@ -256,6 +258,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 +278,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/ast_map.rs b/src/comp/middle/ast_map.rs index 78e3da6b03037..0d0eb08698961 100644 --- a/src/comp/middle/ast_map.rs +++ b/src/comp/middle/ast_map.rs @@ -7,6 +7,7 @@ import syntax::{visit, codemap}; tag ast_node { node_item(@item); node_obj_ctor(@item); + node_obj_method(@method); node_native_item(@native_item); node_method(@method); node_expr(@expr); @@ -14,6 +15,7 @@ tag ast_node { // order they are introduced. node_arg(arg, uint); node_local(uint); + node_res_ctor(@item); } type map = std::map::hashmap; @@ -63,10 +65,19 @@ fn map_arm(cx: ctx, arm: arm) { fn map_item(cx: ctx, i: @item) { cx.map.insert(i.id, node_item(i)); alt i.node { - item_obj(_, _, ctor_id) { cx.map.insert(ctor_id, node_obj_ctor(i)); } + item_obj(ob, _, ctor_id) { + cx.map.insert(ctor_id, node_obj_ctor(i)); + for m in ob.methods { + cx.map.insert(m.node.id, node_obj_method(m)); + } + } item_impl(_, _, ms) { for m in ms { cx.map.insert(m.node.id, node_method(m)); } } + item_res(_, dtor_id, _, ctor_id) { + cx.map.insert(ctor_id, node_res_ctor(i)); + cx.map.insert(dtor_id, node_item(i)); + } _ { } } } diff --git a/src/comp/middle/debuginfo.rs b/src/comp/middle/debuginfo.rs new file mode 100644 index 0000000000000..c11f65dc34f10 --- /dev/null +++ b/src/comp/middle/debuginfo.rs @@ -0,0 +1,804 @@ +import core::{vec, str, option, sys, ctypes, unsafe}; +import std::fs; +import std::map::hashmap; +import lib::llvm::llvm; +import lib::llvm::llvm::ValueRef; +import middle::trans_common::*; +import middle::trans_build::B; +import middle::ty; +import syntax::{ast, codemap}; +import ast::ty; +import util::ppaux::ty_to_str; + +export create_local_var; +export create_function; +export create_arg; +export update_source_pos; +export debug_ctxt; + +const LLVMDebugVersion: int = (9 << 16); + +const DW_LANG_RUST: int = 0x9000; +const DW_VIRTUALITY_none: int = 0; + +const CompileUnitTag: int = 17; +const FileDescriptorTag: int = 41; +const SubprogramTag: int = 46; +const SubroutineTag: int = 21; +const BasicTypeDescriptorTag: int = 36; +const AutoVariableTag: int = 256; +const ArgVariableTag: int = 257; +const ReturnVariableTag: int = 258; +const LexicalBlockTag: int = 11; +const PointerTypeTag: int = 15; +const StructureTypeTag: int = 19; +const MemberTag: int = 13; +const ArrayTypeTag: int = 1; +const SubrangeTag: int = 33; + +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 llstr(s: str) -> ValueRef { + str::as_buf(s, {|sbuf| + llvm::LLVMMDString(sbuf, str::byte_len(s)) + }) +} +fn lltag(lltag: int) -> ValueRef { + 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) +} +fn llmdnode(elems: [ValueRef]) -> ValueRef unsafe { + llvm::LLVMMDNode(vec::unsafe::to_ptr(elems), + vec::len(elems)) +} +fn llunused() -> ValueRef { + lli32(0x0) +} +fn llnull() -> ValueRef unsafe { + unsafe::reinterpret_cast(ptr::null::()) +} + +fn add_named_metadata(cx: @crate_ctxt, name: str, val: ValueRef) { + str::as_buf(name, {|sbuf| + llvm::LLVMAddNamedMetadataOperand(cx.llmod, sbuf, str::byte_len(name), + val) + }) +} + +//////////////// + +type debug_ctxt = { + llmetadata: metadata_cache, + names: trans_common::namegen +}; + +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 = {path: str}; +type local_var_md = {id: ast::node_id}; +type tydesc_md = {hash: uint}; +type block_md = {start: codemap::loc, end: codemap::loc}; +type argument_md = {id: ast::node_id}; +type retval_md = {id: ast::node_id}; + +type metadata_cache = hashmap; + +tag debug_metadata { + file_metadata(@metadata); + compile_unit_metadata(@metadata); + subprogram_metadata(@metadata); + local_var_metadata(@metadata); + tydesc_metadata(@metadata); + block_metadata(@metadata); + argument_metadata(@metadata); + retval_metadata(@metadata); +} + +fn cast_safely(val: T) -> U unsafe { + let val2 = val; + let val3 = unsafe::reinterpret_cast(val2); + unsafe::leak(val2); + ret val3; +} + +fn md_from_metadata(val: debug_metadata) -> T unsafe { + alt val { + 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) } + argument_metadata(md) { cast_safely(md) } + retval_metadata(md) { cast_safely(md) } + } +} + +fn cached_metadata(cache: metadata_cache, mdtag: int, + eq: block(md: T) -> bool) -> option::t unsafe { + 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 create_compile_unit(cx: @crate_ctxt, full_path: str) + -> @metadata { + let cache = get_cache(cx); + let tg = CompileUnitTag; + alt cached_metadata::<@metadata>(cache, tg, + {|md| md.data.path == full_path}) { + option::some(md) { ret md; } + option::none. {} + } + + let work_dir = cx.sess.get_working_dir(); + let file_path = if str::starts_with(full_path, work_dir) { + str::slice(full_path, str::byte_len(work_dir), + str::byte_len(full_path)) + } else { + full_path + }; + let unit_metadata = [lltag(tg), + llunused(), + lli32(DW_LANG_RUST), + llstr(file_path), + llstr(work_dir), + 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); + add_named_metadata(cx, "llvm.dbg.cu", unit_node); + let mdval = @{node: unit_node, data: {path: full_path}}; + update_cache(cache, tg, compile_unit_metadata(mdval)); + ret mdval; +} + +fn get_cache(cx: @crate_ctxt) -> metadata_cache { + option::get(cx.dbg_cx).llmetadata +} + +fn create_file(cx: @crate_ctxt, full_path: str) -> @metadata { + let cache = get_cache(cx);; + let tg = FileDescriptorTag; + alt cached_metadata::<@metadata>( + cache, tg, {|md| md.data.path == full_path}) { + option::some(md) { ret md; } + option::none. {} + } + + let fname = fs::basename(full_path); + let path = fs::dirname(full_path); + let unit_node = create_compile_unit(cx, full_path).node; + let file_md = [lltag(tg), + llstr(fname), + llstr(path), + unit_node]; + let val = llmdnode(file_md); + let mdval = @{node: val, data: {path: full_path}}; + update_cache(cache, tg, file_metadata(mdval)); + ret mdval; +} + +fn line_from_span(cm: codemap::codemap, sp: codemap::span) -> uint { + codemap::lookup_char_pos(cm, sp.lo).line +} + +fn create_block(cx: @block_ctxt) -> @metadata { + let cache = get_cache(bcx_ccx(cx)); + 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); + let tg = LexicalBlockTag; + alt cached_metadata::<@metadata>( + cache, tg, + {|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. { create_function(cx.fcx).node } + trans_common::parent_some(bcx) { create_block(cx).node } + }; + let file_node = create_file(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(tg), + 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, tg, block_metadata(mdval)); + ret mdval; +} + +fn size_and_align_of() -> (int, int) { + (sys::size_of::() as int, sys::align_of::() as int) +} + +fn create_basic_type(cx: @crate_ctxt, t: ty::t, ty: @ast::ty) + -> @metadata { + let cache = get_cache(cx); + let tg = BasicTypeDescriptorTag; + alt cached_metadata::<@metadata>( + cache, tg, + {|md| ty::hash_ty(t) == ty::hash_ty(md.data.hash)}) { + option::some(md) { ret md; } + option::none. {} + } + + let (name, (size, align), encoding) = alt ty.node { + ast::ty_bool. {("bool", size_and_align_of::(), DW_ATE_boolean)} + ast::ty_int(m) { alt m { + ast::ty_char. {("char", size_and_align_of::(), DW_ATE_unsigned)} + ast::ty_i. {("int", size_and_align_of::(), DW_ATE_signed)} + ast::ty_i8. {("i8", size_and_align_of::(), DW_ATE_signed_char)} + ast::ty_i16. {("i16", size_and_align_of::(), DW_ATE_signed)} + ast::ty_i32. {("i32", size_and_align_of::(), DW_ATE_signed)} + ast::ty_i64. {("i64", size_and_align_of::(), DW_ATE_signed)} + }} + ast::ty_uint(m) { alt m { + ast::ty_u. {("uint", size_and_align_of::(), DW_ATE_unsigned)} + ast::ty_u8. {("u8", size_and_align_of::(), DW_ATE_unsigned_char)} + ast::ty_u16. {("u16", size_and_align_of::(), DW_ATE_unsigned)} + ast::ty_u32. {("u32", size_and_align_of::(), DW_ATE_unsigned)} + ast::ty_u64. {("u64", size_and_align_of::(), DW_ATE_unsigned)} + }} + ast::ty_float(m) { alt m { + ast::ty_f. {("float", size_and_align_of::(), DW_ATE_float)} + ast::ty_f32. {("f32", size_and_align_of::(), DW_ATE_float)} + ast::ty_f64. {("f64", size_and_align_of::(), DW_ATE_float)} + }} + }; + + let fname = filename_from_span(cx, ty.span); + let file_node = create_file(cx, fname); + let cu_node = create_compile_unit(cx, fname); + let lldata = [lltag(tg), + cu_node.node, + llstr(name), + file_node.node, + lli32(0), //XXX source line + lli64(size * 8), // size in bits + lli64(align * 8), // alignment in bits + lli64(0), //XXX offset? + lli32(0), //XXX flags? + lli32(encoding)]; + let llnode = llmdnode(lldata); + let mdval = @{node: llnode, data: {hash: ty::hash_ty(t)}}; + update_cache(cache, tg, tydesc_metadata(mdval)); + add_named_metadata(cx, "llvm.dbg.ty", llnode); + ret mdval; +} + +fn create_pointer_type(cx: @crate_ctxt, t: ty::t, span: codemap::span, + pointee: @metadata) + -> @metadata { + let tg = PointerTypeTag; + /*let cache = cx.llmetadata; + alt cached_metadata::<@metadata>( + cache, tg, {|md| ty::hash_ty(t) == ty::hash_ty(md.data.hash)}) { + option::some(md) { ret md; } + option::none. {} + }*/ + let (size, align) = size_and_align_of::(); + let fname = filename_from_span(cx, span); + let file_node = create_file(cx, fname); + //let cu_node = create_compile_unit(cx, fname); + let llnode = create_derived_type(tg, file_node.node, "", 0, size * 8, + align * 8, 0, pointee.node); + let mdval = @{node: llnode, data: {hash: ty::hash_ty(t)}}; + //update_cache(cache, tg, tydesc_metadata(mdval)); + add_named_metadata(cx, "llvm.dbg.ty", llnode); + ret mdval; +} + +type struct_ctxt = { + file: ValueRef, + name: str, + line: int, + mutable members: [ValueRef], + mutable total_size: int, + align: int +}; + +fn finish_structure(cx: @struct_ctxt) -> ValueRef { + ret create_composite_type(StructureTypeTag, cx.name, cx.file, cx.line, + cx.total_size, cx.align, 0, option::none, + option::some(cx.members)); +} + +fn create_structure(file: @metadata, name: str, line: int) + -> @struct_ctxt { + let cx = @{file: file.node, + name: name, + line: line, + mutable members: [], + mutable total_size: 0, + align: 64 //XXX different alignment per arch? + }; + ret cx; +} + +fn create_derived_type(type_tag: int, file: ValueRef, name: str, line: int, + size: int, align: int, offset: int, ty: ValueRef) + -> ValueRef { + let lldata = [lltag(type_tag), + file, + llstr(name), + file, + lli32(line), + lli64(size), + lli64(align), + lli64(offset), + lli32(0), + ty]; + ret llmdnode(lldata); +} + +fn add_member(cx: @struct_ctxt, name: str, line: int, size: int, align: int, + ty: ValueRef) { + cx.members += [create_derived_type(MemberTag, cx.file, name, line, + size * 8, align * 8, cx.total_size, + ty)]; + cx.total_size += size * 8; +} + +fn create_record(cx: @crate_ctxt, t: ty::t, fields: [ast::ty_field], + span: codemap::span) -> @metadata { + let fname = filename_from_span(cx, span); + let file_node = create_file(cx, fname); + let scx = create_structure(file_node, + option::get(cx.dbg_cx).names.next("rec"), + line_from_span(cx.sess.get_codemap(), + span) as int); + for field in fields { + let field_t = ty::get_field(ccx_tcx(cx), t, field.node.ident).mt.ty; + let ty_md = create_ty(cx, field_t, field.node.mt.ty); + let (size, align) = member_size_and_align(field.node.mt.ty); + add_member(scx, field.node.ident, + line_from_span(cx.sess.get_codemap(), field.span) as int, + size as int, align as int, ty_md.node); + } + let mdval = @{node: finish_structure(scx), data:{hash: t}}; + ret mdval; +} + +fn create_boxed_type(cx: @crate_ctxt, outer: ty::t, _inner: ty::t, + span: codemap::span, boxed: @metadata) + -> @metadata { + //let tg = StructureTypeTag; + /*let cache = cx.llmetadata; + alt cached_metadata::<@metadata>( + cache, tg, {|md| ty::hash_ty(outer) == ty::hash_ty(md.data.hash)}) { + option::some(md) { ret md; } + option::none. {} + }*/ + let fname = filename_from_span(cx, span); + let file_node = create_file(cx, fname); + //let cu_node = create_compile_unit_metadata(cx, fname); + let tcx = ccx_tcx(cx); + let uint_t = ty::mk_uint(tcx); + let uint_ty = @{node: ast::ty_uint(ast::ty_u), span: span}; + let refcount_type = create_basic_type(cx, uint_t, uint_ty); + let scx = create_structure(file_node, ty_to_str(ccx_tcx(cx), outer), 0); + add_member(scx, "refcnt", 0, sys::size_of::() as int, + sys::align_of::() as int, refcount_type.node); + add_member(scx, "boxed", 0, 8, //XXX member_size_and_align(??) + 8, //XXX just a guess + boxed.node); + let llnode = finish_structure(scx); + let mdval = @{node: llnode, data: {hash: outer}}; + //update_cache(cache, tg, tydesc_metadata(mdval)); + add_named_metadata(cx, "llvm.dbg.ty", llnode); + ret mdval; +} + +fn create_composite_type(type_tag: int, name: str, file: ValueRef, line: int, + size: int, align: int, offset: int, + derived: option::t, + members: option::t<[ValueRef]>) + -> ValueRef { + let lldata = [lltag(type_tag), + file, + llstr(name), // type name + file, // source file definition + lli32(line), // source line definition + lli64(size), // size of members + lli64(align), // align + lli64(offset), // offset + lli32(0), // flags + option::is_none(derived) ? llnull() : // derived from + option::get(derived), + option::is_none(members) ? llnull() : // members + llmdnode(option::get(members)), + lli32(0), // runtime language + llnull() + ]; + ret llmdnode(lldata); +} + +fn create_vec(cx: @crate_ctxt, vec_t: ty::t, elem_t: ty::t, vec_ty: @ast::ty) + -> @metadata { + let fname = filename_from_span(cx, vec_ty.span); + let file_node = create_file(cx, fname); + let elem_ty = alt vec_ty.node { ast::ty_vec(mt) { mt.ty } }; + let elem_ty_md = create_ty(cx, elem_t, elem_ty); + let tcx = ccx_tcx(cx); + let scx = create_structure(file_node, ty_to_str(tcx, vec_t), 0); + let uint_ty = @{node: ast::ty_uint(ast::ty_u), span: vec_ty.span}; + let size_t_type = create_basic_type(cx, ty::mk_uint(tcx), uint_ty); + add_member(scx, "fill", 0, sys::size_of::() as int, + sys::align_of::() as int, size_t_type.node); + add_member(scx, "alloc", 0, sys::size_of::() as int, + sys::align_of::() as int, size_t_type.node); + let subrange = llmdnode([lltag(SubrangeTag), lli64(0), lli64(0)]); + let (arr_size, arr_align) = member_size_and_align(elem_ty); + let data_ptr = create_composite_type(ArrayTypeTag, "", file_node.node, 0, + arr_size, arr_align, 0, + option::some(elem_ty_md.node), + option::some([subrange])); + add_member(scx, "data", 0, 0, // clang says the size should be 0 + sys::align_of::() as int, data_ptr); + let llnode = finish_structure(scx); + ret @{node: llnode, data: {hash: vec_t}}; +} + +fn member_size_and_align(ty: @ast::ty) -> (int, int) { + alt ty.node { + ast::ty_bool. { size_and_align_of::() } + ast::ty_int(m) { alt m { + ast::ty_char. { size_and_align_of::() } + ast::ty_i. { size_and_align_of::() } + ast::ty_i8. { size_and_align_of::() } + ast::ty_i16. { size_and_align_of::() } + ast::ty_i32. { size_and_align_of::() } + }} + ast::ty_uint(m) { alt m { + ast::ty_u. { size_and_align_of::() } + ast::ty_u8. { size_and_align_of::() } + ast::ty_u16. { size_and_align_of::() } + ast::ty_u32. { size_and_align_of::() } + }} + ast::ty_float(m) { alt m { + ast::ty_f. { size_and_align_of::() } + ast::ty_f32. { size_and_align_of::() } + ast::ty_f64. { size_and_align_of::() } + }} + ast::ty_box(_) | ast::ty_uniq(_) { + size_and_align_of::() + } + ast::ty_rec(fields) { + let total_size = 0; + for field in fields { + let (size, _) = member_size_and_align(field.node.mt.ty); + total_size += size; + } + (total_size, 64) //XXX different align for other arches? + } + ast::ty_vec(_) { + size_and_align_of::() + } + } +} + +fn create_ty(cx: @crate_ctxt, t: ty::t, ty: @ast::ty) + -> @metadata { + /*let cache = get_cache(cx); + alt cached_metadata::<@metadata>( + cache, tg, {|md| t == md.data.hash}) { + option::some(md) { ret md; } + option::none. {} + }*/ + + fn t_to_ty(cx: @crate_ctxt, t: ty::t, span: codemap::span) -> @ast::ty { + let ty = alt ty::struct(ccx_tcx(cx), t) { + ty::ty_nil. { ast::ty_nil } + ty::ty_bot. { ast::ty_bot } + ty::ty_bool. { ast::ty_bool } + ty::ty_int(t) { ast::ty_int(t) } + ty::ty_float(t) { ast::ty_float(t) } + ty::ty_uint(t) { ast::ty_uint(t) } + ty::ty_box(mt) { ast::ty_box({ty: t_to_ty(cx, mt.ty, span), + mut: mt.mut}) } + ty::ty_uniq(mt) { ast::ty_uniq({ty: t_to_ty(cx, mt.ty, span), + mut: mt.mut}) } + ty::ty_rec(fields) { + let fs = []; + for field in fields { + fs += [{node: {ident: field.ident, + mt: {ty: t_to_ty(cx, field.mt.ty, span), + mut: field.mt.mut}}, + span: span}]; + } + ast::ty_rec(fs) + } + ty::ty_vec(mt) { ast::ty_vec({ty: t_to_ty(cx, mt.ty, span), + mut: mt.mut}) } + }; + ret @{node: ty, span: span}; + } + + alt ty.node { + ast::ty_box(mt) { + let inner_t = alt ty::struct(ccx_tcx(cx), t) { + ty::ty_box(boxed) { boxed.ty } + }; + let md = create_ty(cx, inner_t, mt.ty); + let box = create_boxed_type(cx, t, inner_t, ty.span, md); + ret create_pointer_type(cx, t, ty.span, box); + } + + ast::ty_uniq(mt) { + let inner_t = alt ty::struct(ccx_tcx(cx), t) { + ty::ty_uniq(boxed) { boxed.ty } + }; + let md = create_ty(cx, inner_t, mt.ty); + ret create_pointer_type(cx, t, ty.span, md); + } + + ast::ty_infer. { + let inferred = t_to_ty(cx, t, ty.span); + ret create_ty(cx, t, inferred); + } + + ast::ty_rec(fields) { + ret create_record(cx, t, fields, ty.span); + } + + ast::ty_vec(mt) { + let inner_t = ty::sequence_element_type(ccx_tcx(cx), t); + let v = create_vec(cx, t, inner_t, ty); + ret create_pointer_type(cx, t, ty.span, v); + } + + _ { ret create_basic_type(cx, t, ty); } + }; +} + +fn filename_from_span(cx: @crate_ctxt, sp: codemap::span) -> str { + codemap::lookup_char_pos(cx.sess.get_codemap(), sp.lo).filename +} + +fn create_var(type_tag: int, context: ValueRef, name: str, file: ValueRef, + line: int, ret_ty: ValueRef) -> ValueRef { + let lldata = [lltag(type_tag), + context, + llstr(name), + file, + lli32(line), + ret_ty, + lli32(0) + ]; + ret llmdnode(lldata); +} + +fn create_local_var(bcx: @block_ctxt, local: @ast::local) + -> @metadata unsafe { + let cx = bcx_ccx(bcx); + let cache = get_cache(cx); + let tg = AutoVariableTag; + alt cached_metadata::<@metadata>( + cache, tg, {|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 /*XXX deal w/ optional node binding*/ } + }; + 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 = create_ty(cx, ty, local.node.ty); + let filemd = create_file(cx, loc.filename); + let context = alt bcx.parent { + trans_common::parent_none. { create_function(bcx.fcx).node } + trans_common::parent_some(_) { create_block(bcx).node } + }; + let mdnode = create_var(tg, context, name, filemd.node, + loc.line as int, tymd.node); + 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]; + trans_build::Call(bcx, cx.intrinsics.get("llvm.dbg.declare"), + declargs); + ret mdval; +} + +fn create_arg(bcx: @block_ctxt, arg: ast::arg) + -> @metadata unsafe { + let fcx = bcx_fcx(bcx); + let cx = fcx_ccx(fcx); + let cache = get_cache(cx); + let tg = ArgVariableTag; + alt cached_metadata::<@metadata>( + cache, ArgVariableTag, {|md| md.data.id == arg.id}) { + option::some(md) { ret md; } + option::none. {} + } + + /*let arg_n = alt cx.ast_map.get(arg.id) { + ast_map::node_arg(_, n) { n - 2u } + };*/ + let loc = codemap::lookup_char_pos(cx.sess.get_codemap(), + fcx.sp.lo); + let ty = trans::node_id_type(cx, arg.id); + let tymd = create_ty(cx, ty, arg.ty); + let filemd = create_file(cx, loc.filename); + let context = create_function(bcx.fcx); + let mdnode = create_var(tg, context.node, arg.ident, filemd.node, + loc.line as int, tymd.node); + let mdval = @{node: mdnode, data: {id: arg.id}}; + update_cache(cache, tg, argument_metadata(mdval)); + + let llptr = alt fcx.llargs.get(arg.id) { + local_mem(v) | local_imm(v) { v } + }; + let declargs = [llmdnode([llptr]), mdnode]; + trans_build::Call(bcx, cx.intrinsics.get("llvm.dbg.declare"), + declargs); + ret mdval; +} + +fn update_source_pos(cx: @block_ctxt, s: codemap::span) { + if !bcx_ccx(cx).sess.get_opts().debuginfo { + ret; + } + let cm = bcx_ccx(cx).sess.get_codemap(); + let blockmd = create_block(cx); + let loc = codemap::lookup_char_pos(cm, s.lo); + let scopedata = [lli32(loc.line as int), + lli32(loc.col as int), + blockmd.node, + llnull()]; + let dbgscope = llmdnode(scopedata); + llvm::LLVMSetCurrentDebugLocation(trans_build::B(cx), dbgscope); +} + +fn create_function(fcx: @fn_ctxt) -> @metadata { + let cx = fcx_ccx(fcx); + let dbg_cx = option::get(cx.dbg_cx); + + log "~~"; + log fcx.id; + log cx.sess.span_str(fcx.sp); + + let (ident, ret_ty, id) = alt cx.ast_map.get(fcx.id) { + ast_map::node_item(item) { + alt item.node { + ast::item_fn(f, _) | ast::item_res(f, _, _, _) { + (item.ident, f.decl.output, item.id) + } + } + } + ast_map::node_obj_method(method) { + (method.node.ident, method.node.meth.decl.output, method.node.id) + } + ast_map::node_res_ctor(item) { + alt item.node { ast::item_res(f, _, _, ctor_id) { + (item.ident, f.decl.output, ctor_id) + }} + } + ast_map::node_expr(expr) { + alt expr.node { + ast::expr_fn(f, _) { + (dbg_cx.names.next("fn"), f.decl.output, expr.id) + } + } + } + }; + + log ident; + log id; + + let path = str::connect(fcx.lcx.path + [ident], "::"); + + let cache = get_cache(cx); + alt cached_metadata::<@metadata>( + cache, SubprogramTag, {|md| md.data.path == path && + /*md.data.path == ??*/ true}) { + option::some(md) { ret md; } + option::none. {} + } + + let loc = codemap::lookup_char_pos(cx.sess.get_codemap(), + fcx.sp.lo); + let file_node = create_file(cx, loc.filename).node; + let key = cx.item_symbols.contains_key(fcx.id) ? fcx.id : id; + let mangled = cx.item_symbols.get(key); + let ty_node = if cx.sess.get_opts().extra_debuginfo { + alt ret_ty.node { + ast::ty_nil. { llnull() } + _ { create_ty(cx, ty::node_id_to_type(ccx_tcx(cx), id), + ret_ty).node } + } + } else { + llnull() + }; + let sub_node = create_composite_type(SubroutineTag, "", file_node, 0, 0, + 0, 0, option::none, + option::some([ty_node])); + + let fn_metadata = [lltag(SubprogramTag), + llunused(), + file_node, + llstr(ident), + llstr(path), //XXX fully-qualified C++ name + llstr(mangled), //XXX MIPS name????? + file_node, + lli32(loc.line as int), + sub_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), + fcx.llfn + //list of template params + //func decl descriptor + //list of func vars + ]; + let val = llmdnode(fn_metadata); + add_named_metadata(cx, "llvm.dbg.sp", val); + let mdval = @{node: val, data: {path: path}}; + update_cache(cache, SubprogramTag, subprogram_metadata(mdval)); + ret mdval; +} diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index acec464e6f8e8..8a37d27d973c9 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -20,10 +20,10 @@ 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}; +import syntax::{ast, ast_util, codemap}; import syntax::visit; import syntax::codemap::span; import syntax::print::pprust::{expr_to_str, stmt_to_str}; @@ -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.span); + 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.span); + 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().extra_debuginfo { + debuginfo::create_local_var(bcx, local); + } } } ast::decl_item(i) { trans_item(cx.fcx.lcx, *i); } @@ -4030,6 +4037,7 @@ fn trans_stmt(cx: @block_ctxt, s: ast::stmt) -> @block_ctxt { } _ { bcx_ccx(cx).sess.unimpl("stmt variant"); } } + ret bcx; } @@ -4252,16 +4260,19 @@ fn trans_block_dps(bcx: @block_ctxt, b: ast::blk, dest: dest) let bcx = bcx; block_locals(b) {|local| bcx = alloc_local(bcx, local); }; for s: @ast::stmt in b.node.stmts { + debuginfo::update_source_pos(bcx, b.span); bcx = trans_stmt(bcx, *s); } alt b.node.expr { some(e) { let bt = ty::type_is_bot(bcx_tcx(bcx), ty::expr_ty(bcx_tcx(bcx), e)); + debuginfo::update_source_pos(bcx, e.span); bcx = trans_expr(bcx, e, bt ? ignore : 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)); + ret rv; } fn new_local_ctxt(ccx: @crate_ctxt) -> @local_ctxt { @@ -4390,6 +4401,11 @@ fn create_llargs_for_fn_args(cx: @fn_ctxt, ty_self: self_arg, fn copy_args_to_allocas(fcx: @fn_ctxt, bcx: @block_ctxt, args: [ast::arg], arg_tys: [ty::arg]) -> @block_ctxt { + if fcx_ccx(fcx).sess.get_opts().extra_debuginfo { + llvm::LLVMAddAttribute(llvm::LLVMGetFirstParam(fcx.llfn), + lib::llvm::LLVMStructRetAttribute as + lib::llvm::llvm::Attribute); + } let arg_n: uint = 0u, bcx = bcx; for arg in arg_tys { let id = args[arg_n].id; @@ -4409,6 +4425,9 @@ fn copy_args_to_allocas(fcx: @fn_ctxt, bcx: @block_ctxt, args: [ast::arg], } ast::by_ref. {} } + if fcx_ccx(fcx).sess.get_opts().extra_debuginfo { + debuginfo::create_arg(bcx, args[arg_n]); + } arg_n += 1u; } ret bcx; @@ -4542,7 +4561,12 @@ fn trans_fn(cx: @local_ctxt, sp: span, f: ast::_fn, llfndecl: ValueRef, id: ast::node_id) { 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|}); + let fcx = option::none; + trans_closure(cx, sp, f, llfndecl, ty_self, ty_params, id, + {|new_fcx| fcx = option::some(new_fcx);}); + if cx.ccx.sess.get_opts().extra_debuginfo { + debuginfo::create_function(option::get(fcx)); + } if do_time { let end = time::get_time(); log_fn_time(cx.ccx, str::connect(cx.path, "::"), start, end); @@ -5459,6 +5483,18 @@ fn declare_intrinsics(llmod: ModuleRef) -> hashmap { ret intrinsics; } +fn declare_dbg_intrinsics(llmod: ModuleRef, + intrinsics: hashmap) { + 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") { @@ -5595,6 +5631,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().extra_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); @@ -5610,6 +5649,12 @@ fn trans_crate(sess: session::session, crate: @ast::crate, tcx: ty::ctxt, let sha1s = map::mk_hashmap::(hasher, eqer); let short_names = map::mk_hashmap::(hasher, eqer); let crate_map = decl_crate_map(sess, link_meta.name, llmod); + let dbg_cx = if sess.get_opts().debuginfo { + option::some(@{llmetadata: map::new_int_hash(), + names: namegen(0)}) + } else { + option::none + }; let ccx = @{sess: sess, llmod: llmod, @@ -5659,7 +5704,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, + dbg_cx: dbg_cx}; let cx = new_local_ctxt(ccx); collect_items(ccx, crate); collect_tag_ctors(ccx, crate); diff --git a/src/comp/middle/trans_build.rs b/src/comp/middle/trans_build.rs index 210626deffbe8..5a9c07731473b 100644 --- a/src/comp/middle/trans_build.rs +++ b/src/comp/middle/trans_build.rs @@ -93,8 +93,14 @@ 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()); + if bcx_ccx(cx).sess.get_opts().debuginfo { + llvm::LLVMAddInstrAttribute(instr, 1u, + lib::llvm::LLVMStructRetAttribute as + lib::llvm::llvm::Attribute); + } } } @@ -338,7 +344,8 @@ 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), + ret llvm::LLVMBuildInBoundsGEP(B(cx), Pointer, + vec::to_ptr(Indices), vec::len(Indices), noname()); } } @@ -508,7 +515,9 @@ fn _UndefReturn(cx: @block_ctxt, Fn: ValueRef) -> ValueRef { fn add_span_comment(bcx: @block_ctxt, sp: span, text: str) { let ccx = bcx_ccx(bcx); if (!ccx.sess.get_opts().no_asm_comments) { - add_comment(bcx, text + " (" + ccx.sess.span_str(sp) + ")"); + let s = text + " (" + ccx.sess.span_str(sp) + ")"; + log s; + add_comment(bcx, s); } } @@ -622,8 +631,8 @@ 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()); + llvm::LLVMBuildCall(b, T, vec::to_ptr(Args), vec::len(Args), + noname()); } } diff --git a/src/comp/middle/trans_closure.rs b/src/comp/middle/trans_closure.rs index 94e1cdc8ca342..43cb0c3f39d45 100644 --- a/src/comp/middle/trans_closure.rs +++ b/src/comp/middle/trans_closure.rs @@ -336,6 +336,7 @@ fn trans_expr_fn(bcx: @block_ctxt, f: ast::_fn, sp: span, let sub_cx = extend_path(bcx.fcx.lcx, ccx.names.next("anon")); let s = mangle_internal_name_by_path(ccx, sub_cx.path); let llfn = decl_internal_cdecl_fn(ccx.llmod, s, llfnty); + register_fn(ccx, sp, sub_cx.path, "anon fn", [], id); let trans_closure_env = lambda(ck: ty::closure_kind) -> ValueRef { let upvars = get_freevars(ccx.tcx, id); diff --git a/src/comp/middle/trans_common.rs b/src/comp/middle/trans_common.rs index 19f439235e421..f30184124b050 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, + dbg_cx: option::t<@debuginfo::debug_ctxt>}; type local_ctxt = {path: [str], @@ -463,6 +464,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(); } @@ -800,6 +803,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 u64, True); +} + fn C_int(cx: @crate_ctxt, i: int) -> ValueRef { ret C_integral(cx.int_type, i as u64, True); } diff --git a/src/comp/rustc.rc b/src/comp/rustc.rc index 51a01378fb3a9..9f88b369749a1 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/llvm b/src/llvm index 80c896f8ad46f..a320b2aa41fbe 160000 --- a/src/llvm +++ b/src/llvm @@ -1 +1 @@ -Subproject commit 80c896f8ad46f5c3fb777c32c0c71d58a4bdb1e7 +Subproject commit a320b2aa41fbe3f944bad33780626d65d1b11e6f diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index d50cc527dfd1a..ad7fec1586b40 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 381c5293eb73c..8456f69e7f38b 100644 --- a/src/rustllvm/rustllvm.def.in +++ b/src/rustllvm/rustllvm.def.in @@ -60,6 +60,7 @@ LLVMAddLoopRotatePass LLVMAddLoopUnrollPass LLVMAddLoopUnswitchPass LLVMAddMemCpyOptPass +LLVMAddNamedMetadataOperand LLVMAddPromoteMemoryToRegisterPass LLVMAddPruneEHPass LLVMAddReassociatePass @@ -487,6 +488,8 @@ LLVMMDNode LLVMMDNodeInContext LLVMMDString LLVMMDStringInContext +LLVMMetadataType +LLVMMetadataTypeInContext LLVMModuleCreateWithName LLVMModuleCreateWithNameInContext LLVMMoveBasicBlockAfter