summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com>2021-01-28 16:58:20 -0500
committerAlan Wu <XrXr@users.noreply.github.com>2021-10-20 18:19:28 -0400
commit2e561ff255ea69e50f2d53240332cfd96463ef06 (patch)
tree3711eca121d493aa1f7a7f4e557333275a905c1e
parent3c7251b41b8850aab7c64c4885669e9c099d2465 (diff)
Restore interpreter regs in ujit hook. Implement leave bytecode.
-rw-r--r--iseq.c13
-rw-r--r--iseq.h4
-rw-r--r--tool/ruby_vm/models/micro_jit.rb10
-rw-r--r--tool/ruby_vm/models/micro_jit/example_instructions.rb12
-rw-r--r--tool/ruby_vm/views/vm.inc.erb9
-rw-r--r--ujit_codegen.c44
-rw-r--r--vm_exec.h2
7 files changed, 59 insertions, 35 deletions
diff --git a/iseq.c b/iseq.c
index d4d5261c3c..958f7f847f 100644
--- a/iseq.c
+++ b/iseq.c
@@ -3485,21 +3485,12 @@ trace_set_i(void *vstart, void *vend, size_t stride, void *data)
return 0;
}
-VALUE *
-rb_ujit_empty_func(rb_control_frame_t *cfp)
+void
+rb_ujit_empty_func_with_ec(rb_control_frame_t *cfp, rb_execution_context_t *ec)
{
- // okay, not really empty, so maybe think of another name.
// it's put in this file instead of say, compile.c to dodge long C compile time.
// it just needs to be in a different unit from vm.o so the compiler can't see the definition
// and is forced to emit a call that respects the calling convention.
- return NULL;
-}
-
-VALUE *
-rb_ujit_empty_func_with_ec(rb_control_frame_t *cfp, rb_execution_context_t *ec)
-{
- // see rb_ujit_empty_func
- return NULL;
}
void
diff --git a/iseq.h b/iseq.h
index cd939a0684..8275bab575 100644
--- a/iseq.h
+++ b/iseq.h
@@ -315,9 +315,7 @@ VALUE rb_iseq_defined_string(enum defined_type type);
/* vm.c */
VALUE rb_iseq_local_variables(const rb_iseq_t *iseq);
-NOINLINE(VALUE *rb_ujit_empty_func(rb_control_frame_t *cfp));
-NOINLINE(VALUE *rb_ujit_empty_func_with_ec(rb_control_frame_t *cfp, rb_execution_context_t *ec));
-
+NOINLINE(void rb_ujit_empty_func_with_ec(rb_control_frame_t *cfp, rb_execution_context_t *ec));
RUBY_SYMBOL_EXPORT_END
diff --git a/tool/ruby_vm/models/micro_jit.rb b/tool/ruby_vm/models/micro_jit.rb
index 9bbce73150..5e376ebb3a 100644
--- a/tool/ruby_vm/models/micro_jit.rb
+++ b/tool/ruby_vm/models/micro_jit.rb
@@ -176,10 +176,9 @@ module RubyVM::MicroJIT
disassemble(handler_offset)
end
- def make_result(success, without_pc, with_pc)
+ def make_result(success, with_pc)
[success ? 1 : 0,
[
- ['ujit_without_ec', without_pc],
['ujit_with_ec', with_pc],
]
]
@@ -193,19 +192,18 @@ module RubyVM::MicroJIT
when :linux
linux_scrape(instruction_id)
else
- raise 'Unkonwn platform. Only Mach-O on macOS and ELF on Linux are supported'
+ raise 'Unknown platform. Only Mach-O on macOS and ELF on Linux are supported'
end
end
def scrape
- without_ec = scrape_instruction(RubyVM::Instructions.find_index { |insn| insn.name == 'ujit_call_example' })
with_ec = scrape_instruction(RubyVM::Instructions.find_index { |insn| insn.name == 'ujit_call_example_with_ec' })
- make_result(true, without_ec, with_ec)
+ make_result(true, with_ec)
rescue => e
print_warning("scrape failed: #{e.message}")
int3 = '0xcc'
failure_result = ScrapeResult.new(int3, int3, ['int3'])
- make_result(false, failure_result, failure_result)
+ make_result(false, failure_result)
end
def print_warning(text)
diff --git a/tool/ruby_vm/models/micro_jit/example_instructions.rb b/tool/ruby_vm/models/micro_jit/example_instructions.rb
index 111346f6e9..23ecf72a3b 100644
--- a/tool/ruby_vm/models/micro_jit/example_instructions.rb
+++ b/tool/ruby_vm/models/micro_jit/example_instructions.rb
@@ -13,11 +13,10 @@
class RubyVM::MicroJIT::ExampleInstructions
include RubyVM::CEscape
- attr_reader :name, :call_line
+ attr_reader :name
- def initialize(name, call_line)
+ def initialize(name)
@name = name
- @call_line = call_line
end
def pretty_name
@@ -64,12 +63,7 @@ class RubyVM::MicroJIT::ExampleInstructions
false
end
- @all_examples = [
- new('ujit_call_example', 'reg_pc = rb_ujit_empty_func(GET_CFP());'),
- new('ujit_call_example_with_ec', 'reg_pc = rb_ujit_empty_func_with_ec(GET_CFP(), ec);')
- ]
-
def self.to_a
- @all_examples
+ [new('ujit_call_example_with_ec')]
end
end
diff --git a/tool/ruby_vm/views/vm.inc.erb b/tool/ruby_vm/views/vm.inc.erb
index 235eb30b11..57a860efcf 100644
--- a/tool/ruby_vm/views/vm.inc.erb
+++ b/tool/ruby_vm/views/vm.inc.erb
@@ -26,15 +26,16 @@
% end
%
% RubyVM::MicroJIT::ExampleInstructions.to_a.each do |insn|
-INSN_ENTRY(<%= insn.name %>)
+INSN_ENTRY(ujit_call_example_with_ec)
{
- START_OF_ORIGINAL_INSN(<%= insn.name %>);
+ START_OF_ORIGINAL_INSN(ujit_call_example_with_ec);
#if USE_MACHINE_REGS
// assumes USE_MACHINE_REGS, aka reg_pc setup,
// aka #define SET_PC(x) (reg_cfp->pc = reg_pc = (x))
- <%= insn.call_line %>
+ rb_ujit_empty_func_with_ec(GET_CFP(), ec);
+ RESTORE_REGS();
#endif
- END_INSN(<%= insn.name %>);
+ END_INSN(ujit_call_example_with_ec);
}
% end
%
diff --git a/ujit_codegen.c b/ujit_codegen.c
index 43da3e6473..7c7457aea3 100644
--- a/ujit_codegen.c
+++ b/ujit_codegen.c
@@ -371,7 +371,7 @@ gen_setlocal_wc0(jitstate_t* jit, ctx_t* ctx)
mov(cb, REG0, member_opnd(REG_CFP, rb_control_frame_t, ep));
// flags & VM_ENV_FLAG_WB_REQUIRED
- x86opnd_t flags_opnd = mem_opnd(64, REG0, 8 * VM_ENV_DATA_INDEX_FLAGS);
+ x86opnd_t flags_opnd = mem_opnd(64, REG0, sizeof(VALUE) * VM_ENV_DATA_INDEX_FLAGS);
test(cb, flags_opnd, imm_opnd(VM_ENV_FLAG_WB_REQUIRED));
// Create a size-exit to fall back to the interpreter
@@ -1062,6 +1062,47 @@ gen_opt_send_without_block(jitstate_t* jit, ctx_t* ctx)
return true;
}
+static bool
+gen_leave(jitstate_t* jit, ctx_t* ctx)
+{
+ // Only the return value should be on the stack
+ RUBY_ASSERT(ctx->stack_size == 1);
+
+ // Create a size-exit to fall back to the interpreter
+ uint8_t* side_exit = ujit_side_exit(jit, ctx);
+
+ // Load environment pointer EP from CFP
+ mov(cb, REG0, member_opnd(REG_CFP, rb_control_frame_t, ep));
+
+ // flags & VM_FRAME_FLAG_FINISH
+ x86opnd_t flags_opnd = mem_opnd(64, REG0, sizeof(VALUE) * VM_ENV_DATA_INDEX_FLAGS);
+ test(cb, flags_opnd, imm_opnd(VM_FRAME_FLAG_FINISH));
+
+ // if (flags & VM_FRAME_FLAG_FINISH) != 0
+ jnz_ptr(cb, side_exit);
+
+ // TODO:
+ // RUBY_VM_CHECK_INTS(ec);
+
+ // Load the return value
+ mov(cb, REG0, ctx_stack_pop(ctx, 1));
+
+ // Pop the current CFP (ec->cfp++)
+ // Note: the return PC is already in the previous CFP
+ add(cb, REG_CFP, imm_opnd(sizeof(rb_control_frame_t)));
+ mov(cb, member_opnd(REG_EC, rb_execution_context_t, cfp), REG_CFP);
+
+ // Push the return value on the caller frame
+ mov(cb, REG1, member_opnd(REG_CFP, rb_control_frame_t, sp));
+ mov(cb, mem_opnd(64, REG1, 0), REG0);
+ add(cb, member_opnd(REG_CFP, rb_control_frame_t, sp), imm_opnd(SIZEOF_VALUE));
+
+ // Write the post call bytes
+ cb_write_post_call_bytes(cb);
+
+ return true;
+}
+
void ujit_reg_op(int opcode, codegen_fn gen_fn, bool is_branch)
{
// Check that the op wasn't previously registered
@@ -1111,4 +1152,5 @@ ujit_init_codegen(void)
ujit_reg_op(BIN(branchunless), gen_branchunless, true);
ujit_reg_op(BIN(jump), gen_jump, true);
ujit_reg_op(BIN(opt_send_without_block), gen_opt_send_without_block, true);
+ ujit_reg_op(BIN(leave), gen_leave, true);
}
diff --git a/vm_exec.h b/vm_exec.h
index 998dbfb48d..1e12f5cbe3 100644
--- a/vm_exec.h
+++ b/vm_exec.h
@@ -81,7 +81,7 @@ error !
RSTRING_PTR(rb_iseq_path(reg_cfp->iseq)), \
rb_iseq_line_no(reg_cfp->iseq, reg_pc - reg_cfp->iseq->body->iseq_encoded)); \
} \
- if (USE_INSNS_COUNTER && BIN(insn) != BIN(ujit_call_example)) vm_insns_counter_count_insn(BIN(insn));
+ if (USE_INSNS_COUNTER && BIN(insn) != BIN(ujit_call_example_with_ec)) vm_insns_counter_count_insn(BIN(insn));
#define INSN_DISPATCH_SIG(insn)