diff --git a/mk/rt.mk b/mk/rt.mk index f65fa99ee9d..d858fec0976 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -33,6 +33,8 @@ RUNTIME_CS := rt/sync/timer.cpp \ RUNTIME_LL := rt/new_exit.ll +RUNTIME_S := rt/activate_glue.s + RUNTIME_HDR := rt/globals.h \ rt/rust.h \ rt/rust_dwarf.h \ @@ -65,7 +67,7 @@ RUNTIME_HDR := rt/globals.h \ RUNTIME_DEF := rt/rustrt$(CFG_DEF_SUFFIX) RUNTIME_INCS := -I $(S)src/rt/isaac -I $(S)src/rt/uthash -RUNTIME_OBJS := $(RUNTIME_CS:.cpp=.o) $(RUNTIME_LL:.ll=.o) +RUNTIME_OBJS := $(RUNTIME_CS:.cpp=.o) $(RUNTIME_LL:.ll=.o) $(RUNTIME_S:.s=.o) RUNTIME_LIBS := $(CFG_GCCISH_POST_LIB_FLAGS) @@ -73,6 +75,10 @@ rt/%.o: rt/%.cpp $(MKFILES) @$(call E, compile: $@) $(Q)$(call CFG_COMPILE_C, $@, $(RUNTIME_INCS)) $< +rt/%.o: rt/%.s $(MKFILES) + @$(call E, compile: $@) + $(Q)$(call CFG_COMPILE_C, $@, $(RUNTIME_INCS)) $< + rt/%.o: rt/%.ll $(MKFILES) @$(call E, llc: $@) $(Q)$(LLC) -filetype=obj -relocation-model=pic -march=x86 -o $@ $< diff --git a/src/comp/back/abi.rs b/src/comp/back/abi.rs index 54f800c1667..b508309e1a2 100644 --- a/src/comp/back/abi.rs +++ b/src/comp/back/abi.rs @@ -99,10 +99,6 @@ fn native_glue_name(int n, native_glue_type ngt) -> str { ret prefix + util::common::istr(n); } -fn activate_glue_name() -> str { - ret "rust_activate_glue"; -} - fn yield_glue_name() -> str { ret "rust_yield_glue"; } diff --git a/src/comp/back/x86.rs b/src/comp/back/x86.rs index 839851c4968..7d32a21caa3 100644 --- a/src/comp/back/x86.rs +++ b/src/comp/back/x86.rs @@ -87,92 +87,6 @@ fn store_esp_to_runtime_sp_second_arg() -> vec[str] { } -/* - * This is a bit of glue-code. It should be emitted once per - * compilation unit. - * - * - save regs on C stack - * - align sp on a 16-byte boundary - * - save sp to task.runtime_sp (runtime_sp is thus always aligned) - * - load saved task sp (switch stack) - * - restore saved task regs - * - return to saved task pc - * - * Our incoming stack looks like this: - * - * *esp+4 = [arg1 ] = task ptr - * *esp = [retpc ] - */ - -fn rust_activate_glue() -> vec[str] { - ret ["movl 4(%esp), %ecx # ecx = rust_task"] - + save_callee_saves() - + store_esp_to_runtime_sp_first_arg() - + load_esp_from_rust_sp_first_arg() - - /* - * There are two paths we can arrive at this code from: - * - * - * 1. We are activating a task for the first time. When we switch - * into the task stack and 'ret' to its first instruction, we'll - * start doing whatever the first instruction says. Probably - * saving registers and starting to establish a frame. Harmless - * stuff, doesn't look at task->rust_sp again except when it - * clobbers it during a later native call. - * - * - * 2. We are resuming a task that was descheduled by the yield glue - * below. When we switch into the task stack and 'ret', we'll be - * ret'ing to a very particular instruction: - * - * "esp <- task->rust_sp" - * - * this is the first instruction we 'ret' to after this glue, - * because it is the first instruction following *any* native - * call, and the task we are activating was descheduled - * mid-native-call. - * - * Unfortunately for us, we have already restored esp from - * task->rust_sp and are about to eat the 5 words off the top of - * it. - * - * - * | ... | <-- where esp will be once we restore + ret, below, - * | retpc | and where we'd *like* task->rust_sp to wind up. - * | ebp | - * | edi | - * | 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 - * will be a no-op. Esp won't move, and the task's stack won't - * grow. - */ - + ["addl $20, " + wstr(abi::task_field_rust_sp) + "(%ecx)"] - - - /* - * In most cases, the function we're returning to (activating) - * will have saved any caller-saves before it yielded via native call, - * so no work to do here. With one exception: when we're initially - * activating, the task needs to be in the fastcall 2nd parameter - * expected by the rust main function. That's edx. - */ - + ["mov %ecx, %edx"] - - + restore_callee_saves() - + ["ret"]; -} - /* More glue code, this time the 'bottom half' of yielding. * * We arrived here because an native call decided to deschedule the @@ -306,12 +220,8 @@ fn get_module_asm() -> str { auto glues = [decl_glue(align, prefix, - abi::activate_glue_name(), - rust_activate_glue()), - - decl_glue(align, prefix, - abi::yield_glue_name(), - rust_yield_glue())] + abi::yield_glue_name(), + rust_yield_glue())] + vec::init_fn[str](bind decl_native_glue(align, prefix, abi::ngt_rust, _), (abi::n_native_glues + 1) as uint) diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index b29af974fee..880187df458 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -55,8 +55,7 @@ state obj namegen(mutable int i) { type derived_tydesc_info = rec(ValueRef lltydesc, bool escapes); -type glue_fns = rec(ValueRef activate_glue, - ValueRef yield_glue, +type glue_fns = rec(ValueRef yield_glue, vec[ValueRef] native_glues_rust, vec[ValueRef] native_glues_pure_rust, vec[ValueRef] native_glues_cdecl, @@ -7661,9 +7660,6 @@ fn create_crate_constant(ValueRef crate_ptr, @glue_fns glues) { let ValueRef crate_addr = p2i(crate_ptr); - let ValueRef activate_glue_off = - llvm::LLVMConstSub(p2i(glues.activate_glue), crate_addr); - let ValueRef yield_glue_off = llvm::LLVMConstSub(p2i(glues.yield_glue), crate_addr); @@ -7674,7 +7670,7 @@ fn create_crate_constant(ValueRef crate_ptr, @glue_fns glues) { C_null(T_int()), // size_t debug_abbrev_sz C_null(T_int()), // ptrdiff_t debug_info_off C_null(T_int()), // size_t debug_info_sz - activate_glue_off, // size_t activate_glue_off + C_null(T_int()), // size_t pad yield_glue_off, // size_t yield_glue_off C_null(T_int()), // size_t unwind_glue_off C_null(T_int()), // size_t gc_glue_off @@ -8030,8 +8026,7 @@ fn trans_vec_append_glue(@local_ctxt cx, &ast::span sp) { fn make_glues(ModuleRef llmod, &type_names tn) -> @glue_fns { - ret @rec(activate_glue = decl_glue(llmod, tn, abi::activate_glue_name()), - yield_glue = decl_glue(llmod, tn, abi::yield_glue_name()), + ret @rec(yield_glue = decl_glue(llmod, tn, abi::yield_glue_name()), native_glues_rust = vec::init_fn[ValueRef](bind decl_native_glue(llmod, tn, diff --git a/src/rt/activate_glue.s b/src/rt/activate_glue.s new file mode 100644 index 00000000000..5b55f568880 --- /dev/null +++ b/src/rt/activate_glue.s @@ -0,0 +1,89 @@ +/* + * This is a bit of glue-code. + * + * - save regs on C stack + * - save sp to task.runtime_sp (runtime_sp is thus always aligned) + * - load saved task sp (switch stack) + * - restore saved task regs + * - return to saved task pc + * + * Our incoming stack looks like this: + * + * *esp+4 = [arg1 ] = task ptr + * *esp = [retpc ] + */ + + .globl new_rust_activate_glue + .balign 4 +new_rust_activate_glue: + movl 4(%esp), %ecx # ecx = rust_task + pushl %ebp + pushl %edi + pushl %esi + pushl %ebx + movl %esp, 12(%ecx) + movl 16(%ecx), %esp + + /* + * There are two paths we can arrive at this code from: + * + * + * 1. We are activating a task for the first time. When we switch + * into the task stack and 'ret' to its first instruction, we'll + * start doing whatever the first instruction says. Probably + * saving registers and starting to establish a frame. Harmless + * stuff, doesn't look at task->rust_sp again except when it + * clobbers it during a later native call. + * + * + * 2. We are resuming a task that was descheduled by the yield glue + * below. When we switch into the task stack and 'ret', we'll be + * ret'ing to a very particular instruction: + * + * "esp <- task->rust_sp" + * + * this is the first instruction we 'ret' to after this glue, + * because it is the first instruction following *any* native + * call, and the task we are activating was descheduled + * mid-native-call. + * + * Unfortunately for us, we have already restored esp from + * task->rust_sp and are about to eat the 5 words off the top of + * it. + * + * + * | ... | <-- where esp will be once we restore + ret, below, + * | retpc | and where we'd *like* task->rust_sp to wind up. + * | ebp | + * | edi | + * | 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 + * will be a no-op. Esp won't move, and the task's stack won't + * grow. + */ + addl $20, 16(%ecx) + + /* + * In most cases, the function we're returning to (activating) + * will have saved any caller-saves before it yielded via native call, + * so no work to do here. With one exception: when we're initially + * activating, the task needs to be in the fastcall 2nd parameter + * expected by the rust main function. That's edx. + */ + mov %ecx, %edx + + popl %ebx + popl %esi + popl %edi + popl %ebp + ret diff --git a/src/rt/rust_crate.cpp b/src/rt/rust_crate.cpp index 6f1889ffc02..410f2bd0be9 100644 --- a/src/rt/rust_crate.cpp +++ b/src/rt/rust_crate.cpp @@ -11,11 +11,6 @@ rust_crate::get_relocation_diff() const { return ((uintptr_t)this - self_addr); } -activate_glue_ty -rust_crate::get_activate_glue() const { - return (activate_glue_ty) ((uintptr_t)this + activate_glue_off); -} - uintptr_t rust_crate::get_unwind_glue() const { return ((uintptr_t)this + unwind_glue_off); diff --git a/src/rt/rust_dom.cpp b/src/rt/rust_dom.cpp index d5187ca7480..e3bbfe39449 100644 --- a/src/rt/rust_dom.cpp +++ b/src/rt/rust_dom.cpp @@ -48,10 +48,13 @@ rust_dom::~rust_dom() { } } +extern "C" void new_rust_activate_glue(rust_task *) + asm("new_rust_activate_glue"); + void rust_dom::activate(rust_task *task) { curr_task = task; - root_crate->get_activate_glue()(task); + new_rust_activate_glue(task); curr_task = NULL; } @@ -262,7 +265,6 @@ rust_dom::start_main_loop() { rust_timer timer(this); DLOG(this, dom, "started domain loop"); - DLOG(this, dom, "activate glue: " PTR, root_crate->get_activate_glue()); while (number_of_live_tasks() > 0) { A(this, kernel->is_deadlocked() == false, "deadlock"); diff --git a/src/rt/rust_internal.h b/src/rt/rust_internal.h index 213c724e7ce..cb6dd162594 100644 --- a/src/rt/rust_internal.h +++ b/src/rt/rust_internal.h @@ -247,7 +247,6 @@ public: uintptr_t get_image_base() const; ptrdiff_t get_relocation_diff() const; - activate_glue_ty get_activate_glue() const; uintptr_t get_yield_glue() const; uintptr_t get_unwind_glue() const; uintptr_t get_gc_glue() const;