From af4d6ae76b05d4edb9d7074b971600a447c9c9a4 Mon Sep 17 00:00:00 2001 From: Graydon Hoare Date: Tue, 22 Feb 2011 16:37:01 -0800 Subject: [PATCH] Add ABI tagging to crates, adjust rustc output and runtime stack-frame setup so access to argv works. --- src/boot/be/abi.ml | 34 +++++++++++++------- src/boot/be/x86.ml | 2 ++ src/boot/me/trans.ml | 4 +++ src/comp/back/abi.rs | 3 ++ src/comp/back/x86.rs | 4 +-- src/comp/middle/trans.rs | 13 +++++--- src/rt/rust.cpp | 8 +++-- src/rt/rust_internal.h | 6 ++++ src/rt/rust_task.cpp | 52 ++++++++++++++++++++----------- src/rt/rust_task.h | 1 + src/rt/rust_upcall.cpp | 11 +++++-- src/rt/test/rust_test_runtime.cpp | 1 + 12 files changed, 97 insertions(+), 42 deletions(-) diff --git a/src/boot/be/abi.ml b/src/boot/be/abi.ml index 035d1f058d5..89e308bfab9 100644 --- a/src/boot/be/abi.ml +++ b/src/boot/be/abi.ml @@ -110,23 +110,33 @@ let indirect_args_elt_closure = 0;; (* Current worst case is by vec grow glue *) let worst_case_glue_call_args = 8;; +(* + * ABI tags used to inform the runtime which sort of frame to set up for new + * spawned functions. FIXME: There is almost certainly a better abstraction to + * use. + *) +let abi_x86_rustboot_cdecl = 1;; +let abi_x86_rustc_fastcall = 2;; + type abi = - { - abi_word_sz: int64; - abi_word_bits: Il.bits; - abi_word_ty: Common.ty_mach; + { + abi_word_sz: int64; + abi_word_bits: Il.bits; + abi_word_ty: Common.ty_mach; - abi_has_pcrel_data: bool; - abi_has_pcrel_code: bool; + abi_tag: int; - abi_n_hardregs: int; - abi_str_of_hardreg: (int -> string); + abi_has_pcrel_data: bool; + abi_has_pcrel_code: bool; - abi_emit_target_specific: (Il.emitter -> Il.quad -> unit); - abi_constrain_vregs: (Il.quad -> (Il.vreg,Bits.t) Hashtbl.t -> unit); + abi_n_hardregs: int; + abi_str_of_hardreg: (int -> string); - abi_emit_fn_prologue: (Il.emitter - -> Common.size (* framesz *) + abi_emit_target_specific: (Il.emitter -> Il.quad -> unit); + abi_constrain_vregs: (Il.quad -> (Il.vreg,Bits.t) Hashtbl.t -> unit); + + abi_emit_fn_prologue: (Il.emitter + -> Common.size (* framesz *) -> Common.size (* callsz *) -> Common.nabi -> Common.fixup (* grow_task *) diff --git a/src/boot/be/x86.ml b/src/boot/be/x86.ml index 30b49ed1610..49b660be5ae 100644 --- a/src/boot/be/x86.ml +++ b/src/boot/be/x86.ml @@ -1851,6 +1851,8 @@ let (abi:Abi.abi) = Abi.abi_word_bits = word_bits; Abi.abi_word_ty = word_ty; + Abi.abi_tag = Abi.abi_x86_rustboot_cdecl; + Abi.abi_has_pcrel_data = false; Abi.abi_has_pcrel_code = true; diff --git a/src/boot/me/trans.ml b/src/boot/me/trans.ml index 4f7172197c8..bbf49e83b3d 100644 --- a/src/boot/me/trans.ml +++ b/src/boot/me/trans.ml @@ -2727,6 +2727,7 @@ let trans_visitor [| Il.Cell new_task; exit_task_glue_fptr; + (imm (Int64.of_int abi.Abi.abi_tag)); fptr_operand; callsz |]; @@ -2739,6 +2740,7 @@ let trans_visitor [| Il.Cell new_task; exit_task_glue_fptr; + (imm (Int64.of_int abi.Abi.abi_tag)); fptr_operand; callsz |]; @@ -6183,6 +6185,8 @@ let trans_visitor tab_sz cx.ctxt_required_rust_sym_num; tab_sz cx.ctxt_required_c_sym_num; tab_sz cx.ctxt_required_lib_num; + + Asm.WORD (word_ty_mach, Asm.IMM (Int64.of_int abi.Abi.abi_tag)); |])) in diff --git a/src/comp/back/abi.rs b/src/comp/back/abi.rs index 82a85a6c1f7..f41f6e205c9 100644 --- a/src/comp/back/abi.rs +++ b/src/comp/back/abi.rs @@ -60,6 +60,9 @@ const int worst_case_glue_call_args = 7; const int n_upcall_glues = 7; +const int abi_x86_rustboot_cdecl = 1; +const int abi_x86_rustc_fastcall = 2; + fn memcpy_glue_name() -> str { ret "rust_memcpy_glue"; } diff --git a/src/comp/back/x86.rs b/src/comp/back/x86.rs index 907cb32e3fe..10227df7ca5 100644 --- a/src/comp/back/x86.rs +++ b/src/comp/back/x86.rs @@ -98,12 +98,12 @@ fn rust_activate_glue() -> vec[str] { * | esi | * | ebx | <-- current task->rust_sp == current esp * - * + * * This is a problem. If we return to "esp <- task->rust_sp" it * will push esp back down by 5 words. This manifests as a rust * stack that grows by 5 words on each yield/reactivate. Not * good. - * + * * So what we do here is just adjust task->rust_sp up 5 words as * well, to mirror the movement in esp we're about to * perform. That way the "esp <- task->rust_sp" we 'ret' to below diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index 9da7e0099f0..4a672f49139 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -345,7 +345,8 @@ fn T_crate(type_names tn) -> TypeRef { T_int(), // size_t main_exit_task_glue_off T_int(), // int n_rust_syms T_int(), // int n_c_syms - T_int() // int n_libs + T_int(), // int n_libs + T_int() // uintptr_t abi_tag )); tn.associate(s, t); ret t; @@ -4545,7 +4546,7 @@ fn trans_exit_task_glue(@crate_ctxt cx) { let vec[ValueRef] V_args = vec(); auto llfn = cx.glues.exit_task_glue; - let ValueRef lltaskptr = llvm.LLVMGetParam(llfn, 0u); + let ValueRef lltaskptr = llvm.LLVMGetParam(llfn, 3u); auto fcx = @rec(llfn=llfn, lltaskptr=lltaskptr, llenv=C_null(T_opaque_closure_ptr(cx.tn)), @@ -4596,7 +4597,8 @@ fn create_crate_constant(@crate_ctxt cx) { exit_task_glue_off, // size_t main_exit_task_glue_off C_null(T_int()), // int n_rust_syms C_null(T_int()), // int n_c_syms - C_null(T_int()) // int n_libs + C_null(T_int()), // int n_libs + C_int(abi.abi_x86_rustc_fastcall) // uintptr_t abi_tag )); llvm.LLVMSetInitializer(cx.crate_ptr, crate_val); @@ -4810,7 +4812,10 @@ fn make_glues(ModuleRef llmod, type_names tn) -> @glue_fns { * this is the signature required to retrieve it. */ exit_task_glue = decl_cdecl_fn(llmod, abi.exit_task_glue_name(), - T_fn(vec(T_taskptr(tn)), + T_fn(vec(T_int(), + T_int(), + T_int(), + T_taskptr(tn)), T_void())), upcall_glues = diff --git a/src/rt/rust.cpp b/src/rt/rust.cpp index 0ea167a4a8c..46fcb22ea0b 100644 --- a/src/rt/rust.cpp +++ b/src/rt/rust.cpp @@ -78,7 +78,7 @@ command_line_args : public dom_owned extern "C" CDECL int rust_start(uintptr_t main_fn, rust_crate const *crate, int argc, - char **argv) { + char **argv) { rust_srv *srv = new rust_srv(); rust_kernel *kernel = new rust_kernel(srv); @@ -87,7 +87,8 @@ rust_start(uintptr_t main_fn, rust_crate const *crate, int argc, rust_dom *dom = handle->referent(); command_line_args *args = new (dom) command_line_args(dom, argc, argv); - dom->log(rust_log::DOM, "startup: %d args", args->argc); + dom->log(rust_log::DOM, "startup: %d args in 0x%" PRIxPTR, + args->argc, (uintptr_t)args->args); for (int i = 0; i < args->argc; i++) { dom->log(rust_log::DOM, "startup: arg[%d] = '%s'", i, args->argv[i]); @@ -99,7 +100,8 @@ rust_start(uintptr_t main_fn, rust_crate const *crate, int argc, uintptr_t main_args[4] = {0, 0, 0, (uintptr_t)args->args}; dom->root_task->start(crate->get_exit_task_glue(), - main_fn, (uintptr_t)&main_args, sizeof(main_args)); + crate->abi_tag, main_fn, + (uintptr_t)&main_args, sizeof(main_args)); int ret = dom->start_main_loop(); delete args; kernel->destroy_domain(dom); diff --git a/src/rt/rust_internal.h b/src/rt/rust_internal.h index 61716703deb..42b61801f53 100644 --- a/src/rt/rust_internal.h +++ b/src/rt/rust_internal.h @@ -88,6 +88,10 @@ static size_t const TIME_SLICE_IN_MS = 10; static intptr_t const CONST_REFCOUNT = 0x7badface; +// ABI tags for rust_start, rust_task::start and friends. +static uintptr_t const ABI_X86_RUSTBOOT_CDECL = 1; +static uintptr_t const ABI_X86_RUSTC_FASTCALL = 2; + // This accounts for logging buffers. static size_t const BUF_BYTES = 2048; @@ -241,6 +245,8 @@ public: size_t n_c_syms; size_t n_libs; + uintptr_t abi_tag; + // Crates are immutable, constructed by the compiler. uintptr_t get_image_base() const; diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp index 3564b9e342e..1afbfdd6b3b 100644 --- a/src/rt/rust_task.cpp +++ b/src/rt/rust_task.cpp @@ -123,6 +123,7 @@ rust_task::~rust_task() void rust_task::start(uintptr_t exit_task_glue, + uintptr_t spawnee_abi, uintptr_t spawnee_fn, uintptr_t args, size_t callsz) @@ -147,39 +148,46 @@ rust_task::start(uintptr_t exit_task_glue, // The exit_task_glue frame we synthesize above the frame we activate: *spp-- = (uintptr_t) 0; // closure-or-obj *spp-- = (uintptr_t) this; // task - *spp-- = (uintptr_t) 0; // output - *spp-- = (uintptr_t) 0; // retpc + *spp-- = (uintptr_t) 0x0; // output + *spp-- = (uintptr_t) 0x0; // retpc uintptr_t exit_task_frame_base; - for (size_t j = 0; j < n_callee_saves; ++j) { + if (spawnee_abi == ABI_X86_RUSTBOOT_CDECL) { + for (size_t j = 0; j < n_callee_saves; ++j) { - // We want 'frame_base' to point to the old fp in this (exit-task) - // frame, because we're going to inject this frame-pointer into the - // callee-save frame pointer value in the *next* (spawnee) frame. A - // cheap trick, but this means the spawnee frame will restore the - // proper frame pointer of the glue frame as it runs its epilogue. - if (j == callee_save_fp) - exit_task_frame_base = (uintptr_t)spp; + // We want 'frame_base' to point to the old fp in this (exit-task) + // frame, because we're going to inject this frame-pointer into + // the callee-save frame pointer value in the *next* (spawnee) + // frame. A cheap trick, but this means the spawnee frame will + // restore the proper frame pointer of the glue frame as it runs + // its epilogue. + if (j == callee_save_fp) + exit_task_frame_base = (uintptr_t)spp; - *spp-- = 0; + *spp-- = 0; + } + + *spp-- = (uintptr_t) dom->root_crate; // crate ptr + *spp-- = (uintptr_t) 0; // frame_glue_fns } - *spp-- = (uintptr_t) dom->root_crate; // crate ptr - *spp-- = (uintptr_t) 0; // frame_glue_fns - // Copy args from spawner to spawnee. if (args) { uintptr_t *src = (uintptr_t *)args; src += 1; // spawn-call output slot src += 1; // spawn-call task slot src += 1; // spawn-call closure-or-obj slot + + // Undo previous sp-- so we're pointing at the last word pushed. + ++spp; + // Memcpy all but the task, output and env pointers callsz -= (3 * sizeof(uintptr_t)); spp = (uintptr_t*) (((uintptr_t)spp) - callsz); memcpy(spp, src, callsz); - // Move sp down to point to task cell. + // Move sp down to point to last implicit-arg cell (env). spp--; } else { // We're at root, starting up. @@ -188,10 +196,18 @@ rust_task::start(uintptr_t exit_task_glue, // The *implicit* incoming args to the spawnee frame we're // activating: + *spp-- = (uintptr_t) 0x0; // closure-or-obj + + if (spawnee_abi == ABI_X86_RUSTBOOT_CDECL) { + // in CDECL mode we write the task + outptr to the spawnee stack. + *spp-- = (uintptr_t) this; // task + *spp-- = (uintptr_t) 0; // output addr + } else { + // in FASTCALL mode we don't, the outptr will be in ecx and the task + // in edx, and the activate_glue will make sure to set that up. + I(dom, spawnee_abi == ABI_X86_RUSTC_FASTCALL); + } - *spp-- = (uintptr_t) 0; // closure-or-obj - *spp-- = (uintptr_t) this; // task - *spp-- = (uintptr_t) 0; // output addr *spp-- = (uintptr_t) exit_task_glue; // retpc // The context the activate_glue needs to switch stack. diff --git a/src/rt/rust_task.h b/src/rt/rust_task.h index 9fbc67acff5..5318ab71edf 100644 --- a/src/rt/rust_task.h +++ b/src/rt/rust_task.h @@ -56,6 +56,7 @@ rust_task : public maybe_proxy, ~rust_task(); void start(uintptr_t exit_task_glue, + uintptr_t spawnee_abi, uintptr_t spawnee_fn, uintptr_t args, size_t callsz); diff --git a/src/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp index 46e53048899..1dba11024f4 100644 --- a/src/rt/rust_upcall.cpp +++ b/src/rt/rust_upcall.cpp @@ -559,6 +559,7 @@ extern "C" CDECL rust_task * upcall_start_task(rust_task *spawner, rust_task *task, uintptr_t exit_task_glue, + uintptr_t spawnee_abi, uintptr_t spawnee_fn, size_t callsz) { LOG_UPCALL_ENTRY(spawner); @@ -570,7 +571,8 @@ upcall_start_task(rust_task *spawner, ", spawnee 0x%" PRIxPTR ", callsz %" PRIdPTR ")", task->name, task, exit_task_glue, spawnee_fn, callsz); - task->start(exit_task_glue, spawnee_fn, spawner->rust_sp, callsz); + task->start(exit_task_glue, spawnee_abi, spawnee_fn, + spawner->rust_sp, callsz); return task; } @@ -623,6 +625,7 @@ extern "C" CDECL maybe_proxy * upcall_start_thread(rust_task *task, rust_proxy *child_task_proxy, uintptr_t exit_task_glue, + uintptr_t spawnee_abi, uintptr_t spawnee_fn, size_t callsz) { LOG_UPCALL_ENTRY(task); @@ -630,9 +633,11 @@ upcall_start_thread(rust_task *task, rust_handle *child_task_handle = child_task_proxy->handle(); task->log(rust_log::UPCALL | rust_log::MEM | rust_log::TASK, "exit_task_glue: " PTR ", spawnee_fn " PTR - ", callsz %" PRIdPTR ")", exit_task_glue, spawnee_fn, callsz); + ", callsz %" PRIdPTR ")", + exit_task_glue, spawnee_fn, callsz); rust_task *child_task = child_task_handle->referent(); - child_task->start(exit_task_glue, spawnee_fn, task->rust_sp, callsz); + child_task->start(exit_task_glue, spawnee_abi, spawnee_fn, + task->rust_sp, callsz); #if defined(__WIN32__) HANDLE thread; thread = CreateThread(NULL, 0, rust_thread_start, child_task->dom, 0, diff --git a/src/rt/test/rust_test_runtime.cpp b/src/rt/test/rust_test_runtime.cpp index 1cde532e94a..e0e24156e6f 100644 --- a/src/rt/test/rust_test_runtime.cpp +++ b/src/rt/test/rust_test_runtime.cpp @@ -54,6 +54,7 @@ rust_task_test::worker::run() { kernel->create_domain(crate, "test"); rust_dom *domain = handle->referent(); domain->root_task->start(crate->get_exit_task_glue(), + ABI_X86_RUSTBOOT_CDECL, (uintptr_t)&task_entry, (uintptr_t)NULL, 0); domain->start_main_loop(); kernel->destroy_domain(domain);