auto merge of #8855 : michaelwoerister/rust/captured_vars, r=jdm

This pull request includes
* support for variables captured in closures*,
* a fix for issue #8512: arguments of non-immediate type (structs, tuples, etc) passed by value can now be accessed correctly in GDB. (I managed to fix this by using `llvm::DIBuilder::createComplexVariable()`. ~~However, I am not sure if this relies on unstable implementation details of LLVM's debug info handling. I'll try to clarify this on the LLVM mailing list~~).
* simplification of the `debuginfo` module's public interface: the caller of functions like `create_local_var_metadata()` doesn't have to know and catch all cases when it mustn't call the function,
* a cleanup refactoring with unified handling for locals, [self] arguments, captured variables, and match bindings,
* and proper span information for self arguments.

\* However, see comment at 1d916ace13/src/test/debug-info/var-captured-in-nested-closure.rs (L62) . This is the same problem as with the fix for issue #8512 above: We are probably using `llvm.dbg.declare` in an unsupported way that works today but might not work after the next LLVM update.

Cheers,
Michael

Fixes #8512
Fixes #1341
This commit is contained in:
bors 2013-09-04 11:55:52 -07:00
commit 45c3ca72bc
17 changed files with 897 additions and 233 deletions

View File

@ -2084,6 +2084,31 @@ pub mod llvm {
ColumnNo: c_uint)
-> ValueRef;
#[fast_ffi]
pub fn LLVMDIBuilderCreateOpDeref(IntType: TypeRef) -> ValueRef;
#[fast_ffi]
pub fn LLVMDIBuilderCreateOpPlus(IntType: TypeRef) -> ValueRef;
#[fast_ffi]
pub fn LLVMDIBuilderCreateComplexVariable(Builder: DIBuilderRef,
Tag: c_uint,
Scope: ValueRef,
Name: *c_char,
File: ValueRef,
LineNo: c_uint,
Ty: ValueRef,
AddrOps: *ValueRef,
AddrOpsCount: c_uint,
ArgNo: c_uint)
-> ValueRef;
#[fast_ffi]
pub fn LLVMIsAArgument(value_ref: ValueRef) -> ValueRef;
#[fast_ffi]
pub fn LLVMIsAAllocaInst(value_ref: ValueRef) -> ValueRef;
pub fn LLVMInitializeX86TargetInfo();
pub fn LLVMInitializeX86Target();
pub fn LLVMInitializeX86TargetMC();

View File

