Yeah, this actually works!
This commit is contained in:
parent
cec197696f
commit
16c5ce863c
@ -1105,6 +1105,10 @@ incs: $(INSNS) {$(VPATH)}node_name.inc {$(VPATH)}known_errors.inc \
|
|||||||
|
|
||||||
insns: $(INSNS)
|
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
|
id.h: $(tooldir)/generic_erb.rb $(srcdir)/template/id.h.tmpl $(srcdir)/defs/id.def
|
||||||
$(ECHO) generating $@
|
$(ECHO) generating $@
|
||||||
$(Q) $(BASERUBY) $(tooldir)/generic_erb.rb --output=$@ \
|
$(Q) $(BASERUBY) $(tooldir)/generic_erb.rb --output=$@ \
|
||||||
@ -7002,6 +7006,7 @@ iseq.$(OBJEXT): {$(VPATH)}util.h
|
|||||||
iseq.$(OBJEXT): {$(VPATH)}vm_callinfo.h
|
iseq.$(OBJEXT): {$(VPATH)}vm_callinfo.h
|
||||||
iseq.$(OBJEXT): {$(VPATH)}vm_core.h
|
iseq.$(OBJEXT): {$(VPATH)}vm_core.h
|
||||||
iseq.$(OBJEXT): {$(VPATH)}vm_opts.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)/check_type/check_type.h
|
||||||
load.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
|
load.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
|
||||||
load.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
load.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
||||||
|
@ -856,6 +856,8 @@ rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node)
|
|||||||
return iseq_setup(iseq, ret);
|
return iseq_setup(iseq, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern uint8_t *native_pop_code; // TODO global hack
|
||||||
|
|
||||||
static int
|
static int
|
||||||
rb_iseq_translate_threaded_code(rb_iseq_t *iseq)
|
rb_iseq_translate_threaded_code(rb_iseq_t *iseq)
|
||||||
{
|
{
|
||||||
@ -868,6 +870,9 @@ rb_iseq_translate_threaded_code(rb_iseq_t *iseq)
|
|||||||
int insn = (int)iseq->body->iseq_encoded[i];
|
int insn = (int)iseq->body->iseq_encoded[i];
|
||||||
int len = insn_len(insn);
|
int len = insn_len(insn);
|
||||||
encoded[i] = (VALUE)table[insn];
|
encoded[i] = (VALUE)table[insn];
|
||||||
|
|
||||||
|
if (insn == BIN(pop)) encoded[i] = (VALUE)native_pop_code;
|
||||||
|
|
||||||
i += len;
|
i += len;
|
||||||
}
|
}
|
||||||
FL_SET((VALUE)iseq, ISEQ_TRANSLATED);
|
FL_SET((VALUE)iseq, ISEQ_TRANSLATED);
|
||||||
|
@ -2,7 +2,7 @@ def get_example_instruction_id
|
|||||||
# TODO we could get this from the script that generates vm.inc instead of dothings this song and dance
|
# 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|
|
`dwarfdump --name='YARVINSN_ujit_call_example' vm.o`.each_line do |line|
|
||||||
if (id = line[/DW_AT_const_value\s\((\d+\))/, 1])
|
if (id = line[/DW_AT_const_value\s\((\d+\))/, 1])
|
||||||
p [__method__, line]
|
p [__method__, line] if $DEBUG
|
||||||
return id.to_i
|
return id.to_i
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -13,7 +13,7 @@ def get_fileoff
|
|||||||
# use the load command to figure out the offset to the start of the content of vm.o
|
# 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|
|
`otool -l vm.o`.each_line do |line|
|
||||||
if (fileoff = line[/fileoff (\d+)/, 1])
|
if (fileoff = line[/fileoff (\d+)/, 1])
|
||||||
p [__method__, line]
|
p [__method__, line] if $DEBUG
|
||||||
return fileoff.to_i
|
return fileoff.to_i
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -23,7 +23,7 @@ end
|
|||||||
def get_symbol_offset(symbol)
|
def get_symbol_offset(symbol)
|
||||||
`nm vm.o`.each_line do |line|
|
`nm vm.o`.each_line do |line|
|
||||||
if (offset = line[Regexp.compile('(\h+).+' + Regexp.escape(symbol) + '\Z'), 1])
|
if (offset = line[Regexp.compile('(\h+).+' + Regexp.escape(symbol) + '\Z'), 1])
|
||||||
p [__method__, line]
|
p [__method__, line] if $DEBUG
|
||||||
return Integer(offset, 16)
|
return Integer(offset, 16)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -42,15 +42,16 @@ def disassemble(offset)
|
|||||||
puts "feel free to verify with --reloc"
|
puts "feel free to verify with --reloc"
|
||||||
disassembly = `#{command}`
|
disassembly = `#{command}`
|
||||||
instructions = []
|
instructions = []
|
||||||
puts disassembly
|
puts disassembly if $DEBUG
|
||||||
disassembly.each_line do |line|
|
disassembly.each_line do |line|
|
||||||
line = line.strip
|
line = line.strip
|
||||||
match = /\h+: ((?:\h\h\s?)+)\s+(\w+)/.match(line) do |match_data|
|
match_data = /\h+: ((?:\h\h\s?)+)\s+(\w+)/.match(line)
|
||||||
|
if match_data
|
||||||
bytes = match_data[1]
|
bytes = match_data[1]
|
||||||
mnemonic = match_data[2]
|
mnemonic = match_data[2]
|
||||||
instructions << [bytes, mnemonic, line]
|
instructions << [bytes, mnemonic, line]
|
||||||
end
|
break if mnemonic == 'jmp'
|
||||||
if !match && !instructions.empty?
|
elsif !instructions.empty?
|
||||||
p line
|
p line
|
||||||
raise "expected a continuous sequence of disassembly lines"
|
raise "expected a continuous sequence of disassembly lines"
|
||||||
end
|
end
|
||||||
@ -70,7 +71,7 @@ def disassemble(offset)
|
|||||||
call_idx = handler_instructions.find_index { |_, mnemonic, _| mnemonic == 'call' }
|
call_idx = handler_instructions.find_index { |_, mnemonic, _| mnemonic == 'call' }
|
||||||
|
|
||||||
|
|
||||||
puts "\n\nDisassembly for the handler:"
|
puts "Disassembly for the example handler:"
|
||||||
puts handler_instructions.map{|_,_,line|line}
|
puts handler_instructions.map{|_,_,line|line}
|
||||||
|
|
||||||
pre_call_bytes = []
|
pre_call_bytes = []
|
||||||
@ -78,28 +79,30 @@ def disassemble(offset)
|
|||||||
handler_instructions.take(call_idx).each do |bytes, mnemonic, _|
|
handler_instructions.take(call_idx).each do |bytes, mnemonic, _|
|
||||||
pre_call_bytes += bytes.split
|
pre_call_bytes += bytes.split
|
||||||
end
|
end
|
||||||
handler_instructions[((call_idx+1)...)].each do |bytes, _, _|
|
handler_instructions[call_idx + 1, handler_instructions.size].each do |bytes, _, _|
|
||||||
post_call_bytes += bytes.split
|
post_call_bytes += bytes.split
|
||||||
end
|
end
|
||||||
|
|
||||||
File.write("ujit_examples.h", <<-EOF)
|
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_pre_call_bytes[] = { #{pre_call_bytes.map{ |byte| '0x'+byte}.join(', ')} };
|
||||||
static const uint8_t ujit_postall_bytes[] = { #{post_call_bytes.map{ |byte| '0x'+byte}.join(', ')} };
|
static const uint8_t ujit_post_call_bytes[] = { #{post_call_bytes.map{ |byte| '0x'+byte}.join(', ')} };
|
||||||
EOF
|
EOF
|
||||||
puts "file:"
|
if $DEBUG
|
||||||
puts File.binread("ujit_examples.h")
|
puts "file:"
|
||||||
|
puts File.binread("ujit_examples.h")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
instruction_id = get_example_instruction_id
|
instruction_id = get_example_instruction_id
|
||||||
fileoff = get_fileoff
|
fileoff = get_fileoff
|
||||||
tc_table_offset = get_symbol_offset('vm_exec_core.insns_address_table')
|
tc_table_offset = get_symbol_offset('vm_exec_core.insns_address_table')
|
||||||
vm_exec_core_offset = get_symbol_offset('vm_exec_core')
|
vm_exec_core_offset = get_symbol_offset('vm_exec_core')
|
||||||
p instruction_id
|
p instruction_id if $DEBUG
|
||||||
p fileoff
|
p fileoff if $DEBUG
|
||||||
p tc_table_offset.to_s(16)
|
p tc_table_offset.to_s(16) if $DEBUG
|
||||||
offset_to_insn_in_tc_table = fileoff + tc_table_offset + 8 * instruction_id
|
offset_to_insn_in_tc_table = fileoff + tc_table_offset + 8 * instruction_id
|
||||||
p offset_to_insn_in_tc_table
|
p offset_to_insn_in_tc_table if $DEBUG
|
||||||
offset_to_handler_code_from_vm_exec_core = readint8b(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
|
p offset_to_handler_code_from_vm_exec_core if $DEBUG
|
||||||
disassemble(vm_exec_core_offset + offset_to_handler_code_from_vm_exec_core)
|
disassemble(vm_exec_core_offset + offset_to_handler_code_from_vm_exec_core)
|
||||||
|
|
34
iseq.c
34
iseq.c
@ -42,6 +42,10 @@
|
|||||||
#include "builtin.h"
|
#include "builtin.h"
|
||||||
#include "insns.inc"
|
#include "insns.inc"
|
||||||
#include "insns_info.inc"
|
#include "insns_info.inc"
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include "ujit_examples.h"
|
||||||
|
|
||||||
|
uint8_t *native_pop_code; // TODO: hack. see addr2insn
|
||||||
|
|
||||||
VALUE rb_cISeq;
|
VALUE rb_cISeq;
|
||||||
static VALUE iseqw_new(const rb_iseq_t *iseq);
|
static VALUE iseqw_new(const rb_iseq_t *iseq);
|
||||||
@ -3205,6 +3209,22 @@ rb_vm_encoded_insn_data_table_init(void)
|
|||||||
st_add_direct(encoded_insn_data, key1, (st_data_t)&insn_data[insn]);
|
st_add_direct(encoded_insn_data, key1, (st_data_t)&insn_data[insn]);
|
||||||
st_add_direct(encoded_insn_data, key2, (st_data_t)&insn_data[insn]);
|
st_add_direct(encoded_insn_data, key2, (st_data_t)&insn_data[insn]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
native_pop_code = mmap(0, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, 0, 0);
|
||||||
|
if (native_pop_code == MAP_FAILED) rb_bug("mmap failed");
|
||||||
|
uint8_t *head = native_pop_code;
|
||||||
|
memcpy(head, ujit_pre_call_bytes, sizeof(ujit_pre_call_bytes));
|
||||||
|
head += sizeof(ujit_pre_call_bytes);
|
||||||
|
const uint8_t handmade_pop[] = { // TODO assmeble this from a separate file
|
||||||
|
0x48, 0x83, 0x6f, 0x08, 0x08, // subq $8, 8(%rdi)
|
||||||
|
0x48, 0x83, 0xc6, 0x08, // addq $8, %rsi
|
||||||
|
0x48, 0x89, 0x37, // movq %rsi, (%rdi)
|
||||||
|
0x48, 0x89, 0xf0 // movq %rsi, %rax
|
||||||
|
};
|
||||||
|
memcpy(head, handmade_pop, sizeof(handmade_pop));
|
||||||
|
head += sizeof(handmade_pop);
|
||||||
|
memcpy(head, ujit_post_call_bytes, sizeof(ujit_post_call_bytes));
|
||||||
|
// TODO this is small enough to fit in the page we allocated but that can change
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -3218,6 +3238,12 @@ rb_vm_insn_addr2insn(const void *addr)
|
|||||||
return (int)e->insn;
|
return (int)e->insn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO this is a hack. The proper way to do this is to refactor this so that it takes
|
||||||
|
// the iseq body.
|
||||||
|
if (addr && addr == native_pop_code) {
|
||||||
|
return BIN(pop);
|
||||||
|
}
|
||||||
|
|
||||||
rb_bug("rb_vm_insn_addr2insn: invalid insn address: %p", addr);
|
rb_bug("rb_vm_insn_addr2insn: invalid insn address: %p", addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3248,6 +3274,12 @@ encoded_iseq_trace_instrument(VALUE *iseq_encoded_insn, rb_event_flag_t turnon,
|
|||||||
return e->insn_len;
|
return e->insn_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO this is a hack. The proper way to do this is to refactor this so that it takes
|
||||||
|
// the iseq body.
|
||||||
|
if (key && (uint8_t *)key == native_pop_code) {
|
||||||
|
return insn_len(BIN(pop));
|
||||||
|
}
|
||||||
|
|
||||||
rb_bug("trace_instrument: invalid insn address: %p", (void *)*iseq_encoded_insn);
|
rb_bug("trace_instrument: invalid insn address: %p", (void *)*iseq_encoded_insn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3456,7 +3488,7 @@ trace_set_i(void *vstart, void *vend, size_t stride, void *data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
VALUE *
|
VALUE *
|
||||||
rb_ujit_empty_func(rb_control_frame_t *cfp)
|
rb_ujit_empty_func(rb_control_frame_t *cfp, const VALUE *pc)
|
||||||
{
|
{
|
||||||
// okay, not really empty, so maybe think of another name.
|
// 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's put in this file instead of say, compile.c to dodge long C compile time.
|
||||||
|
2
iseq.h
2
iseq.h
@ -313,7 +313,7 @@ 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));
|
NOINLINE(VALUE *rb_ujit_empty_func(rb_control_frame_t *cfp, const VALUE *pc));
|
||||||
|
|
||||||
|
|
||||||
RUBY_SYMBOL_EXPORT_END
|
RUBY_SYMBOL_EXPORT_END
|
||||||
|
@ -34,7 +34,7 @@ INSN_ENTRY(<%= insn.name %>)
|
|||||||
START_OF_ORIGINAL_INSN(<%= insn.name %>);
|
START_OF_ORIGINAL_INSN(<%= insn.name %>);
|
||||||
// assumes USE_MACHINE_REGS, aka reg_pc setup,
|
// assumes USE_MACHINE_REGS, aka reg_pc setup,
|
||||||
// aka #define SET_PC(x) (reg_cfp->pc = reg_pc = (x))
|
// aka #define SET_PC(x) (reg_cfp->pc = reg_pc = (x))
|
||||||
reg_pc = rb_ujit_empty_func(GET_CFP());
|
reg_pc = rb_ujit_empty_func(GET_CFP(), reg_pc);
|
||||||
END_INSN(<%= insn.name %>);
|
END_INSN(<%= insn.name %>);
|
||||||
}
|
}
|
||||||
% end
|
% end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user