Implement a no-op JIT compiler

This commit is contained in:
Takashi Kokubun 2022-12-11 21:42:25 -08:00
Родитель baa120ee80
Коммит fd04e1b4db
6 изменённых файлов: 97 добавлений и 18 удалений

Просмотреть файл

@ -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

Просмотреть файл

@ -1,6 +1,14 @@
require 'mjit/x86_64/assembler'
class RubyVM::MJIT::Compiler class RubyVM::MJIT::Compiler
C = RubyVM::MJIT.const_get(:C, false) # MJIT internals
INSNS = RubyVM::MJIT.const_get(:INSNS, false) Assembler = RubyVM::MJIT::Assembler
C = RubyVM::MJIT::C
# Ruby constants
Qundef = Fiddle::Qundef
attr_accessor :write_pos
# @param mem_block [Integer] JIT buffer address # @param mem_block [Integer] JIT buffer address
def initialize(mem_block) def initialize(mem_block)
@ -10,6 +18,16 @@ class RubyVM::MJIT::Compiler
# @param iseq [RubyVM::MJIT::CPointer::Struct] # @param iseq [RubyVM::MJIT::CPointer::Struct]
def compile(iseq) 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
end end

8
mjit.c
Просмотреть файл

@ -371,8 +371,8 @@ mjit_compile(FILE *f, const rb_iseq_t *iseq, const char *funcname, int id)
// New stuff from here // New stuff from here
// //
// TODO: Make it configurable // JIT buffer
#define MJIT_CODE_SIZE 16 * 1024 * 1024 uint8_t *rb_mjit_mem_block = NULL;
void void
rb_mjit_compile(const rb_iseq_t *iseq) rb_mjit_compile(const rb_iseq_t *iseq)
@ -393,7 +393,7 @@ mjit_init(const struct mjit_options *opts)
mjit_opts = *opts; mjit_opts = *opts;
extern uint8_t* rb_yjit_reserve_addr_space(uint32_t mem_size); 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. // MJIT doesn't support miniruby, but it might reach here by MJIT_FORCE_ENABLE.
rb_mMJIT = rb_const_get(rb_cRubyVM, rb_intern("MJIT")); 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")); rb_mMJITC = rb_const_get(rb_mMJIT, rb_intern("C"));
VALUE rb_cMJITCompiler = rb_const_get(rb_mMJIT, rb_intern("Compiler")); 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); rb_cMJITIseqPtr = rb_funcall(rb_mMJITC, rb_intern("rb_iseq_t"), 0);
mjit_call_p = true; mjit_call_p = true;

14
mjit.rb
Просмотреть файл

@ -23,15 +23,7 @@ if RubyVM::MJIT.enabled?
return # miniruby doesn't support MJIT return # miniruby doesn't support MJIT
end end
# forward declaration for ruby_vm/mjit/compiler RubyVM::MJIT::C = Object.new # forward declaration for mjit/compiler
RubyVM::MJIT::C = Object.new # :nodoc: require 'mjit/c_type'
require 'mjit/compiler'
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
end end

Просмотреть файл

@ -94,4 +94,14 @@ struct compile_status {
struct inlined_call_context inline_context; 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 */ #endif /* MJIT_C_H */

Просмотреть файл

@ -5,6 +5,30 @@ module RubyVM::MJIT # :nodoc: all
# This `class << C` section is for calling C functions. For importing variables # This `class << C` section is for calling C functions. For importing variables
# or macros as is, please consider using tool/mjit/bindgen.rb instead. # or macros as is, please consider using tool/mjit/bindgen.rb instead.
class << C 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) def rb_hash_values(cdhash_addr)
Primitive.cexpr! 'rb_hash_values((VALUE)NUM2PTR(cdhash_addr))' Primitive.cexpr! 'rb_hash_values((VALUE)NUM2PTR(cdhash_addr))'
end end