class RubyVM::RJIT::Invariants - ruby_vm: Ruby Standard Library Documentation

# class RubyVM::RJIT::Invariants

### Public Class Methods

assume_bop_not_redefined(jit, klass, op) click to toggle source

@param jit [RubyVM::RJIT::JITState] @param klass [Integer] @param op [Integer]

    # File ruby_vm/rjit/invariants.rb, line 27
    def assume_bop_not_redefined(jit, klass, op)
      return false unless C.BASIC_OP_UNREDEFINED_P(klass, op)

      ensure_block_entry_exit(jit, cause: 'assume_bop_not_redefined')
      @bop_blocks << jit.block
      true
    end

assume_method_basic_definition(jit, klass, mid) click to toggle source

@param jit [RubyVM::RJIT::JITState]

    # File ruby_vm/rjit/invariants.rb, line 42
    def assume_method_basic_definition(jit, klass, mid)
      if C.rb_method_basic_definition_p(klass, mid)
        cme = C.rb_callable_method_entry(klass, mid)
        assume_method_lookup_stable(jit, cme)
        true
      else
        false
      end
    end

assume_method_lookup_stable(jit, cme) click to toggle source

@param jit [RubyVM::RJIT::JITState]

    # File ruby_vm/rjit/invariants.rb, line 36
    def assume_method_lookup_stable(jit, cme)
      ensure_block_entry_exit(jit, cause: 'assume_method_lookup_stable')
      @cme_blocks[cme.to_i] << jit.block
    end

assume_stable_constant_names(jit, idlist) click to toggle source

    # File ruby_vm/rjit/invariants.rb, line 52
    def assume_stable_constant_names(jit, idlist)
      (0..).each do |i|
        break if (id = idlist[i]) == 0
        @const_blocks[id] << jit.block
      end
    end

ensure_block_entry_exit(jit, cause:) click to toggle source

@param jit [RubyVM::RJIT::JITState] @param block [RubyVM::RJIT::Block]

    # File ruby_vm/rjit/invariants.rb, line 119
    def ensure_block_entry_exit(jit, cause:)
      block = jit.block
      if block.entry_exit.nil?
        block.entry_exit = Assembler.new.then do |asm|
          @exit_compiler.compile_entry_exit(block.pc, block.ctx, asm, cause:)
          @ocb.write(asm)
        end
      end
    end

initialize(cb, ocb, compiler, exit_compiler) click to toggle source

Called by `RubyVM::RJIT::Compiler` to lazily initialize this @param cb [CodeBlock] @param ocb [CodeBlock] @param compiler [RubyVM::RJIT::Compiler] @param exit_compiler [RubyVM::RJIT::ExitCompiler]

    # File ruby_vm/rjit/invariants.rb, line 11
    def initialize(cb, ocb, compiler, exit_compiler)
      @cb = cb
      @ocb = ocb
      @compiler = compiler
      @exit_compiler = exit_compiler
      @bop_blocks = Set.new # TODO: actually invalidate this
      @cme_blocks = Hash.new { |h, k| h[k] = Set.new }
      @const_blocks = Hash.new { |h, k| h[k] = Set.new }
      @patches = {}

      # freeze # workaround a binding.irb issue. TODO: resurrect this
    end

on_cme_invalidate(cme) click to toggle source

    # File ruby_vm/rjit/invariants.rb, line 69
    def on_cme_invalidate(cme)
      @cme_blocks.fetch(cme.to_i, []).each do |block|
        @cb.with_write_addr(block.start_addr) do
          asm = Assembler.new
          asm.comment('on_cme_invalidate')
          asm.jmp(block.entry_exit)
          @cb.write(asm)
        end
        # TODO: re-generate branches that refer to this block
      end
      @cme_blocks.delete(cme.to_i)
    end

on_constant_ic_update(iseq, ic, insn_idx) click to toggle source

    # File ruby_vm/rjit/invariants.rb, line 82
    def on_constant_ic_update(iseq, ic, insn_idx)
      # TODO: check multi ractor as well
      if ic.entry.ic_cref
        # No need to recompile the slowpath
        return
      end

      pc = iseq.body.iseq_encoded + insn_idx
      insn_name = Compiler.decode_insn(pc.*).name
      if insn_name != :opt_getconstant_path && insn_name != :trace_opt_getconstant_path
        raise 'insn_idx was not at opt_getconstant_path'
      end
      if ic.to_i != pc[1]
        raise 'insn_idx + 1 was not at the updated IC'
      end
      @compiler.invalidate_blocks(iseq, pc.to_i)
    end

on_constant_state_changed(id) click to toggle source

    # File ruby_vm/rjit/invariants.rb, line 100
    def on_constant_state_changed(id)
      @const_blocks.fetch(id, []).each do |block|
        @compiler.invalidate_block(block)
      end
    end

on_tracing_invalidate_all() click to toggle source

    # File ruby_vm/rjit/invariants.rb, line 106
    def on_tracing_invalidate_all
      invalidate_all
    end

on_update_references() click to toggle source

    # File ruby_vm/rjit/invariants.rb, line 110
    def on_update_references
      # Give up. In order to support GC.compact, you'd have to update ISEQ
      # addresses in BranchStub, etc. Ideally, we'd need to update moved
      # pointers in JITed code here, but we just invalidate all for now.
      invalidate_all
    end

record_global_inval_patch(asm, target) click to toggle source

@param asm [RubyVM::RJIT::Assembler]

    # File ruby_vm/rjit/invariants.rb, line 60
    def record_global_inval_patch(asm, target)
      asm.pos_marker do |address|
        if @patches.key?(address)
          raise 'multiple patches in the same address'
        end
        @patches[address] = target
      end
    end

### Private Class Methods

invalidate_all() click to toggle source

    # File ruby_vm/rjit/invariants.rb, line 131
    def invalidate_all
      # On-Stack Replacement
      @patches.each do |address, target|
        # TODO: assert patches don't overlap each other
        @cb.with_write_addr(address) do
          asm = Assembler.new
          asm.comment('on_tracing_invalidate_all')
          asm.jmp(target)
          @cb.write(asm)
        end
      end
      @patches.clear

      C.rjit_for_each_iseq do |iseq|
        # Avoid entering past code
        iseq.body.jit_entry = 0
        # Avoid reusing past code
        iseq.body.rjit_blocks.clear if iseq.body.rjit_blocks
        # Compile this again if not converted to trace_* insns
        iseq.body.jit_entry_calls = 0
      end
    end