diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index 091614b6bc74c..745c9a2a29a7d 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -220,6 +220,7 @@ use util::common::indenter; use util::ppaux::{Repr, vec_map_to_str}; use std::hashmap::HashMap; +use std::ptr; use std::vec; use syntax::ast; use syntax::ast::Ident; @@ -2046,7 +2047,10 @@ pub fn store_arg(mut bcx: @mut Block, // 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(); + let arg_is_alloca = unsafe { llvm::LLVMIsAAllocaInst(llval) != ptr::null() }; + + let fast_path = (arg_is_alloca || !bcx.ccx().sess.opts.extra_debuginfo) + && simple_identifier(pat).is_some(); if fast_path { // Optimized path for `x: T` case. This just adopts diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index d829236a4dc52..abb3e22edb72d 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -875,8 +875,11 @@ pub fn trans_external_path(ccx: &mut CrateContext, did: ast::DefId, t: ty::t) -> } } -pub fn invoke(bcx: @mut Block, llfn: ValueRef, llargs: ~[ValueRef], - attributes: &[(uint, lib::llvm::Attribute)]) +pub fn invoke(bcx: @mut Block, + llfn: ValueRef, + llargs: ~[ValueRef], + attributes: &[(uint, lib::llvm::Attribute)], + call_info: Option) -> (ValueRef, @mut Block) { let _icx = push_ctxt("invoke_"); if bcx.unreachable { @@ -899,11 +902,18 @@ pub fn invoke(bcx: @mut Block, llfn: ValueRef, llargs: ~[ValueRef], } } let normal_bcx = sub_block(bcx, "normal return"); + let landing_pad = get_landing_pad(bcx); + + match call_info { + Some(info) => debuginfo::set_source_location(bcx.fcx, info.id, info.span), + None => debuginfo::clear_source_location(bcx.fcx) + }; + let llresult = Invoke(bcx, llfn, llargs, normal_bcx.llbb, - get_landing_pad(bcx), + landing_pad, attributes); return (llresult, normal_bcx); } else { @@ -913,6 +923,12 @@ pub fn invoke(bcx: @mut Block, llfn: ValueRef, llargs: ~[ValueRef], debug!("arg: {}", llarg); } } + + match call_info { + Some(info) => debuginfo::set_source_location(bcx.fcx, info.id, info.span), + None => debuginfo::clear_source_location(bcx.fcx) + }; + let llresult = Call(bcx, llfn, llargs, attributes); return (llresult, bcx); } @@ -1551,6 +1567,7 @@ pub fn alloca_maybe_zeroed(cx: @mut Block, ty: Type, name: &str, zero: bool) -> return llvm::LLVMGetUndef(ty.ptr_to().to_ref()); } } + debuginfo::clear_source_location(cx.fcx); let p = Alloca(cx, ty, name); if zero { let b = cx.fcx.ccx.builder(); @@ -1567,6 +1584,7 @@ pub fn arrayalloca(cx: @mut Block, ty: Type, v: ValueRef) -> ValueRef { return llvm::LLVMGetUndef(ty.to_ref()); } } + debuginfo::clear_source_location(cx.fcx); return ArrayAlloca(cx, ty, v); } @@ -1810,6 +1828,7 @@ pub fn finish_fn(fcx: @mut FunctionContext, last_bcx: @mut Block) { None => last_bcx }; build_return_block(fcx, ret_cx); + debuginfo::clear_source_location(fcx); fcx.cleanup(); } diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs index 5230a5c81b223..db193b9200ae1 100644 --- a/src/librustc/middle/trans/callee.rs +++ b/src/librustc/middle/trans/callee.rs @@ -697,7 +697,7 @@ pub fn trans_call_inner(in_cx: @mut Block, } // Invoke the actual rust fn and update bcx/llresult. - let (llret, b) = base::invoke(bcx, llfn, llargs, attrs); + let (llret, b) = base::invoke(bcx, llfn, llargs, attrs, call_info); bcx = b; llresult = llret; diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index acb308f6eee76..0783d5b418f21 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -85,6 +85,43 @@ continuation, storing all state needed to continue traversal at the type members been registered with the cache. (This implementation approach might be a tad over-engineered and may change in the future) + +## Source Locations and Line Information +In addition to data type descriptions the debugging information must also allow to map machine code +locations back to source code locations in order to be useful. This functionality is also handled in +this module. The following functions allow to control source mappings: + ++ set_source_location() ++ clear_source_location() ++ start_emitting_source_locations() + +`set_source_location()` allows to set the current source location. All IR instructions created after +a call to this function will be linked to the given source location, until another location is +specified with `set_source_location()` or the source location is cleared with +`clear_source_location()`. In the later case, subsequent IR instruction will not be linked to any +source location. As you can see, this is a stateful API (mimicking the one in LLVM), so be careful +with source locations set by previous calls. It's probably best to not rely on any specific state +being present at a given point in code. + +One topic that deserves some extra attention is *function prologues*. At the beginning of a +function's machine code there are typically a few instructions for loading argument values into +allocas and checking if there's enough stack space for the function to execute. This *prologue* is +not visible in the source code and LLVM puts a special PROLOGUE END marker into the line table at +the first non-prologue instruction of the function. In order to find out where the prologue ends, +LLVM looks for the first instruction in the function body that is linked to a source location. So, +when generating prologue instructions we have to make sure that we don't emit source location +information until the 'real' function body begins. For this reason, source location emission is +disabled by default for any new function being translated and is only activated after a call to the +third function from the list above, `start_emitting_source_locations()`. This function should be +called right before regularly starting to translate the top-level block of the given function. + +There is one exception to the above rule: `llvm.dbg.declare` instruction must be linked to the +source location of the variable being declared. For function parameters these `llvm.dbg.declare` +instructions typically occur in the middle of the prologue, however, they are ignored by LLVM's +prologue detection. The `create_argument_metadata()` and related functions take care of linking the +`llvm.dbg.declare` instructions to the correct source locations even while source location emission +is still disabled, so there is no need to do anything special with source location handling here. + */ @@ -651,7 +688,16 @@ pub fn create_function_debug_context(cx: &mut CrateContext, (function_name.clone(), file_metadata) }; - let scope_line = get_scope_line(cx, top_level_block, loc.line); + // Clang sets this parameter to the opening brace of the function's block, so let's do this too. + let scope_line = span_start(cx, top_level_block.span).line; + + // The is_local_to_unit flag indicates whether a function is local to the current compilation + // unit (i.e. if it is *static* in the C-sense). The *reachable* set should provide a good + // approximation of this, as it contains everything that might leak out of the current crate + // (by being externally visible or by being inlined into something externally visible). It might + // better to use the `exported_items` set from `driver::CrateAnalysis` in the future, but (atm) + // this set is not available in the translation pass. + let is_local_to_unit = !cx.reachable.contains(&fn_ast_id); let fn_metadata = function_name.with_c_str(|function_name| { linkage_name.with_c_str(|linkage_name| { @@ -664,7 +710,7 @@ pub fn create_function_debug_context(cx: &mut CrateContext, file_metadata, loc.line as c_uint, function_type_metadata, - false, + is_local_to_unit, true, scope_line as c_uint, FlagPrototyped as c_uint, @@ -687,6 +733,9 @@ pub fn create_function_debug_context(cx: &mut CrateContext, let arg_pats = 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); + // Clear the debug location so we don't assign them in the function prelude + set_debug_location(cx, UnknownLocation); + return FunctionDebugContext(fn_debug_context); fn get_function_signature(cx: &mut CrateContext, @@ -837,21 +886,6 @@ pub fn create_function_debug_context(cx: &mut CrateContext, return create_DIArray(DIB(cx), template_params); } - - fn get_scope_line(cx: &CrateContext, - top_level_block: &ast::Block, - default: uint) - -> uint { - match *top_level_block { - ast::Block { stmts: ref statements, .. } if statements.len() > 0 => { - span_start(cx, statements[0].span).line - } - ast::Block { expr: Some(@ref expr), .. } => { - span_start(cx, expr.span).line - } - _ => default - } - } } //=------------------------------------------------------------------------------------------------- @@ -2128,7 +2162,8 @@ fn set_debug_location(cx: &mut CrateContext, debug_location: DebugLocation) { let metadata_node; match debug_location { - KnownLocation { scope, line, col } => { + KnownLocation { scope, line, .. } => { + let col = 0; // Always set the column to zero like Clang and GCC debug!("setting debug location to {} {}", line, col); let elements = [C_i32(line as i32), C_i32(col as i32), scope, ptr::null()]; unsafe { @@ -2244,7 +2279,14 @@ fn populate_scope_map(cx: &mut CrateContext, }) } - walk_block(cx, fn_entry_block, &mut scope_stack, scope_map); + // Clang creates a separate scope for function bodies, so let's do this too + with_new_scope(cx, + fn_entry_block.span, + &mut scope_stack, + scope_map, + |cx, scope_stack, scope_map| { + walk_block(cx, fn_entry_block, scope_stack, scope_map); + }); // local helper functions for walking the AST. fn with_new_scope(cx: &mut CrateContext, diff --git a/src/librustc/middle/trans/glue.rs b/src/librustc/middle/trans/glue.rs index 2a27a7cee3c20..acbe2d13d12d0 100644 --- a/src/librustc/middle/trans/glue.rs +++ b/src/librustc/middle/trans/glue.rs @@ -429,7 +429,7 @@ pub fn trans_struct_drop(bcx: @mut Block, t: ty::t, v0: ValueRef, dtor_did: ast: add_clean(bcx, llfld_a, fld.mt.ty); } - let (_, bcx) = invoke(bcx, dtor_addr, args, []); + let (_, bcx) = invoke(bcx, dtor_addr, args, [], None); bcx }) } diff --git a/src/test/debug-info/basic-types-metadata.rs b/src/test/debug-info/basic-types-metadata.rs index 18683102b2abb..0e48b5db7ce78 100644 --- a/src/test/debug-info/basic-types-metadata.rs +++ b/src/test/debug-info/basic-types-metadata.rs @@ -45,8 +45,7 @@ // debugger:whatis f64 // check:type = f64 // debugger:info functions _yyy -// check:[...] -// check:![...]_yyy()(); +// check:[...]![...]_yyy()(); // debugger:detach // debugger:quit diff --git a/src/test/debug-info/function-arg-initialization.rs b/src/test/debug-info/function-arg-initialization.rs new file mode 100644 index 0000000000000..e0a4afd4bdfdd --- /dev/null +++ b/src/test/debug-info/function-arg-initialization.rs @@ -0,0 +1,244 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// xfail-android: FIXME(#10381) + +// This test case checks if function arguments already have the correct value when breaking at the +// first line of the function, that is if the function prologue has already been executed at the +// first line. Note that because of the __morestack part of the prologue GDB incorrectly breaks at +// before the arguments have been properly loaded when setting the breakpoint via the function name. +// Therefore the setup here sets them using line numbers (so be careful when changing this file). + +// compile-flags:-Z extra-debug-info +// debugger:set print pretty off +// debugger:break function-arg-initialization.rs:139 +// debugger:break function-arg-initialization.rs:154 +// debugger:break function-arg-initialization.rs:158 +// debugger:break function-arg-initialization.rs:162 +// debugger:break function-arg-initialization.rs:166 +// debugger:break function-arg-initialization.rs:170 +// debugger:break function-arg-initialization.rs:174 +// debugger:break function-arg-initialization.rs:178 +// debugger:break function-arg-initialization.rs:182 +// debugger:break function-arg-initialization.rs:190 +// debugger:break function-arg-initialization.rs:197 + + +// debugger:run + +// IMMEDIATE ARGS +// debugger:print a +// check:$1 = 1 +// debugger:print b +// check:$2 = true +// debugger:print c +// check:$3 = 2.5 +// debugger:continue + +// NON IMMEDIATE ARGS +// debugger:print a +// check:$4 = {a = 3, b = 4, c = 5, d = 6, e = 7, f = 8, g = 9, h = 10} +// debugger:print b +// check:$5 = {a = 11, b = 12, c = 13, d = 14, e = 15, f = 16, g = 17, h = 18} +// debugger:continue + +// BINDING +// debugger:print a +// check:$6 = 19 +// debugger:print b +// check:$7 = 20 +// debugger:print c +// check:$8 = 21.5 +// debugger:continue + +// ASSIGNMENT +// debugger:print a +// check:$9 = 22 +// debugger:print b +// check:$10 = 23 +// debugger:print c +// check:$11 = 24.5 +// debugger:continue + +// FUNCTION CALL +// debugger:print x +// check:$12 = 25 +// debugger:print y +// check:$13 = 26 +// debugger:print z +// check:$14 = 27.5 +// debugger:continue + +// EXPR +// debugger:print x +// check:$15 = 28 +// debugger:print y +// check:$16 = 29 +// debugger:print z +// check:$17 = 30.5 +// debugger:continue + +// RETURN EXPR +// debugger:print x +// check:$18 = 31 +// debugger:print y +// check:$19 = 32 +// debugger:print z +// check:$20 = 33.5 +// debugger:continue + +// ARITHMETIC EXPR +// debugger:print x +// check:$21 = 34 +// debugger:print y +// check:$22 = 35 +// debugger:print z +// check:$23 = 36.5 +// debugger:continue + +// IF EXPR +// debugger:print x +// check:$24 = 37 +// debugger:print y +// check:$25 = 38 +// debugger:print z +// check:$26 = 39.5 +// debugger:continue + +// WHILE EXPR +// debugger:print x +// check:$27 = 40 +// debugger:print y +// check:$28 = 41 +// debugger:print z +// check:$29 = 42 +// debugger:continue + +// LOOP EXPR +// debugger:print x +// check:$30 = 43 +// debugger:print y +// check:$31 = 44 +// debugger:print z +// check:$32 = 45 +// debugger:continue + +#[allow(unused_variable)]; + + + + +fn immediate_args(a: int, b: bool, c: f64) { + () +} + +struct BigStruct { + a: u64, + b: u64, + c: u64, + d: u64, + e: u64, + f: u64, + g: u64, + h: u64 +} + +fn non_immediate_args(a: BigStruct, b: BigStruct) { + () +} + +fn binding(a: i64, b: u64, c: f64) { + let x = 0; +} + +fn assignment(mut a: u64, b: u64, c: f64) { + a = b; +} + +fn function_call(x: u64, y: u64, z: f64) { + print("Hi!") +} + +fn identifier(x: u64, y: u64, z: f64) -> u64 { + x +} + +fn return_expr(x: u64, y: u64, z: f64) -> u64 { + return x; +} + +fn arithmetic_expr(x: u64, y: u64, z: f64) -> u64 { + x + y +} + +fn if_expr(x: u64, y: u64, z: f64) -> u64 { + if x + y < 1000 { + x + } else { + y + } +} + +fn while_expr(mut x: u64, y: u64, z: u64) -> u64 { + while x + y < 1000 { + x += z + } + return x; +} + +fn loop_expr(mut x: u64, y: u64, z: u64) -> u64 { + loop { + x += z; + + if x + y > 1000 { + return x; + } + } +} + +fn main() { + immediate_args(1, true, 2.5); + + non_immediate_args( + BigStruct { + a: 3, + b: 4, + c: 5, + d: 6, + e: 7, + f: 8, + g: 9, + h: 10 + }, + BigStruct { + a: 11, + b: 12, + c: 13, + d: 14, + e: 15, + f: 16, + g: 17, + h: 18 + } + ); + + binding(19, 20, 21.5); + assignment(22, 23, 24.5); + function_call(25, 26, 27.5); + identifier(28, 29, 30.5); + return_expr(31, 32, 33.5); + arithmetic_expr(34, 35, 36.5); + if_expr(37, 38, 39.5); + while_expr(40, 41, 42); + loop_expr(43, 44, 45); +} + + + diff --git a/src/test/debug-info/function-prologue-stepping-no-split-stack.rs b/src/test/debug-info/function-prologue-stepping-no-split-stack.rs new file mode 100644 index 0000000000000..b0528744cff7c --- /dev/null +++ b/src/test/debug-info/function-prologue-stepping-no-split-stack.rs @@ -0,0 +1,249 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// xfail-android: FIXME(#10381) + +// This test case checks if function arguments already have the correct value when breaking at the +// beginning of a function. Functions with the #[no_split_stack] attribute have the same prologue as +// regular C functions compiled with GCC or Clang and therefore are better handled by GDB. As a +// consequence, and as opposed to regular Rust functions, we can set the breakpoints via the +// function name (and don't have to fall back on using line numbers). + +// compile-flags:-Z extra-debug-info +// debugger:set print pretty off +// debugger:rbreak immediate_args +// debugger:rbreak binding +// debugger:rbreak assignment +// debugger:rbreak function_call +// debugger:rbreak identifier +// debugger:rbreak return_expr +// debugger:rbreak arithmetic_expr +// debugger:rbreak if_expr +// debugger:rbreak while_expr +// debugger:rbreak loop_expr +// debugger:run + +// IMMEDIATE ARGS +// debugger:print a +// check:$1 = 1 +// debugger:print b +// check:$2 = true +// debugger:print c +// check:$3 = 2.5 +// debugger:continue + +// NON IMMEDIATE ARGS +// debugger:print a +// check:$4 = {a = 3, b = 4, c = 5, d = 6, e = 7, f = 8, g = 9, h = 10} +// debugger:print b +// check:$5 = {a = 11, b = 12, c = 13, d = 14, e = 15, f = 16, g = 17, h = 18} +// debugger:continue + +// BINDING +// debugger:print a +// check:$6 = 19 +// debugger:print b +// check:$7 = 20 +// debugger:print c +// check:$8 = 21.5 +// debugger:continue + +// ASSIGNMENT +// debugger:print a +// check:$9 = 22 +// debugger:print b +// check:$10 = 23 +// debugger:print c +// check:$11 = 24.5 +// debugger:continue + +// FUNCTION CALL +// debugger:print x +// check:$12 = 25 +// debugger:print y +// check:$13 = 26 +// debugger:print z +// check:$14 = 27.5 +// debugger:continue + +// EXPR +// debugger:print x +// check:$15 = 28 +// debugger:print y +// check:$16 = 29 +// debugger:print z +// check:$17 = 30.5 +// debugger:continue + +// RETURN EXPR +// debugger:print x +// check:$18 = 31 +// debugger:print y +// check:$19 = 32 +// debugger:print z +// check:$20 = 33.5 +// debugger:continue + +// ARITHMETIC EXPR +// debugger:print x +// check:$21 = 34 +// debugger:print y +// check:$22 = 35 +// debugger:print z +// check:$23 = 36.5 +// debugger:continue + +// IF EXPR +// debugger:print x +// check:$24 = 37 +// debugger:print y +// check:$25 = 38 +// debugger:print z +// check:$26 = 39.5 +// debugger:continue + +// WHILE EXPR +// debugger:print x +// check:$27 = 40 +// debugger:print y +// check:$28 = 41 +// debugger:print z +// check:$29 = 42 +// debugger:continue + +// LOOP EXPR +// debugger:print x +// check:$30 = 43 +// debugger:print y +// check:$31 = 44 +// debugger:print z +// check:$32 = 45 +// debugger:continue + +#[allow(unused_variable)]; + +#[no_split_stack] +fn immediate_args(a: int, b: bool, c: f64) { + () +} + +struct BigStruct { + a: u64, + b: u64, + c: u64, + d: u64, + e: u64, + f: u64, + g: u64, + h: u64 +} + +#[no_split_stack] +fn non_immediate_args(a: BigStruct, b: BigStruct) { + () +} + +#[no_split_stack] +fn binding(a: i64, b: u64, c: f64) { + let x = 0; +} + +#[no_split_stack] +fn assignment(mut a: u64, b: u64, c: f64) { + a = b; +} + +#[no_split_stack] +fn function_call(x: u64, y: u64, z: f64) { + print("Hi!") +} + +#[no_split_stack] +fn identifier(x: u64, y: u64, z: f64) -> u64 { + x +} + +#[no_split_stack] +fn return_expr(x: u64, y: u64, z: f64) -> u64 { + return x; +} + +#[no_split_stack] +fn arithmetic_expr(x: u64, y: u64, z: f64) -> u64 { + x + y +} + +#[no_split_stack] +fn if_expr(x: u64, y: u64, z: f64) -> u64 { + if x + y < 1000 { + x + } else { + y + } +} + +#[no_split_stack] +fn while_expr(mut x: u64, y: u64, z: u64) -> u64 { + while x + y < 1000 { + x += z + } + return x; +} + +#[no_split_stack] +fn loop_expr(mut x: u64, y: u64, z: u64) -> u64 { + loop { + x += z; + + if x + y > 1000 { + return x; + } + } +} + +fn main() { + immediate_args(1, true, 2.5); + + non_immediate_args( + BigStruct { + a: 3, + b: 4, + c: 5, + d: 6, + e: 7, + f: 8, + g: 9, + h: 10 + }, + BigStruct { + a: 11, + b: 12, + c: 13, + d: 14, + e: 15, + f: 16, + g: 17, + h: 18 + } + ); + + binding(19, 20, 21.5); + assignment(22, 23, 24.5); + function_call(25, 26, 27.5); + identifier(28, 29, 30.5); + return_expr(31, 32, 33.5); + arithmetic_expr(34, 35, 36.5); + if_expr(37, 38, 39.5); + while_expr(40, 41, 42); + loop_expr(43, 44, 45); +} + + +