Add example handler for ujit and scrape it from vm.o
This commit is contained in:
parent
b0c3f18ec5
commit
cec197696f
105
gen-ujit-example-header.rb
Normal file
105
gen-ujit-example-header.rb
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
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)
|
||||||
|
|
11
iseq.c
11
iseq.c
@ -3455,6 +3455,16 @@ trace_set_i(void *vstart, void *vend, size_t stride, void *data)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VALUE *
|
||||||
|
rb_ujit_empty_func(rb_control_frame_t *cfp)
|
||||||
|
{
|
||||||
|
// okay, not really empty, so maybe think of another name.
|
||||||
|
// it's put in this file instead of say, compile.c to dodge long C compile time.
|
||||||
|
// it just needs to be in a different unit from vm.o so the compiler can't see the definition
|
||||||
|
// and is forced to emit a call that respects the calling convention.
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
rb_iseq_trace_set_all(rb_event_flag_t turnon_events)
|
rb_iseq_trace_set_all(rb_event_flag_t turnon_events)
|
||||||
{
|
{
|
||||||
@ -3570,7 +3580,6 @@ struct succ_index_table {
|
|||||||
#define imm_block_rank_get(v, i) (((int)((v) >> ((i) * 7))) & 0x7f)
|
#define imm_block_rank_get(v, i) (((int)((v) >> ((i) * 7))) & 0x7f)
|
||||||
#define small_block_rank_set(v, i, r) (v) |= (uint64_t)(r) << (9 * ((i) - 1))
|
#define small_block_rank_set(v, i, r) (v) |= (uint64_t)(r) << (9 * ((i) - 1))
|
||||||
#define small_block_rank_get(v, i) ((i) == 0 ? 0 : (((int)((v) >> (((i) - 1) * 9))) & 0x1ff))
|
#define small_block_rank_get(v, i) ((i) == 0 ? 0 : (((int)((v) >> (((i) - 1) * 9))) & 0x1ff))
|
||||||
|
|
||||||
static struct succ_index_table *
|
static struct succ_index_table *
|
||||||
succ_index_table_create(int max_pos, int *data, int size)
|
succ_index_table_create(int max_pos, int *data, int size)
|
||||||
{
|
{
|
||||||
|
3
iseq.h
3
iseq.h
@ -313,6 +313,9 @@ VALUE rb_iseq_defined_string(enum defined_type type);
|
|||||||
/* vm.c */
|
/* vm.c */
|
||||||
VALUE rb_iseq_local_variables(const rb_iseq_t *iseq);
|
VALUE rb_iseq_local_variables(const rb_iseq_t *iseq);
|
||||||
|
|
||||||
|
NOINLINE(VALUE *rb_ujit_empty_func(rb_control_frame_t *cfp));
|
||||||
|
|
||||||
|
|
||||||
RUBY_SYMBOL_EXPORT_END
|
RUBY_SYMBOL_EXPORT_END
|
||||||
|
|
||||||
#endif /* RUBY_ISEQ_H */
|
#endif /* RUBY_ISEQ_H */
|
||||||
|
@ -14,9 +14,72 @@ require_relative 'bare_instructions'
|
|||||||
require_relative 'operands_unifications'
|
require_relative 'operands_unifications'
|
||||||
require_relative 'instructions_unifications'
|
require_relative 'instructions_unifications'
|
||||||
|
|
||||||
|
class RubyVM::UJITExampleInstructions
|
||||||
|
include RubyVM::CEscape
|
||||||
|
|
||||||
|
attr_reader :name
|
||||||
|
|
||||||
|
def initialize name
|
||||||
|
@name = name
|
||||||
|
end
|
||||||
|
|
||||||
|
def pretty_name
|
||||||
|
return sprintf "%s(...)(...)(...)", @name
|
||||||
|
end
|
||||||
|
|
||||||
|
def jump_destination
|
||||||
|
return @orig.name
|
||||||
|
end
|
||||||
|
|
||||||
|
def bin
|
||||||
|
return sprintf "BIN(%s)", @name
|
||||||
|
end
|
||||||
|
|
||||||
|
def width
|
||||||
|
1
|
||||||
|
end
|
||||||
|
|
||||||
|
def operands_info
|
||||||
|
""
|
||||||
|
end
|
||||||
|
|
||||||
|
def rets
|
||||||
|
return ['...']
|
||||||
|
end
|
||||||
|
|
||||||
|
def pops
|
||||||
|
return ['...']
|
||||||
|
end
|
||||||
|
|
||||||
|
def attributes
|
||||||
|
return []
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_attribute? *;
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
def handles_sp?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def always_leaf?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
@all_examples = [new('ujit_call_example')]
|
||||||
|
|
||||||
|
def self.to_a
|
||||||
|
@all_examples
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
RubyVM::Instructions = RubyVM::BareInstructions.to_a + \
|
RubyVM::Instructions = RubyVM::BareInstructions.to_a + \
|
||||||
RubyVM::OperandsUnifications.to_a + \
|
RubyVM::OperandsUnifications.to_a + \
|
||||||
RubyVM::InstructionsUnifications.to_a
|
RubyVM::InstructionsUnifications.to_a + \
|
||||||
|
RubyVM::UJITExampleInstructions.to_a
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
require_relative 'trace_instructions'
|
require_relative 'trace_instructions'
|
||||||
RubyVM::Instructions.freeze
|
RubyVM::Instructions.freeze
|
||||||
|
@ -28,3 +28,13 @@
|
|||||||
% RubyVM::TraceInstructions.to_a.each do |insn|
|
% RubyVM::TraceInstructions.to_a.each do |insn|
|
||||||
<%= render 'trace_instruction', locals: { insn: insn } -%>
|
<%= render 'trace_instruction', locals: { insn: insn } -%>
|
||||||
% end
|
% end
|
||||||
|
% RubyVM::UJITExampleInstructions.to_a.each do |insn|
|
||||||
|
INSN_ENTRY(<%= insn.name %>)
|
||||||
|
{
|
||||||
|
START_OF_ORIGINAL_INSN(<%= insn.name %>);
|
||||||
|
// assumes USE_MACHINE_REGS, aka reg_pc setup,
|
||||||
|
// aka #define SET_PC(x) (reg_cfp->pc = reg_pc = (x))
|
||||||
|
reg_pc = rb_ujit_empty_func(GET_CFP());
|
||||||
|
END_INSN(<%= insn.name %>);
|
||||||
|
}
|
||||||
|
% end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user