summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakashi Kokubun <[email protected]>2022-12-11 21:42:25 -0800
committerTakashi Kokubun <[email protected]>2023-03-05 22:11:20 -0800
commitfd04e1b4dbbb0dae130f3de79d69ca94ecdf883e (patch)
tree5d55a3c9c6650ef0116b2378eab5d35547798b49
parentbaa120ee8008a30c11066daa30cb03fcedc2e02f (diff)
Implement a no-op JIT compiler
-rw-r--r--lib/mjit/x86_64/assembler.rb35
-rw-r--r--lib/ruby_vm/mjit/compiler.rb24
-rw-r--r--mjit.c8
-rw-r--r--mjit.rb14
-rw-r--r--mjit_c.h10
-rw-r--r--mjit_c.rb24
6 files changed, 97 insertions, 18 deletions
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 == '<main>'
+
+ 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