@ -143,7 +143,7 @@ use syntax::visit;
use syntax::visit::Visitor;
use syntax::codemap::Span;
#[deriving(Encodable, Decodable)]
#[deriving(Eq, Encodable, Decodable)]
pub enum CaptureMode {
CapCopy, // Copy the value into the closure.
CapMove, // Move the value into the closure.

View File

@ -2000,19 +2000,20 @@ pub fn store_arg(mut bcx: @mut Block,
let arg_ty = node_id_type(bcx, pat.id);
add_clean(bcx, llval, arg_ty);
match simple_identifier(pat) {
Some(_) => {
// Optimized path for `x: T` case. This just adopts
// `llval` wholesale as the pointer for `x`, avoiding the
// general logic which may copy out of `llval`.
bcx.fcx.llargs.insert(pat.id, llval);
}
// Debug information (the llvm.dbg.declare intrinsic to be precise) always expects to get an
// alloca, which only is the case on the general path, so lets disable the optimized path when
// debug info is enabled.
let fast_path = !bcx.ccx().sess.opts.extra_debuginfo && simple_identifier(pat).is_some();
None => {
// General path. Copy out the values that are used in the
// pattern.
bcx = bind_irrefutable_pat(bcx, pat, llval, BindArgument);
}
if fast_path {
// Optimized path for `x: T` case. This just adopts
// `llval` wholesale as the pointer for `x`, avoiding the
// general logic which may copy out of `llval`.
bcx.fcx.llargs.insert(pat.id, llval);
} else {
// General path. Copy out the values that are used in the
// pattern.
bcx = bind_irrefutable_pat(bcx, pat, llval, BindArgument);
}
return bcx;

View File

@ -131,20 +131,6 @@ pub fn push_ctxt(s: &'static str) -> _InsnCtxt {
_InsnCtxt { _x: () }
}
fn fcx_has_nonzero_span(fcx: &FunctionContext) -> bool {
match fcx.span {
None => false,
Some(span) => *span.lo != 0 || *span.hi != 0
}
}
fn span_is_empty(opt_span: &Option<Span>) -> bool {
match *opt_span {
None => true,
Some(span) => *span.lo == 0 && *span.hi == 0
}
}
struct StatRecorder<'self> {
ccx: @mut CrateContext,
name: &'self str,
@ -1132,8 +1118,7 @@ pub fn trans_stmt(cx: @mut Block, s: &ast::Stmt) -> @mut Block {
match d.node {
ast::DeclLocal(ref local) => {
bcx = init_local(bcx, *local);
if cx.sess().opts.extra_debuginfo
&& fcx_has_nonzero_span(bcx.fcx) {
if cx.sess().opts.extra_debuginfo {
debuginfo::create_local_var_metadata(bcx, *local);
}
}
@ -1633,12 +1618,7 @@ pub fn new_fn_ctxt_w_id(ccx: @mut CrateContext,
}
};
let uses_outptr = type_of::return_uses_outptr(ccx.tcx, substd_output_type);
let debug_context = if id != -1 && ccx.sess.opts.debuginfo && !span_is_empty(&sp) {
Some(debuginfo::create_function_debug_context(ccx, id, param_substs, llfndecl))
} else {
None
};
let debug_context = debuginfo::create_function_debug_context(ccx, id, param_substs, llfndecl);
let fcx = @mut FunctionContext {
llfn: llfndecl,
@ -1784,7 +1764,7 @@ pub fn copy_args_to_allocas(fcx: @mut FunctionContext,
fcx.llself = Some(ValSelfData {v: self_val, ..slf});
add_clean(bcx, self_val, slf.t);
if fcx.ccx.sess.opts.extra_debuginfo && fcx_has_nonzero_span(fcx) {
if fcx.ccx.sess.opts.extra_debuginfo {
debuginfo::create_self_argument_metadata(bcx, slf.t, self_val);
}
}
@ -1811,7 +1791,7 @@ pub fn copy_args_to_allocas(fcx: @mut FunctionContext,
};
bcx = _match::store_arg(bcx, args[arg_n].pat, llarg);
if fcx.ccx.sess.opts.extra_debuginfo && fcx_has_nonzero_span(fcx) {
if fcx.ccx.sess.opts.extra_debuginfo {
debuginfo::create_argument_metadata(bcx, &args[arg_n]);
}
}

View File

@ -17,6 +17,7 @@ use middle::trans::base::*;
use middle::trans::build::*;
use middle::trans::common::*;
use middle::trans::datum::{Datum, INIT};
use middle::trans::debuginfo;
use middle::trans::expr;
use middle::trans::glue;
use middle::trans::type_of::*;
@ -307,7 +308,17 @@ pub fn load_environment(fcx: @mut FunctionContext,
// Load a pointer to the closure data, skipping over the box header:
let llcdata = opaque_box_body(bcx, cdata_ty, fcx.llenv);
// Populate the upvars from the environment.
// Store the pointer to closure data in an alloca for debug info because that's what the
// llvm.dbg.declare intrinsic expects
let env_pointer_alloca = if fcx.ccx.sess.opts.extra_debuginfo {
let alloc = alloc_ty(bcx, ty::mk_mut_ptr(bcx.tcx(), cdata_ty), "__debuginfo_env_ptr");
Store(bcx, llcdata, alloc);
Some(alloc)
} else {
None
};
// Populate the upvars from the environment
let mut i = 0u;
for cap_var in cap_vars.iter() {
let mut upvarptr = GEPi(bcx, llcdata, [0u, i]);
@ -317,6 +328,18 @@ pub fn load_environment(fcx: @mut FunctionContext,
}
let def_id = ast_util::def_id_of_def(cap_var.def);
fcx.llupvars.insert(def_id.node, upvarptr);
for &env_pointer_alloca in env_pointer_alloca.iter() {
debuginfo::create_captured_var_metadata(
bcx,
def_id.node,
cdata_ty,
env_pointer_alloca,
i,
sigil,
cap_var.span);
}
i += 1u;
}
}

View File

@ -228,7 +228,7 @@ pub struct FunctionContext {
ccx: @mut CrateContext,
// Used and maintained by the debuginfo module.
debug_context: Option<~debuginfo::FunctionDebugContext>
debug_context: debuginfo::FunctionDebugContext,
}
impl FunctionContext {

View File

@ -111,7 +111,7 @@ pub struct CrateContext {
// decl_gc_metadata knows whether to link to the module metadata, which
// is not emitted by LLVM's GC pass when no functions use GC.
uses_gc: bool,
dbg_cx: Option<debuginfo::DebugContext>,
dbg_cx: Option<debuginfo::CrateDebugContext>,
do_not_commit_warning_issued: bool
}
@ -161,7 +161,7 @@ impl CrateContext {
let crate_map = decl_crate_map(sess, link_meta, llmod);
let dbg_cx = if sess.opts.debuginfo {
Some(debuginfo::DebugContext::new(llmod, name.to_owned()))
Some(debuginfo::CrateDebugContext::new(llmod, name.to_owned()))
} else {
None
};

View File

@ -27,7 +27,7 @@ where possible. This will hopefully ease the adaption of this module to future L
The public API of the module is a set of functions that will insert the correct metadata into the
LLVM IR when called with the right parameters. The module is thus driven from an outside client with
functions like `debuginfo::local_var_metadata(bcx: block, local: &ast::local)`.
functions like `debuginfo::create_local_var_metadata(bcx: block, local: &ast::local)`.
Internally the module will try to reuse already created metadata by utilizing a cache. The way to
get a shared metadata node when needed is thus to just call the corresponding function in this
@ -37,9 +37,8 @@ module:
The function will take care of probing the cache for an existing node for that exact file path.
All private state used by the module is stored within a DebugContext struct, which in turn is
contained in the CrateContext.
All private state used by the module is stored within either the CrateDebugContext struct (owned by
the CrateContext) or the FunctionDebugContext (owned by the FunctionContext).
This file consists of three conceptual sections:
1. The public interface of the module
@ -72,7 +71,7 @@ use syntax::codemap::Span;
use syntax::{ast, codemap, ast_util, ast_map, opt_vec};
use syntax::parse::token::special_idents;
static DW_LANG_RUST: int = 0x9000;
static DW_LANG_RUST: c_uint = 0x9000;
static DW_TAG_auto_variable: c_uint = 0x100;
static DW_TAG_arg_variable: c_uint = 0x101;
@ -92,7 +91,7 @@ static DW_ATE_unsigned_char: c_uint = 0x08;
//=-------------------------------------------------------------------------------------------------
/// A context object for maintaining all state needed by the debuginfo module.
pub struct DebugContext {
pub struct CrateDebugContext {
priv crate_file: ~str,
priv llcontext: ContextRef,
priv builder: DIBuilderRef,
@ -101,13 +100,13 @@ pub struct DebugContext {
priv created_types: HashMap<uint, DIType>,
}
impl DebugContext {
pub fn new(llmod: ModuleRef, crate: ~str) -> DebugContext {
debug!("DebugContext::new");
impl CrateDebugContext {
pub fn new(llmod: ModuleRef, crate: ~str) -> CrateDebugContext {
debug!("CrateDebugContext::new");
let builder = unsafe { llvm::LLVMDIBuilderCreate(llmod) };
// DIBuilder inherits context from the module, so we'd better use the same one
let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) };
return DebugContext {
return CrateDebugContext {
crate_file: crate,
llcontext: llcontext,
builder: builder,
@ -118,11 +117,64 @@ impl DebugContext {
}
}
pub struct FunctionDebugContext {
priv scope_map: HashMap<ast::NodeId, DIScope>,
priv fn_metadata: DISubprogram,
priv argument_counter: uint,
priv source_locations_enabled: bool,
pub enum FunctionDebugContext {
priv FunctionDebugContext(~FunctionDebugContextData),
priv DebugInfoDisabled,
priv FunctionWithoutDebugInfo,
}
impl FunctionDebugContext {
fn get_ref<'a>(&'a self, cx: &CrateContext, span: Span) -> &'a FunctionDebugContextData {
match *self {
FunctionDebugContext(~ref data) => data,
DebugInfoDisabled => {
cx.sess.span_bug(span, "debuginfo: Error trying to access FunctionDebugContext \
although debug info is disabled!");
}
FunctionWithoutDebugInfo => {
cx.sess.span_bug(span, "debuginfo: Error trying to access FunctionDebugContext \
for function that should be ignored by debug info!");
}
}
}
fn get_mut_ref<'a>(&'a mut self,
cx: &CrateContext,
span: Span)
-> &'a mut FunctionDebugContextData {
match *self {
FunctionDebugContext(~ref mut data) => data,
DebugInfoDisabled => {
cx.sess.span_bug(span, "debuginfo: Error trying to access FunctionDebugContext \
although debug info is disabled!");
}
FunctionWithoutDebugInfo => {
cx.sess.span_bug(span, "debuginfo: Error trying to access FunctionDebugContext \
for function that should be ignored by debug info!");
}
}
}
}
struct FunctionDebugContextData {
scope_map: HashMap<ast::NodeId, DIScope>,
fn_metadata: DISubprogram,
argument_counter: uint,
source_locations_enabled: bool,
}
enum VariableAccess<'self> {
// The llptr given is an alloca containing the variable's value
DirectVariable { alloca: ValueRef },
// The llptr given is an alloca containing the start of some pointer chain leading to the
// variable's content.
IndirectVariable { alloca: ValueRef, address_operations: &'self [ValueRef] }
}
enum VariableKind {
ArgumentVariable(uint /*index*/),
LocalVariable,
CapturedVariable,
}
/// Create any deferred debug metadata nodes
@ -138,7 +190,12 @@ pub fn finalize(cx: @mut CrateContext) {
/// Creates debug information for the given local variable.
///
/// Adds the created metadata nodes directly to the crate's IR.
pub fn create_local_var_metadata(bcx: @mut Block, local: &ast::Local) {
pub fn create_local_var_metadata(bcx: @mut Block,
local: &ast::Local) {
if fn_should_be_ignored(bcx.fcx) {
return;
}
let cx = bcx.ccx();
let def_map = cx.tcx.def_map;
@ -147,10 +204,92 @@ pub fn create_local_var_metadata(bcx: @mut Block, local: &ast::Local) {
let var_ident = ast_util::path_to_ident(path_ref);
let var_type = node_id_type(bcx, node_id);
declare_local(bcx, var_ident, node_id, var_type, span);
let llptr = match bcx.fcx.lllocals.find_copy(&node_id) {
Some(v) => v,
None => {
bcx.tcx().sess.span_bug(span, fmt!("No entry in lllocals table for %?", node_id));
}
};
let scope_metadata = scope_metadata(bcx.fcx, node_id, span);
declare_local(bcx,
var_ident,
var_type,
scope_metadata,
DirectVariable { alloca: llptr },
LocalVariable,
span);
}
}
/// Creates debug information for a variable captured in a closure.
///
/// Adds the created metadata nodes directly to the crate's IR.
pub fn create_captured_var_metadata(bcx: @mut Block,
node_id: ast::NodeId,
env_data_type: ty::t,
env_pointer: ValueRef,
env_index: uint,
closure_sigil: ast::Sigil,
span: Span) {
if fn_should_be_ignored(bcx.fcx) {
return;
}
let cx = bcx.ccx();
let ast_item = cx.tcx.items.find_copy(&node_id);
let variable_ident = match ast_item {
None => {
cx.sess.span_bug(span, "debuginfo::create_captured_var_metadata() - NodeId not found");
}
Some(ast_map::node_local(ident)) => ident,
Some(ast_map::node_arg(@ast::Pat { node: ast::PatIdent(_, ref path, _), _ })) => {
ast_util::path_to_ident(path)
}
_ => {
cx.sess.span_bug(span, fmt!("debuginfo::create_captured_var_metadata() - \
Captured var-id refers to unexpected ast_map variant: %?", ast_item));
}
};
let variable_type = node_id_type(bcx, node_id);
let scope_metadata = bcx.fcx.debug_context.get_ref(cx, span).fn_metadata;
let llvm_env_data_type = type_of::type_of(cx, env_data_type);
let byte_offset_of_var_in_env = machine::llelement_offset(cx, llvm_env_data_type, env_index);
let address_operations = unsafe {
[llvm::LLVMDIBuilderCreateOpDeref(Type::i64().to_ref()),
llvm::LLVMDIBuilderCreateOpPlus(Type::i64().to_ref()),
C_i64(byte_offset_of_var_in_env as i64),
llvm::LLVMDIBuilderCreateOpDeref(Type::i64().to_ref())]
};
let address_op_count = match closure_sigil {
ast::BorrowedSigil => {
address_operations.len()
}
ast::ManagedSigil | ast::OwnedSigil => {
address_operations.len() - 1
}
};
let variable_access = IndirectVariable {
alloca: env_pointer,
address_operations: address_operations.slice_to(address_op_count)
};
declare_local(bcx,
variable_ident,
variable_type,
scope_metadata,
variable_access,
CapturedVariable,
span);
}
/// Creates debug information for a local variable introduced in the head of a match-statement arm.
///
/// Adds the created metadata nodes directly to the crate's IR.
@ -159,61 +298,84 @@ pub fn create_match_binding_metadata(bcx: @mut Block,
node_id: ast::NodeId,
variable_type: ty::t,
span: Span) {
declare_local(bcx, variable_ident, node_id, variable_type, span);
if fn_should_be_ignored(bcx.fcx) {
return;
}
let llptr = match bcx.fcx.lllocals.find_copy(&node_id) {
Some(v) => v,
None => {
bcx.tcx().sess.span_bug(span, fmt!("No entry in lllocals table for %?", node_id));
}
};
let scope_metadata = scope_metadata(bcx.fcx, node_id, span);
declare_local(bcx,
variable_ident,
variable_type,
scope_metadata,
DirectVariable { alloca: llptr },
LocalVariable,
span);
}
/// Creates debug information for the self argument of a method.
///
/// Adds the created metadata nodes directly to the crate's IR.
pub fn create_self_argument_metadata(bcx: @mut Block,
variable_type: ty::t,
type_of_self: ty::t,
llptr: ValueRef) {
assert_fcx_has_span(bcx.fcx);
let span = bcx.fcx.span.unwrap();
if fn_should_be_ignored(bcx.fcx) {
return;
}
let cx = bcx.ccx();
// Extract the span of the self argument from the method's AST
let fnitem = bcx.ccx().tcx.items.get_copy(&bcx.fcx.id);
let span = match fnitem {
ast_map::node_method(@ast::method { explicit_self: explicit_self, _ }, _, _) => {
explicit_self.span
}
ast_map::node_trait_method(
@ast::provided(
@ast::method {
explicit_self: explicit_self,
_
}),
_,
_) => {
explicit_self.span
}
_ => bcx.ccx().sess.bug(
fmt!("create_self_argument_metadata: unexpected sort of node: %?", fnitem))
};
let filename = span_start(cx, span).file.name;
let file_metadata = file_metadata(cx, filename);
let loc = span_start(cx, span);
let type_metadata = type_metadata(cx, variable_type, span);
let scope = bcx.fcx.debug_context.get_ref().fn_metadata;
let scope_metadata = bcx.fcx.debug_context.get_ref(bcx.ccx(), span).fn_metadata;
let argument_index = {
let counter = &mut bcx.fcx.debug_context.get_mut_ref().argument_counter;
let counter = &mut bcx.fcx.debug_context.get_mut_ref(bcx.ccx(), span).argument_counter;
let argument_index = *counter;
*counter += 1;
argument_index as c_uint
argument_index
};
let var_metadata = do cx.sess.str_of(special_idents::self_).to_c_str().with_ref |name| {
unsafe {
llvm::LLVMDIBuilderCreateLocalVariable(
DIB(cx),
DW_TAG_arg_variable,
scope,
name,
file_metadata,
loc.line as c_uint,
type_metadata,
false,
0,
argument_index)
}
let address_operations = &[unsafe { llvm::LLVMDIBuilderCreateOpDeref(Type::i64().to_ref()) }];
let variable_access = if unsafe { llvm::LLVMIsAAllocaInst(llptr) } != ptr::null() {
DirectVariable { alloca: llptr }
} else {
// This is not stable and may break with future LLVM versions. llptr should really always
// be an alloca. Anything else is not supported and just works by chance.
IndirectVariable { alloca: llptr, address_operations: address_operations }
};
set_debug_location(cx, DebugLocation::new(scope, loc.line, *loc.col));
unsafe {
let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd(
DIB(cx),
llptr,
var_metadata,
bcx.llbb);
llvm::LLVMSetInstDebugLocation(trans::build::B(bcx).llbuilder, instr);
}
set_debug_location(cx, UnknownLocation);
declare_local(bcx,
special_idents::self_,
type_of_self,
scope_metadata,
variable_access,
ArgumentVariable(argument_index),
span);
}
/// Creates debug information for the given function argument.
@ -221,53 +383,17 @@ pub fn create_self_argument_metadata(bcx: @mut Block,
/// Adds the created metadata nodes directly to the crate's IR.
pub fn create_argument_metadata(bcx: @mut Block,
arg: &ast::arg) {
let fcx = bcx.fcx;
let cx = fcx.ccx;
let pattern = arg.pat;
let filename = span_start(cx, pattern.span).file.name;
if fcx.id == -1 ||
fcx.span.is_none() ||
"<intrinsic>" == filename {
if fn_should_be_ignored(bcx.fcx) {
return;
}
let fcx = bcx.fcx;
let cx = fcx.ccx;
let def_map = cx.tcx.def_map;
let file_metadata = file_metadata(cx, filename);
let scope = bcx.fcx.debug_context.get_ref().fn_metadata;//create_function_metadata(fcx);
let scope_metadata = bcx.fcx.debug_context.get_ref(cx, arg.pat.span).fn_metadata;
do pat_util::pat_bindings(def_map, pattern) |_, node_id, span, path_ref| {
let ty = node_id_type(bcx, node_id);
let type_metadata = type_metadata(cx, ty, codemap::dummy_sp());
let loc = span_start(cx, span);
let ident = ast_util::path_to_ident(path_ref);
let name: &str = cx.sess.str_of(ident);
debug!("create_argument_metadata: %s", name);
let argument_index = {
let counter = &mut fcx.debug_context.get_mut_ref().argument_counter;
let argument_index = *counter;
*counter += 1;
argument_index as c_uint
};
let arg_metadata = do name.with_c_str |name| {
unsafe {
llvm::LLVMDIBuilderCreateLocalVariable(
DIB(cx),
DW_TAG_arg_variable,
scope,
name,
file_metadata,
loc.line as c_uint,
type_metadata,
false,
0,
argument_index)
}
};
do pat_util::pat_bindings(def_map, arg.pat) |_, node_id, span, path_ref| {
let llptr = match bcx.fcx.llargs.find_copy(&node_id) {
Some(v) => v,
@ -276,17 +402,28 @@ pub fn create_argument_metadata(bcx: @mut Block,
}
};
set_debug_location(cx, DebugLocation::new(scope, loc.line, *loc.col));
unsafe {
let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd(
DIB(cx),
llptr,
arg_metadata,
bcx.llbb);
llvm::LLVMSetInstDebugLocation(trans::build::B(bcx).llbuilder, instr);
if unsafe { llvm::LLVMIsAAllocaInst(llptr) } == ptr::null() {
cx.sess.span_bug(span, "debuginfo::create_argument_metadata() - \
Referenced variable location is not an alloca!");
}
set_debug_location(cx, UnknownLocation);
let argument_type = node_id_type(bcx, node_id);
let argument_ident = ast_util::path_to_ident(path_ref);
let argument_index = {
let counter = &mut fcx.debug_context.get_mut_ref(cx, span).argument_counter;
let argument_index = *counter;
*counter += 1;
argument_index
};
declare_local(bcx,
argument_ident,
argument_type,
scope_metadata,
DirectVariable { alloca: llptr },
ArgumentVariable(argument_index),
span);
}
}
@ -297,15 +434,15 @@ pub fn create_argument_metadata(bcx: @mut Block,
pub fn set_source_location(fcx: &FunctionContext,
node_id: ast::NodeId,
span: Span) {
let cx: &mut CrateContext = fcx.ccx;
if !cx.sess.opts.debuginfo || (*span.lo == 0 && *span.hi == 0) {
if fn_should_be_ignored(fcx) {
return;
}
let cx = fcx.ccx;
debug!("set_source_location: %s", cx.sess.codemap.span_to_str(span));
if fcx.debug_context.get_ref().source_locations_enabled {
if fcx.debug_context.get_ref(cx, span).source_locations_enabled {
let loc = span_start(cx, span);
let scope = scope_metadata(fcx, node_id, span);
@ -315,17 +452,35 @@ pub fn set_source_location(fcx: &FunctionContext,
}
}
/// Enables emitting source locations for the given functions.
///
/// Since we don't want source locations to be emitted for the function prelude, they are disabled
/// when beginning to translate a new function. This functions switches source location emitting on
/// and must therefore be called before the first real statement/expression of the function is
/// translated.
pub fn start_emitting_source_locations(fcx: &mut FunctionContext) {
for debug_context in fcx.debug_context.mut_iter() {
debug_context.source_locations_enabled = true;
match fcx.debug_context {
FunctionDebugContext(~ref mut data) => data.source_locations_enabled = true,
_ => { /* safe to ignore */ }
}
}
/// Creates the function-specific debug context.
///
/// Returns the FunctionDebugContext for the function which holds state needed for debug info
/// creation. The function may also return another variant of the FunctionDebugContext enum which
/// indicates why no debuginfo should be created for the function.
pub fn create_function_debug_context(cx: &mut CrateContext,
fn_ast_id: ast::NodeId,
param_substs: Option<@param_substs>,
llfn: ValueRef) -> ~FunctionDebugContext {
assert!(fn_ast_id != -1);
llfn: ValueRef) -> FunctionDebugContext {
if !cx.sess.opts.debuginfo {
return DebugInfoDisabled;
}
if fn_ast_id == -1 {
return FunctionWithoutDebugInfo;
}
let empty_generics = ast::Generics { lifetimes: opt_vec::Empty, ty_params: opt_vec::Empty };
@ -395,9 +550,18 @@ pub fn create_function_debug_context(cx: &mut CrateContext,
_) => {
(ident, fn_decl, generics, None, span)
}
ast_map::node_variant(*) |
ast_map::node_struct_ctor(*) => {
return FunctionWithoutDebugInfo;
}
_ => cx.sess.bug(fmt!("create_function_debug_context: unexpected sort of node: %?", fnitem))
};
// This can be the case for functions inlined from another crate
if span == codemap::dummy_sp() {
return FunctionWithoutDebugInfo;
}
let loc = span_start(cx, span);
let file_metadata = file_metadata(cx, loc.file.name);
@ -438,7 +602,7 @@ pub fn create_function_debug_context(cx: &mut CrateContext,
};
// Initialize fn debug context (including scope map)
let mut fn_debug_context = ~FunctionDebugContext {
let mut fn_debug_context = ~FunctionDebugContextData {
scope_map: HashMap::new(),
fn_metadata: fn_metadata,
argument_counter: 1,
@ -448,7 +612,7 @@ pub fn create_function_debug_context(cx: &mut CrateContext,
let arg_pats = do fn_decl.inputs.map |arg_ref| { arg_ref.pat };
populate_scope_map(cx, arg_pats, top_level_block, fn_metadata, &mut fn_debug_context.scope_map);
return fn_debug_context;
return FunctionDebugContext(fn_debug_context);
fn get_function_signature(cx: &mut CrateContext,
fn_ast_id: ast::NodeId,
@ -631,18 +795,26 @@ fn compile_unit_metadata(cx: @mut CrateContext) {
do "".with_c_str |flags| {
do "".with_c_str |split_name| {
unsafe {
llvm::LLVMDIBuilderCreateCompileUnit(dcx.builder,
DW_LANG_RUST as c_uint, crate_name, work_dir, producer,
llvm::LLVMDIBuilderCreateCompileUnit(
dcx.builder,
DW_LANG_RUST,
crate_name,
work_dir,
producer,
cx.sess.opts.optimize != session::No,
flags, 0, split_name);
flags,
0,
split_name);
}
}}}}};
}
fn declare_local(bcx: @mut Block,
variable_ident: ast::Ident,
node_id: ast::NodeId,
variable_type: ty::t,
scope_metadata: DIScope,
variable_access: VariableAccess,
variable_kind: VariableKind,
span: Span) {
let cx: &mut CrateContext = bcx.ccx();
@ -652,41 +824,68 @@ fn declare_local(bcx: @mut Block,
let name: &str = cx.sess.str_of(variable_ident);
let loc = span_start(cx, span);
let type_metadata = type_metadata(cx, variable_type, span);
let scope = scope_metadata(bcx.fcx, node_id, span);
let var_metadata = do name.with_c_str |name| {
unsafe {
llvm::LLVMDIBuilderCreateLocalVariable(
DIB(cx),
DW_TAG_auto_variable,
scope,
name,
file_metadata,
loc.line as c_uint,
type_metadata,
false,
0,
0)
let argument_index = match variable_kind {
ArgumentVariable(index) => index,
LocalVariable |
CapturedVariable => 0
} as c_uint;
let (var_alloca, var_metadata) = do name.with_c_str |name| {
match variable_access {
DirectVariable { alloca } => (
alloca,
unsafe {
llvm::LLVMDIBuilderCreateLocalVariable(
DIB(cx),
DW_TAG_auto_variable,
scope_metadata,
name,
file_metadata,
loc.line as c_uint,
type_metadata,
cx.sess.opts.optimize != session::No,
0,
argument_index)
}
),
IndirectVariable { alloca, address_operations } => (
alloca,
unsafe {
llvm::LLVMDIBuilderCreateComplexVariable(
DIB(cx),
DW_TAG_auto_variable,
scope_metadata,
name,
file_metadata,
loc.line as c_uint,
type_metadata,
vec::raw::to_ptr(address_operations),
address_operations.len() as c_uint,
argument_index)
}
)
}
};
let llptr = match bcx.fcx.lllocals.find_copy(&node_id) {
Some(v) => v,
None => {
bcx.tcx().sess.span_bug(span, fmt!("No entry in lllocals table for %?", node_id));
}
};
set_debug_location(cx, DebugLocation::new(scope, loc.line, *loc.col));
set_debug_location(cx, DebugLocation::new(scope_metadata, loc.line, *loc.col));
unsafe {
let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd(
DIB(cx),
llptr,
var_alloca,
var_metadata,
bcx.llbb);
llvm::LLVMSetInstDebugLocation(trans::build::B(bcx).llbuilder, instr);
}
match variable_kind {
ArgumentVariable(_) | CapturedVariable => {
assert!(!bcx.fcx.debug_context.get_ref(cx, span).source_locations_enabled);
set_debug_location(cx, UnknownLocation);
}
_ => { /* fallthrough */ }
}
}
fn file_metadata(cx: &mut CrateContext, full_path: &str) -> DIFile {
@ -720,13 +919,9 @@ fn file_metadata(cx: &mut CrateContext, full_path: &str) -> DIFile {
/// Finds the scope metadata node for the given AST node.
fn scope_metadata(fcx: &FunctionContext,
node_id: ast::NodeId,
span: Span) -> DIScope {
if fcx.debug_context.is_none() {
fcx.ccx.sess.span_bug(span, "debuginfo: FunctionDebugContext should be initialized \
but is not!");
}
let scope_map = &fcx.debug_context.get_ref().scope_map;
span: Span)
-> DIScope {
let scope_map = &fcx.debug_context.get_ref(fcx.ccx, span).scope_map;
match scope_map.find_copy(&node_id) {
Some(scope_metadata) => scope_metadata,
@ -1260,30 +1455,31 @@ fn vec_slice_metadata(cx: &mut CrateContext,
}
}
fn bare_fn_metadata(cx: &mut CrateContext,
_fn_ty: ty::t,
inputs: ~[ty::t],
output: ty::t,
span: Span)
-> DICompositeType {
debug!("bare_fn_metadata: %?", ty::get(_fn_ty));
fn subroutine_type_metadata(cx: &mut CrateContext,
signature: &ty::FnSig,
span: Span)
-> DICompositeType {
let loc = span_start(cx, span);
let file_metadata = file_metadata(cx, loc.file.name);
let nil_pointer_type_metadata = type_metadata(cx, ty::mk_nil_ptr(cx.tcx), span);
let output_metadata = type_metadata(cx, output, span);
let output_ptr_metadata = pointer_type_metadata(cx, output, output_metadata);
let mut signature_metadata: ~[DIType] = vec::with_capacity(signature.inputs.len() + 1);
let inputs_vals = do inputs.map |arg| { type_metadata(cx, *arg, span) };
let members = ~[output_ptr_metadata, nil_pointer_type_metadata] + inputs_vals;
// return type
signature_metadata.push(match ty::get(signature.output).sty {
ty::ty_nil => ptr::null(),
_ => type_metadata(cx, signature.output, span)
});
// regular arguments
for &argument_type in signature.inputs.iter() {
signature_metadata.push(type_metadata(cx, argument_type, span));
}
return unsafe {
llvm::LLVMDIBuilderCreateSubroutineType(
DIB(cx),
file_metadata,
create_DIArray(DIB(cx), members))
create_DIArray(DIB(cx), signature_metadata))
};
}
@ -1407,13 +1603,10 @@ fn type_metadata(cx: &mut CrateContext,
pointer_type_metadata(cx, t, pointee)
},
ty::ty_bare_fn(ref barefnty) => {
let inputs = barefnty.sig.inputs.map(|a| *a);
let output = barefnty.sig.output;
bare_fn_metadata(cx, t, inputs, output, span)
subroutine_type_metadata(cx, &barefnty.sig, span)
},
ty::ty_closure(ref _closurety) => {
cx.sess.span_note(span, "debuginfo for closure NYI");
unimplemented_type_metadata(cx, t)
ty::ty_closure(ref closurety) => {
subroutine_type_metadata(cx, &closurety.sig, span)
},
ty::ty_trait(_did, ref _substs, ref _vstore, _, _bounds) => {
cx.sess.span_note(span, "debuginfo for trait NYI");
@ -1458,7 +1651,6 @@ fn set_debug_location(cx: &mut CrateContext, debug_location: DebugLocation) {
return;
}
let metadata_node;
match debug_location {
@ -1507,7 +1699,7 @@ fn bytes_to_bits(bytes: uint) -> c_ulonglong {
}
#[inline]
fn dbg_cx<'a>(cx: &'a mut CrateContext) -> &'a mut DebugContext {
fn dbg_cx<'a>(cx: &'a mut CrateContext) -> &'a mut CrateDebugContext {
cx.dbg_cx.get_mut_ref()
}
@ -1524,6 +1716,13 @@ fn assert_fcx_has_span(fcx: &FunctionContext) {
}
}
fn fn_should_be_ignored(fcx: &FunctionContext) -> bool {
match fcx.debug_context {
FunctionDebugContext(_) => false,
_ => true
}
}
// This procedure builds the *scope map* for a given function, which maps any given ast::NodeId in
// the function's AST to the correct DIScope metadata instance.
//

View File

@ -73,7 +73,7 @@ pub enum ast_node {
node_variant(variant, @item, @path),
node_expr(@Expr),
node_stmt(@Stmt),
node_arg,
node_arg(@Pat),
node_local(Ident),
node_block(Block),
node_struct_ctor(@struct_def, @item, @path),
@ -171,7 +171,7 @@ impl Ctx {
sp: codemap::Span,
id: NodeId) {
for a in decl.inputs.iter() {
self.map.insert(a.id, node_arg);
self.map.insert(a.id, node_arg(a.pat));
}
visit::walk_fn(self, fk, decl, body, sp, id, ());
}
@ -487,8 +487,8 @@ pub fn node_id_to_str(map: map, id: NodeId, itr: @ident_interner) -> ~str {
fmt!("stmt %s (id=%?)",
pprust::stmt_to_str(stmt, itr), id)
}
Some(&node_arg) => {
fmt!("arg (id=%?)", id)
Some(&node_arg(pat)) => {
fmt!("arg %s (id=%?)", pprust::pat_to_str(pat, itr), id)
}
Some(&node_local(ident)) => {
fmt!("local (id=%?, name=%s)", id, itr.get(ident.name))

View File

@ -724,3 +724,39 @@ extern "C" LLVMValueRef LLVMDIBuilderCreateTemplateTypeParameter(
LineNo,
ColumnNo));
}
extern "C" LLVMValueRef LLVMDIBuilderCreateOpDeref(LLVMTypeRef IntTy)
{
return LLVMConstInt(IntTy, DIBuilder::OpDeref, true);
}
extern "C" LLVMValueRef LLVMDIBuilderCreateOpPlus(LLVMTypeRef IntTy)
{
return LLVMConstInt(IntTy, DIBuilder::OpPlus, true);
}
extern "C" LLVMValueRef LLVMDIBuilderCreateComplexVariable(
DIBuilderRef Builder,
unsigned Tag,
LLVMValueRef Scope,
const char *Name,
LLVMValueRef File,
unsigned LineNo,
LLVMValueRef Ty,
LLVMValueRef* AddrOps,
unsigned AddrOpsCount,
unsigned ArgNo)
{
llvm::ArrayRef<llvm::Value*> addr_ops((llvm::Value**)AddrOps, AddrOpsCount);
return wrap(Builder->createComplexVariable(
Tag,
unwrapDI<DIDescriptor>(Scope),
Name,
unwrapDI<DIFile>(File),
LineNo,
unwrapDI<DIType>(Ty),
addr_ops,
ArgNo
));
}

View File

@ -608,6 +608,9 @@ LLVMDIBuilderCreateEnumerator
LLVMDIBuilderCreateEnumerationType
LLVMDIBuilderCreateUnionType
LLVMDIBuilderCreateTemplateTypeParameter
LLVMDIBuilderCreateOpDeref
LLVMDIBuilderCreateOpPlus
LLVMDIBuilderCreateComplexVariable
LLVMSetUnnamedAddr
LLVMRustAddPass
LLVMRustAddAnalysisPasses

View File

@ -0,0 +1,99 @@
// Copyright 2013 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.
// compile-flags:-Z extra-debug-info
// debugger:break zzz
// debugger:run
// debugger:finish
// debugger:print s
// check:$1 = {a = 1, b = 2.5}
// debugger:continue
// debugger:finish
// debugger:print x
// check:$2 = {a = 3, b = 4.5}
// debugger:print y
// check:$3 = 5
// debugger:print z
// check:$4 = 6.5
// debugger:continue
// debugger:finish
// debugger:print a
// check:$5 = {7, 8, 9.5, 10.5}
// debugger:continue
// debugger:finish
// debugger:print a
// check:$6 = {11.5, 12.5, 13, 14}
// debugger:continue
// debugger:finish
// debugger:print x
// check:$7 = {{Case1, x = 0, y = 8970181431921507452}, {Case1, 0, 2088533116, 2088533116}}
// debugger:continue
#[deriving(Clone)]
struct Struct {
a: int,
b: float
}
#[deriving(Clone)]
struct StructStruct {
a: Struct,
b: Struct
}
fn fun(s: Struct) {
zzz();
}
fn fun_fun(StructStruct { a: x, b: Struct { a: y, b: z } }: StructStruct) {
zzz();
}
fn tup(a: (int, uint, float, float)) {
zzz();
}
struct Newtype(float, float, int, uint);
fn new_type(a: Newtype) {
zzz();
}
// The first element is to ensure proper alignment, irrespective of the machines word size. Since
// the size of the discriminant value is machine dependent, this has be taken into account when
// datatype layout should be predictable as in this case.
enum Enum {
Case1 { x: i64, y: i64 },
Case2 (i64, i32, i32),
}
fn by_val_enum(x: Enum) {
zzz();
}
fn main() {
fun(Struct { a: 1, b: 2.5 });
fun_fun(StructStruct { a: Struct { a: 3, b: 4.5 }, b: Struct { a: 5, b: 6.5 } });
tup((7, 8, 9.5, 10.5));
new_type(Newtype(11.5, 12.5, 13, 14));
// 0b0111110001111100011111000111110001111100011111000111110001111100 = 8970181431921507452
// 0b01111100011111000111110001111100 = 2088533116
// 0b0111110001111100 = 31868
// 0b01111100 = 124
by_val_enum(Case1 { x: 0, y: 8970181431921507452 });
}
fn zzz() {()}

View File

@ -0,0 +1,79 @@
// Copyright 2013 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.
// compile-flags:-Z extra-debug-info
// debugger:break zzz
// debugger:run
// debugger:finish
// debugger:print self
// check:$1 = 1111
// debugger:continue
// debugger:finish
// debugger:print self
// check:$2 = {x = 2222, y = 3333}
// debugger:continue
// debugger:finish
// debugger:print self
// check:$3 = {4444.5, 5555, 6666, 7777.5}
// debugger:continue
// debugger:finish
// debugger:print self->val
// check:$4 = 8888
// debugger:continue
trait Trait {
fn method(self) -> Self;
}
impl Trait for int {
fn method(self) -> int {
zzz();
self
}
}
struct Struct {
x: uint,
y: uint,
}
impl Trait for Struct {
fn method(self) -> Struct {
zzz();
self
}
}
impl Trait for (float, int, int, float) {
fn method(self) -> (float, int, int, float) {
zzz();
self
}
}
impl Trait for @int {
fn method(self) -> @int {
zzz();
self
}
}
fn main() {
let _ = (1111 as int).method();
let _ = Struct { x: 2222, y: 3333 }.method();
let _ = (4444.5, 5555, 6666, 7777.5).method();
let _ = (@8888).method();
}
fn zzz() {()}

View File

@ -0,0 +1,56 @@
// Copyright 2013 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.
// compile-flags:-Z extra-debug-info
// debugger:break zzz
// debugger:run
// debugger:finish
// debugger:print constant
// check:$1 = 1
// debugger:print a_struct
// check:$2 = {a = -2, b = 3.5, c = 4}
// debugger:print *owned
// check:$3 = 5
// debugger:print managed->val
// check:$4 = 6
#[allow(unused_variable)];
struct Struct {
a: int,
b: float,
c: uint
}
fn main() {
let constant = 1;
let a_struct = Struct {
a: -2,
b: 3.5,
c: 4
};
let owned = ~5;
let managed = @6;
let closure: @fn() = || {
zzz();
do_something(&constant, &a_struct.a, owned, managed);
};
closure();
}
fn do_something(_: &int, _:&int, _:&int, _:&int) {
}
fn zzz() {()}

View File

@ -0,0 +1,87 @@
// Copyright 2013 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.
// compile-flags:-Z extra-debug-info
// debugger:break zzz
// debugger:run
// debugger:finish
// debugger:print variable
// check:$1 = 1
// debugger:print constant
// check:$2 = 2
// debugger:print a_struct
// check:$3 = {a = -3, b = 4.5, c = 5}
// debugger:print *struct_ref
// check:$4 = {a = -3, b = 4.5, c = 5}
// debugger:print *owned
// check:$5 = 6
// debugger:print managed->val
// check:$6 = 7
// debugger:print closure_local
// check:$7 = 8
// debugger:continue
// debugger:finish
// debugger:print variable
// check:$8 = 1
// debugger:print constant
// check:$9 = 2
// debugger:print a_struct
// check:$10 = {a = -3, b = 4.5, c = 5}
// debugger:print *struct_ref
// check:$11 = {a = -3, b = 4.5, c = 5}
// debugger:print *owned
// check:$12 = 6
// debugger:print managed->val
// check:$13 = 7
// debugger:print closure_local
// check:$14 = 8
// debugger:continue
#[allow(unused_variable)];
struct Struct {
a: int,
b: float,
c: uint
}
fn main() {
let mut variable = 1;
let constant = 2;
let a_struct = Struct {
a: -3,
b: 4.5,
c: 5
};
let struct_ref = &a_struct;
let owned = ~6;
let managed = @7;
let closure = || {
let closure_local = 8;
let nested_closure = || {
zzz();
variable = constant + a_struct.a + struct_ref.a + *owned + *managed + closure_local;
};
zzz();
nested_closure();
};
closure();
}
fn zzz() {()}

View File

@ -8,30 +8,47 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Does not work yet, see issue #8512
// xfail-test
// compile-flags:-Z extra-debug-info
// debugger:break zzz
// debugger:run
// debugger:finish
// debugger:print s
// check:$1 = {a = 1, b = 2.5}
// debugger:continue
#[deriving(Clone)]
// debugger:print constant
// check:$1 = 1
// debugger:print a_struct
// check:$2 = {a = -2, b = 3.5, c = 4}
// debugger:print *owned
// check:$3 = 5
#[allow(unused_variable)];
struct Struct {
a: int,
b: float
}
fn fun(s: Struct) {
zzz();
b: float,
c: uint
}
fn main() {
fun(Struct { a: 1, b: 2.5 });
let constant = 1;
let a_struct = Struct {
a: -2,
b: 3.5,
c: 4
};
let owned = ~5;
let closure: ~fn() = || {
zzz();
do_something(&constant, &a_struct.a, owned);
};
closure();
}
fn do_something(_: &int, _:&int, _:&int) {
}
fn zzz() {()}

View File

@ -0,0 +1,59 @@
// Copyright 2013 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.
// compile-flags:-Z extra-debug-info
// debugger:break zzz
// debugger:run
// debugger:finish
// debugger:print variable
// check:$1 = 1
// debugger:print constant
// check:$2 = 2
// debugger:print a_struct
// check:$3 = {a = -3, b = 4.5, c = 5}
// debugger:print *struct_ref
// check:$4 = {a = -3, b = 4.5, c = 5}
// debugger:print *owned
// check:$5 = 6
// debugger:print managed->val
// check:$6 = 7
#[allow(unused_variable)];
struct Struct {
a: int,
b: float,
c: uint
}
fn main() {
let mut variable = 1;
let constant = 2;
let a_struct = Struct {
a: -3,
b: 4.5,
c: 5
};
let struct_ref = &a_struct;
let owned = ~6;
let managed = @7;
let closure = || {
zzz();
variable = constant + a_struct.a + struct_ref.a + *owned + *managed;
};
closure();
}
fn zzz() {()}