diff --git a/rakelib/vm.rake b/rakelib/vm.rake
index 25e30d7..2d526e8 100644
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;