10d930d51e
If a node has a (0, 0) span, it was not in the source, so debug symbols should not be generated for it.
1065 lines
36 KiB
Rust
1065 lines
36 KiB
Rust
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
|
// file at the top-level directory of this distribution and at
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
// option. This file may not be copied, modified, or distributed
|
|
// except according to those terms.
|
|
|
|
use core::prelude::*;
|
|
|
|
use driver::session;
|
|
use lib::llvm::ValueRef;
|
|
use lib::llvm::llvm;
|
|
use middle::trans::common::*;
|
|
use middle::trans::machine;
|
|
use middle::trans::type_of;
|
|
use middle::trans;
|
|
use middle::ty;
|
|
use util::ppaux::ty_to_str;
|
|
|
|
use core::hashmap::HashMap;
|
|
use core::libc;
|
|
use core::option;
|
|
use core::sys;
|
|
use syntax::codemap::span;
|
|
use syntax::parse::token::ident_interner;
|
|
use syntax::{ast, codemap, ast_util, ast_map};
|
|
|
|
static LLVMDebugVersion: int = (9 << 16);
|
|
|
|
static DW_LANG_RUST: int = 0x9000;
|
|
static DW_VIRTUALITY_none: int = 0;
|
|
|
|
static CompileUnitTag: int = 17;
|
|
static FileDescriptorTag: int = 41;
|
|
static SubprogramTag: int = 46;
|
|
static SubroutineTag: int = 21;
|
|
static BasicTypeDescriptorTag: int = 36;
|
|
static AutoVariableTag: int = 256;
|
|
static ArgVariableTag: int = 257;
|
|
static ReturnVariableTag: int = 258;
|
|
static LexicalBlockTag: int = 11;
|
|
static PointerTypeTag: int = 15;
|
|
static StructureTypeTag: int = 19;
|
|
static MemberTag: int = 13;
|
|
static ArrayTypeTag: int = 1;
|
|
static SubrangeTag: int = 33;
|
|
|
|
static DW_ATE_boolean: int = 0x02;
|
|
static DW_ATE_float: int = 0x04;
|
|
static DW_ATE_signed: int = 0x05;
|
|
static DW_ATE_signed_char: int = 0x06;
|
|
static DW_ATE_unsigned: int = 0x07;
|
|
static DW_ATE_unsigned_char: int = 0x08;
|
|
|
|
fn llstr(s: &str) -> ValueRef {
|
|
do str::as_c_str(s) |sbuf| {
|
|
unsafe {
|
|
llvm::LLVMMDString(sbuf, s.len() as libc::c_uint)
|
|
}
|
|
}
|
|
}
|
|
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_i1(bval)
|
|
}
|
|
fn llmdnode(elems: &[ValueRef]) -> ValueRef {
|
|
unsafe {
|
|
llvm::LLVMMDNode(vec::raw::to_ptr(elems), elems.len() as libc::c_uint)
|
|
}
|
|
}
|
|
fn llunused() -> ValueRef {
|
|
lli32(0x0)
|
|
}
|
|
fn llnull() -> ValueRef {
|
|
unsafe {
|
|
cast::reinterpret_cast(&ptr::null::<ValueRef>())
|
|
}
|
|
}
|
|
|
|
fn add_named_metadata(cx: @CrateContext, name: ~str, val: ValueRef) {
|
|
str::as_c_str(name, |sbuf| {
|
|
unsafe {
|
|
llvm::LLVMAddNamedMetadataOperand(cx.llmod, sbuf, val)
|
|
}
|
|
})
|
|
}
|
|
|
|
////////////////
|
|
|
|
pub struct DebugContext {
|
|
llmetadata: metadata_cache,
|
|
names: namegen,
|
|
crate_file: ~str
|
|
}
|
|
|
|
pub fn mk_ctxt(+crate: ~str, intr: @ident_interner) -> DebugContext {
|
|
DebugContext {
|
|
llmetadata: @mut HashMap::new(),
|
|
names: new_namegen(intr),
|
|
crate_file: crate
|
|
}
|
|
}
|
|
|
|
fn update_cache(cache: metadata_cache, mdtag: int, val: debug_metadata) {
|
|
let mut existing = match cache.pop(&mdtag) {
|
|
Some(arr) => arr, None => ~[]
|
|
};
|
|
existing.push(val);
|
|
cache.insert(mdtag, existing);
|
|
}
|
|
|
|
struct Metadata<T> {
|
|
node: ValueRef,
|
|
data: T
|
|
}
|
|
|
|
struct FileMetadata {
|
|
path: ~str
|
|
}
|
|
struct CompileUnitMetadata {
|
|
name: ~str
|
|
}
|
|
struct SubProgramMetadata {
|
|
id: ast::node_id
|
|
}
|
|
struct LocalVarMetadata {
|
|
id: ast::node_id
|
|
}
|
|
struct TyDescMetadata {
|
|
hash: uint
|
|
}
|
|
struct BlockMetadata {
|
|
start: codemap::Loc,
|
|
end: codemap::Loc
|
|
}
|
|
struct ArgumentMetadata {
|
|
id: ast::node_id
|
|
}
|
|
struct RetvalMetadata {
|
|
id: ast::node_id
|
|
}
|
|
|
|
type metadata_cache = @mut HashMap<int, ~[debug_metadata]>;
|
|
|
|
enum debug_metadata {
|
|
file_metadata(@Metadata<FileMetadata>),
|
|
compile_unit_metadata(@Metadata<CompileUnitMetadata>),
|
|
subprogram_metadata(@Metadata<SubProgramMetadata>),
|
|
local_var_metadata(@Metadata<LocalVarMetadata>),
|
|
tydesc_metadata(@Metadata<TyDescMetadata>),
|
|
block_metadata(@Metadata<BlockMetadata>),
|
|
argument_metadata(@Metadata<ArgumentMetadata>),
|
|
retval_metadata(@Metadata<RetvalMetadata>),
|
|
}
|
|
|
|
fn cast_safely<T:Copy,U>(val: T) -> U {
|
|
unsafe {
|
|
let val2 = val;
|
|
return cast::transmute(val2);
|
|
}
|
|
}
|
|
|
|
fn md_from_metadata<T>(val: debug_metadata) -> T {
|
|
unsafe {
|
|
match 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<T:Copy>(cache: metadata_cache,
|
|
mdtag: int,
|
|
eq_fn: &fn(md: T) -> bool)
|
|
-> Option<T> {
|
|
unsafe {
|
|
if cache.contains_key(&mdtag) {
|
|
let items = cache.get(&mdtag);
|
|
for items.each |item| {
|
|
let md: T = md_from_metadata::<T>(*item);
|
|
if eq_fn(md) {
|
|
return option::Some(md);
|
|
}
|
|
}
|
|
}
|
|
return option::None;
|
|
}
|
|
}
|
|
|
|
fn create_compile_unit(cx: @CrateContext) -> @Metadata<CompileUnitMetadata> {
|
|
unsafe {
|
|
let cache = get_cache(cx);
|
|
let crate_name = /*bad*/copy (/*bad*/copy cx.dbg_cx).get().crate_file;
|
|
let tg = CompileUnitTag;
|
|
match cached_metadata::<@Metadata<CompileUnitMetadata>>(cache, tg,
|
|
|md| md.data.name == crate_name) {
|
|
option::Some(md) => return md,
|
|
option::None => ()
|
|
}
|
|
|
|
let (_, work_dir) = get_file_path_and_dir(
|
|
cx.sess.working_dir.to_str(), crate_name);
|
|
let unit_metadata = ~[lltag(tg),
|
|
llunused(),
|
|
lli32(DW_LANG_RUST),
|
|
llstr(crate_name),
|
|
llstr(work_dir),
|
|
llstr(env!("CFG_VERSION")),
|
|
lli1(true), // deprecated: main compile unit
|
|
lli1(cx.sess.opts.optimize != session::No),
|
|
llstr(~""), // flags (???)
|
|
lli32(0) // runtime version (???)
|
|
];
|
|
let unit_node = llmdnode(unit_metadata);
|
|
add_named_metadata(cx, ~"llvm.dbg.cu", unit_node);
|
|
let mdval = @Metadata {
|
|
node: unit_node,
|
|
data: CompileUnitMetadata {
|
|
name: crate_name
|
|
}
|
|
};
|
|
update_cache(cache, tg, compile_unit_metadata(mdval));
|
|
|
|
return mdval;
|
|
}
|
|
}
|
|
|
|
fn get_cache(cx: @CrateContext) -> metadata_cache {
|
|
(/*bad*/copy cx.dbg_cx).get().llmetadata
|
|
}
|
|
|
|
fn get_file_path_and_dir(work_dir: &str, full_path: &str) -> (~str, ~str) {
|
|
(if str::starts_with(full_path, work_dir) {
|
|
str::slice(full_path, str::len(work_dir) + 1u,
|
|
str::len(full_path)).to_owned()
|
|
} else {
|
|
full_path.to_owned()
|
|
}, work_dir.to_owned())
|
|
}
|
|
|
|
fn create_file(cx: @CrateContext, +full_path: ~str)
|
|
-> @Metadata<FileMetadata> {
|
|
let cache = get_cache(cx);;
|
|
let tg = FileDescriptorTag;
|
|
match cached_metadata::<@Metadata<FileMetadata>>(
|
|
cache, tg, |md| md.data.path == full_path) {
|
|
option::Some(md) => return md,
|
|
option::None => ()
|
|
}
|
|
|
|
let (file_path, work_dir) =
|
|
get_file_path_and_dir(cx.sess.working_dir.to_str(),
|
|
full_path);
|
|
let unit_node = create_compile_unit(cx).node;
|
|
let file_md = ~[lltag(tg),
|
|
llstr(file_path),
|
|
llstr(work_dir),
|
|
unit_node];
|
|
let val = llmdnode(file_md);
|
|
let mdval = @Metadata {
|
|
node: val,
|
|
data: FileMetadata {
|
|
path: full_path
|
|
}
|
|
};
|
|
update_cache(cache, tg, file_metadata(mdval));
|
|
return mdval;
|
|
}
|
|
|
|
fn line_from_span(cm: @codemap::CodeMap, sp: span) -> uint {
|
|
cm.lookup_char_pos(sp.lo).line
|
|
}
|
|
|
|
fn create_block(cx: block) -> @Metadata<BlockMetadata> {
|
|
let cache = get_cache(cx.ccx());
|
|
let mut cx = cx;
|
|
while cx.node_info.is_none() {
|
|
match cx.parent {
|
|
Some(b) => cx = b,
|
|
None => fail!()
|
|
}
|
|
}
|
|
let sp = cx.node_info.get().span;
|
|
|
|
let start = cx.sess().codemap.lookup_char_pos(sp.lo);
|
|
let fname = /*bad*/copy start.file.name;
|
|
let end = cx.sess().codemap.lookup_char_pos(sp.hi);
|
|
let tg = LexicalBlockTag;
|
|
/*match cached_metadata::<@Metadata<BlockMetadata>>(
|
|
cache, tg,
|
|
{|md| start == md.data.start && end == md.data.end}) {
|
|
option::Some(md) { return md; }
|
|
option::None {}
|
|
}*/
|
|
|
|
let parent = match cx.parent {
|
|
None => create_function(cx.fcx).node,
|
|
Some(bcx) => create_block(bcx).node
|
|
};
|
|
let file_node = create_file(cx.ccx(), fname);
|
|
let unique_id = match cache.find(&LexicalBlockTag) {
|
|
option::Some(v) => v.len() as int,
|
|
option::None => 0
|
|
};
|
|
let lldata = ~[lltag(tg),
|
|
parent,
|
|
lli32(start.line.to_int()),
|
|
lli32(start.col.to_int()),
|
|
file_node.node,
|
|
lli32(unique_id)
|
|
];
|
|
let val = llmdnode(lldata);
|
|
let mdval = @Metadata {
|
|
node: val,
|
|
data: BlockMetadata {
|
|
start: start,
|
|
end: end
|
|
}
|
|
};
|
|
//update_cache(cache, tg, block_metadata(mdval));
|
|
return mdval;
|
|
}
|
|
|
|
fn size_and_align_of(cx: @CrateContext, t: ty::t) -> (int, int) {
|
|
let llty = type_of::type_of(cx, t);
|
|
(machine::llsize_of_real(cx, llty) as int,
|
|
machine::llalign_of_pref(cx, llty) as int)
|
|
}
|
|
|
|
fn create_basic_type(cx: @CrateContext, t: ty::t, span: span)
|
|
-> @Metadata<TyDescMetadata> {
|
|
let cache = get_cache(cx);
|
|
let tg = BasicTypeDescriptorTag;
|
|
match cached_metadata::<@Metadata<TyDescMetadata>>(
|
|
cache, tg, |md| ty::type_id(t) == md.data.hash) {
|
|
option::Some(md) => return md,
|
|
option::None => ()
|
|
}
|
|
|
|
let (name, encoding) = match ty::get(t).sty {
|
|
ty::ty_nil | ty::ty_bot => (~"uint", DW_ATE_unsigned),
|
|
ty::ty_bool => (~"bool", DW_ATE_boolean),
|
|
ty::ty_int(int_ty) => match int_ty {
|
|
ast::ty_i => (~"int", DW_ATE_signed),
|
|
ast::ty_char => (~"char", DW_ATE_signed_char),
|
|
ast::ty_i8 => (~"i8", DW_ATE_signed),
|
|
ast::ty_i16 => (~"i16", DW_ATE_signed),
|
|
ast::ty_i32 => (~"i32", DW_ATE_signed),
|
|
ast::ty_i64 => (~"i64", DW_ATE_signed)
|
|
},
|
|
ty::ty_uint(uint_ty) => match uint_ty {
|
|
ast::ty_u => (~"uint", DW_ATE_unsigned),
|
|
ast::ty_u8 => (~"u8", DW_ATE_unsigned),
|
|
ast::ty_u16 => (~"u16", DW_ATE_unsigned),
|
|
ast::ty_u32 => (~"u32", DW_ATE_unsigned),
|
|
ast::ty_u64 => (~"u64", DW_ATE_unsigned)
|
|
},
|
|
ty::ty_float(float_ty) => match float_ty {
|
|
ast::ty_f => (~"float", DW_ATE_float),
|
|
ast::ty_f32 => (~"f32", DW_ATE_float),
|
|
ast::ty_f64 => (~"f64", DW_ATE_float)
|
|
},
|
|
_ => cx.sess.bug(~"debuginfo::create_basic_type - t is invalid type")
|
|
};
|
|
|
|
let fname = filename_from_span(cx, span);
|
|
let file_node = create_file(cx, fname);
|
|
let cu_node = create_compile_unit(cx);
|
|
let (size, align) = size_and_align_of(cx, t);
|
|
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 = @Metadata {
|
|
node: llnode,
|
|
data: TyDescMetadata {
|
|
hash: ty::type_id(t)
|
|
}
|
|
};
|
|
update_cache(cache, tg, tydesc_metadata(mdval));
|
|
add_named_metadata(cx, ~"llvm.dbg.ty", llnode);
|
|
return mdval;
|
|
}
|
|
|
|
fn create_pointer_type(cx: @CrateContext, t: ty::t, span: span,
|
|
pointee: @Metadata<TyDescMetadata>)
|
|
-> @Metadata<TyDescMetadata> {
|
|
let tg = PointerTypeTag;
|
|
/*let cache = cx.llmetadata;
|
|
match cached_metadata::<@Metadata<TyDescMetadata>>(
|
|
cache, tg, {|md| ty::hash_ty(t) == ty::hash_ty(md.data.hash)}) {
|
|
option::Some(md) { return md; }
|
|
option::None {}
|
|
}*/
|
|
let (size, align) = size_and_align_of(cx, t);
|
|
let fname = filename_from_span(cx, span);
|
|
let file_node = create_file(cx, fname);
|
|
//let cu_node = create_compile_unit(cx, fname);
|
|
let name = ty_to_str(cx.tcx, t);
|
|
let llnode = create_derived_type(tg, file_node.node, name, 0, size * 8,
|
|
align * 8, 0, pointee.node);
|
|
let mdval = @Metadata {
|
|
node: llnode,
|
|
data: TyDescMetadata {
|
|
hash: ty::type_id(t)
|
|
}
|
|
};
|
|
//update_cache(cache, tg, tydesc_metadata(mdval));
|
|
add_named_metadata(cx, ~"llvm.dbg.ty", llnode);
|
|
return mdval;
|
|
}
|
|
|
|
struct StructCtxt {
|
|
file: ValueRef,
|
|
name: @~str,
|
|
line: int,
|
|
members: ~[ValueRef],
|
|
total_size: int,
|
|
align: int
|
|
}
|
|
|
|
fn finish_structure(cx: @mut StructCtxt) -> ValueRef {
|
|
return create_composite_type(StructureTypeTag,
|
|
*cx.name,
|
|
cx.file,
|
|
cx.line,
|
|
cx.total_size,
|
|
cx.align,
|
|
0,
|
|
None,
|
|
Some(/*bad*/copy cx.members));
|
|
}
|
|
|
|
fn create_structure(file: @Metadata<FileMetadata>, name: @~str, line: int)
|
|
-> @mut StructCtxt {
|
|
let cx = @mut StructCtxt {
|
|
file: file.node,
|
|
name: name,
|
|
line: line,
|
|
members: ~[],
|
|
total_size: 0,
|
|
align: 64 //XXX different alignment per arch?
|
|
};
|
|
return 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];
|
|
return llmdnode(lldata);
|
|
}
|
|
|
|
fn add_member(cx: @mut StructCtxt,
|
|
name: &str,
|
|
line: int,
|
|
size: int,
|
|
align: int,
|
|
ty: ValueRef) {
|
|
cx.members.push(create_derived_type(MemberTag, cx.file, name, line,
|
|
size * 8, align * 8, cx.total_size,
|
|
ty));
|
|
cx.total_size += size * 8;
|
|
}
|
|
|
|
fn create_struct(cx: @CrateContext, t: ty::t, fields: ~[ty::field],
|
|
span: span) -> @Metadata<TyDescMetadata> {
|
|
let fname = filename_from_span(cx, span);
|
|
let file_node = create_file(cx, fname);
|
|
let scx = create_structure(file_node, @ty_to_str(cx.tcx, t),
|
|
line_from_span(cx.sess.codemap, span) as int);
|
|
for fields.each |field| {
|
|
let field_t = field.mt.ty;
|
|
let ty_md = create_ty(cx, field_t, span);
|
|
let (size, align) = size_and_align_of(cx, field_t);
|
|
add_member(scx, *cx.sess.str_of(field.ident),
|
|
line_from_span(cx.sess.codemap, span) as int,
|
|
size as int, align as int, ty_md.node);
|
|
}
|
|
let mdval = @Metadata {
|
|
node: finish_structure(scx),
|
|
data: TyDescMetadata {
|
|
hash: ty::type_id(t)
|
|
}
|
|
};
|
|
return mdval;
|
|
}
|
|
|
|
fn create_tuple(cx: @CrateContext, t: ty::t, elements: &[ty::t], span: span)
|
|
-> @Metadata<TyDescMetadata> {
|
|
let fname = filename_from_span(cx, span);
|
|
let file_node = create_file(cx, fname);
|
|
let scx = create_structure(file_node,
|
|
cx.sess.str_of(
|
|
((/*bad*/copy cx.dbg_cx).get().names)
|
|
(~"tuple")),
|
|
line_from_span(cx.sess.codemap, span) as int);
|
|
for elements.each |element| {
|
|
let ty_md = create_ty(cx, *element, span);
|
|
let (size, align) = size_and_align_of(cx, *element);
|
|
add_member(scx, ~"", line_from_span(cx.sess.codemap, span) as int,
|
|
size as int, align as int, ty_md.node);
|
|
}
|
|
let mdval = @Metadata {
|
|
node: finish_structure(scx),
|
|
data: TyDescMetadata {
|
|
hash: ty::type_id(t)
|
|
}
|
|
};
|
|
return mdval;
|
|
}
|
|
|
|
// returns (void* type as a ValueRef, size in bytes, align in bytes)
|
|
fn voidptr() -> (ValueRef, int, int) {
|
|
let null = ptr::null();
|
|
let size = sys::size_of::<ValueRef>() as int;
|
|
let align = sys::min_align_of::<ValueRef>() as int;
|
|
let vp = create_derived_type(PointerTypeTag, null, ~"", 0,
|
|
size, align, 0, null);
|
|
return (vp, size, align);
|
|
}
|
|
|
|
fn create_boxed_type(cx: @CrateContext, contents: ty::t,
|
|
span: span, boxed: @Metadata<TyDescMetadata>)
|
|
-> @Metadata<TyDescMetadata> {
|
|
//let tg = StructureTypeTag;
|
|
/*let cache = cx.llmetadata;
|
|
match cached_metadata::<@Metadata<TyDescMetadata>>(
|
|
cache, tg, {|md| ty::hash_ty(contents) == ty::hash_ty(md.data.hash)}) {
|
|
option::Some(md) { return 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 int_t = ty::mk_int(cx.tcx);
|
|
let refcount_type = create_basic_type(cx, int_t, span);
|
|
let name = ty_to_str(cx.tcx, contents);
|
|
let scx = create_structure(file_node, @fmt!("box<%s>", name), 0);
|
|
add_member(scx, ~"refcnt", 0, sys::size_of::<uint>() as int,
|
|
sys::min_align_of::<uint>() as int, refcount_type.node);
|
|
// the tydesc and other pointers should be irrelevant to the
|
|
// debugger, so treat them as void* types
|
|
let (vp, vpsize, vpalign) = voidptr();
|
|
add_member(scx, ~"tydesc", 0, vpsize, vpalign, vp);
|
|
add_member(scx, ~"prev", 0, vpsize, vpalign, vp);
|
|
add_member(scx, ~"next", 0, vpsize, vpalign, vp);
|
|
let (size, align) = size_and_align_of(cx, contents);
|
|
add_member(scx, ~"boxed", 0, size, align, boxed.node);
|
|
let llnode = finish_structure(scx);
|
|
let mdval = @Metadata {
|
|
node: llnode,
|
|
data: TyDescMetadata {
|
|
hash: ty::type_id(contents)
|
|
}
|
|
};
|
|
//update_cache(cache, tg, tydesc_metadata(mdval));
|
|
add_named_metadata(cx, ~"llvm.dbg.ty", llnode);
|
|
return mdval;
|
|
}
|
|
|
|
fn create_composite_type(type_tag: int, name: &str, file: ValueRef,
|
|
line: int, size: int, align: int, offset: int,
|
|
derived: Option<ValueRef>,
|
|
+members: Option<~[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
|
|
lli32/*64*/(offset), // offset
|
|
lli32(0), // flags
|
|
if derived.is_none() {
|
|
llnull()
|
|
} else { // derived from
|
|
derived.get()
|
|
},
|
|
if members.is_none() {
|
|
llnull()
|
|
} else { //members
|
|
llmdnode(members.get())
|
|
},
|
|
lli32(0), // runtime language
|
|
llnull()
|
|
];
|
|
return llmdnode(lldata);
|
|
}
|
|
|
|
fn create_fixed_vec(cx: @CrateContext, vec_t: ty::t, elem_t: ty::t,
|
|
len: int, span: span) -> @Metadata<TyDescMetadata> {
|
|
let t_md = create_ty(cx, elem_t, span);
|
|
let fname = filename_from_span(cx, span);
|
|
let file_node = create_file(cx, fname);
|
|
let (size, align) = size_and_align_of(cx, elem_t);
|
|
let subrange = llmdnode(~[lltag(SubrangeTag), lli64(0), lli64(len - 1)]);
|
|
let name = fmt!("[%s]", ty_to_str(cx.tcx, elem_t));
|
|
let array = create_composite_type(ArrayTypeTag, name, file_node.node, 0,
|
|
size * len, align, 0, Some(t_md.node),
|
|
Some(~[subrange]));
|
|
@Metadata {
|
|
node: array,
|
|
data: TyDescMetadata {
|
|
hash: ty::type_id(vec_t)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn create_boxed_vec(cx: @CrateContext, vec_t: ty::t, elem_t: ty::t,
|
|
vec_ty_span: codemap::span)
|
|
-> @Metadata<TyDescMetadata> {
|
|
let fname = filename_from_span(cx, vec_ty_span);
|
|
let file_node = create_file(cx, fname);
|
|
let elem_ty_md = create_ty(cx, elem_t, vec_ty_span);
|
|
let vec_scx = create_structure(file_node,
|
|
@/*bad*/ copy ty_to_str(cx.tcx, vec_t), 0);
|
|
let size_t_type = create_basic_type(cx, ty::mk_uint(cx.tcx), vec_ty_span);
|
|
add_member(vec_scx, ~"fill", 0, sys::size_of::<libc::size_t>() as int,
|
|
sys::min_align_of::<libc::size_t>() as int, size_t_type.node);
|
|
add_member(vec_scx, ~"alloc", 0, sys::size_of::<libc::size_t>() as int,
|
|
sys::min_align_of::<libc::size_t>() as int, size_t_type.node);
|
|
let subrange = llmdnode(~[lltag(SubrangeTag), lli64(0), lli64(0)]);
|
|
let (arr_size, arr_align) = size_and_align_of(cx, elem_t);
|
|
let name = fmt!("[%s]", ty_to_str(cx.tcx, elem_t));
|
|
let data_ptr = create_composite_type(ArrayTypeTag, name, file_node.node, 0,
|
|
arr_size, arr_align, 0,
|
|
Some(elem_ty_md.node),
|
|
Some(~[subrange]));
|
|
add_member(vec_scx, ~"data", 0, 0, // clang says the size should be 0
|
|
sys::min_align_of::<u8>() as int, data_ptr);
|
|
let llnode = finish_structure(vec_scx);
|
|
let vec_md = @Metadata {
|
|
node: llnode,
|
|
data: TyDescMetadata {
|
|
hash: ty::type_id(vec_t)
|
|
}
|
|
};
|
|
|
|
let box_scx = create_structure(file_node, @fmt!("box<%s>", name), 0);
|
|
let int_t = ty::mk_int(cx.tcx);
|
|
let refcount_type = create_basic_type(cx, int_t, vec_ty_span);
|
|
add_member(box_scx, ~"refcnt", 0, sys::size_of::<uint>() as int,
|
|
sys::min_align_of::<uint>() as int, refcount_type.node);
|
|
let (vp, vpsize, vpalign) = voidptr();
|
|
add_member(box_scx, ~"tydesc", 0, vpsize, vpalign, vp);
|
|
add_member(box_scx, ~"prev", 0, vpsize, vpalign, vp);
|
|
add_member(box_scx, ~"next", 0, vpsize, vpalign, vp);
|
|
let size = 2 * sys::size_of::<int>() as int;
|
|
let align = sys::min_align_of::<int>() as int;
|
|
add_member(box_scx, ~"boxed", 0, size, align, vec_md.node);
|
|
let llnode = finish_structure(box_scx);
|
|
let mdval = @Metadata {
|
|
node: llnode,
|
|
data: TyDescMetadata {
|
|
hash: ty::type_id(elem_t)
|
|
}
|
|
};
|
|
return mdval;
|
|
}
|
|
|
|
fn create_vec_slice(cx: @CrateContext, vec_t: ty::t, elem_t: ty::t, span: span)
|
|
-> @Metadata<TyDescMetadata> {
|
|
let fname = filename_from_span(cx, span);
|
|
let file_node = create_file(cx, fname);
|
|
let elem_ty_md = create_ty(cx, elem_t, span);
|
|
let uint_type = create_basic_type(cx, ty::mk_uint(cx.tcx), span);
|
|
let elem_ptr = create_pointer_type(cx, elem_t, span, elem_ty_md);
|
|
let scx = create_structure(file_node, @ty_to_str(cx.tcx, vec_t), 0);
|
|
let (_, ptr_size, ptr_align) = voidptr();
|
|
add_member(scx, ~"vec", 0, ptr_size, ptr_align, elem_ptr.node);
|
|
add_member(scx, ~"length", 0, sys::size_of::<uint>() as int,
|
|
sys::min_align_of::<uint>() as int, uint_type.node);
|
|
let llnode = finish_structure(scx);
|
|
let mdval = @Metadata {
|
|
node: llnode,
|
|
data: TyDescMetadata {
|
|
hash: ty::type_id(vec_t)
|
|
}
|
|
};
|
|
return mdval;
|
|
}
|
|
|
|
fn create_fn_ty(cx: @CrateContext, fn_ty: ty::t, inputs: ~[ty::t], output: ty::t,
|
|
span: span) -> @Metadata<TyDescMetadata> {
|
|
let fname = filename_from_span(cx, span);
|
|
let file_node = create_file(cx, fname);
|
|
let (vp, _, _) = voidptr();
|
|
let output_md = create_ty(cx, output, span);
|
|
let output_ptr_md = create_pointer_type(cx, output, span, output_md);
|
|
let inputs_vals = do inputs.map |arg| { create_ty(cx, *arg, span).node };
|
|
let members = ~[output_ptr_md.node, vp] + inputs_vals;
|
|
let llnode = create_composite_type(SubroutineTag, ~"", file_node.node,
|
|
0, 0, 0, 0, None, Some(members));
|
|
let mdval = @Metadata {
|
|
node: llnode,
|
|
data: TyDescMetadata {
|
|
hash: ty::type_id(fn_ty)
|
|
}
|
|
};
|
|
return mdval;
|
|
}
|
|
|
|
fn create_ty(cx: @CrateContext, t: ty::t, span: span)
|
|
-> @Metadata<TyDescMetadata> {
|
|
debug!("create_ty: %?", ty::get(t));
|
|
/*let cache = get_cache(cx);
|
|
match cached_metadata::<@Metadata<TyDescMetadata>>(
|
|
cache, tg, {|md| t == md.data.hash}) {
|
|
option::Some(md) { return md; }
|
|
option::None {}
|
|
}*/
|
|
|
|
let sty = copy ty::get(t).sty;
|
|
match sty {
|
|
ty::ty_nil | ty::ty_bot | ty::ty_bool | ty::ty_int(_) | ty::ty_uint(_)
|
|
| ty::ty_float(_) => create_basic_type(cx, t, span),
|
|
ty::ty_estr(ref vstore) => {
|
|
let i8_t = ty::mk_i8(cx.tcx);
|
|
match *vstore {
|
|
ty::vstore_fixed(len) => {
|
|
create_fixed_vec(cx, t, i8_t, len as int + 1, span)
|
|
},
|
|
ty::vstore_uniq | ty::vstore_box => {
|
|
let box_md = create_boxed_vec(cx, t, i8_t, span);
|
|
create_pointer_type(cx, t, span, box_md)
|
|
}
|
|
ty::vstore_slice(_region) => {
|
|
create_vec_slice(cx, t, i8_t, span)
|
|
}
|
|
}
|
|
},
|
|
ty::ty_enum(_did, ref _substs) => {
|
|
cx.sess.span_bug(span, ~"debuginfo for enum NYI")
|
|
}
|
|
ty::ty_box(ref mt) | ty::ty_uniq(ref mt) => {
|
|
let boxed = create_ty(cx, mt.ty, span);
|
|
let box_md = create_boxed_type(cx, mt.ty, span, boxed);
|
|
create_pointer_type(cx, t, span, box_md)
|
|
},
|
|
ty::ty_evec(ref mt, ref vstore) => {
|
|
match *vstore {
|
|
ty::vstore_fixed(len) => {
|
|
create_fixed_vec(cx, t, mt.ty, len as int, span)
|
|
},
|
|
ty::vstore_uniq | ty::vstore_box => {
|
|
let box_md = create_boxed_vec(cx, t, mt.ty, span);
|
|
create_pointer_type(cx, t, span, box_md)
|
|
},
|
|
ty::vstore_slice(_region) => {
|
|
create_vec_slice(cx, t, mt.ty, span)
|
|
}
|
|
}
|
|
},
|
|
ty::ty_ptr(ref mt) => {
|
|
let pointee = create_ty(cx, mt.ty, span);
|
|
create_pointer_type(cx, t, span, pointee)
|
|
},
|
|
ty::ty_rptr(ref _region, ref _mt) => {
|
|
cx.sess.span_bug(span, ~"debuginfo for rptr NYI")
|
|
},
|
|
ty::ty_bare_fn(ref barefnty) => {
|
|
let inputs = do barefnty.sig.inputs.map |a| { a.ty };
|
|
let output = barefnty.sig.output;
|
|
create_fn_ty(cx, t, inputs, output, span)
|
|
},
|
|
ty::ty_closure(ref _closurety) => {
|
|
cx.sess.span_bug(span, ~"debuginfo for closure NYI")
|
|
},
|
|
ty::ty_trait(_did, ref _substs, ref _vstore) => {
|
|
cx.sess.span_bug(span, ~"debuginfo for trait NYI")
|
|
},
|
|
ty::ty_struct(did, ref substs) => {
|
|
let fields = ty::struct_fields(cx.tcx, did, substs);
|
|
create_struct(cx, t, fields, span)
|
|
},
|
|
ty::ty_tup(ref elements) => {
|
|
create_tuple(cx, t, *elements, span)
|
|
},
|
|
_ => cx.sess.bug(~"debuginfo: unexpected type in create_ty")
|
|
}
|
|
}
|
|
|
|
fn filename_from_span(cx: @CrateContext, sp: codemap::span) -> ~str {
|
|
/*bad*/copy cx.sess.codemap.lookup_char_pos(sp.lo).file.name
|
|
}
|
|
|
|
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)
|
|
];
|
|
return llmdnode(lldata);
|
|
}
|
|
|
|
pub fn create_local_var(bcx: block, local: @ast::local)
|
|
-> @Metadata<LocalVarMetadata> {
|
|
unsafe {
|
|
let cx = bcx.ccx();
|
|
let cache = get_cache(cx);
|
|
let tg = AutoVariableTag;
|
|
match cached_metadata::<@Metadata<LocalVarMetadata>>(
|
|
cache, tg, |md| md.data.id == local.node.id) {
|
|
option::Some(md) => return md,
|
|
option::None => ()
|
|
}
|
|
|
|
let name = match local.node.pat.node {
|
|
ast::pat_ident(_, pth, _) => ast_util::path_to_ident(pth),
|
|
// FIXME this should be handled (#2533)
|
|
_ => fail!(~"no single variable name for local")
|
|
};
|
|
let loc = cx.sess.codemap.lookup_char_pos(local.span.lo);
|
|
let ty = node_id_type(bcx, local.node.id);
|
|
let tymd = create_ty(cx, ty, local.node.ty.span);
|
|
let filemd = create_file(cx, /*bad*/copy loc.file.name);
|
|
let context = match bcx.parent {
|
|
None => create_function(bcx.fcx).node,
|
|
Some(_) => create_block(bcx).node
|
|
};
|
|
let mdnode = create_var(tg, context, *cx.sess.str_of(name),
|
|
filemd.node, loc.line as int, tymd.node);
|
|
let mdval = @Metadata {
|
|
node: mdnode,
|
|
data: LocalVarMetadata {
|
|
id: local.node.id
|
|
}
|
|
};
|
|
update_cache(cache, AutoVariableTag, local_var_metadata(mdval));
|
|
|
|
let llptr = match bcx.fcx.lllocals.find(&local.node.id) {
|
|
option::Some(&local_mem(v)) => v,
|
|
option::Some(_) => {
|
|
bcx.tcx().sess.span_bug(local.span, ~"local is bound to \
|
|
something weird");
|
|
}
|
|
option::None => {
|
|
match *bcx.fcx.lllocals.get(&local.node.pat.id) {
|
|
local_imm(v) => v,
|
|
_ => bcx.tcx().sess.span_bug(local.span, ~"local is bound to \
|
|
something weird")
|
|
}
|
|
}
|
|
};
|
|
let declargs = ~[llmdnode(~[llptr]), mdnode];
|
|
trans::build::Call(bcx, *cx.intrinsics.get(&~"llvm.dbg.declare"),
|
|
declargs);
|
|
return mdval;
|
|
}
|
|
}
|
|
|
|
pub fn create_arg(bcx: block, arg: ast::arg, sp: span)
|
|
-> Option<@Metadata<ArgumentMetadata>> {
|
|
unsafe {
|
|
let fcx = bcx.fcx, cx = *fcx.ccx;
|
|
let cache = get_cache(cx);
|
|
let tg = ArgVariableTag;
|
|
match cached_metadata::<@Metadata<ArgumentMetadata>>(
|
|
cache, ArgVariableTag, |md| md.data.id == arg.id) {
|
|
option::Some(md) => return Some(md),
|
|
option::None => ()
|
|
}
|
|
|
|
let loc = cx.sess.codemap.lookup_char_pos(sp.lo);
|
|
if loc.file.name == ~"<intrinsic>" {
|
|
return None;
|
|
}
|
|
let ty = node_id_type(bcx, arg.id);
|
|
let tymd = create_ty(cx, ty, arg.ty.span);
|
|
let filemd = create_file(cx, /*bad*/copy loc.file.name);
|
|
let context = create_function(bcx.fcx);
|
|
|
|
match arg.pat.node {
|
|
ast::pat_ident(_, path, _) => {
|
|
// XXX: This is wrong; it should work for multiple bindings.
|
|
let mdnode = create_var(
|
|
tg,
|
|
context.node,
|
|
*cx.sess.str_of(*path.idents.last()),
|
|
filemd.node,
|
|
loc.line as int,
|
|
tymd.node
|
|
);
|
|
|
|
let mdval = @Metadata {
|
|
node: mdnode,
|
|
data: ArgumentMetadata {
|
|
id: arg.id
|
|
}
|
|
};
|
|
update_cache(cache, tg, argument_metadata(mdval));
|
|
|
|
let llptr = match *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);
|
|
return Some(mdval);
|
|
}
|
|
_ => {
|
|
return None;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn update_source_pos(cx: block, s: span) {
|
|
if !cx.sess().opts.debuginfo || (*s.lo == 0 && *s.hi == 0) {
|
|
return;
|
|
}
|
|
let cm = cx.sess().codemap;
|
|
let blockmd = create_block(cx);
|
|
let loc = cm.lookup_char_pos(s.lo);
|
|
let scopedata = ~[lli32(loc.line.to_int()),
|
|
lli32(loc.col.to_int()),
|
|
blockmd.node,
|
|
llnull()];
|
|
let dbgscope = llmdnode(scopedata);
|
|
unsafe {
|
|
llvm::LLVMSetCurrentDebugLocation(trans::build::B(cx), dbgscope);
|
|
}
|
|
}
|
|
|
|
pub fn create_function(fcx: fn_ctxt) -> @Metadata<SubProgramMetadata> {
|
|
let cx = *fcx.ccx;
|
|
let dbg_cx = (/*bad*/copy cx.dbg_cx).get();
|
|
|
|
debug!("~~");
|
|
|
|
let fcx = &mut *fcx;
|
|
|
|
let sp = fcx.span.get();
|
|
debug!("%s", cx.sess.codemap.span_to_str(sp));
|
|
|
|
let (ident, ret_ty, id) = match *cx.tcx.items.get(&fcx.id) {
|
|
ast_map::node_item(item, _) => {
|
|
match item.node {
|
|
ast::item_fn(ref decl, _, _, _, _) => {
|
|
(item.ident, decl.output, item.id)
|
|
}
|
|
_ => fcx.ccx.sess.span_bug(item.span, ~"create_function: item \
|
|
bound to non-function")
|
|
}
|
|
}
|
|
ast_map::node_method(method, _, _) => {
|
|
(method.ident, method.decl.output, method.id)
|
|
}
|
|
ast_map::node_expr(expr) => {
|
|
match expr.node {
|
|
ast::expr_fn_block(ref decl, _) => {
|
|
((dbg_cx.names)(~"fn"), decl.output, expr.id)
|
|
}
|
|
_ => fcx.ccx.sess.span_bug(expr.span,
|
|
~"create_function: \
|
|
expected an expr_fn_block here")
|
|
}
|
|
}
|
|
ast_map::node_dtor(_, _, did, _) => {
|
|
((dbg_cx.names)(~"dtor"), ast_util::dtor_ty(), did.node)
|
|
}
|
|
_ => fcx.ccx.sess.bug(~"create_function: unexpected \
|
|
sort of node")
|
|
};
|
|
|
|
debug!("%?", ident);
|
|
debug!("%?", id);
|
|
|
|
let cache = get_cache(cx);
|
|
match cached_metadata::<@Metadata<SubProgramMetadata>>(
|
|
cache, SubprogramTag, |md| md.data.id == id) {
|
|
option::Some(md) => return md,
|
|
option::None => ()
|
|
}
|
|
|
|
let loc = cx.sess.codemap.lookup_char_pos(sp.lo);
|
|
let file_node = create_file(cx, copy loc.file.name).node;
|
|
let ty_node = if cx.sess.opts.extra_debuginfo {
|
|
match ret_ty.node {
|
|
ast::ty_nil => llnull(),
|
|
_ => create_ty(cx, ty::node_id_to_type(cx.tcx, id),
|
|
ret_ty.span).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(*cx.sess.str_of(ident)),
|
|
//XXX fully-qualified C++ name:
|
|
llstr(*cx.sess.str_of(ident)),
|
|
llstr(~""), //XXX MIPS name?????
|
|
file_node,
|
|
lli32(loc.line as int),
|
|
sub_node,
|
|
lli1(false), //XXX static (check export)
|
|
lli1(true), // defined in compilation unit
|
|
lli32(DW_VIRTUALITY_none), // virtual-ness
|
|
lli32(0i), //index into virt func
|
|
/*llnull()*/ lli32(0), // base type with vtbl
|
|
lli32(256), // flags
|
|
lli1(cx.sess.opts.optimize != session::No),
|
|
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 = @Metadata {
|
|
node: val,
|
|
data: SubProgramMetadata {
|
|
id: id
|
|
}
|
|
};
|
|
update_cache(cache, SubprogramTag, subprogram_metadata(mdval));
|
|
|
|
return mdval;
|
|
}
|