Pastie now auto-senses if line-wrap is a bad or good idea. Feedback?
## mark a section (Learn more)
diff --git a/rakelib/vm.rake b/rakelib/vm.rake index 25e30d7..2d526e8 100644 --- a/rakelib/vm.rake +++ b/rakelib/vm.rake @@ -479,10 +479,14 @@ end files TYPE_GEN, field_extract_headers + %w[vm/codegen/field_extract.rb] + [:run_field_extract] do end -file 'vm/jit-test' => EXTERNALS + objs + jit_objs + ["vm/assembler/libudis86.a"] do |t| +file 'vm/jit-test' => EXTERNALS + objs + jit_objs + ["vm/drivers/jit-test.o", "vm/assembler/libudis86.a"] do |t| ld t end +file "vm/drivers/jit-test.o" => "vm/drivers/jit-test.cpp" do + compile_c "vm/drivers/jit-test.o", "vm/drivers/jit-test.cpp" +end + file 'vm/vm' => EXTERNALS + objs + vm_objs do |t| ld t end diff --git a/vm/assembler/jit.cpp b/vm/assembler/jit.cpp index 551f4f7..3c74d5a 100644 --- a/vm/assembler/jit.cpp +++ b/vm/assembler/jit.cpp @@ -18,12 +18,17 @@ using namespace assembler_x86; using namespace operations; using namespace rubinius; +extern "C" Object* send_slowly(STATE, VMMethod* vmm, CallFrame* const call_frame, Symbol* name, size_t args); + +// It's not thread safe. Although it's okay for now (becuase we use GIL), +// at some point in the future you might want to get rid of it +static uint8_t * global_buffer = new uint8_t[1024*1024]; namespace rubinius { JITCompiler::JITCompiler() : stack_cached_(false) , own_buffer_(true) - , buffer_(new uint8_t[1024*1024]) + , buffer_(global_buffer) , a(buffer_) , s(a, ebx) , ops(s) { } @@ -36,13 +41,6 @@ namespace rubinius { , s(a, ebx) , ops(s) { } - JITCompiler::~JITCompiler() { - if(own_buffer_) { - memset(buffer_, 0, 1024*1024); - delete[] buffer_; - } - } - void JITCompiler::cache_stack(bool force) { if(!force && stack_cached_) return; stack_cached_ = true; @@ -55,42 +53,29 @@ namespace rubinius { ops.save_stack_pointer(); } -/** @todo Fix JIT. Task is gone. --rue */ -// ExecuteStatus JITCompiler::check_interrupts(VMMethod* const vmm, Task* const task, -// MethodContext* const ctx) { -// task->state->events->poll(); -// return task->state->interrupts.check_events ? cExecuteRestart : cExecuteContinue; -// } -// -// ExecuteStatus JITCompiler::slow_plus_path(VMMethod* const vmm, Task* const task, -// MethodContext* const ctx) { -// return send_slowly(vmm, task, ctx, task->state->globals.sym_plus.get(), 1); -// } -// -// ExecuteStatus JITCompiler::slow_minus_path(VMMethod* const vmm, Task* const task, -// MethodContext* const ctx) { -// return send_slowly(vmm, task, ctx, task->state->globals.sym_minus.get(), 1); -// } -// -// ExecuteStatus JITCompiler::slow_equal_path(VMMethod* const vmm, Task* const task, -// MethodContext* const ctx) { -// return send_slowly(vmm, task, ctx, task->state->globals.sym_equal.get(), 1); -// } -// -// ExecuteStatus JITCompiler::slow_nequal_path(VMMethod* const vmm, Task* const task, -// MethodContext* const ctx) { -// return send_slowly(vmm, task, ctx, task->state->globals.sym_nequal.get(), 1); -// } -// -// ExecuteStatus JITCompiler::slow_lt_path(VMMethod* const vmm, Task* const task, -// MethodContext* const ctx) { -// return send_slowly(vmm, task, ctx, task->state->globals.sym_lt.get(), 1); -// } -// -// ExecuteStatus JITCompiler::slow_gt_path(VMMethod* const vmm, Task* const task, -// MethodContext* const ctx) { -// return send_slowly(vmm, task, ctx, task->state->globals.sym_gt.get(), 1); -// } + Object * JITCompiler::slow_plus_path(STATE, VMMethod* const vmm, CallFrame* const call_frame) { + return send_slowly(state, vmm, call_frame, G(sym_plus), 1); + } + + Object * JITCompiler::slow_minus_path(STATE, VMMethod* const vmm, CallFrame* const call_frame) { + return send_slowly(state, vmm, call_frame, G(sym_minus), 1); + } + + Object * JITCompiler::slow_equal_path(STATE, VMMethod* const vmm, CallFrame* const call_frame) { + return send_slowly(state, vmm, call_frame, G(sym_equal), 1); + } + + Object * JITCompiler::slow_nequal_path(STATE, VMMethod* const vmm, CallFrame* const call_frame) { + return send_slowly(state, vmm, call_frame, G(sym_nequal), 1); + } + + Object * JITCompiler::slow_lt_path(STATE, VMMethod* const vmm, CallFrame* const call_frame) { + return send_slowly(state, vmm, call_frame, G(sym_lt), 1); + } + + Object * JITCompiler::slow_gt_path(STATE, VMMethod* const vmm, CallFrame* const call_frame) { + return send_slowly(state, vmm, call_frame, G(sym_gt), 1); + } void JITCompiler::maybe_return(int i, uintptr_t **last_imm, AssemblerX86::NearJumpLocation &fin) { @@ -105,12 +90,12 @@ namespace rubinius { // back into the MethodContext in the epilogue // The + 1 is to match the interpreter, where the ip points // to the next instruction rather than the current one - a.mov(ecx, i + 1); + a.mov(ecx, i); // If the return value of the operation (located in eax), - // is cExecuteRestart, then jump to the epilogue, which + // is NULL, then jump to the epilogue, which // stores ecx as the virtual ip and returns. - a.cmp(eax, cExecuteRestart); + a.cmp(eax, 0); a.jump_if_equal(fin); } @@ -156,24 +141,14 @@ namespace rubinius { a.add(eax, 1); } - // Remove one from the stack - s.pop(); - - // Put the result on the stack - s.set_top(eax); - a.jump(done); a.set_label(slow_path); uncache_stack(); if(add) { - /** @todo Fix through Message::send, Task is gone. --rue */ - abort(); -// ops.call_via_symbol((void*)JITCompiler::slow_plus_path); + ops.call_via_symbol((void*)JITCompiler::slow_plus_path); } else { - /** @todo Fix through Message::send, Task is gone. --rue */ - abort(); -// ops.call_via_symbol((void*)JITCompiler::slow_minus_path); + ops.call_via_symbol((void*)JITCompiler::slow_minus_path); } cache_stack(); } @@ -205,20 +180,20 @@ namespace rubinius { a.jump_if_equal(equal_path); if(equal) { - s.set_top(cFalse); + a.mov(eax, cFalse); a.jump(done); a.set_label(equal_path); - s.set_top(cTrue); + a.mov(eax, cTrue); a.jump(done); } else { - s.set_top(cTrue); + a.mov(eax, cTrue); a.jump(done); a.set_label(equal_path); - s.set_top(cFalse); + a.mov(eax, cFalse); a.jump(done); } @@ -226,16 +201,13 @@ namespace rubinius { uncache_stack(); if(equal) { - /** @todo Fix through Message::send, Task is gone. --rue */ - abort(); -// ops.call_via_symbol((void*)JITCompiler::slow_equal_path); + ops.call_via_symbol((void*)JITCompiler::slow_equal_path); } else { - /** @todo Fix through Message::send, Task is gone. --rue */ - abort(); -// ops.call_via_symbol((void*)JITCompiler::slow_nequal_path); + ops.call_via_symbol((void*)JITCompiler::slow_nequal_path); } cache_stack(); + s.pop(); } void JITCompiler::emit_fast_compare(AssemblerX86::NearJumpLocation& done, bool less) { @@ -262,21 +234,21 @@ namespace rubinius { AssemblerX86::NearJumpLocation less_path; a.jump_if_less(less_path); - s.set_top(cFalse); + a.mov(eax, cFalse); a.jump(done); a.set_label(less_path); - s.set_top(cTrue); + a.mov(eax, cTrue); a.jump(done); } else { AssemblerX86::NearJumpLocation greater_path; a.jump_if_greater(greater_path); - s.set_top(cFalse); + a.mov(eax, cFalse); a.jump(done); a.set_label(greater_path); - s.set_top(cTrue); + a.mov(eax, cTrue); a.jump(done); } @@ -284,16 +256,12 @@ namespace rubinius { uncache_stack(); if(less) { - /** @todo Fix through Message::send, Task is gone. --rue */ - abort(); -// ops.call_via_symbol((void*)JITCompiler::slow_lt_path); + ops.call_via_symbol((void*)JITCompiler::slow_lt_path); } else { - /** @todo Fix through Message::send, Task is gone. --rue */ - abort(); -// ops.call_via_symbol((void*)JITCompiler::slow_gt_path); + ops.call_via_symbol((void*)JITCompiler::slow_gt_path); } - cache_stack(); + s.pop(); } void JITCompiler::compile(STATE, VMMethod* vmm) { @@ -336,6 +304,7 @@ namespace rubinius { for(size_t i = 0; i < vmm->total;) { opcode op = vmm->opcodes[i]; + op = instructions::reverse_superop(op); size_t width = InstructionSequence::instruction_width(op); // Set the label location @@ -441,9 +410,12 @@ namespace rubinius { ops.load_self(eax); s.push(eax); break; - + case InstructionSequence::insn_ret: + s.load_nth(eax, 0); + a.jump(real_fin); + break; // Now, for a bit more complicated ones... - // + case InstructionSequence::insn_push_local: ops.get_local(eax, vmm->opcodes[i + 1]); s.push(eax); @@ -463,13 +435,17 @@ namespace rubinius { case InstructionSequence::insn_meta_send_op_plus: { AssemblerX86::NearJumpLocation done; emit_fast_math(done, op == InstructionSequence::insn_meta_send_op_plus); - maybe_return(i, &last_imm, fin); + maybe_return(i+width, &last_imm, fin); // This is a phi point, where the fast path and slow path merge. // We have to be sure that the stack cache settings are in sync // for both paths taken at this point. To be sure of that, we // always run cache_stack() after calling slow_path_plus. a.set_label(done); + // Remove one from the stack + s.pop(); + // Put the result on the stack + s.set_top(eax); break; } @@ -477,9 +453,13 @@ namespace rubinius { case InstructionSequence::insn_meta_send_op_nequal: { AssemblerX86::NearJumpLocation done; emit_fast_equal(done, op == InstructionSequence::insn_meta_send_op_equal); - maybe_return(i, &last_imm, fin); + maybe_return(i+width, &last_imm, fin); a.set_label(done); + + // Put the result on the stack + s.set_top(eax); + break; } @@ -487,41 +467,38 @@ namespace rubinius { case InstructionSequence::insn_meta_send_op_gt: { AssemblerX86::NearJumpLocation done; emit_fast_compare(done, op == InstructionSequence::insn_meta_send_op_lt); - maybe_return(i, &last_imm, fin); + maybe_return(i+width, &last_imm, fin); a.set_label(done); + // Put the result on the stack + s.set_top(eax); break; } - - case InstructionSequence::insn_push_const_fast: { - AssemblerX86::NearJumpLocation slow_path; - AssemblerX86::NearJumpLocation done; - - ops.get_literal(eax, vmm->opcodes[i + 2]); - a.cmp(eax, reinterpret_cast<uintptr_t>(Qnil)); - a.jump_if_equal(slow_path); - a.mov(eax, a.address(eax, FIELD_OFFSET(rubinius::LookupTableAssociation, value_))); - // TODO this doesn't support autoload! - s.push(eax); - - a.jump(done); - - a.set_label(slow_path); - uncache_stack(); - const instructions::Implementation* impl = instructions::implementation(op); - ops.call_operation(impl->address, impl->name, - vmm->opcodes[i + 1], - vmm->opcodes[i + 2]); - maybe_return(i, &last_imm, fin); - - a.set_label(done); - break; - } - - case InstructionSequence::insn_set_call_flags: - ops.store_call_flags(vmm->opcodes[i + 1]); - break; - + // + // case InstructionSequence::insn_push_const_fast: { + // AssemblerX86::NearJumpLocation slow_path; + // AssemblerX86::NearJumpLocation done; + // + // ops.get_literal(eax, vmm->opcodes[i + 2]); + // a.cmp(eax, reinterpret_cast<uintptr_t>(Qnil)); + // a.jump_if_equal(slow_path); + // a.mov(eax, a.address(eax, FIELD_OFFSET(rubinius::LookupTableAssociation, value_))); + // // TODO this doesn't support autoload! + // s.push(eax); + // + // a.jump(done); + // + // a.set_label(slow_path); + // uncache_stack(); + // const instructions::Implementation* impl = instructions::implementation(op); + // ops.call_operation(impl->address, impl->name, + // vmm->opcodes[i + 1], + // vmm->opcodes[i + 2]); + // maybe_return(i, &last_imm, fin); + // + // a.set_label(done); + // break; + // } // for any instruction we don't handle with a special code sequence, // just call the regular function for it. default: { @@ -546,15 +523,13 @@ call_op: op << "'\n"; abort(); } + cache_stack(); instructions::Status status = instructions::check_status(op); if(status == instructions::MightReturn) { - maybe_return(i, &last_imm, fin); + maybe_return(i + width, &last_imm, fin); } else if(status == instructions::Terminate) { a.jump(real_fin); - cache_stack(); - } else { - cache_stack(); } break; } @@ -700,75 +675,75 @@ call_op: ops.tag_fixnum(eax); s.push(eax); break; - case InstructionSequence::insn_push_self: - ops.load_self(eax); - s.push(eax); - break; - case InstructionSequence::insn_push_local: - ops.load_next_opcode(ecx); - ops.get_local(eax, ecx); // eax == local - s.push(eax); - break; - case InstructionSequence::insn_set_local: - ops.load_next_opcode(ecx); - s.load_nth(edx, 0); - ops.set_local(edx, ecx); - break; - case InstructionSequence::insn_push_literal: - ops.load_next_opcode(ecx); - ops.get_literal(eax, ecx); - s.push(eax); - break; - case InstructionSequence::insn_meta_send_op_minus: - case InstructionSequence::insn_meta_send_op_plus: { - AssemblerX86::NearJumpLocation done; - emit_fast_math(done, op == InstructionSequence::insn_meta_send_op_plus); - - // If the return value of the operation (located in eax), - // is cExecuteRestart, then jump to the epilogue, which - // stores ecx as the virtual ip and returns. - a.cmp(eax, cExecuteRestart); - a.jump_if_equal(fin); - - // This is a phi point, where the fast path and slow path merge. - // We have to be sure that the stack cache settings are in sync - // for both paths taken at this point. To be sure of that, we - // always run cache_stack() after calling slow_path_plus. - a.set_label(done); - break; - } - - case InstructionSequence::insn_meta_send_op_equal: - case InstructionSequence::insn_meta_send_op_nequal: { - AssemblerX86::NearJumpLocation done; - emit_fast_equal(done, op == InstructionSequence::insn_meta_send_op_equal); - - // If the return value of the operation (located in eax), - // is cExecuteRestart, then jump to the epilogue, which - // stores ecx as the virtual ip and returns. - a.cmp(eax, cExecuteRestart); - a.jump_if_equal(fin); - - a.set_label(done); - break; - } - - - case InstructionSequence::insn_meta_send_op_lt: - case InstructionSequence::insn_meta_send_op_gt: { - AssemblerX86::NearJumpLocation done; - emit_fast_compare(done, op == InstructionSequence::insn_meta_send_op_lt); - a.cmp(eax, cExecuteRestart); - a.jump_if_equal(fin); - - a.set_label(done); - break; - } - - case InstructionSequence::insn_set_call_flags: - ops.load_next_opcode(ecx); - ops.store_call_flags(ecx); - break; + // case InstructionSequence::insn_push_self: + // ops.load_self(eax); + // s.push(eax); + // break; + // case InstructionSequence::insn_push_local: + // ops.load_next_opcode(ecx); + // ops.get_local(eax, ecx); // eax == local + // s.push(eax); + // break; + // case InstructionSequence::insn_set_local: + // ops.load_next_opcode(ecx); + // s.load_nth(edx, 0); + // ops.set_local(edx, ecx); + // break; + // case InstructionSequence::insn_push_literal: + // ops.load_next_opcode(ecx); + // ops.get_literal(eax, ecx); + // s.push(eax); + // break; + // case InstructionSequence::insn_meta_send_op_minus: + // case InstructionSequence::insn_meta_send_op_plus: { + // AssemblerX86::NearJumpLocation done; + // emit_fast_math(done, op == InstructionSequence::insn_meta_send_op_plus); + // + // // If the return value of the operation (located in eax), + // // is cExecuteRestart, then jump to the epilogue, which + // // stores ecx as the virtual ip and returns. + // a.cmp(eax, cExecuteRestart); + // a.jump_if_equal(fin); + // + // // This is a phi point, where the fast path and slow path merge. + // // We have to be sure that the stack cache settings are in sync + // // for both paths taken at this point. To be sure of that, we + // // always run cache_stack() after calling slow_path_plus. + // a.set_label(done); + // break; + // } + // + // case InstructionSequence::insn_meta_send_op_equal: + // case InstructionSequence::insn_meta_send_op_nequal: { + // AssemblerX86::NearJumpLocation done; + // emit_fast_equal(done, op == InstructionSequence::insn_meta_send_op_equal); + // + // // If the return value of the operation (located in eax), + // // is cExecuteRestart, then jump to the epilogue, which + // // stores ecx as the virtual ip and returns. + // a.cmp(eax, cExecuteRestart); + // a.jump_if_equal(fin); + // + // a.set_label(done); + // break; + // } + // + // + // case InstructionSequence::insn_meta_send_op_lt: + // case InstructionSequence::insn_meta_send_op_gt: { + // AssemblerX86::NearJumpLocation done; + // emit_fast_compare(done, op == InstructionSequence::insn_meta_send_op_lt); + // a.cmp(eax, cExecuteRestart); + // a.jump_if_equal(fin); + // + // a.set_label(done); + // break; + // } + + // case InstructionSequence::insn_set_call_flags: + // ops.load_next_opcode(ecx); + // ops.store_call_flags(ecx); + // break; default: { uncache_stack(); diff --git a/vm/assembler/jit.hpp b/vm/assembler/jit.hpp index 8cefb21..a41a38a 100644 --- a/vm/assembler/jit.hpp +++ b/vm/assembler/jit.hpp @@ -31,7 +31,6 @@ namespace rubinius { JITCompiler(); JITCompiler(uint8_t* buffer); - ~JITCompiler(); assembler_x86::AssemblerX86& assembler() { return a; @@ -55,24 +54,17 @@ namespace rubinius { void emit_fast_compare(assembler_x86::AssemblerX86::NearJumpLocation& done, bool less); void emit_opcode(opcode op, assembler_x86::AssemblerX86::NearJumpLocation& fin); -/** @todo Fix JIT. Task is gone. --rue */ -// static ExecuteStatus slow_plus_path(VMMethod* const vmm, Task* const task, -// MethodContext* const ctx); -// -// static ExecuteStatus slow_minus_path(VMMethod* const vmm, Task* const task, -// MethodContext* const ctx); -// -// static ExecuteStatus slow_equal_path(VMMethod* const vmm, Task* const task, -// MethodContext* const ctx); -// -// static ExecuteStatus slow_nequal_path(VMMethod* const vmm, Task* const task, -// MethodContext* const ctx); -// -// static ExecuteStatus slow_lt_path(VMMethod* const vmm, Task* const task, -// MethodContext* const ctx); -// -// static ExecuteStatus slow_gt_path(VMMethod* const vmm, Task* const task, -// MethodContext* const ctx); + static Object * slow_plus_path(STATE, VMMethod* const vmm, CallFrame* const call_frame); + + static Object * slow_minus_path(STATE, VMMethod* const vmm, CallFrame* const call_frame); + + static Object * slow_equal_path(STATE, VMMethod* const vmm, CallFrame* const call_frame); + + static Object * slow_nequal_path(STATE, VMMethod* const vmm, CallFrame* const call_frame); + + static Object * slow_lt_path(STATE, VMMethod* const vmm, CallFrame* const call_frame); + + static Object * slow_gt_path(STATE, VMMethod* const vmm, CallFrame* const call_frame); // // static ExecuteStatus check_interrupts(VMMethod* const vmm, Task* const task, // MethodContext* const ctx); @@ -82,6 +74,7 @@ namespace rubinius { // Emit code to check %eax and determine if a new context was // installed. void maybe_return(int i, uintptr_t **last_imm, assembler_x86::AssemblerX86::NearJumpLocation& fin); + void must_return(int i, uintptr_t **last_imm, assembler_x86::AssemblerX86::NearJumpLocation& fin); // Pull the stack pointer into ebx if it's not there already void cache_stack(bool force = false); diff --git a/vm/assembler/operations.hpp b/vm/assembler/operations.hpp index c794d62..90a2265 100644 --- a/vm/assembler/operations.hpp +++ b/vm/assembler/operations.hpp @@ -5,6 +5,7 @@ #include "oop.hpp" #include "jit_state.h" #include "builtin/tuple.hpp" +#include "call_frame.hpp" namespace operations { @@ -74,8 +75,8 @@ namespace operations { }; // HACK to eliminate using MethodContext quickly -#undef FIELD_OFFSET -#define FIELD_OFFSET(a,b) 0 +// #undef FIELD_OFFSET +// #define FIELD_OFFSET(a,b) 0 class ObjectOperations { StackOperations &s; @@ -112,7 +113,7 @@ namespace operations { // Pull jit_state.stack into the stack pointer s.assembler().mov(s.stack_pointer(), s.assembler().address(esi, - FIELD_OFFSET(rubinius::MethodContext, js.stack))); + FIELD_OFFSET(rubinius::CallFrame, js.stack))); } void save_stack_pointer() { @@ -121,7 +122,7 @@ namespace operations { // Mov the stack pointer into jit_state.stack s.assembler().mov( s.assembler().address(esi, - FIELD_OFFSET(rubinius::MethodContext, js.stack)), + FIELD_OFFSET(rubinius::CallFrame, js.stack)), s.stack_pointer()); } @@ -131,14 +132,19 @@ namespace operations { // Do the normal prologue a.prologue(0); + // Interpreter state + a.push(0); + a.push(0); + a.mov(eax, esp); // seed the stack with the operation arguments so we don't have // to push them over and over again // - // First, add some space for 5 extra args + // First, add some space for 2 extra args // This seems like a lot, but we need space for at least 2 AND // we have to keep the stack aligned properly. - a.sub(esp, 0x14); + a.sub(esp, 0x08); + a.push(eax); // Now push the incoming arguments on a.push_arg(2); a.push_arg(1); @@ -146,6 +152,7 @@ namespace operations { } void epilogue() { + AssemblerX86::NearJumpLocation fail; // Remove the spots for 8 args we setup in prologue s.assembler().add(esp, 0x20); @@ -155,16 +162,18 @@ namespace operations { // Attempts to call +func+ as a symbol. void call_via_symbol(void* func) { - Dl_info info; - if(!dladdr(func, &info)) { - throw std::runtime_error("Unable to find symbol"); - } - - if(!info.dli_sname || info.dli_saddr != func) { - throw std::runtime_error("Unable to resolve symbol properly"); - } + // Who cares about func name? + // Dl_info info; + // if(!dladdr(func, &info)) { + // throw std::runtime_error("Unable to find symbol"); + // } + // + // if(!info.dli_sname || info.dli_saddr != func) { + // throw std::runtime_error("Unable to resolve symbol properly"); + // } - s.assembler().call(func, info.dli_sname); + // s.assembler().call(func, info.dli_sname); + s.assembler().call(func); } void call_operation(void* func, const char* name) { @@ -173,27 +182,27 @@ namespace operations { void call_operation(void* func, const char* name, int arg) { AssemblerX86 &a = s.assembler(); - a.mov(a.address(esp, 12), arg); + a.mov(a.address(esp, 16), arg); a.call(func, name); } void call_operation(void* func, const char* name, Register& arg) { AssemblerX86 &a = s.assembler(); - a.mov(a.address(esp, 12), arg); + a.mov(a.address(esp, 16), arg); a.call(func, name); } void call_operation(void* func, const char* name, int arg, int arg2) { AssemblerX86 &a = s.assembler(); - a.mov(a.address(esp, 12), arg); - a.mov(a.address(esp, 16), arg2); + a.mov(a.address(esp, 16), arg); + a.mov(a.address(esp, 20), arg2); a.call(func, name); } void call_operation(void* func, const char* name, Register& arg, Register& arg2) { AssemblerX86 &a = s.assembler(); - a.mov(a.address(esp, 12), arg); - a.mov(a.address(esp, 16), arg2); + a.mov(a.address(esp, 16), arg); + a.mov(a.address(esp, 20), arg2); a.call(func, name); } @@ -210,29 +219,29 @@ namespace operations { } void store_virtual_ip(Register& reg) { - store_mc_field(reg, FIELD_OFFSET(rubinius::MethodContext, ip)); + store_mc_field(reg, FIELD_OFFSET(rubinius::CallFrame, ip)); } void store_native_ip(Register& reg) { - store_mc_field(reg, FIELD_OFFSET(rubinius::MethodContext, native_ip)); + store_mc_field(reg, FIELD_OFFSET(rubinius::CallFrame, native_ip)); } void load_native_ip(Register& reg) { - load_mc_field(reg, FIELD_OFFSET(rubinius::MethodContext, native_ip)); + load_mc_field(reg, FIELD_OFFSET(rubinius::CallFrame, native_ip)); } void store_ip(Register& virtual_ip, Register& native_ip) { load_mc(); AssemblerX86 &a = s.assembler(); - a.mov(a.address(esi, FIELD_OFFSET(rubinius::MethodContext, ip)), virtual_ip); - a.mov(a.address(esi, FIELD_OFFSET(rubinius::MethodContext, native_ip)), native_ip); + a.mov(a.address(esi, FIELD_OFFSET(rubinius::CallFrame, ip)), virtual_ip); + a.mov(a.address(esi, FIELD_OFFSET(rubinius::CallFrame, native_ip)), native_ip); } void load_and_increment_ip(Register& reg) { load_mc(); AssemblerX86 &a = s.assembler(); - a.mov(reg, a.address(esi, FIELD_OFFSET(rubinius::MethodContext, ip))); - a.add(a.address(esi, FIELD_OFFSET(rubinius::MethodContext, ip)), 1); + a.mov(reg, a.address(esi, FIELD_OFFSET(rubinius::CallFrame, ip))); + a.add(a.address(esi, FIELD_OFFSET(rubinius::CallFrame, ip)), 1); } void load_opcodes(Register& reg) { @@ -253,48 +262,54 @@ namespace operations { } void load_self(Register& reg) { - load_home(reg); AssemblerX86 &a = s.assembler(); - a.mov(reg, a.address(reg, FIELD_OFFSET(rubinius::MethodContext, self_))); + load_call_frame(reg); + a.mov(reg, a.address(reg, FIELD_OFFSET(rubinius::CallFrame, scope))); + a.mov(reg, a.address(reg, FIELD_OFFSET(rubinius::VariableScope, self_))); } - void load_home(Register& reg) { - load_mc_field(reg, FIELD_OFFSET(rubinius::MethodContext, home_)); + void load_call_frame(Register& reg) { + s.assembler().load_arg(reg, 2); } void load_literals(Register& reg) { - load_mc_field(reg, FIELD_OFFSET(rubinius::MethodContext, cm_)); AssemblerX86 &a = s.assembler(); + load_call_frame(reg); + a.mov(reg, a.address(reg, FIELD_OFFSET(rubinius::CallFrame, cm))); a.mov(reg, a.address(reg, FIELD_OFFSET(rubinius::CompiledMethod, literals_))); } void set_local(Register& val, int which) { - load_home(eax); + load_call_frame(eax); AssemblerX86 &a = s.assembler(); - int base = FIELD_OFFSET(rubinius::MethodContext, stk); + int base = FIELD_OFFSET(rubinius::VariableScope, locals_); int offset = which * sizeof(void*); + a.mov(eax, a.address(eax, FIELD_OFFSET(rubinius::CallFrame, top_scope))); a.mov(a.address(eax, base + offset), val); } void set_local(Register& val, Register& which) { - load_home(eax); + load_call_frame(eax); AssemblerX86 &a = s.assembler(); - int base = FIELD_OFFSET(rubinius::MethodContext, stk); + int base = FIELD_OFFSET(rubinius::VariableScope, locals_); + a.mov(eax, a.address(eax, FIELD_OFFSET(rubinius::CallFrame, top_scope))); a.mov_to_table(eax, which, sizeof(void*), base, val); } void get_local(Register& val, int which) { - load_home(eax); + load_call_frame(eax); AssemblerX86 &a = s.assembler(); - int base = FIELD_OFFSET(rubinius::MethodContext, stk); + int base = FIELD_OFFSET(rubinius::VariableScope, locals_); int offset = which * sizeof(void*); + a.mov(eax, a.address(eax, FIELD_OFFSET(rubinius::CallFrame, top_scope))); a.mov(val, a.address(eax, base + offset)); } void get_local(Register& val, Register& which) { - load_home(eax); + load_call_frame(eax); AssemblerX86 &a = s.assembler(); - int base = FIELD_OFFSET(rubinius::MethodContext, stk); + int base = FIELD_OFFSET(rubinius::VariableScope, locals_); + a.mov(eax, a.address(eax, FIELD_OFFSET(rubinius::CallFrame, top_scope))); a.mov_from_table(val, eax, which, sizeof(void*), base); } diff --git a/vm/builtin/compiledmethod.cpp b/vm/builtin/compiledmethod.cpp index 0009293..db0fb31 100644 --- a/vm/builtin/compiledmethod.cpp +++ b/vm/builtin/compiledmethod.cpp @@ -19,6 +19,8 @@ #include "dispatch.hpp" #include "call_frame.hpp" #include "object_utils.hpp" +#include "vm/object_utils.hpp" +#include "assembler/jit.hpp" namespace rubinius { @@ -156,17 +158,13 @@ namespace rubinius { } MachineMethod* CompiledMethod::make_machine_method(STATE) { - if(backend_method_ == 0) { + if(backend_method_ == NULL) { formalize(state, false); } - /* JITCompiler jit; jit.compile(state, backend_method_); return MachineMethod::create(state, backend_method_, jit); - */ - - return NULL; } bool CompiledMethod::is_rescue_target(STATE, int ip) { diff --git a/vm/builtin/machine_method.cpp b/vm/builtin/machine_method.cpp index ade3ac0..92893d7 100644 --- a/vm/builtin/machine_method.cpp +++ b/vm/builtin/machine_method.cpp @@ -2,6 +2,7 @@ #include "builtin/class.hpp" #include "builtin/machine_method.hpp" #include "vmmethod.hpp" +#include "assembler/jit.hpp" #include "vm/exception.hpp" @@ -15,7 +16,7 @@ namespace rubinius { GO(machine_method).set(state->new_class_under("MachineMethod", G(rubinius))); } - /* + static void* adjust(void* old_base, void* new_base, void* address) { uintptr_t diff = reinterpret_cast<uintptr_t>(new_base) - reinterpret_cast<uintptr_t>(old_base); @@ -23,11 +24,8 @@ namespace rubinius { return reinterpret_cast<void*>( reinterpret_cast<uintptr_t>(address) + diff); } - */ MachineMethod* MachineMethod::create(STATE, VMMethod* vmm, JITCompiler& jit) { - return NULL; - /* size_t code_size = jit.assembler().used_bytes(); MachineMethod* mm = state->new_struct<MachineMethod>(G(machine_method)); @@ -71,7 +69,6 @@ namespace rubinius { } return mm; - */ } void* MachineMethod::resolve_virtual_ip(int ip) { @@ -81,7 +78,6 @@ namespace rubinius { } Object* MachineMethod::show() { - /* std::cout << "== stats ==\n"; std::cout << "number of bytecodes: " << vmmethod_->total << "\n"; std::cout << " bytes of assembley: " << code_size_ << "\n"; @@ -91,16 +87,15 @@ namespace rubinius { std::cout << " memory ratio: " << ratio << "\n"; std::cout << "\n== x86 assembly ==\n"; assembler_x86::AssemblerX86::show_buffer(function(), code_size_, false, comments_); - */ return Qnil; } - void MachineMethod::run_code(STATE, VMMethod* const vmm, + Object * MachineMethod::run_code(STATE, VMMethod* const vmm, CallFrame* const call_frame) { #ifdef IS_X86 MachineMethod* mm = vmm->machine_method(); void* func = mm->function(); - ((Runner)func)(state, vmm, call_frame); + return ((Runner)func)(state, vmm, call_frame); #else Assertion::raise("Only supported on x86"); #endif diff --git a/vm/builtin/machine_method.hpp b/vm/builtin/machine_method.hpp index 718a865..bc74e8f 100644 --- a/vm/builtin/machine_method.hpp +++ b/vm/builtin/machine_method.hpp @@ -25,6 +25,8 @@ namespace rubinius { static void init(STATE); static MachineMethod* create(STATE, VMMethod* vmm, JITCompiler& jit); + void cleanup(); + void* function() { return reinterpret_cast<void*>(function_); } @@ -34,7 +36,7 @@ namespace rubinius { } // Used for debugging. Gives us a place to break on before entering jit'd code - static void run_code(STATE, VMMethod* const vmm, CallFrame* const call_frame); + static Object * run_code(STATE, VMMethod* const vmm, CallFrame* const call_frame); // Ruby.primitive :machine_method_show Object* show(); diff --git a/vm/builtin/variable_scope.hpp b/vm/builtin/variable_scope.hpp index b93b0e2..5640585 100644 --- a/vm/builtin/variable_scope.hpp +++ b/vm/builtin/variable_scope.hpp @@ -122,7 +122,9 @@ namespace rubinius { CompiledMethod* method_; // slot Module* module_; // slot VariableScope* parent_; // slot + public: // required for JIT Object* self_; // slot + public: int number_of_locals_; Object* locals_[]; diff --git a/vm/call_frame.hpp b/vm/call_frame.hpp index f9d2010..0e55f1f 100644 --- a/vm/call_frame.hpp +++ b/vm/call_frame.hpp @@ -28,6 +28,7 @@ namespace rubinius { int flags; int args; int ip; + int native_ip; int stack_size; struct jit_state js; @@ -47,6 +48,7 @@ namespace rubinius { void prepare(int stack) { is_block = false; ip = 0; + native_ip = 0; current_unwind = 0; stack_size = stack; @@ -133,6 +135,10 @@ namespace rubinius { ip = new_ip; } + void set_native_ip(void * new_ip) { + native_ip = (int)new_ip; + } + // Manage the dynamic Unwind stack for this context void push_unwind(int target_ip, UnwindType type) { assert(current_unwind < kMaxUnwindInfos); diff --git a/vm/codegen/instructions_gen.rb b/vm/codegen/instructions_gen.rb index 1edfe76..b32ca5b 100644 --- a/vm/codegen/instructions_gen.rb +++ b/vm/codegen/instructions_gen.rb @@ -66,7 +66,7 @@ class Instructions # Indicates if this opcode dynamicly defines whether # execution should continue def custom_continue? - /RETURN\(/.match(body) + /RETURN\(/.match(body) || /HANDLE_EXCEPTION\(/.match(body) || /RUN_EXCEPTION\(/.match(body) end # Generate a C signature for the implementation code, to be used as a @@ -197,9 +197,9 @@ class Instructions methods.each do |impl| io.puts "#{impl.signature} {" io.puts impl.body - unless impl.custom_continue? - io.puts "return NULL;" - end + # unless impl.custom_continue? + io.puts "return stack_top();" + # end io.puts "}" end end @@ -458,6 +458,17 @@ CODE code << " return -1;\n" code << "}\n" + regular = InstructionSet::OpCodes.size + code << "int reverse_superop(opcode superop) {\n" + code << " static int superops[] = {" + @superinsns.each_with_index do |combo, superop| + insn = InstructionSet[combo.first] + code << " #{insn.bytecode}," + end + code << "};\n" + code << " if(superop < #{regular}) return superop;\n" + code << " return superops[superop - #{regular}];\n" + code << "}\n" =begin code << "int find_superop(opcode* stream) {\n" code << " opcode one = stream[0];\n" @@ -515,10 +526,10 @@ CODE str << "static Status check_status[] = {\n" methods = decode_methods() methods.each do |impl| - if impl.custom_continue? - str << "MightReturn,\n" - elsif [:return, :raise].include?(impl.name.flow) + if [:return, :raise].include?(impl.name.flow) str << "Terminate,\n" + elsif impl.custom_continue? + str << "MightReturn,\n" else str << "Unchanged,\n" end diff --git a/vm/drivers/jit-test.cpp b/vm/drivers/jit-test.cpp index 40b3010..b7a2a6f 100644 --- a/vm/drivers/jit-test.cpp +++ b/vm/drivers/jit-test.cpp @@ -1,9 +1,12 @@ #include "prelude.hpp" #include "object_utils.hpp" +#include "vmmethod.hpp" #include "jit.hpp" #include "environment.hpp" +#include "config_parser.hpp" #include "builtin/compiledmethod.hpp" +#include "builtin/machine_method.hpp" #include "compiled_file.hpp" #include <iostream> @@ -14,7 +17,16 @@ using namespace std; int main(int argc, char **argv) { JITCompiler compiler; - VM state; + VMManager * manager = new VMManager; + SharedState * shared = manager->create_shared_state(); + ConfigParser * config = new ConfigParser; + shared->user_config = config; + VM * state = shared->new_vm(); + + state->initialize(VM::default_bytes); + state->boot(); + + state->global_lock().lock(); std::ifstream stream(argv[1]); if(!stream) { @@ -27,11 +39,12 @@ int main(int argc, char **argv) { cout << "Invalid file.\n"; } - VMMethod* vmm = as<CompiledMethod>(cf->body(&state))->formalize(&state, false); + VMMethod* vmm = as<CompiledMethod>(cf->body(state))->formalize(state, false); delete cf; - MachineMethod* mm = compiler.compile(vmm); + compiler.compile(state, vmm); + MachineMethod* mm = MachineMethod::create(state, vmm, compiler); mm->show(); return 0; } diff --git a/vm/instructions.hpp b/vm/instructions.hpp index 0c387a1..fdb6c93 100644 --- a/vm/instructions.hpp +++ b/vm/instructions.hpp @@ -20,6 +20,7 @@ namespace rubinius { Status check_status(int op); int find_superop(opcode* stream); + int reverse_superop(opcode superop); } } diff --git a/vm/vmmethod.cpp b/vm/vmmethod.cpp index f2a977a..cba5246 100644 --- a/vm/vmmethod.cpp +++ b/vm/vmmethod.cpp @@ -25,6 +25,8 @@ #include "raise_reason.hpp" +#include "assembler/jit.hpp" + #define CALLS_TIL_JIT 50 #define JIT_MAX_METHOD_SIZE 2048 @@ -476,6 +478,23 @@ namespace rubinius { CompiledMethod* cm = as<CompiledMethod>(msg.method); VMMethod* vmm = cm->backend_method_; +#ifdef USE_USAGE_JIT + // A negative call_count means we've disabled usage based JIT + // for this method. + if(vmm->call_count >= 0) { + if(vmm->call_count >= CALLS_TIL_JIT) { + state->stats.jitted_methods++; + uint64_t start = get_current_time(); + MachineMethod* mm = cm->make_machine_method(state); + mm->activate(); + vmm->call_count = -1; + state->stats.jit_timing += (get_current_time() - start); + } else { + vmm->call_count++; + } + } +#endif + VariableScope* scope = (VariableScope*)alloca(sizeof(VariableScope) + (vmm->number_of_locals * sizeof(Object*))); @@ -700,6 +719,9 @@ namespace rubinius { UnwindInfo& info = call_frame->pop_unwind(); call_frame->position_stack(info.stack_depth); call_frame->set_ip(info.target_ip); + if(vmm->machine_method_.get()) { + call_frame->set_native_ip(vmm->machine_method_->resolve_virtual_ip(info.target_ip)); + } } else { return NULL; } @@ -714,6 +736,9 @@ namespace rubinius { if(info.for_ensure()) { call_frame->position_stack(info.stack_depth); call_frame->set_ip(info.target_ip); + if(vmm->machine_method_.get()) { + call_frame->set_native_ip(vmm->machine_method_->resolve_virtual_ip(info.target_ip)); + } // Don't reset ep here, we're still handling the return/break. goto continue_to_run;
This paste will be private.
From the Design Piracy series on my blog: