diff --git a/Makefile.in b/Makefile.in index ec08f124b728..aab593f3046a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -353,12 +353,24 @@ $(foreach build,$(CFG_TARGET_TRIPLES), \ # Builds a functional Rustc for the given host. ###################################################################### +define DEF_RUSTC_STAGE_TARGET +# $(1) == architecture +# $(2) == stage + +rustc-stage$(2)-H-$(1): \ + $$(foreach target,$$(CFG_TARGET_TRIPLES), \ + $$(SREQ$(2)_T_$$(target)_H_$(1))) + +endef + +$(foreach host,$(CFG_TARGET_TRIPLES), \ + $(eval $(foreach stage,1 2 3, \ + $(eval $(call DEF_RUSTC_STAGE_TARGET,$(host),$(stage)))))) + define DEF_RUSTC_TARGET # $(1) == architecture -rustc-H-$(1): \ - $$(foreach target,$$(CFG_TARGET_TRIPLES), \ - $$(SREQ3_T_$$(target)_H_$(1))) +rustc-H-$(1): rustc-stage3-H-$(1) endef $(foreach host,$(CFG_TARGET_TRIPLES), \ diff --git a/src/comp/back/upcall.rs b/src/comp/back/upcall.rs index 419a7fdc34c2..71f33ccb3729 100644 --- a/src/comp/back/upcall.rs +++ b/src/comp/back/upcall.rs @@ -27,7 +27,8 @@ dynastack_free: ValueRef, alloc_c_stack: ValueRef, call_shim_on_c_stack: ValueRef, - rust_personality: ValueRef}; + rust_personality: ValueRef, + reset_stack_limit: ValueRef}; fn declare_upcalls(targ_cfg: @session::config, _tn: type_names, @@ -89,7 +90,8 @@ fn decl(llmod: ModuleRef, name: str, tys: [TypeRef], rv: TypeRef) -> // arguments: void *args, void *fn_ptr [T_ptr(T_i8()), T_ptr(T_i8())], int_t), - rust_personality: d("rust_personality", [], T_i32()) + rust_personality: d("rust_personality", [], T_i32()), + reset_stack_limit: dv("reset_stack_limit", []) }; } // diff --git a/src/comp/middle/mut.rs b/src/comp/middle/mut.rs index 12598f3729bf..63dadd347e16 100644 --- a/src/comp/middle/mut.rs +++ b/src/comp/middle/mut.rs @@ -150,6 +150,7 @@ fn visit_decl(cx: @ctx, d: @decl, &&e: (), v: visit::vt<()>) { fn visit_expr(cx: @ctx, ex: @expr, &&e: (), v: visit::vt<()>) { alt ex.node { expr_call(f, args, _) { check_call(cx, f, args); } + expr_bind(f, args) { check_bind(cx, f, args); } expr_swap(lhs, rhs) { check_lval(cx, lhs, msg_assign); check_lval(cx, rhs, msg_assign); @@ -230,6 +231,30 @@ fn check_call(cx: @ctx, f: @expr, args: [@expr]) { } } +fn check_bind(cx: @ctx, f: @expr, args: [option::t<@expr>]) { + let arg_ts = ty::ty_fn_args(cx.tcx, ty::expr_ty(cx.tcx, f)); + let i = 0u; + for arg in args { + alt arg { + some(expr) { + alt (alt arg_ts[i].mode { + by_mut_ref. { some("by mutable reference") } + by_move. { some("by move") } + _ { none } + }) { + some(name) { + cx.tcx.sess.span_err( + expr.span, "can not bind an argument passed " + name); + } + none. {} + } + } + _ {} + } + i += 1u; + } +} + fn is_immutable_def(def: def) -> option::t { alt def { def_fn(_, _) | def_mod(_) | def_native_mod(_) | def_const(_) | diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index 6804434bb4ce..107c53cfac37 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -3462,6 +3462,12 @@ fn trans_bind_thunk(cx: @local_ctxt, sp: span, incoming_fty: ty::t, bcx = bound_arg.bcx; let val = bound_arg.val; if out_arg.mode == ast::by_val { val = Load(bcx, val); } + if out_arg.mode == ast::by_copy { + let {bcx: cx, val: alloc} = alloc_ty(bcx, out_arg.ty); + bcx = memmove_ty(cx, alloc, val, out_arg.ty); + bcx = take_ty(bcx, alloc, out_arg.ty); + val = alloc; + } // If the type is parameterized, then we need to cast the // type we actually have to the parameterized out type. if ty::type_contains_params(cx.ccx.tcx, out_arg.ty) { @@ -3904,6 +3910,11 @@ fn trans_landing_pad(bcx: @block_ctxt, // The landing pad block is a cleanup SetCleanup(bcx, llpad); + // Because we may have unwound across a stack boundary, we must call into + // the runtime to figure out which stack segment we are on and place the + // stack limit back into the TLS. + Call(bcx, bcx_ccx(bcx).upcalls.reset_stack_limit, []); + // FIXME: This seems like a very naive and redundant way to generate the // landing pads, as we're re-generating all in-scope cleanups for each // function call. Probably good optimization opportunities here. @@ -4531,7 +4542,9 @@ fn zero_alloca(cx: @block_ctxt, llptr: ValueRef, t: ty::t) fn trans_stmt(cx: @block_ctxt, s: ast::stmt) -> @block_ctxt { // FIXME Fill in cx.sp - add_span_comment(cx, s.span, stmt_to_str(s)); + if (!bcx_ccx(cx).sess.get_opts().no_asm_comments) { + add_span_comment(cx, s.span, stmt_to_str(s)); + } let bcx = cx; alt s.node { diff --git a/src/comp/middle/trans_build.rs b/src/comp/middle/trans_build.rs index 02894769ae69..b7756b0902d8 100644 --- a/src/comp/middle/trans_build.rs +++ b/src/comp/middle/trans_build.rs @@ -515,7 +515,9 @@ fn add_span_comment(bcx: @block_ctxt, sp: span, text: str) { fn add_comment(bcx: @block_ctxt, text: str) { let ccx = bcx_ccx(bcx); if (!ccx.sess.get_opts().no_asm_comments) { - let comment_text = "; " + text; + check str::is_not_empty("$"); + let sanitized = str::replace(text, "$", ""); + let comment_text = "; " + sanitized; let asm = str::as_buf(comment_text, { |c| str::as_buf("", { |e| llvm::LLVMConstInlineAsm(T_fn([], T_void()), c, e, 0, 0)})}); diff --git a/src/comp/middle/tstate/pre_post_conditions.rs b/src/comp/middle/tstate/pre_post_conditions.rs index d0ac63704d78..d8e57c909866 100644 --- a/src/comp/middle/tstate/pre_post_conditions.rs +++ b/src/comp/middle/tstate/pre_post_conditions.rs @@ -452,6 +452,10 @@ fn find_pre_post_expr(fcx: fn_ctxt, e: @expr) { expr_alt(ex, alts) { find_pre_post_expr(fcx, ex); fn do_an_alt(fcx: fn_ctxt, an_alt: arm) -> pre_and_post { + alt an_alt.guard { + some(e) { find_pre_post_expr(fcx, e); } + _ {} + } find_pre_post_block(fcx, an_alt.body); ret block_pp(fcx.ccx, an_alt.body); } diff --git a/src/comp/middle/tstate/states.rs b/src/comp/middle/tstate/states.rs index ed84520a6b9c..aa7977d142d3 100644 --- a/src/comp/middle/tstate/states.rs +++ b/src/comp/middle/tstate/states.rs @@ -530,6 +530,12 @@ fn find_pre_post_state_expr(fcx: fn_ctxt, pres: prestate, e: @expr) -> bool { if vec::len(alts) > 0u { a_post = false_postcond(num_constrs); for an_alt: arm in alts { + alt an_alt.guard { + some(e) { + changed |= find_pre_post_state_expr(fcx, e_post, e); + } + _ {} + } changed |= find_pre_post_state_block(fcx, e_post, an_alt.body); intersect(a_post, block_poststate(fcx.ccx, an_alt.body)); diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs index b5b5559f4aa1..022b249674b9 100644 --- a/src/comp/middle/ty.rs +++ b/src/comp/middle/ty.rs @@ -1299,23 +1299,23 @@ fn type_autoderef(cx: ctxt, t: ty::t) -> ty::t { fn hash_type_structure(st: sty) -> uint { fn hash_uint(id: uint, n: uint) -> uint { let h = id; - h += h << 5u + n; + h += (h << 5u) + n; ret h; } fn hash_def(id: uint, did: ast::def_id) -> uint { let h = id; - h += h << 5u + (did.crate as uint); - h += h << 5u + (did.node as uint); + h += (h << 5u) + (did.crate as uint); + h += (h << 5u) + (did.node as uint); ret h; } fn hash_subty(id: uint, subty: t) -> uint { let h = id; - h += h << 5u + hash_ty(subty); + h += (h << 5u) + hash_ty(subty); ret h; } fn hash_type_constr(id: uint, c: @type_constr) -> uint { let h = id; - h += h << 5u + hash_def(h, c.node.id); + h += (h << 5u) + hash_def(h, c.node.id); ret hash_type_constr_args(h, c.node.args); } fn hash_type_constr_args(id: uint, args: [@ty_constr_arg]) -> uint { @@ -1338,8 +1338,8 @@ fn hash_type_constr_args(id: uint, args: [@ty_constr_arg]) -> uint { fn hash_fn(id: uint, args: [arg], rty: t) -> uint { let h = id; - for a: arg in args { h += h << 5u + hash_ty(a.ty); } - h += h << 5u + hash_ty(rty); + for a: arg in args { h += (h << 5u) + hash_ty(a.ty); } + h += (h << 5u) + hash_ty(rty); ret h; } alt st { @@ -1366,19 +1366,19 @@ fn hash_fn(id: uint, args: [arg], rty: t) -> uint { ty_str. { ret 17u; } ty_tag(did, tys) { let h = hash_def(18u, did); - for typ: t in tys { h += h << 5u + hash_ty(typ); } + for typ: t in tys { h += (h << 5u) + hash_ty(typ); } ret h; } ty_box(mt) { ret hash_subty(19u, mt.ty); } ty_vec(mt) { ret hash_subty(21u, mt.ty); } ty_rec(fields) { let h = 26u; - for f: field in fields { h += h << 5u + hash_ty(f.mt.ty); } + for f: field in fields { h += (h << 5u) + hash_ty(f.mt.ty); } ret h; } ty_tup(ts) { let h = 25u; - for tt in ts { h += h << 5u + hash_ty(tt); } + for tt in ts { h += (h << 5u) + hash_ty(tt); } ret h; } @@ -1389,7 +1389,7 @@ fn hash_fn(id: uint, args: [arg], rty: t) -> uint { ty_native_fn(args, rty) { ret hash_fn(28u, args, rty); } ty_obj(methods) { let h = 29u; - for m: method in methods { h += h << 5u + str::hash(m.ident); } + for m: method in methods { h += (h << 5u) + str::hash(m.ident); } ret h; } ty_var(v) { ret hash_uint(30u, v as uint); } @@ -1400,15 +1400,15 @@ fn hash_fn(id: uint, args: [arg], rty: t) -> uint { ty_ptr(mt) { ret hash_subty(35u, mt.ty); } ty_res(did, sub, tps) { let h = hash_subty(hash_def(18u, did), sub); - for tp: t in tps { h += h << 5u + hash_ty(tp); } + for tp: t in tps { h += (h << 5u) + hash_ty(tp); } ret h; } ty_constr(t, cs) { let h = 36u; - for c: @type_constr in cs { h += h << 5u + hash_type_constr(h, c); } + for c: @type_constr in cs { h += (h << 5u) + hash_type_constr(h, c); } ret h; } - ty_uniq(mt) { let h = 37u; h += h << 5u + hash_ty(mt.ty); ret h; } + ty_uniq(mt) { let h = 37u; h += (h << 5u) + hash_ty(mt.ty); ret h; } } } @@ -1416,7 +1416,7 @@ fn hash_type_info(st: sty, cname_opt: option::t) -> uint { let h = hash_type_structure(st); alt cname_opt { none. {/* no-op */ } - some(s) { h += h << 5u + str::hash(s); } + some(s) { h += (h << 5u) + str::hash(s); } } ret h; } diff --git a/src/comp/syntax/ast_util.rs b/src/comp/syntax/ast_util.rs index e9b89b59cd51..60b3f2b6fa40 100644 --- a/src/comp/syntax/ast_util.rs +++ b/src/comp/syntax/ast_util.rs @@ -193,10 +193,13 @@ fn is_constraint_arg(e: @expr) -> bool { fn eq_ty(&&a: @ty, &&b: @ty) -> bool { ret std::box::ptr_eq(a, b); } -fn hash_ty(&&t: @ty) -> uint { ret t.span.lo << 16u + t.span.hi; } +fn hash_ty(&&t: @ty) -> uint { + let res = (t.span.lo << 16u) + t.span.hi; + ret res; +} fn hash_def_id(&&id: def_id) -> uint { - id.crate as uint << 16u + (id.node as uint) + (id.crate as uint << 16u) + (id.node as uint) } fn eq_def_id(&&a: def_id, &&b: def_id) -> bool { diff --git a/src/etc/gen-intrinsics b/src/etc/gen-intrinsics index 757df61596c7..660c367991b6 100755 --- a/src/etc/gen-intrinsics +++ b/src/etc/gen-intrinsics @@ -17,10 +17,11 @@ do -Isrc/rt/arch/$ARCH -fno-stack-protector \ -o src/rt/intrinsics/intrinsics.$ARCH.ll.in \ src/rt/intrinsics/intrinsics.cpp - sed -i \ + sed -i .orig \ -e 's/^target datalayout =/; target datalayout =/' \ src/rt/intrinsics/intrinsics.$ARCH.ll.in - sed -i \ + sed -i .orig \ -e 's/^target triple = "[^"]*"/target triple = "@CFG_TARGET_TRIPLE@"/' \ src/rt/intrinsics/intrinsics.$ARCH.ll.in -done \ No newline at end of file + rm src/rt/intrinsics/intrinsics.$ARCH.ll.in.orig +done diff --git a/src/libstd/map.rs b/src/libstd/map.rs index c802b5b889ff..a4ef46740174 100644 --- a/src/libstd/map.rs +++ b/src/libstd/map.rs @@ -137,21 +137,25 @@ fn make_buckets(nbkts: uint) -> [mutable bucket] { // is always a power of 2), so that all buckets are probed for a // fixed key. - fn hashl(n: uint, _nbkts: uint) -> uint { ret (n >>> 16u) * 2u + 1u; } - fn hashr(n: uint, _nbkts: uint) -> uint { ret 0x0000_ffff_u & n; } - fn hash(h: uint, nbkts: uint, i: uint) -> uint { - ret (hashl(h, nbkts) * i + hashr(h, nbkts)) % nbkts; + fn hashl(n: u32) -> u32 { ret (n >>> 16u32) * 2u32 + 1u32; } + fn hashr(n: u32) -> u32 { ret 0x0000_ffff_u32 & n; } + fn hash(h: u32, nbkts: uint, i: uint) -> uint { + ret ((hashl(h) as uint) * i + (hashr(h) as uint)) % nbkts; } + + fn to_u64(h: uint) -> u32 { + ret (h as u32) ^ ((h >>> 16u) as u32); + } + /** * We attempt to never call this with a full table. If we do, it * will fail. */ - fn insert_common(hasher: hashfn, eqer: eqfn, bkts: [mutable bucket], nbkts: uint, key: K, val: V) -> bool { let i: uint = 0u; - let h: uint = hasher(key); + let h = to_u64(hasher(key)); while i < nbkts { let j: uint = hash(h, nbkts, i); alt bkts[j] { @@ -171,7 +175,7 @@ fn find_common(hasher: hashfn, eqer: eqfn, bkts: [mutable bucket], nbkts: uint, key: K) -> option::t { let i: uint = 0u; - let h: uint = hasher(key); + let h = to_u64(hasher(key)); while i < nbkts { let j: uint = hash(h, nbkts, i); alt bkts[j] { @@ -244,7 +248,7 @@ fn find(key: K) -> option::t { } fn remove(key: K) -> option::t { let i: uint = 0u; - let h: uint = hasher(key); + let h = to_u64(hasher(key)); while i < nbkts { let j: uint = hash(h, nbkts, i); alt bkts[j] { diff --git a/src/libstd/str.rs b/src/libstd/str.rs index 2bae8d699a5c..521dc33758e9 100644 --- a/src/libstd/str.rs +++ b/src/libstd/str.rs @@ -324,7 +324,7 @@ fn char_range_at(s: str, i: uint) -> {ch: char, next: uint} { // Clunky way to get the right bits from the first byte. Uses two shifts, // the first to clip off the marker bits at the left of the byte, and then // a second (as uint) to get it to the right position. - val += (b0 << (w + 1u as u8) as uint) << (w - 1u) * 6u - w - 1u; + val += (b0 << (w + 1u as u8) as uint) << ((w - 1u) * 6u - w - 1u); ret {ch: val as char, next: i}; } diff --git a/src/libstd/task.rs b/src/libstd/task.rs index a8765407f3af..3e396d3ac63c 100644 --- a/src/libstd/task.rs +++ b/src/libstd/task.rs @@ -288,7 +288,7 @@ fn spawn_inner(-data: T, f: fn(T), notify: option>) -> task unsafe { - fn wrapper(-data: *u8, f: fn(T)) unsafe { + fn wrapper(data: *u8, f: fn(T)) unsafe { let data: ~T = unsafe::reinterpret_cast(data); f(*data); } diff --git a/src/rt/arch/i386/record_sp.S b/src/rt/arch/i386/record_sp.S index 153225e8187b..3f299de5d154 100644 --- a/src/rt/arch/i386/record_sp.S +++ b/src/rt/arch/i386/record_sp.S @@ -2,11 +2,14 @@ #if defined(__APPLE__) || defined(_WIN32) #define RECORD_SP _record_sp +#define GET_SP _get_sp #else #define RECORD_SP record_sp +#define GET_SP get_sp #endif .globl RECORD_SP +.globl GET_SP #if defined(__linux__) RECORD_SP: @@ -25,3 +28,7 @@ RECORD_SP: ret #endif #endif + +GET_SP: + movl %esp, %eax + ret \ No newline at end of file diff --git a/src/rt/arch/x86_64/morestack.S b/src/rt/arch/x86_64/morestack.S index ef0edcee7209..535b6190f1c2 100644 --- a/src/rt/arch/x86_64/morestack.S +++ b/src/rt/arch/x86_64/morestack.S @@ -132,6 +132,10 @@ MORESTACK: addq $8, %rsp popq %rbp +#ifdef __linux__ + .cfi_restore %rbp + .cfi_def_cfa %rsp, 8 +#endif ret #if defined(__ELF__) diff --git a/src/rt/arch/x86_64/record_sp.S b/src/rt/arch/x86_64/record_sp.S index e8284c5fac9a..415f6685655c 100644 --- a/src/rt/arch/x86_64/record_sp.S +++ b/src/rt/arch/x86_64/record_sp.S @@ -2,11 +2,14 @@ #if defined(__APPLE__) || defined(_WIN32) #define RECORD_SP _record_sp +#define GET_SP _get_sp #else #define RECORD_SP record_sp +#define GET_SP get_sp #endif .globl RECORD_SP +.globl GET_SP #if defined(__linux__) RECORD_SP: @@ -23,3 +26,7 @@ RECORD_SP: ret #endif #endif + +GET_SP: + movq %rsp, %rax + ret diff --git a/src/rt/rust_scheduler.cpp b/src/rt/rust_scheduler.cpp index 42b2e490404a..601a7c3f0ef9 100644 --- a/src/rt/rust_scheduler.cpp +++ b/src/rt/rust_scheduler.cpp @@ -286,7 +286,6 @@ rust_scheduler::start_main_loop() { scheduled_task->state->name); place_task_in_tls(scheduled_task); - //pthread_setspecific(89, (void *)scheduled_task->stk->limit); interrupt_flag = 0; diff --git a/src/rt/rust_shape.cpp b/src/rt/rust_shape.cpp index 326e4e2a5140..3593d0ff335f 100644 --- a/src/rt/rust_shape.cpp +++ b/src/rt/rust_shape.cpp @@ -530,9 +530,9 @@ log::walk_res(const rust_fn *dtor, unsigned n_params, } // end namespace shape extern "C" void -upcall_cmp_type(int8_t *result, const type_desc *tydesc, - const type_desc **subtydescs, uint8_t *data_0, - uint8_t *data_1, uint8_t cmp_type) { +shape_cmp_type(int8_t *result, const type_desc *tydesc, + const type_desc **subtydescs, uint8_t *data_0, + uint8_t *data_1, uint8_t cmp_type) { rust_task *task = rust_scheduler::get_task(); shape::arena arena; @@ -553,7 +553,7 @@ upcall_cmp_type(int8_t *result, const type_desc *tydesc, } extern "C" void -upcall_log_type(const type_desc *tydesc, uint8_t *data, uint32_t level) { +shape_log_type(const type_desc *tydesc, uint8_t *data, uint32_t level) { rust_task *task = rust_scheduler::get_task(); if (task->sched->log_lvl < level) return; // TODO: Don't evaluate at all? diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp index b540225243f9..bdc2f7ff7ce5 100644 --- a/src/rt/rust_task.cpp +++ b/src/rt/rust_task.cpp @@ -14,15 +14,22 @@ #include "globals.h" +// Each stack gets some guard bytes that valgrind will verify we don't touch +#ifndef NVALGRIND +#define STACK_NOACCESS_SIZE 16 +#else +#define STACK_NOACCESS_SIZE 0 +#endif + // The amount of extra space at the end of each stack segment, available // to the rt, compiler and dynamic linker for running small functions // FIXME: We want this to be 128 but need to slim the red zone calls down #ifdef __i386__ -#define RED_ZONE_SIZE 2048 +#define RED_ZONE_SIZE (65536 + STACK_NOACCESS_SIZE) #endif #ifdef __x86_64__ -#define RED_ZONE_SIZE 2048 +#define RED_ZONE_SIZE (65536 + STACK_NOACCESS_SIZE) #endif // Stack size @@ -51,11 +58,14 @@ new_stk(rust_scheduler *sched, rust_task *task, size_t minsz) LOGPTR(task->sched, "new stk", (uintptr_t)stk); memset(stk, 0, sizeof(stk_seg)); stk->next = task->stk; - stk->limit = (uintptr_t) &stk->data[minsz + RED_ZONE_SIZE]; - LOGPTR(task->sched, "stk limit", stk->limit); + stk->end = (uintptr_t) &stk->data[minsz + RED_ZONE_SIZE]; + LOGPTR(task->sched, "stk end", stk->end); stk->valgrind_id = VALGRIND_STACK_REGISTER(&stk->data[0], &stk->data[minsz + RED_ZONE_SIZE]); +#ifndef NVALGRIND + VALGRIND_MAKE_MEM_NOACCESS(stk->data, STACK_NOACCESS_SIZE); +#endif task->stk = stk; return stk; } @@ -67,6 +77,9 @@ del_stk(rust_task *task, stk_seg *stk) task->stk = stk->next; +#ifndef NVALGRIND + VALGRIND_MAKE_MEM_DEFINED(stk->data, STACK_NOACCESS_SIZE); +#endif VALGRIND_STACK_DEREGISTER(stk->valgrind_id); LOGPTR(task->sched, "freeing stk segment", (uintptr_t)stk); task->free(stk); @@ -106,7 +119,7 @@ rust_task::rust_task(rust_scheduler *sched, rust_task_list *state, user.notify_enabled = 0; stk = new_stk(sched, this, 0); - user.rust_sp = stk->limit; + user.rust_sp = stk->end; if (supervisor) { supervisor->ref(); } @@ -582,7 +595,7 @@ rust_task::new_stack(size_t stk_sz, void *args_addr, size_t args_sz) { stk_seg *stk_seg = new_stk(sched, this, stk_sz + args_sz); - uint8_t *new_sp = (uint8_t*)stk_seg->limit; + uint8_t *new_sp = (uint8_t*)stk_seg->end; size_t sizeof_retaddr = sizeof(void*); // Make enough room on the new stack to hold the old stack pointer // in addition to the function arguments @@ -608,11 +621,34 @@ rust_task::record_stack_limit() { // account for those 256 bytes. const unsigned LIMIT_OFFSET = 256; A(sched, - (uintptr_t)stk->limit - RED_ZONE_SIZE + (uintptr_t)stk->end - RED_ZONE_SIZE - (uintptr_t)stk->data >= LIMIT_OFFSET, "Stack size must be greater than LIMIT_OFFSET"); record_sp(stk->data + LIMIT_OFFSET + RED_ZONE_SIZE); } + +extern "C" uintptr_t get_sp(); + +/* +Called by landing pads during unwinding to figure out which +stack segment we are currently running on, delete the others, +and record the stack limit (which was not restored when unwinding +through __morestack). + */ +void +rust_task::reset_stack_limit() { + uintptr_t sp = get_sp(); + // Not positive these bounds for sp are correct. + // I think that the first possible value for esp on a new + // stack is stk->end, which points one word in front of + // the first work to be pushed onto a new stack. + while (sp <= (uintptr_t)stk->data || stk->end < sp) { + del_stk(this, stk); + A(sched, stk != NULL, "Failed to find the current stack"); + } + record_stack_limit(); +} + // // Local Variables: // mode: C++ diff --git a/src/rt/rust_task.h b/src/rt/rust_task.h index 00a9fc277dc7..3d5cbff99103 100644 --- a/src/rt/rust_task.h +++ b/src/rt/rust_task.h @@ -25,7 +25,7 @@ struct rust_box; struct stk_seg { stk_seg *next; - uintptr_t limit; + uintptr_t end; unsigned int valgrind_id; #ifndef _LP64 uint32_t pad; @@ -200,6 +200,7 @@ rust_task : public kernel_owned, rust_cond void *new_stack(size_t stk_sz, void *args_addr, size_t args_sz); void del_stack(); void record_stack_limit(); + void reset_stack_limit(); }; // diff --git a/src/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp index 653db471fc01..e4b646e39ffa 100644 --- a/src/rt/rust_upcall.cpp +++ b/src/rt/rust_upcall.cpp @@ -6,7 +6,31 @@ #include "rust_upcall.h" #include -// Upcalls. +extern "C" void record_sp(void *limit); + +/** + * Switches to the C-stack and invokes |fn_ptr|, passing |args| as argument. + */ +extern "C" CDECL void +upcall_call_shim_on_c_stack(void *args, void *fn_ptr) { + rust_task *task = rust_scheduler::get_task(); + + // FIXME (1226) - The shim functions generated by rustc contain the + // morestack prologue, so we need to let them know they have enough + // stack. + record_sp(0); + + rust_scheduler *sched = task->sched; + try { + sched->c_context.call_shim_on_c_stack(args, fn_ptr); + } catch (...) { + task = rust_scheduler::get_task(); + task->record_stack_limit(); + throw; + } + task = rust_scheduler::get_task(); + task->record_stack_limit(); +} #if defined(__i386__) || defined(__x86_64__) || defined(_M_X64) void @@ -47,24 +71,34 @@ copy_elements(rust_task *task, type_desc *elem_t, } } +struct s_fail_args { + char const *expr; + char const *file; + size_t line; +}; + extern "C" CDECL void -upcall_fail(char const *expr, - char const *file, - size_t line) { +upcall_s_fail(s_fail_args *args) { rust_task *task = rust_scheduler::get_task(); LOG_UPCALL_ENTRY(task); - LOG_ERR(task, upcall, "upcall fail '%s', %s:%" PRIdPTR, expr, file, line); + LOG_ERR(task, upcall, "upcall fail '%s', %s:%" PRIdPTR, + args->expr, args->file, args->line); task->fail(); } +struct s_malloc_args { + size_t nbytes; + type_desc *td; +}; + extern "C" CDECL uintptr_t -upcall_malloc(size_t nbytes, type_desc *td) { +upcall_s_malloc(s_malloc_args *args) { rust_task *task = rust_scheduler::get_task(); LOG_UPCALL_ENTRY(task); LOG(task, mem, "upcall malloc(%" PRIdPTR ", 0x%" PRIxPTR ")", - nbytes, td); + args->nbytes, args->td); gc::maybe_gc(task); cc::maybe_cc(task); @@ -72,52 +106,278 @@ upcall_malloc(size_t nbytes, type_desc *td) { // TODO: Maybe use dladdr here to find a more useful name for the // type_desc. - void *p = task->malloc(nbytes, "tdesc", td); - memset(p, '\0', nbytes); + void *p = task->malloc(args->nbytes, "tdesc", args->td); + memset(p, '\0', args->nbytes); - task->local_allocs[p] = td; + task->local_allocs[p] = args->td; debug::maybe_track_origin(task, p); LOG(task, mem, "upcall malloc(%" PRIdPTR ", 0x%" PRIxPTR ") = 0x%" PRIxPTR, - nbytes, td, (uintptr_t)p); + args->nbytes, args->td, (uintptr_t)p); return (uintptr_t) p; } +struct s_free_args { + void *ptr; + uintptr_t is_gc; +}; + +/** + * Called whenever an object's ref count drops to zero. + */ +extern "C" CDECL void +upcall_s_free(s_free_args *args) { + rust_task *task = rust_scheduler::get_task(); + LOG_UPCALL_ENTRY(task); + + rust_scheduler *sched = task->sched; + DLOG(sched, mem, + "upcall free(0x%" PRIxPTR ", is_gc=%" PRIdPTR ")", + (uintptr_t)args->ptr, args->is_gc); + + task->local_allocs.erase(args->ptr); + debug::maybe_untrack_origin(task, args->ptr); + + task->free(args->ptr, (bool) args->is_gc); +} + +struct s_shared_malloc_args { + size_t nbytes; + type_desc *td; +}; + +extern "C" CDECL uintptr_t +upcall_s_shared_malloc(s_shared_malloc_args *args) { + rust_task *task = rust_scheduler::get_task(); + LOG_UPCALL_ENTRY(task); + + LOG(task, mem, + "upcall shared_malloc(%" PRIdPTR ", 0x%" PRIxPTR ")", + args->nbytes, args->td); + void *p = task->kernel->malloc(args->nbytes, "shared malloc"); + memset(p, '\0', args->nbytes); + LOG(task, mem, + "upcall shared_malloc(%" PRIdPTR ", 0x%" PRIxPTR + ") = 0x%" PRIxPTR, + args->nbytes, args->td, (uintptr_t)p); + return (uintptr_t) p; +} + +struct s_shared_free_args { + void *ptr; +}; + +/** + * Called whenever an object's ref count drops to zero. + */ +extern "C" CDECL void +upcall_s_shared_free(s_shared_free_args *args) { + rust_task *task = rust_scheduler::get_task(); + LOG_UPCALL_ENTRY(task); + + rust_scheduler *sched = task->sched; + DLOG(sched, mem, + "upcall shared_free(0x%" PRIxPTR")", + (uintptr_t)args->ptr); + task->kernel->free(args->ptr); +} + +struct s_get_type_desc_args { + size_t size; + size_t align; + size_t n_descs; + type_desc const **descs; + uintptr_t n_obj_params; +}; + +extern "C" CDECL type_desc * +upcall_s_get_type_desc(s_get_type_desc_args *args) { + rust_task *task = rust_scheduler::get_task(); + check_stack(task); + LOG_UPCALL_ENTRY(task); + + LOG(task, cache, "upcall get_type_desc with size=%" PRIdPTR + ", align=%" PRIdPTR ", %" PRIdPTR " descs", args->size, args->align, + args->n_descs); + rust_crate_cache *cache = task->get_crate_cache(); + type_desc *td = cache->get_type_desc(args->size, args->align, args->n_descs, + args->descs, args->n_obj_params); + LOG(task, cache, "returning tydesc 0x%" PRIxPTR, td); + return td; +} + +struct s_vec_grow_args { + rust_vec** vp; + size_t new_sz; +}; + +extern "C" CDECL void +upcall_s_vec_grow(s_vec_grow_args *args) { + rust_task *task = rust_scheduler::get_task(); + LOG_UPCALL_ENTRY(task); + reserve_vec(task, args->vp, args->new_sz); + (*args->vp)->fill = args->new_sz; +} + +struct s_vec_push_args { + rust_vec** vp; + type_desc* elt_ty; + void* elt; +}; + +extern "C" CDECL void +upcall_s_vec_push(s_vec_push_args *args) { + rust_task *task = rust_scheduler::get_task(); + LOG_UPCALL_ENTRY(task); + size_t new_sz = (*args->vp)->fill + args->elt_ty->size; + reserve_vec(task, args->vp, new_sz); + rust_vec* v = *args->vp; + copy_elements(task, args->elt_ty, &v->data[0] + v->fill, + args->elt, args->elt_ty->size); + v->fill += args->elt_ty->size; +} + +/** + * Returns a token that can be used to deallocate all of the allocated space + * space in the dynamic stack. + */ +extern "C" CDECL void * +upcall_s_dynastack_mark() { + return rust_scheduler::get_task()->dynastack.mark(); +} + +struct s_dynastack_alloc_args { + size_t sz; +}; + +/** + * Allocates space in the dynamic stack and returns it. + * + * FIXME: Deprecated since dynamic stacks need to be self-describing for GC. + */ +extern "C" CDECL void * +upcall_s_dynastack_alloc(s_dynastack_alloc_args *args) { + size_t sz = args->sz; + return sz ? rust_scheduler::get_task()->dynastack.alloc(sz, NULL) : NULL; +} + +struct s_dynastack_alloc_2_args { + size_t sz; + type_desc *ty; +}; + +/** + * Allocates space associated with a type descriptor in the dynamic stack and + * returns it. + */ +extern "C" CDECL void * +upcall_s_dynastack_alloc_2(s_dynastack_alloc_2_args *args) { + size_t sz = args->sz; + type_desc *ty = args->ty; + return sz ? rust_scheduler::get_task()->dynastack.alloc(sz, ty) : NULL; +} + +struct s_dynastack_free_args { + void *ptr; +}; + +/** Frees space in the dynamic stack. */ +extern "C" CDECL void +upcall_s_dynastack_free(s_dynastack_free_args *args) { + return rust_scheduler::get_task()->dynastack.free(args->ptr); +} + +extern "C" _Unwind_Reason_Code +__gxx_personality_v0(int version, + _Unwind_Action actions, + uint64_t exception_class, + _Unwind_Exception *ue_header, + _Unwind_Context *context); + +struct s_rust_personality_args { + int version; + _Unwind_Action actions; + uint64_t exception_class; + _Unwind_Exception *ue_header; + _Unwind_Context *context; +}; + +extern "C" _Unwind_Reason_Code +upcall_s_rust_personality(s_rust_personality_args *args) { + return __gxx_personality_v0(args->version, + args->actions, + args->exception_class, + args->ue_header, + args->context); +} + +extern "C" void +shape_cmp_type(int8_t *result, const type_desc *tydesc, + const type_desc **subtydescs, uint8_t *data_0, + uint8_t *data_1, uint8_t cmp_type); + +struct s_cmp_type_args { + int8_t *result; + const type_desc *tydesc; + const type_desc **subtydescs; + uint8_t *data_0; + uint8_t *data_1; + uint8_t cmp_type; +}; + +extern "C" void +upcall_s_cmp_type(s_cmp_type_args *args) { + shape_cmp_type(args->result, args->tydesc, args->subtydescs, + args->data_0, args->data_1, args->cmp_type); +} + +extern "C" void +shape_log_type(const type_desc *tydesc, uint8_t *data, uint32_t level); + +struct s_log_type_args { + const type_desc *tydesc; + uint8_t *data; + uint32_t level; +}; + +extern "C" void +upcall_s_log_type(s_log_type_args *args) { + shape_log_type(args->tydesc, args->data, args->level); +} + + +// ______________________________________________________________________________ +// Upcalls in original format: deprecated and should be removed once snapshot +// transitions them away. + +extern "C" CDECL void +upcall_fail(char const *expr, + char const *file, + size_t line) { + s_fail_args args = {expr,file,line}; + upcall_s_fail(&args); +} + +extern "C" CDECL uintptr_t +upcall_malloc(size_t nbytes, type_desc *td) { + s_malloc_args args = {nbytes, td}; + return upcall_s_malloc(&args); +} + /** * Called whenever an object's ref count drops to zero. */ extern "C" CDECL void upcall_free(void* ptr, uintptr_t is_gc) { - rust_task *task = rust_scheduler::get_task(); - LOG_UPCALL_ENTRY(task); - - rust_scheduler *sched = task->sched; - DLOG(sched, mem, - "upcall free(0x%" PRIxPTR ", is_gc=%" PRIdPTR ")", - (uintptr_t)ptr, is_gc); - - task->local_allocs.erase(ptr); - debug::maybe_untrack_origin(task, ptr); - - task->free(ptr, (bool) is_gc); + s_free_args args = {ptr, is_gc}; + upcall_s_free(&args); } extern "C" CDECL uintptr_t upcall_shared_malloc(size_t nbytes, type_desc *td) { - rust_task *task = rust_scheduler::get_task(); - LOG_UPCALL_ENTRY(task); - - LOG(task, mem, - "upcall shared_malloc(%" PRIdPTR ", 0x%" PRIxPTR ")", - nbytes, td); - void *p = task->kernel->malloc(nbytes, "shared malloc"); - memset(p, '\0', nbytes); - LOG(task, mem, - "upcall shared_malloc(%" PRIdPTR ", 0x%" PRIxPTR - ") = 0x%" PRIxPTR, - nbytes, td, (uintptr_t)p); - return (uintptr_t) p; + s_shared_malloc_args args = {nbytes, td}; + return upcall_s_shared_malloc(&args); } /** @@ -125,14 +385,8 @@ upcall_shared_malloc(size_t nbytes, type_desc *td) { */ extern "C" CDECL void upcall_shared_free(void* ptr) { - rust_task *task = rust_scheduler::get_task(); - LOG_UPCALL_ENTRY(task); - - rust_scheduler *sched = task->sched; - DLOG(sched, mem, - "upcall shared_free(0x%" PRIxPTR")", - (uintptr_t)ptr); - task->kernel->free(ptr); + s_shared_free_args args = {ptr}; + upcall_s_shared_free(&args); } extern "C" CDECL type_desc * @@ -142,37 +396,20 @@ upcall_get_type_desc(void *curr_crate, // ignored, legacy compat. size_t n_descs, type_desc const **descs, uintptr_t n_obj_params) { - rust_task *task = rust_scheduler::get_task(); - check_stack(task); - LOG_UPCALL_ENTRY(task); - - LOG(task, cache, "upcall get_type_desc with size=%" PRIdPTR - ", align=%" PRIdPTR ", %" PRIdPTR " descs", size, align, - n_descs); - rust_crate_cache *cache = task->get_crate_cache(); - type_desc *td = cache->get_type_desc(size, align, n_descs, descs, - n_obj_params); - LOG(task, cache, "returning tydesc 0x%" PRIxPTR, td); - return td; + s_get_type_desc_args args = {size,align,n_descs,descs,n_obj_params}; + return upcall_s_get_type_desc(&args); } extern "C" CDECL void upcall_vec_grow(rust_vec** vp, size_t new_sz) { - rust_task *task = rust_scheduler::get_task(); - LOG_UPCALL_ENTRY(task); - reserve_vec(task, vp, new_sz); - (*vp)->fill = new_sz; + s_vec_grow_args args = {vp, new_sz}; + upcall_s_vec_grow(&args); } extern "C" CDECL void upcall_vec_push(rust_vec** vp, type_desc* elt_ty, void* elt) { - rust_task *task = rust_scheduler::get_task(); - LOG_UPCALL_ENTRY(task); - size_t new_sz = (*vp)->fill + elt_ty->size; - reserve_vec(task, vp, new_sz); - rust_vec* v = *vp; - copy_elements(task, elt_ty, &v->data[0] + v->fill, elt, elt_ty->size); - v->fill += elt_ty->size; + s_vec_push_args args = {vp, elt_ty, elt}; + upcall_s_vec_push(&args); } /** @@ -181,7 +418,7 @@ upcall_vec_push(rust_vec** vp, type_desc* elt_ty, void* elt) { */ extern "C" CDECL void * upcall_dynastack_mark() { - return rust_scheduler::get_task()->dynastack.mark(); + return upcall_s_dynastack_mark(); } /** @@ -191,7 +428,8 @@ upcall_dynastack_mark() { */ extern "C" CDECL void * upcall_dynastack_alloc(size_t sz) { - return sz ? rust_scheduler::get_task()->dynastack.alloc(sz, NULL) : NULL; + s_dynastack_alloc_args args = {sz}; + return upcall_s_dynastack_alloc(&args); } /** @@ -200,40 +438,40 @@ upcall_dynastack_alloc(size_t sz) { */ extern "C" CDECL void * upcall_dynastack_alloc_2(size_t sz, type_desc *ty) { - return sz ? rust_scheduler::get_task()->dynastack.alloc(sz, ty) : NULL; + s_dynastack_alloc_2_args args = {sz, ty}; + return upcall_s_dynastack_alloc_2(&args); } /** Frees space in the dynamic stack. */ extern "C" CDECL void upcall_dynastack_free(void *ptr) { - return rust_scheduler::get_task()->dynastack.free(ptr); + s_dynastack_free_args args = {ptr}; + return upcall_s_dynastack_free(&args); } -extern "C" void record_sp(void *limit); +extern "C" _Unwind_Reason_Code +upcall_rust_personality(int version, + _Unwind_Action actions, + uint64_t exception_class, + _Unwind_Exception *ue_header, + _Unwind_Context *context) { + s_rust_personality_args args = {version, actions, exception_class, ue_header, + context}; + return upcall_s_rust_personality(&args); +} -/** - * Switch to the C stack and call the given function, passing a single pointer - * argument. - */ -extern "C" CDECL void -upcall_call_shim_on_c_stack(void *args, void *fn_ptr) { - rust_task *task = rust_scheduler::get_task(); +extern "C" void +upcall_cmp_type(int8_t *result, const type_desc *tydesc, + const type_desc **subtydescs, uint8_t *data_0, + uint8_t *data_1, uint8_t cmp_type) { + s_cmp_type_args args = {result, tydesc, subtydescs, data_0, data_1, cmp_type}; + upcall_s_cmp_type(&args); +} - // FIXME (1226) - The shim functions generated by rustc contain the - // morestack prologue, so we need to let them know they have enough - // stack. - record_sp(0); - - rust_scheduler *sched = task->sched; - try { - sched->c_context.call_shim_on_c_stack(args, fn_ptr); - } catch (...) { - task = rust_scheduler::get_task(); - task->record_stack_limit(); - throw; - } - task = rust_scheduler::get_task(); - task->record_stack_limit(); +extern "C" void +upcall_log_type(const type_desc *tydesc, uint8_t *data, uint32_t level) { + s_log_type_args args = {tydesc, data, level}; + upcall_s_log_type(&args); } struct rust_new_stack2_args { @@ -245,6 +483,8 @@ struct rust_new_stack2_args { // A new stack function suitable for calling through // upcall_call_shim_on_c_stack +// FIXME: Convert this to the same arrangement as +// the other upcalls, simplify __morestack extern "C" CDECL void upcall_new_stack(struct rust_new_stack2_args *args) { rust_task *task = rust_scheduler::get_task(); @@ -253,30 +493,21 @@ upcall_new_stack(struct rust_new_stack2_args *args) { args->args_sz); } +// FIXME: As above extern "C" CDECL void upcall_del_stack() { rust_task *task = rust_scheduler::get_task(); task->del_stack(); } -extern "C" _Unwind_Reason_Code -__gxx_personality_v0(int version, - _Unwind_Action actions, - uint64_t exception_class, - _Unwind_Exception *ue_header, - _Unwind_Context *context); - -extern "C" _Unwind_Reason_Code -upcall_rust_personality(int version, - _Unwind_Action actions, - uint64_t exception_class, - _Unwind_Exception *ue_header, - _Unwind_Context *context) { - return __gxx_personality_v0(version, - actions, - exception_class, - ue_header, - context); +// Landing pads need to call this to insert the +// correct limit into TLS. +// NB: This must run on the Rust stack because it +// needs to acquire the value of the stack pointer +extern "C" CDECL void +upcall_reset_stack_limit() { + rust_task *task = rust_scheduler::get_task(); + task->reset_stack_limit(); } // @@ -286,6 +517,5 @@ upcall_rust_personality(int version, // indent-tabs-mode: nil // c-basic-offset: 4 // buffer-file-coding-system: utf-8-unix -// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; // End: // diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index ba1a9c1403da..306c750c8fbb 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -69,6 +69,7 @@ upcall_vec_push upcall_call_shim_on_c_stack upcall_new_stack upcall_del_stack +upcall_reset_stack_limit asm_call_on_stack rust_uv_default_loop rust_uv_loop_new diff --git a/src/test/bench/task-perf-word-count.rs b/src/test/bench/task-perf-word-count.rs index 2d4e14af0586..d03097136335 100644 --- a/src/test/bench/task-perf-word-count.rs +++ b/src/test/bench/task-perf-word-count.rs @@ -112,12 +112,12 @@ fn reduce_task(args: (str, chan>)) { send(out, chan(p)); - let ref_count = 0; - let is_done = false; + let state = @{mutable ref_count: 0, mutable is_done: false}; - fn get(p: port, &ref_count: int, &is_done: bool) -> - option { - while !is_done || ref_count > 0 { + fn get(p: port, state: @{mutable ref_count: int, + mutable is_done: bool}) + -> option { + while !state.is_done || state.ref_count > 0 { alt recv(p) { emit_val(v) { // log_err #fmt("received %d", v); @@ -125,16 +125,16 @@ fn get(p: port, &ref_count: int, &is_done: bool) -> } done. { // log_err "all done"; - is_done = true; + state.is_done = true; } - ref. { ref_count += 1; } - release. { ref_count -= 1; } + ref. { state.ref_count += 1; } + release. { state.ref_count -= 1; } } } ret none; } - reduce(key, bind get(p, ref_count, is_done)); + reduce(key, bind get(p, state)); } fn map_reduce(-inputs: [str]) { diff --git a/src/test/run-fail/morestack3.rs b/src/test/run-fail/morestack3.rs index 9fdd0326b661..9796017a8eb0 100644 --- a/src/test/run-fail/morestack3.rs +++ b/src/test/run-fail/morestack3.rs @@ -25,10 +25,10 @@ fn getbig(i: int) { getbig(i - 1); } } - getbig(1000); + getbig(100); } fn main() { rustrt::set_min_stack(256u); - std::task::spawn(1000, getbig_and_fail); + std::task::spawn(100, getbig_and_fail); } \ No newline at end of file diff --git a/src/test/stdtest/treemap.rs b/src/test/stdtest/treemap.rs index 1701be37901d..2fba1ebb1961 100644 --- a/src/test/stdtest/treemap.rs +++ b/src/test/stdtest/treemap.rs @@ -39,8 +39,8 @@ fn traverse_in_order() { insert(m, 2, ()); insert(m, 1, ()); - let n = 0; - fn t(&n: int, &&k: int, &&_v: ()) { assert (n == k); n += 1; } + let n = @mutable 0; + fn t(n: @mutable int, &&k: int, &&_v: ()) { assert (*n == k); *n += 1; } traverse(m, bind t(n, _, _)); }