ruby-changes:68552
From: Alan <ko1@a...>
Date: Thu, 21 Oct 2021 08:08:13 +0900 (JST)
Subject: [ruby-changes:68552] 16c5ce863c (master): Yeah, this actually works!
https://git.ruby-lang.org/ruby.git/commit/?id=16c5ce863c From 16c5ce863c06dd3ae5562f4ed86fb40ced670c69 Mon Sep 17 00:00:00 2001 From: Alan Wu <XrXr@u...> Date: Thu, 3 Sep 2020 12:06:53 -0400 Subject: Yeah, this actually works! --- common.mk | 5 ++ compile.c | 5 ++ gen-ujit-example-header.rb | 105 ---------------------------------------- gen_ujit_examples.rb | 108 ++++++++++++++++++++++++++++++++++++++++++ iseq.c | 34 ++++++++++++- iseq.h | 2 +- tool/ruby_vm/views/vm.inc.erb | 2 +- 7 files changed, 153 insertions(+), 108 deletions(-) delete mode 100644 gen-ujit-example-header.rb create mode 100644 gen_ujit_examples.rb diff --git a/common.mk b/common.mk index 872e6e0a10..45ebaab86a 100644 --- a/common.mk +++ b/common.mk @@ -1105,6 +1105,10 @@ incs: $(INSNS) {$(VPATH)}node_name.inc {$(VPATH)}known_errors.inc \ https://github.com/ruby/ruby/blob/trunk/common.mk#L1105 insns: $(INSNS) +ujit_examples.h: gen_ujit_examples.rb vm.$(OBJEXT) + $(ECHO) generating $@ + $(Q) $(BASERUBY) gen_ujit_examples.rb + id.h: $(tooldir)/generic_erb.rb $(srcdir)/template/id.h.tmpl $(srcdir)/defs/id.def $(ECHO) generating $@ $(Q) $(BASERUBY) $(tooldir)/generic_erb.rb --output=$@ \ @@ -7002,6 +7006,7 @@ iseq.$(OBJEXT): {$(VPATH)}util.h https://github.com/ruby/ruby/blob/trunk/common.mk#L7006 iseq.$(OBJEXT): {$(VPATH)}vm_callinfo.h iseq.$(OBJEXT): {$(VPATH)}vm_core.h iseq.$(OBJEXT): {$(VPATH)}vm_opts.h +iseq.$(OBJEXT): {$(VPATH)}ujit_examples.h load.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h load.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h load.$(OBJEXT): $(CCAN_DIR)/list/list.h diff --git a/compile.c b/compile.c index 1e88dc242d..49263002e3 100644 --- a/compile.c +++ b/compile.c @@ -856,6 +856,8 @@ rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node) https://github.com/ruby/ruby/blob/trunk/compile.c#L856 return iseq_setup(iseq, ret); } +extern uint8_t *native_pop_code; // TODO global hack + static int rb_iseq_translate_threaded_code(rb_iseq_t *iseq) { @@ -868,6 +870,9 @@ rb_iseq_translate_threaded_code(rb_iseq_t *iseq) https://github.com/ruby/ruby/blob/trunk/compile.c#L870 int insn = (int)iseq->body->iseq_encoded[i]; int len = insn_len(insn); encoded[i] = (VALUE)table[insn]; + + if (insn == BIN(pop)) encoded[i] = (VALUE)native_pop_code; + i += len; } FL_SET((VALUE)iseq, ISEQ_TRANSLATED); diff --git a/gen-ujit-example-header.rb b/gen-ujit-example-header.rb deleted file mode 100644 index 5ad0c65f02..0000000000 --- a/gen-ujit-example-header.rb +++ /dev/null @@ -1,105 +0,0 @@ https://github.com/ruby/ruby/blob/trunk/compile.c#L0 -def get_example_instruction_id - # TODO we could get this from the script that generates vm.inc instead of dothings this song and dance - `dwarfdump --name='YARVINSN_ujit_call_example' vm.o`.each_line do |line| - if (id = line[/DW_AT_const_value\s\((\d+\))/, 1]) - p [__method__, line] - return id.to_i - end - end - raise -end - -def get_fileoff - # use the load command to figure out the offset to the start of the content of vm.o - `otool -l vm.o`.each_line do |line| - if (fileoff = line[/fileoff (\d+)/, 1]) - p [__method__, line] - return fileoff.to_i - end - end - raise -end - -def get_symbol_offset(symbol) - `nm vm.o`.each_line do |line| - if (offset = line[Regexp.compile('(\h+).+' + Regexp.escape(symbol) + '\Z'), 1]) - p [__method__, line] - return Integer(offset, 16) - end - end - raise -end - -def readint8b(offset) - bytes = IO.binread('vm.o', 8, offset) - bytes.unpack('q').first # this is native endian but we want little endian. it's fine if the host moachine is x86 -end - - -def disassemble(offset) - command = "objdump --x86-asm-syntax=intel --start-address=#{offset} --stop-address=#{offset+50} -d vm.o" - puts "Running: #{command}" - puts "feel free to verify with --reloc" - disassembly = `#{command}` - instructions = [] - puts disassembly - disassembly.each_line do |line| - line = line.strip - match = /\h+: ((?:\h\h\s?)+)\s+(\w+)/.match(line) do |match_data| - bytes = match_data[1] - mnemonic = match_data[2] - instructions << [bytes, mnemonic, line] - end - if !match && !instructions.empty? - p line - raise "expected a continuous sequence of disassembly lines" - end - end - - jmp_idx = instructions.find_index { |_, mnemonic, _| mnemonic == 'jmp' } - raise 'failed to find jmp' unless jmp_idx - raise 'generated code for example too long' unless jmp_idx < 10 - handler_instructions = instructions[(0..jmp_idx)] - raise 'rip reference in example makes copying unsafe' if handler_instructions.any? { |_, _, full_line| full_line.downcase.include?('rip') } - acceptable_mnemonics = %w(mov jmp lea call) - unrecognized = nil - handler_instructions.each { |i| unrecognized = i unless acceptable_mnemonics.include?(i[1]) } - raise "found a unrecognized \"#{unrecognized[1]}\" instruction in the example. List of recognized instructions: #{acceptable_mnemonics.join(', ')}" if unrecognized - raise 'found multiple jmp instructions' if handler_instructions.count { |_, mnemonic, _| mnemonic == 'jmp' } > 1 - raise 'found multiple call instructions' if handler_instructions.count { |_, mnemonic, _| mnemonic == 'call' } > 1 - call_idx = handler_instructions.find_index { |_, mnemonic, _| mnemonic == 'call' } - - - puts "\n\nDisassembly for the handler:" - puts handler_instructions.map{|_,_,line|line} - - pre_call_bytes = [] - post_call_bytes = [] - handler_instructions.take(call_idx).each do |bytes, mnemonic, _| - pre_call_bytes += bytes.split - end - handler_instructions[((call_idx+1)...)].each do |bytes, _, _| - post_call_bytes += bytes.split - end - - File.write("ujit_examples.h", <<-EOF) -static const uint8_t ujit_precall_bytes[] = { #{pre_call_bytes.map{ |byte| '0x'+byte}.join(', ')} }; -static const uint8_t ujit_postall_bytes[] = { #{post_call_bytes.map{ |byte| '0x'+byte}.join(', ')} }; - EOF - puts "file:" - puts File.binread("ujit_examples.h") -end - -instruction_id = get_example_instruction_id -fileoff = get_fileoff -tc_table_offset = get_symbol_offset('vm_exec_core.insns_address_table') -vm_exec_core_offset = get_symbol_offset('vm_exec_core') -p instruction_id -p fileoff -p tc_table_offset.to_s(16) -offset_to_insn_in_tc_table = fileoff + tc_table_offset + 8 * instruction_id -p offset_to_insn_in_tc_table -offset_to_handler_code_from_vm_exec_core = readint8b(offset_to_insn_in_tc_table) -p offset_to_handler_code_from_vm_exec_core -disassemble(vm_exec_core_offset + offset_to_handler_code_from_vm_exec_core) - diff --git a/gen_ujit_examples.rb b/gen_ujit_examples.rb new file mode 100644 index 0000000000..eaacd988b8 --- /dev/null +++ b/gen_ujit_examples.rb @@ -0,0 +1,108 @@ https://github.com/ruby/ruby/blob/trunk/gen_ujit_examples.rb#L1 +def get_example_instruction_id + # TODO we could get this from the script that generates vm.inc instead of dothings this song and dance + `dwarfdump --name='YARVINSN_ujit_call_example' vm.o`.each_line do |line| + if (id = line[/DW_AT_const_value\s\((\d+\))/, 1]) + p [__method__, line] if $DEBUG + return id.to_i + end + end + raise +end + +def get_fileoff + # use the load command to figure out the offset to the start of the content of vm.o + `otool -l vm.o`.each_line do |line| + if (fileoff = line[/fileoff (\d+)/, 1]) + p [__method__, line] if $DEBUG + return fileoff.to_i + end + end + raise +end + +def get_symbol_offset(symbol) + `nm vm.o`.each_line do |line| + if (offset = line[Regexp.compile('(\h+).+' + Regexp.escape(symbol) + '\Z'), 1]) + p [__method__, line] if $DEBUG + return Integer(offset, 16) + end + end + raise +end + +def readint8b(offset) + bytes = IO.binread('vm.o', 8, offset) + bytes.unpack('q').first # this is native endian but we want little endian. it's fine if the host moachine is x86 +end + + +def disassemble(offset) + command = "objdump --x86-asm-syntax=intel --start-address=#{offset} --stop-address=#{offset+50} -d vm.o" + puts "Running: #{command}" + puts "feel free to verify with --reloc" + disassembly = `#{command}` + instructions = [] + puts disassembly if $DEBUG + disassembly.each_line do |line| + line = line.strip + match_data = /\h+: ((?:\h\h\s?)+)\s+(\w+)/.match(line) + if match_data + bytes = match_data[1] + mnemonic = match_data[2] + instructions << [bytes, mnemonic, line] + break if mnemonic == 'jmp' + elsif !instructions.empty? + p line + raise "expected a continuous sequence of disassembly lines" + end + end + + jmp_idx = instructions.find_index { |_, mnemonic, _| mnemonic == 'jmp' } + raise 'failed to find jmp' unless jmp_idx + raise 'generated code for example too long' unless jmp_idx < 10 + handler_instructions = instructions[(0..jmp_idx)] + raise 'rip reference in example makes copying unsafe' if handler_instructions.any? { |_, _, full_line| full_line.downcase.include?('rip') } + acceptable_mnemonics = %w(mov jmp lea call) + unrecognized = nil + handler_instructions.each { |i| unrecognized = i unless acceptable_mnemonics.include?(i[1]) } + raise "found a unrecognized \"#{unrecognized[1]}\" instruction in the example. List of recognized instructions: #{acceptable_mnemonics.join(', ')}" if unrecognized + raise 'found multiple jmp instructions' if handler_instructions.count { |_, mnemonic, _| mnemonic == 'jmp' } > 1 + raise 'found multiple call instructions' if handler_instructions.count { |_, mnemonic, _| mnemonic == 'call' } > 1 + call_idx = handler_instructions.find_index { |_, mnemonic, _| mnemonic == 'call' } + + + puts "Disassembly for the example handler:" + puts handler_instructions.map{|_,_,line|line} + + pre_call_bytes = [] + post_call_bytes = [] + handler_instructions.take(call_idx).each do |bytes, mnemonic, _| + pre_call_bytes += bytes.split + (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/