auto merge of #10966 : michaelwoerister/rust/prelude2, r=cmr
This PR improves the stepping experience in GDB. It contains some fine tuning of line information and makes *rustc* produce nearly the same IR/DWARF as Clang. The focus of the changes is function prologue handling which has caused some problems in the past (https://github.com/mozilla/rust/issues/9641). It seems that GDB does not properly handle function prologues when the function uses segmented stacks, i.e. it does not recognize that the `__morestack` check is part of the prologue. When setting a breakpoint like `break foo` it will set the break point before the arguments of `foo()` have been loaded and still contain bogus values. For function with the #[no_split_stack] attribute this problem has never occurred for me so I'm pretty sure that segmented stacks are the cause of the problem. @jdm mentioned that segmented stack won't be completely abandoned after all. I'd be grateful if you could tell me about what the future might bring in this regard (@brson, @cmr). Anyway, this PR should alleviate this problem at least in the case when setting breakpoints using line numbers and also make it less confusing when setting them via function names because then GDB will break *before* the first statement where one could conceivably argue that arguments need not be initialized yet. Also, a koala: 🐨 Cheers, Michael
This commit is contained in:
commit
4e77c1148f
@ -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
|
||||
|
@ -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<NodeInfo>)
|
||||
-> (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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
})
|
||||
}
|
||||
|
@ -45,8 +45,7 @@
|
||||
// debugger:whatis f64
|
||||
// check:type = f64
|
||||
// debugger:info functions _yyy
|
||||
// check:[...]
|
||||
// check:![...]_yyy()();
|
||||
// check:[...]![...]_yyy()();
|
||||
// debugger:detach
|
||||
// debugger:quit
|
||||
|
||||
|
244
src/test/debug-info/function-arg-initialization.rs
Normal file
244
src/test/debug-info/function-arg-initialization.rs
Normal file
@ -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 <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.
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
|
249
src/test/debug-info/function-prologue-stepping-no-split-stack.rs
Normal file
249
src/test/debug-info/function-prologue-stepping-no-split-stack.rs
Normal file
@ -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 <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.
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user