From fd04e1b4dbbb0dae130f3de79d69ca94ecdf883e Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sun, 11 Dec 2022 21:42:25 -0800 Subject: [PATCH] Implement a no-op JIT compiler --- lib/mjit/x86_64/assembler.rb | 35 +++++++++++++++++++++++++++++++++++ lib/ruby_vm/mjit/compiler.rb | 24 +++++++++++++++++++++--- mjit.c | 8 ++++---- mjit.rb | 14 +++----------- mjit_c.h | 10 ++++++++++ mjit_c.rb | 24 ++++++++++++++++++++++++ 6 files changed, 97 insertions(+), 18 deletions(-) create mode 100644 lib/mjit/x86_64/assembler.rb diff --git a/lib/mjit/x86_64/assembler.rb b/lib/mjit/x86_64/assembler.rb new file mode 100644 index 0000000000..14f414b33b --- /dev/null +++ b/lib/mjit/x86_64/assembler.rb @@ -0,0 +1,35 @@ +class RubyVM::MJIT::Assembler + ByteWriter = RubyVM::MJIT::CType::Immediate.parse('char') + + def initialize + @bytes = [] + end + + def compile(compiler) + RubyVM::MJIT::C.mjit_mark_writable + write_bytes(compiler.write_addr, @bytes) + RubyVM::MJIT::C.mjit_mark_executable + + compiler.write_pos += @bytes.size + @bytes.clear + end + + def mov(_reg, val) + @bytes.push(0xb8, val, 0x00, 0x00, 0x00) + end + + def ret + @bytes.push(0xc3) + end + + private + + def write_bytes(addr, bytes) + writer = ByteWriter.new(addr) + # If you pack bytes containing \x00, Ruby fails to recognize bytes after \x00. + # So writing byte by byte to avoid hitting that situation. + bytes.each_with_index do |byte, index| + writer[index] = byte + end + end +end diff --git a/lib/ruby_vm/mjit/compiler.rb b/lib/ruby_vm/mjit/compiler.rb index cb2930246a..c42f75bf4d 100644 --- a/lib/ruby_vm/mjit/compiler.rb +++ b/lib/ruby_vm/mjit/compiler.rb @@ -1,6 +1,14 @@ +require 'mjit/x86_64/assembler' + class RubyVM::MJIT::Compiler - C = RubyVM::MJIT.const_get(:C, false) - INSNS = RubyVM::MJIT.const_get(:INSNS, false) + # MJIT internals + Assembler = RubyVM::MJIT::Assembler + C = RubyVM::MJIT::C + + # Ruby constants + Qundef = Fiddle::Qundef + + attr_accessor :write_pos # @param mem_block [Integer] JIT buffer address def initialize(mem_block) @@ -10,6 +18,16 @@ class RubyVM::MJIT::Compiler # @param iseq [RubyVM::MJIT::CPointer::Struct] def compile(iseq) - # TODO: implement + return if iseq.body.location.label == '
' + + iseq.body.jit_func = write_addr + asm = Assembler.new + asm.mov(:eax, Qundef) + asm.ret + asm.compile(self) + end + + def write_addr + @mem_block + @write_pos end end diff --git a/mjit.c b/mjit.c index d8e7aae152..8226404084 100644 --- a/mjit.c +++ b/mjit.c @@ -371,8 +371,8 @@ mjit_compile(FILE *f, const rb_iseq_t *iseq, const char *funcname, int id) // New stuff from here // -// TODO: Make it configurable -#define MJIT_CODE_SIZE 16 * 1024 * 1024 +// JIT buffer +uint8_t *rb_mjit_mem_block = NULL; void rb_mjit_compile(const rb_iseq_t *iseq) @@ -393,7 +393,7 @@ mjit_init(const struct mjit_options *opts) mjit_opts = *opts; extern uint8_t* rb_yjit_reserve_addr_space(uint32_t mem_size); - uint8_t *mem_block = rb_yjit_reserve_addr_space(MJIT_CODE_SIZE); + rb_mjit_mem_block = rb_yjit_reserve_addr_space(MJIT_CODE_SIZE); // MJIT doesn't support miniruby, but it might reach here by MJIT_FORCE_ENABLE. rb_mMJIT = rb_const_get(rb_cRubyVM, rb_intern("MJIT")); @@ -404,7 +404,7 @@ mjit_init(const struct mjit_options *opts) } rb_mMJITC = rb_const_get(rb_mMJIT, rb_intern("C")); VALUE rb_cMJITCompiler = rb_const_get(rb_mMJIT, rb_intern("Compiler")); - rb_MJITCompiler = rb_funcall(rb_cMJITCompiler, rb_intern("new"), 1, SIZET2NUM((size_t)mem_block)); + rb_MJITCompiler = rb_funcall(rb_cMJITCompiler, rb_intern("new"), 1, SIZET2NUM((size_t)rb_mjit_mem_block)); rb_cMJITIseqPtr = rb_funcall(rb_mMJITC, rb_intern("rb_iseq_t"), 0); mjit_call_p = true; diff --git a/mjit.rb b/mjit.rb index 717ab832a4..7bcb108233 100644 --- a/mjit.rb +++ b/mjit.rb @@ -23,15 +23,7 @@ if RubyVM::MJIT.enabled? return # miniruby doesn't support MJIT end - # forward declaration for ruby_vm/mjit/compiler - RubyVM::MJIT::C = Object.new # :nodoc: - - require 'ruby_vm/mjit/c_type' - require 'ruby_vm/mjit/instruction' - require 'ruby_vm/mjit/compiler' - require 'ruby_vm/mjit/hooks' - - module RubyVM::MJIT - private_constant(*constants) - end + RubyVM::MJIT::C = Object.new # forward declaration for mjit/compiler + require 'mjit/c_type' + require 'mjit/compiler' end diff --git a/mjit_c.h b/mjit_c.h index cc4040c9df..8bab98bec9 100644 --- a/mjit_c.h +++ b/mjit_c.h @@ -94,4 +94,14 @@ struct compile_status { struct inlined_call_context inline_context; }; +//================================================================================ +// +// New stuff from here +// + +// TODO: Make it configurable +#define MJIT_CODE_SIZE 16 * 1024 * 1024 + +extern uint8_t *rb_mjit_mem_block; + #endif /* MJIT_C_H */ diff --git a/mjit_c.rb b/mjit_c.rb index 905de6139a..7e352c61d7 100644 --- a/mjit_c.rb +++ b/mjit_c.rb @@ -5,6 +5,30 @@ module RubyVM::MJIT # :nodoc: all # This `class << C` section is for calling C functions. For importing variables # or macros as is, please consider using tool/mjit/bindgen.rb instead. class << C + #======================================================================================== + # + # New stuff + # + def mjit_mark_writable + Primitive.cstmt! %{ + extern bool rb_yjit_mark_writable(void *mem_block, uint32_t mem_size); + rb_yjit_mark_writable(rb_mjit_mem_block, MJIT_CODE_SIZE); + return Qnil; + } + end + + def mjit_mark_executable + Primitive.cstmt! %{ + extern bool rb_yjit_mark_executable(void *mem_block, uint32_t mem_size); + rb_yjit_mark_executable(rb_mjit_mem_block, MJIT_CODE_SIZE); + return Qnil; + } + end + + #======================================================================================== + # + # Old stuff + # def rb_hash_values(cdhash_addr) Primitive.cexpr! 'rb_hash_values((VALUE)NUM2PTR(cdhash_addr))' end