ので、やってみます。
(WSL で)
$ git clone --depth 1 --branch v3_4_0_preview1 https://github.com/ruby/ruby.git
(コンテナ内で)
# apt-get install -y autoconf build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev ruby
# cd ruby
# ./autogen.sh
# mkdir build && cd build
# ../configure
# make -j$(nproc)
RJIT がサポートされていない環境なので当然無効になっていますが、無理やり有効にしたらどうなるのか見てみます。
diff --git a/configure.ac b/configure.ac
index e9a452e..b75d232 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3944,7 +3944,7 @@ AS_IF([test "$cross_compiling" = no],
[arm64-darwin*|aarch64-darwin*|x86_64-darwin*], [
RJIT_TARGET_OK=yes
],
- [arm64-*linux*|aarch64-*linux*|x86_64-*linux*], [
+ [arm64-*linux*|aarch64-*linux*|x86_64-*linux*|riscv64-*linux*], [
RJIT_TARGET_OK=yes
],
[arm64-*bsd*|aarch64-*bsd*|x86_64-*bsd*], [
# make -j20 miniruby
...
../revision.h unchanged
linking miniruby
rjit_c.o: in function `builtin_inline_class_97':
/work/ruby/build/../rjit_c.rbinc:144:(.text+0x1e00): relocation truncated to fit: R_RISCV_PCREL_HI20 against `rjit_reserve_addr_space'
/usr/bin/ld: BFD (GNU Binutils for Ubuntu) 2.42.90.20240720 assertion fail ../../bfd/elfnn-riscv.c:3447
/usr/bin/ld: BFD (GNU Binutils for Ubuntu) 2.42.90.20240720 assertion fail ../../bfd/elfnn-riscv.c:3447
collect2: error: ld returned 1 exit status
make: *** [Makefile:298: miniruby] Error 1
どうやら関数の呼び出し元と呼び出し先が遠すぎるとエラーになるようです。アドレス全体を数値でコントロールしようとするのが無理筋なのでしょうか。
- Unable to understand R_RISCV_PCREL_HI20
- All Aboard, Part 2: Relocations in ELF Toolchains
- RISC-V ELF Specification
- All Aboard, Part 4: The RISC-V Code Models
-mcmodel=large
を指定できると話が早いのですが、-fPIC
と併用ができないとのこと。それはそうですね。
ということでいろいろやってみた結果、アドレス取得する関数を変えてさらに最適化を -O2
まで下げるととりあえずビルドできました。
index e6d8d5d..6cc016e 100644
--- a/rjit_c.c
+++ b/rjit_c.c
@@ -55,6 +55,8 @@ align_ptr(uint8_t *ptr, uint32_t multiple)
}
#endif
+static VALUE mprotect_exec(rb_execution_context_t *ec, VALUE self, VALUE rb_mem_block, VALUE rb_mem_size);
+
// Address space reservation. Memory pages are mapped on an as needed basis.
// See the Rust mm module for details.
static uint8_t *
@@ -66,7 +68,7 @@ rjit_reserve_addr_space(uint32_t mem_size)
// On Linux
#if defined(MAP_FIXED_NOREPLACE) && defined(_SC_PAGESIZE)
uint32_t const page_size = (uint32_t)sysconf(_SC_PAGESIZE);
- uint8_t *const cfunc_sample_addr = (void *)&rjit_reserve_addr_space;
+ uint8_t *const cfunc_sample_addr = (void *)&mprotect_exec;
uint8_t *const probe_region_end = cfunc_sample_addr + INT32_MAX;
// Align the requested address to page size
uint8_t *req_addr = align_ptr(cfunc_sample_addr, page_size);
@@ -96,7 +98,7 @@ rjit_reserve_addr_space(uint32_t mem_size)
#else
// Try to map a chunk of memory as executable
mem_block = mmap(
- (void *)rjit_reserve_addr_space,
+ (void *)mprotect_exec,
mem_size,
PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS,
# make -j20 miniruby optflags="-O2 -fno-fast-math"
...
generating miniprelude.c
making ../rjit_c.rbinc
../revision.h unchanged
compiling ../rjit_c.c
miniprelude.c updated
compiling ../miniinit.c
linking miniruby
動作まではわかりませんがビルドだけはできたので、make install
して試してみます。
# ruby --rjit-trace --rjit-call-threshold=2 --rjit -ve 'def add(a, b); a + b; end; 10.times do add(1,2) end'
ruby 3.4.0dev (2024-08-11) +RJIT [riscv64-linux]
warning: RJIT does not support riscv64-linux yet
とのこと。それはそうですね。対応していきます。
diff --git a/lib/ruby_vm/rjit/compiler.rb b/lib/ruby_vm/rjit/compiler.rb
index e5c3adf..41696e1 100644
--- a/lib/ruby_vm/rjit/compiler.rb
+++ b/lib/ruby_vm/rjit/compiler.rb
@@ -510,7 +510,7 @@ def assert(cond)
def supported_platform?
return @supported_platform if defined?(@supported_platform)
- @supported_platform = RUBY_PLATFORM.match?(/x86_64/).tap do |supported|
+ @supported_platform = RUBY_PLATFORM.match?(/x86_64|riscv64/).tap do |supported|
warn "warning: RJIT does not support #{RUBY_PLATFORM} yet" unless supported
end
end
# ruby --disable-gems --rjit-trace --rjit-call-threshold=2 --rjit -ve 'def add(a, b); a + b; end; 10.times do add
(1,2) end'
ruby 3.4.0dev (2024-08-11) +RJIT [riscv64-linux]
RuntimeError: unexpected offset: -139649552344349
/usr/local/lib/ruby/3.4.0+0/ruby_vm/rjit/assembler.rb:1053:in 'block in RubyVM::RJIT::Assembler#resolve_rel32'
<internal:array>:54:in 'Array#each'
/usr/local/lib/ruby/3.4.0+0/ruby_vm/rjit/assembler.rb:1048:in 'Enumerable#each_with_index'
/usr/local/lib/ruby/3.4.0+0/ruby_vm/rjit/assembler.rb:1048:in 'RubyVM::RJIT::Assembler#resolve_rel32'
/usr/local/lib/ruby/3.4.0+0/ruby_vm/rjit/assembler.rb:58:in 'RubyVM::RJIT::Assembler#assemble'
/usr/local/lib/ruby/3.4.0+0/ruby_vm/rjit/code_block.rb:22:in 'RubyVM::RJIT::CodeBlock#write'
/usr/local/lib/ruby/3.4.0+0/ruby_vm/rjit/insn_compiler.rb:5919:in 'block in RubyVM::RJIT::InsnCompiler#jit_direct_jump'
<internal:kernel>:135:in 'Kernel#then'
/usr/local/lib/ruby/3.4.0+0/ruby_vm/rjit/insn_compiler.rb:5917:in 'RubyVM::RJIT::InsnCompiler#jit_direct_jump'
/usr/local/lib/ruby/3.4.0+0/ruby_vm/rjit/insn_compiler.rb:5908:in 'RubyVM::RJIT::InsnCompiler#defer_compilation'
/usr/local/lib/ruby/3.4.0+0/ruby_vm/rjit/insn_compiler.rb:1452:in 'RubyVM::RJIT::InsnCompiler#opt_send_without_block'
/usr/local/lib/ruby/3.4.0+0/ruby_vm/rjit/insn_compiler.rb:91:in 'RubyVM::RJIT::InsnCompiler#compile'
/usr/local/lib/ruby/3.4.0+0/ruby_vm/rjit/compiler.rb:321:in 'RubyVM::RJIT::Compiler#compile_block'
/usr/local/lib/ruby/3.4.0+0/ruby_vm/rjit/compiler.rb:67:in 'RubyVM::RJIT::Compiler#compile'
-e:1:in 'block in <main>'
<internal:numeric>:238:in 'Integer#times'
-e:1:in '<main>'
x86_64 向けですが、JIT 処理に到達しました。 さて、RJIT は Ruby で書かれているので処理を置き換えることが可能らしいです。
RubyVM::RJIT::Compiler#compile
を置き換えるとよさそうです。アセンブラ命令列と変換ロジックを用意するのは大変なので、gcc のインラインアセンブラで手動変換した結果を使います。
# ruby --disable-gems --rjit --rjit-call-threshold=2 -v riscv64.rb
ruby 3.4.0dev (2024-08-11) +RJIT [riscv64-linux]
3
nil
nil
nil
nil
nil を返すだけのコードが生成できました。