diff options
-rw-r--r-- | .github/workflows/mjit.yml | 103 | ||||
-rw-r--r-- | lib/ruby_vm/mjit/assembler.rb | 22 | ||||
-rw-r--r-- | lib/ruby_vm/mjit/insn_compiler.rb | 21 | ||||
-rw-r--r-- | lib/ruby_vm/mjit/stats.rb | 24 | ||||
-rw-r--r-- | mjit_c.h | 11 | ||||
-rw-r--r-- | mjit_c.rb | 9 |
6 files changed, 76 insertions, 114 deletions
diff --git a/.github/workflows/mjit.yml b/.github/workflows/mjit.yml deleted file mode 100644 index 6ef18d9b8b..0000000000 --- a/.github/workflows/mjit.yml +++ /dev/null @@ -1,103 +0,0 @@ -name: MJIT -on: - push: - paths-ignore: - - 'doc/**' - - '**/man' - - '**.md' - - '**.rdoc' - - '**/.document' - pull_request: - paths-ignore: - - 'doc/**' - - '**/man' - - '**.md' - - '**.rdoc' - - '**/.document' - -concurrency: - group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }} - cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }} - -permissions: - contents: read - -jobs: - make: - strategy: - matrix: - test_task: [check] # to make job names consistent - mjit_opts: [--mjit-wait] - fail-fast: false - runs-on: ubuntu-latest - if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} - env: - TESTOPTS: '-q --tty=no' - RUN_OPTS: '--disable-gems ${{ matrix.mjit_opts }} --mjit-debug=-ggdb3' - GITPULLOPTIONS: --no-tags origin ${{github.ref}} - steps: - - run: mkdir build - working-directory: - - name: Install libraries - run: | - set -x - sudo apt-get update -q || : - sudo apt-get install --no-install-recommends -q -y build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev bison autoconf ruby - - name: git config - run: | - git config --global advice.detachedHead 0 - git config --global init.defaultBranch garbage - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 - with: - path: src - - uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3.2.6 - with: - path: src/.downloaded-cache - key: downloaded-cache - - name: Fixed world writable dirs - run: | - chmod -v go-w $HOME $HOME/.config - sudo chmod -R go-w /usr/share - sudo bash -c 'IFS=:; for d in '"$PATH"'; do chmod -v go-w $d; done' || : - - name: Set ENV - run: | - echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV - - run: ./autogen.sh - working-directory: src - - name: Run configure - run: ../src/configure -C --disable-install-doc cppflags=-DVM_CHECK_MODE - - run: make incs - - run: make - - run: sudo make -s install - - name: Run test - run: | - unset GNUMAKEFLAGS - make -s test RUN_OPTS="$RUN_OPTS" - timeout-minutes: 60 - # - name: Run test-all - # run: | - # ulimit -c unlimited - # make -s test-all RUN_OPTS="$RUN_OPTS" - # timeout-minutes: 60 - - name: Run test-spec - run: | - unset GNUMAKEFLAGS - make -s test-spec RUN_OPTS="$RUN_OPTS" - timeout-minutes: 60 - - uses: ruby/action-slack@b6882ea6ef8f556f9f9af9ec1220d3f1ced74acf # v3.0.0 - with: - payload: | - { - "ci": "GitHub Actions", - "env": "${{ github.workflow }} / ${{ matrix.test_task }} ${{ matrix.mjit_opts }}", - "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", - "commit": "${{ github.sha }}", - "branch": "${{ github.ref_name }}" - } - env: - SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot - if: ${{ failure() && github.event_name == 'push' }} - -defaults: - run: - working-directory: build diff --git a/lib/ruby_vm/mjit/assembler.rb b/lib/ruby_vm/mjit/assembler.rb index 6fd5d12bd8..ca663fa575 100644 --- a/lib/ruby_vm/mjit/assembler.rb +++ b/lib/ruby_vm/mjit/assembler.rb @@ -54,26 +54,36 @@ module RubyVM::MJIT def add(dst, src) case [dst, src] - # ADD r/m64, imm8 (Mod 11: reg) - in [Symbol => dst_reg, Integer => src_imm] if r64?(dst_reg) && imm8?(src_imm) + # ADD r/m64, imm8 (Mod 00: [reg]) + in [[Symbol => dst_reg], Integer => src_imm] if r64?(dst_reg) && imm8?(src_imm) # REX.W + 83 /0 ib # MI: Operand 1: ModRM:r/m (r, w), Operand 2: imm8/16/32 insn( prefix: REX_W, opcode: 0x83, - mod_rm: ModRM[mod: Mod11, reg: 0, rm: dst_reg], + mod_rm: ModRM[mod: Mod00, reg: 0, rm: dst_reg], imm: imm8(src_imm), ) - # ADD r/m64, imm8 (Mod 00: [reg]) - in [[Symbol => dst_reg], Integer => src_imm] if r64?(dst_reg) && imm8?(src_imm) + # ADD r/m64, imm8 (Mod 11: reg) + in [Symbol => dst_reg, Integer => src_imm] if r64?(dst_reg) && imm8?(src_imm) # REX.W + 83 /0 ib # MI: Operand 1: ModRM:r/m (r, w), Operand 2: imm8/16/32 insn( prefix: REX_W, opcode: 0x83, - mod_rm: ModRM[mod: Mod00, reg: 0, rm: dst_reg], + mod_rm: ModRM[mod: Mod11, reg: 0, rm: dst_reg], imm: imm8(src_imm), ) + # ADD r/m64 imm32 (Mod 11: reg) + in [Symbol => dst_reg, Integer => src_imm] if r64?(dst_reg) && imm32?(src_imm) + # REX.W + 81 /0 id + # MI: Operand 1: ModRM:r/m (r, w), Operand 2: imm8/16/32 + insn( + prefix: REX_W, + opcode: 0x81, + mod_rm: ModRM[mod: Mod11, reg: 0, rm: dst_reg], + imm: imm32(src_imm), + ) else raise NotImplementedError, "add: not-implemented operands: #{dst.inspect}, #{src.inspect}" end diff --git a/lib/ruby_vm/mjit/insn_compiler.rb b/lib/ruby_vm/mjit/insn_compiler.rb index e5a8cb58d1..686e9554d6 100644 --- a/lib/ruby_vm/mjit/insn_compiler.rb +++ b/lib/ruby_vm/mjit/insn_compiler.rb @@ -508,6 +508,7 @@ module RubyVM::MJIT if flags & C.VM_CALL_KW_SPLAT != 0 # recv_index calculation may not work for this + asm.incr_counter(:send_kw_splat) return CantCompile end assert_equal(ctx.sp_offset, ctx.stack_size) # TODO: support SP motion @@ -525,12 +526,14 @@ module RubyVM::MJIT asm.jne(side_exit(jit, ctx)) else # TODO: support more classes + asm.incr_counter(:send_guard_known_object) return CantCompile end # Do method lookup (vm_cc_cme(cc) != NULL) cme = C.rb_callable_method_entry(comptime_recv_klass, mid) if cme.nil? + asm.incr_counter(:send_missing_cme) return CantCompile # We don't support vm_call_method_name end @@ -541,9 +544,11 @@ module RubyVM::MJIT when C.METHOD_VISI_PRIVATE # Allow only callsites without a receiver if flags & C.VM_CALL_FCALL == 0 + asm.incr_counter(:send_private) return CantCompile end when C.METHOD_VISI_PROTECTED + asm.incr_counter(:send_protected) return CantCompile # TODO: support this else raise 'unreachable' @@ -564,6 +569,7 @@ module RubyVM::MJIT when C.VM_METHOD_TYPE_ISEQ jit_call_iseq_setup(jit, ctx, asm, ci, cme, flags, argc) else + asm.incr_counter(:send_not_iseq) return CantCompile end end @@ -582,6 +588,7 @@ module RubyVM::MJIT if flags & C.VM_CALL_TAILCALL != 0 # We don't support vm_call_iseq_setup_tailcall + asm.incr_counter(:send_tailcall) return CantCompile end jit_call_iseq_setup_normal(jit, ctx, asm, ci, cme, flags, argc, iseq) @@ -612,13 +619,14 @@ module RubyVM::MJIT def jit_push_frame(jit, ctx, asm, ci, cme, flags, argc, iseq, frame_type, next_pc) # TODO: stack overflow check - local_size = iseq.body.local_table_size - if local_size > 0 - # TODO: support local variables - return CantCompile + local_size = iseq.body.local_table_size - iseq.body.param.size + local_size.times do |i| + asm.comment('set local variables') if i == 0 + assert_equal(ctx.sp_offset, ctx.stack_size) # TODO: support SP motion + local_index = ctx.stack_size + i + asm.mov([SP, C.VALUE.size * local_index], Qnil) end - asm.comment('move SP register to callee SP') assert_equal(ctx.sp_offset, ctx.stack_size) # TODO: support SP motion sp_offset = ctx.stack_size + local_size + 3 asm.add(SP, C.VALUE.size * sp_offset) @@ -656,6 +664,7 @@ module RubyVM::MJIT # Stub cfp->jit_return return_ctx = ctx.dup + return_ctx.stack_size -= argc + ((flags & C.VM_CALL_ARGS_BLOCKARG == 0) ? 0 : 1) # Pop args return_ctx.sp_offset = 1 # SP is in the position after popping a receiver and arguments jit_return_stub = BlockStub.new(iseq: jit.iseq, pc: next_pc, ctx: return_ctx) jit_return = Assembler.new.then do |ocb_asm| @@ -718,10 +727,12 @@ module RubyVM::MJIT def jit_caller_setup_arg(jit, ctx, asm, flags) if flags & C.VM_CALL_ARGS_SPLAT != 0 # We don't support vm_caller_setup_arg_splat + asm.incr_counter(:send_args_splat) return CantCompile end if flags & (C.VM_CALL_KWARG | C.VM_CALL_KW_SPLAT) != 0 # We don't support keyword args either + asm.incr_counter(:send_kwarg) return CantCompile end end diff --git a/lib/ruby_vm/mjit/stats.rb b/lib/ruby_vm/mjit/stats.rb index b372ffa07e..7fa9236257 100644 --- a/lib/ruby_vm/mjit/stats.rb +++ b/lib/ruby_vm/mjit/stats.rb @@ -34,6 +34,8 @@ module RubyVM::MJIT stats = runtime_stats $stderr.puts("***MJIT: Printing MJIT statistics on exit***") + print_counters(stats, prefix: 'send_', prompt: 'method call exit reasons') + $stderr.puts "side_exit_count: #{format('%10d', stats[:side_exit_count])}" $stderr.puts "total_insns_count: #{format('%10d', stats[:total_insns_count])}" if stats.key?(:total_insns_count) $stderr.puts "vm_insns_count: #{format('%10d', stats[:vm_insns_count])}" if stats.key?(:vm_insns_count) @@ -43,6 +45,28 @@ module RubyVM::MJIT print_exit_counts(stats) end + def print_counters(stats, prefix:, prompt:) + $stderr.puts("#{prompt}: ") + counters = stats.filter { |key, _| key.start_with?(prefix) } + counters.filter! { |_, value| value != 0 } + counters.transform_keys! { |key| key.to_s.delete_prefix(prefix) } + + if counters.empty? + $stderr.puts(" (all relevant counters are zero)") + return + end + + counters = counters.to_a + counters.sort_by! { |(_, counter_value)| counter_value } + longest_name_length = counters.max_by { |(name, _)| name.length }.first.length + total = counters.sum { |(_, counter_value)| counter_value } + + counters.reverse_each do |(name, value)| + percentage = value.fdiv(total) * 100 + $stderr.printf(" %*s %10d (%4.1f%%)\n", longest_name_length, name, value, percentage) + end + end + def print_exit_counts(stats, how_many: 20, padding: 2) exits = stats.filter_map { |name, count| [name.to_s.delete_prefix('exit_'), count] if name.start_with?('exit_') }.to_h return if exits.empty? @@ -107,6 +107,17 @@ extern uint8_t *rb_mjit_mem_block; #define MJIT_RUNTIME_COUNTERS(...) struct rb_mjit_runtime_counters { size_t __VA_ARGS__; }; MJIT_RUNTIME_COUNTERS( vm_insns_count, + + send_kw_splat, + send_guard_known_object, + send_missing_cme, + send_private, + send_protected, + send_not_iseq, + send_args_splat, + send_kwarg, + send_tailcall, + mjit_insns_count ) #undef MJIT_RUNTIME_COUNTERS @@ -792,6 +792,15 @@ module RubyVM::MJIT # :nodoc: all @rb_mjit_runtime_counters ||= CType::Struct.new( "rb_mjit_runtime_counters", Primitive.cexpr!("SIZEOF(struct rb_mjit_runtime_counters)"), vm_insns_count: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), vm_insns_count)")], + send_kw_splat: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_kw_splat)")], + send_guard_known_object: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_guard_known_object)")], + send_missing_cme: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_missing_cme)")], + send_private: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_private)")], + send_protected: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_protected)")], + send_not_iseq: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_not_iseq)")], + send_args_splat: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_args_splat)")], + send_kwarg: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_kwarg)")], + send_tailcall: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_tailcall)")], mjit_insns_count: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), mjit_insns_count)")], ) end |