summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/mjit/stats.rb68
-rw-r--r--lib/mjit/x86_assembler.rb20
-rw-r--r--lib/ruby_vm/mjit/c_pointer.rb7
-rw-r--r--lib/ruby_vm/mjit/compiler.rb31
4 files changed, 113 insertions, 13 deletions
diff --git a/lib/mjit/stats.rb b/lib/mjit/stats.rb
new file mode 100644
index 0000000000..263948bc0e
--- /dev/null
+++ b/lib/mjit/stats.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+module RubyVM::MJIT
+ def self.runtime_stats
+ stats = {}
+
+ # Insn exits
+ INSNS.each_value do |insn|
+ exits = C.mjit_insn_exits[insn.bin]
+ if exits > 0
+ stats[:"exit_#{insn.name}"] = exits
+ end
+ end
+
+ # Runtime stats
+ C.rb_mjit_runtime_counters.members.each do |member|
+ stats[member] = C.rb_mjit_counters.public_send(member)
+ end
+
+ # Other stats are calculated here
+ stats[:side_exit_count] = stats.select { |name, _count| name.start_with?('exit_') }.sum(&:last)
+ if stats[:vm_insns_count] > 0
+ retired_in_mjit = stats[:mjit_insns_count] - stats[:side_exit_count]
+ stats[:total_insns_count] = retired_in_mjit + stats[:vm_insns_count]
+ stats[:ratio_in_mjit] = 100.0 * retired_in_mjit / stats[:total_insns_count]
+ end
+
+ stats
+ end
+
+ at_exit do
+ if C.mjit_opts.stats
+ print_stats
+ end
+ end
+
+ class << self
+ private
+
+ def print_stats
+ stats = runtime_stats
+ $stderr.puts("***MJIT: Printing MJIT statistics on exit***")
+
+ $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)
+ $stderr.puts "mjit_insns_count: #{format('%10d', stats[:mjit_insns_count])}"
+ $stderr.puts "ratio_in_yjit: #{format('%9.1f', stats[:ratio_in_mjit])}%" if stats.key?(:ratio_in_mjit)
+
+ print_exit_counts(stats)
+ 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?
+
+ top_exits = exits.sort_by { |_name, count| -count }.first(how_many).to_h
+ total_exits = exits.values.sum
+ $stderr.puts "Top-#{top_exits.size} most frequent exit ops (#{format("%.1f", 100.0 * top_exits.values.sum / total_exits)}% of exits):"
+
+ name_width = top_exits.map { |name, _count| name.length }.max + padding
+ count_width = top_exits.map { |_name, count| count.to_s.length }.max + padding
+ top_exits.each do |name, count|
+ ratio = 100.0 * count / total_exits
+ $stderr.puts "#{format("%#{name_width}s", name)}: #{format("%#{count_width}d", count)} (#{format('%.1f', ratio)}%)"
+ end
+ end
+ end
+end
diff --git a/lib/mjit/x86_assembler.rb b/lib/mjit/x86_assembler.rb
index ad194185ae..890fa2b80a 100644
--- a/lib/mjit/x86_assembler.rb
+++ b/lib/mjit/x86_assembler.rb
@@ -34,7 +34,7 @@ module RubyVM::MJIT
def add(dst, src)
case [dst, src]
- # ADD r/m64, imm8
+ # ADD r/m64, imm8 (Mod 11)
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
@@ -44,6 +44,16 @@ module RubyVM::MJIT
mod_rm: mod_rm(mod: 0b11, rm: reg_code(dst_reg)),
imm: imm8(src_imm),
)
+ # ADD r/m64, imm8 (Mod 00)
+ 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: mod_rm(mod: 0b00, rm: reg_code(dst_reg)), # Mod 00: [reg]
+ imm: imm8(src_imm),
+ )
else
raise NotImplementedError, "add: not-implemented operands: #{dst.inspect}, #{src.inspect}"
end
@@ -189,6 +199,14 @@ module RubyVM::MJIT
@labels[label] = @bytes.size
end
+ def incr_counter(name)
+ if C.mjit_opts.stats
+ comment("increment counter #{name}")
+ mov(:rax, C.rb_mjit_counters[name].to_i)
+ add([:rax], 1) # TODO: lock
+ end
+ end
+
private
def insn(prefix: nil, opcode:, mod_rm: nil, disp: nil, imm: nil)
diff --git a/lib/ruby_vm/mjit/c_pointer.rb b/lib/ruby_vm/mjit/c_pointer.rb
index f0f34e949e..0ba9baa7cd 100644
--- a/lib/ruby_vm/mjit/c_pointer.rb
+++ b/lib/ruby_vm/mjit/c_pointer.rb
@@ -57,11 +57,8 @@ module RubyVM::MJIT # :nodoc: all
# Return the offset to a field
define_singleton_method(:offsetof) { |field| members.fetch(field).last / 8 }
- # Get the offset of a member named +name+
- define_singleton_method(:offsetof) { |name|
- _, offset = members.fetch(name)
- offset / 8
- }
+ # Return member names
+ define_singleton_method(:members) { members.keys }
define_method(:initialize) do |addr = nil|
if addr.nil? # TODO: get rid of this feature later
diff --git a/lib/ruby_vm/mjit/compiler.rb b/lib/ruby_vm/mjit/compiler.rb
index 50da0f6fff..3dfea7088e 100644
--- a/lib/ruby_vm/mjit/compiler.rb
+++ b/lib/ruby_vm/mjit/compiler.rb
@@ -26,6 +26,12 @@ module RubyVM::MJIT
# @param ctx [RubyVM::MJIT::Context]
# @param asm [RubyVM::MJIT::X86Assembler]
def self.compile_exit(jit, ctx, asm)
+ if C.mjit_opts.stats
+ insn = decode_insn(C.VALUE.new(jit.pc).*)
+ asm.comment("increment insn exit: #{insn.name}")
+ asm.mov(:rax, (C.mjit_insn_exits + insn.bin).to_i)
+ asm.add([:rax], 1) # TODO: lock
+ end
asm.comment("exit to interpreter")
# Update pc
@@ -45,6 +51,10 @@ module RubyVM::MJIT
asm.ret
end
+ def self.decode_insn(encoded)
+ INSNS.fetch(C.rb_vm_insn_decode(encoded))
+ end
+
# @param mem_block [Integer] JIT buffer address
def initialize(mem_block)
@comments = Hash.new { |h, k| h[k] = [] }
@@ -59,7 +69,7 @@ module RubyVM::MJIT
return if iseq.body.param.flags.has_opt
asm = X86Assembler.new
- asm.comment("Block: #{iseq.body.location.label}@#{iseq.body.location.pathobj}:#{iseq.body.location.first_lineno}")
+ asm.comment("Block: #{iseq.body.location.label}@#{pathobj_path(iseq.body.location.pathobj)}:#{iseq.body.location.first_lineno}")
compile_prologue(asm)
compile_block(asm, iseq)
iseq.body.jit_func = compile(asm)
@@ -121,7 +131,7 @@ module RubyVM::MJIT
index = 0
while index < iseq.body.iseq_size
- insn = decode_insn(iseq.body.iseq_encoded[index])
+ insn = self.class.decode_insn(iseq.body.iseq_encoded[index])
jit.pc = (iseq.body.iseq_encoded + index).to_i
case compile_insn(jit, ctx, asm, insn)
@@ -139,7 +149,9 @@ module RubyVM::MJIT
# @param ctx [RubyVM::MJIT::Context]
# @param asm [RubyVM::MJIT::X86Assembler]
def compile_insn(jit, ctx, asm, insn)
+ asm.incr_counter(:mjit_insns_count)
asm.comment("Insn: #{insn.name}")
+
case insn.name
when :putnil then @insn_compiler.putnil(jit, ctx, asm)
when :leave then @insn_compiler.leave(jit, ctx, asm)
@@ -147,16 +159,12 @@ module RubyVM::MJIT
end
end
- def decode_insn(encoded)
- INSNS.fetch(C.rb_vm_insn_decode(encoded))
- end
-
def dump_disasm(from, to)
C.dump_disasm(from, to).each do |address, mnemonic, op_str|
@comments.fetch(address, []).each do |comment|
puts bold(" # #{comment}")
end
- puts " 0x#{"%x" % address}: #{mnemonic} #{op_str}"
+ puts " 0x#{format("%x", address)}: #{mnemonic} #{op_str}"
end
puts
end
@@ -164,5 +172,14 @@ module RubyVM::MJIT
def bold(text)
"\e[1m#{text}\e[0m"
end
+
+ # vm_core.h: pathobj_path
+ def pathobj_path(pathobj)
+ if pathobj.is_a?(String)
+ pathobj
+ else
+ pathobj.first
+ end
+ end
end
end