зеркало из https://github.com/github/ruby.git
RJIT: Add --rjit-verify-ctx option
This commit is contained in:
Родитель
2c560b976e
Коммит
19506650ef
|
@ -279,6 +279,8 @@ module RubyVM::RJIT
|
||||||
end
|
end
|
||||||
|
|
||||||
# @param asm [RubyVM::RJIT::Assembler]
|
# @param asm [RubyVM::RJIT::Assembler]
|
||||||
|
# @param jit [RubyVM::RJIT::JITState]
|
||||||
|
# @param ctx [RubyVM::RJIT::Context]
|
||||||
def compile_block(asm, jit:, pc:, ctx: Context.new)
|
def compile_block(asm, jit:, pc:, ctx: Context.new)
|
||||||
# Mark the block start address and prepare an exit code storage
|
# Mark the block start address and prepare an exit code storage
|
||||||
ctx = limit_block_versions(jit.iseq, pc, ctx)
|
ctx = limit_block_versions(jit.iseq, pc, ctx)
|
||||||
|
@ -292,6 +294,7 @@ module RubyVM::RJIT
|
||||||
# Compile each insn
|
# Compile each insn
|
||||||
index = (pc - iseq.body.iseq_encoded.to_i) / C.VALUE.size
|
index = (pc - iseq.body.iseq_encoded.to_i) / C.VALUE.size
|
||||||
while index < iseq.body.iseq_size
|
while index < iseq.body.iseq_size
|
||||||
|
# Set the current instruction
|
||||||
insn = self.class.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
|
jit.pc = (iseq.body.iseq_encoded + index).to_i
|
||||||
jit.stack_size_for_pc = ctx.stack_size
|
jit.stack_size_for_pc = ctx.stack_size
|
||||||
|
@ -308,6 +311,11 @@ module RubyVM::RJIT
|
||||||
jit.record_boundary_patch_point = false
|
jit.record_boundary_patch_point = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# In debug mode, verify our existing assumption
|
||||||
|
if C.rjit_opts.verify_ctx && jit.at_current_insn?
|
||||||
|
verify_ctx(jit, ctx)
|
||||||
|
end
|
||||||
|
|
||||||
case status = @insn_compiler.compile(jit, ctx, asm, insn)
|
case status = @insn_compiler.compile(jit, ctx, asm, insn)
|
||||||
when KeepCompiling
|
when KeepCompiling
|
||||||
# For now, reset the chain depth after each instruction as only the
|
# For now, reset the chain depth after each instruction as only the
|
||||||
|
@ -435,5 +443,67 @@ module RubyVM::RJIT
|
||||||
rescue RangeError # bignum too big to convert into `unsigned long long' (RangeError)
|
rescue RangeError # bignum too big to convert into `unsigned long long' (RangeError)
|
||||||
-1
|
-1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Verify the ctx's types and mappings against the compile-time stack, self, and locals.
|
||||||
|
# @param jit [RubyVM::RJIT::JITState]
|
||||||
|
# @param ctx [RubyVM::RJIT::Context]
|
||||||
|
def verify_ctx(jit, ctx)
|
||||||
|
# Only able to check types when at current insn
|
||||||
|
assert(jit.at_current_insn?)
|
||||||
|
|
||||||
|
self_val = jit.peek_at_self
|
||||||
|
self_val_type = Type.from(self_val)
|
||||||
|
|
||||||
|
# Verify self operand type
|
||||||
|
assert_compatible(self_val_type, ctx.get_opnd_type(SelfOpnd))
|
||||||
|
|
||||||
|
# Verify stack operand types
|
||||||
|
[ctx.stack_size, MAX_TEMP_TYPES].min.times do |i|
|
||||||
|
learned_mapping, learned_type = ctx.get_opnd_mapping(StackOpnd[i])
|
||||||
|
stack_val = jit.peek_at_stack(i)
|
||||||
|
val_type = Type.from(stack_val)
|
||||||
|
|
||||||
|
case learned_mapping
|
||||||
|
in MapToSelf
|
||||||
|
if C.to_value(self_val) != C.to_value(stack_val)
|
||||||
|
raise "verify_ctx: stack value was mapped to self, but values did not match:\n"\
|
||||||
|
"stack: #{stack_val.inspect}, self: #{self_val.inspect}"
|
||||||
|
end
|
||||||
|
in MapToLocal[local_idx]
|
||||||
|
local_val = jit.peek_at_local(local_idx)
|
||||||
|
if C.to_value(local_val) != C.to_value(stack_val)
|
||||||
|
raise "verify_ctx: stack value was mapped to local, but values did not match:\n"\
|
||||||
|
"stack: #{stack_val.inspect}, local: #{local_val.inspect}"
|
||||||
|
end
|
||||||
|
in MapToStack
|
||||||
|
# noop
|
||||||
|
end
|
||||||
|
|
||||||
|
# If the actual type differs from the learned type
|
||||||
|
assert_compatible(val_type, learned_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Verify local variable types
|
||||||
|
local_table_size = jit.iseq.body.local_table_size
|
||||||
|
[local_table_size, MAX_TEMP_TYPES].min.times do |i|
|
||||||
|
learned_type = ctx.get_local_type(i)
|
||||||
|
local_val = jit.peek_at_local(i)
|
||||||
|
local_type = Type.from(local_val)
|
||||||
|
|
||||||
|
assert_compatible(local_type, learned_type)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def assert_compatible(actual_type, ctx_type)
|
||||||
|
if actual_type.diff(ctx_type) == TypeDiff::Incompatible
|
||||||
|
raise "verify_ctx: ctx type (#{ctx_type.type.inspect}) is incompatible with actual type (#{actual_type.type.inspect})"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def assert(cond)
|
||||||
|
unless cond
|
||||||
|
raise "'#{cond.inspect}' was not true"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,16 +21,16 @@ module RubyVM::RJIT
|
||||||
# @param insn `RubyVM::RJIT::Instruction`
|
# @param insn `RubyVM::RJIT::Instruction`
|
||||||
def compile(jit, ctx, asm, insn)
|
def compile(jit, ctx, asm, insn)
|
||||||
asm.incr_counter(:rjit_insns_count)
|
asm.incr_counter(:rjit_insns_count)
|
||||||
insn_idx = format('%04d', (jit.pc.to_i - jit.iseq.body.iseq_encoded.to_i) / C.VALUE.size)
|
|
||||||
asm.comment("Insn: #{insn_idx} #{insn.name}")
|
|
||||||
|
|
||||||
# stack = ctx.stack_size.times.map do |stack_idx|
|
stack = ctx.stack_size.times.map do |stack_idx|
|
||||||
# ctx.get_opnd_type(StackOpnd[ctx.stack_size - stack_idx - 1]).type
|
ctx.get_opnd_type(StackOpnd[ctx.stack_size - stack_idx - 1]).type
|
||||||
# end
|
end
|
||||||
# locals = jit.iseq.body.local_table_size.times.map do |local_idx|
|
locals = jit.iseq.body.local_table_size.times.map do |local_idx|
|
||||||
# (ctx.local_types[local_idx] || Type::Unknown).type
|
(ctx.local_types[local_idx] || Type::Unknown).type
|
||||||
# end
|
end
|
||||||
# asm.comment("Insn: #{insn_idx} #{insn.name} (stack: [#{stack.join(', ')}], locals: [#{locals.join(', ')}])")
|
|
||||||
|
insn_idx = format('%04d', (jit.pc.to_i - jit.iseq.body.iseq_encoded.to_i) / C.VALUE.size)
|
||||||
|
asm.comment("Insn: #{insn_idx} #{insn.name} (stack: [#{stack.join(', ')}], locals: [#{locals.join(', ')}])")
|
||||||
|
|
||||||
# 83/102
|
# 83/102
|
||||||
case insn.name
|
case insn.name
|
||||||
|
|
|
@ -27,6 +27,13 @@ module RubyVM::RJIT
|
||||||
pc == cfp.pc.to_i
|
pc == cfp.pc.to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def peek_at_local(n)
|
||||||
|
local_table_size = iseq.body.local_table_size
|
||||||
|
offset = -C::VM_ENV_DATA_SIZE - local_table_size + n + 1
|
||||||
|
value = (cfp.ep + offset).*
|
||||||
|
C.to_ruby(value)
|
||||||
|
end
|
||||||
|
|
||||||
def peek_at_stack(depth_from_top)
|
def peek_at_stack(depth_from_top)
|
||||||
raise 'not at current insn' unless at_current_insn?
|
raise 'not at current insn' unless at_current_insn?
|
||||||
offset = -(1 + depth_from_top)
|
offset = -(1 + depth_from_top)
|
||||||
|
|
21
rjit.c
21
rjit.c
|
@ -118,25 +118,28 @@ rb_rjit_setup_options(const char *s, struct rb_rjit_options *rjit_opt)
|
||||||
if (l == 0) {
|
if (l == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (opt_match_noarg(s, l, "stats")) {
|
|
||||||
rjit_opt->stats = true;
|
|
||||||
}
|
|
||||||
else if (opt_match_noarg(s, l, "trace-exits")) {
|
|
||||||
rjit_opt->trace_exits = true;
|
|
||||||
}
|
|
||||||
else if (opt_match_arg(s, l, "call-threshold")) {
|
else if (opt_match_arg(s, l, "call-threshold")) {
|
||||||
rjit_opt->call_threshold = atoi(s + 1);
|
rjit_opt->call_threshold = atoi(s + 1);
|
||||||
}
|
}
|
||||||
else if (opt_match_arg(s, l, "exec-mem-size")) {
|
else if (opt_match_arg(s, l, "exec-mem-size")) {
|
||||||
rjit_opt->exec_mem_size = atoi(s + 1);
|
rjit_opt->exec_mem_size = atoi(s + 1);
|
||||||
}
|
}
|
||||||
// --rjit=pause is an undocumented feature for experiments
|
else if (opt_match_noarg(s, l, "stats")) {
|
||||||
else if (opt_match_noarg(s, l, "pause")) {
|
rjit_opt->stats = true;
|
||||||
rjit_opt->pause = true;
|
}
|
||||||
|
else if (opt_match_noarg(s, l, "trace-exits")) {
|
||||||
|
rjit_opt->trace_exits = true;
|
||||||
}
|
}
|
||||||
else if (opt_match_noarg(s, l, "dump-disasm")) {
|
else if (opt_match_noarg(s, l, "dump-disasm")) {
|
||||||
rjit_opt->dump_disasm = true;
|
rjit_opt->dump_disasm = true;
|
||||||
}
|
}
|
||||||
|
else if (opt_match_noarg(s, l, "verify-ctx")) {
|
||||||
|
rjit_opt->verify_ctx = true;
|
||||||
|
}
|
||||||
|
// --rjit=pause is an undocumented feature for experiments
|
||||||
|
else if (opt_match_noarg(s, l, "pause")) {
|
||||||
|
rjit_opt->pause = true;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
rb_raise(rb_eRuntimeError,
|
rb_raise(rb_eRuntimeError,
|
||||||
"invalid RJIT option `%s' (--help will show valid RJIT options)", s);
|
"invalid RJIT option `%s' (--help will show valid RJIT options)", s);
|
||||||
|
|
2
rjit.h
2
rjit.h
|
@ -36,6 +36,8 @@ struct rb_rjit_options {
|
||||||
bool trace_exits;
|
bool trace_exits;
|
||||||
// Enable disasm of all JIT code
|
// Enable disasm of all JIT code
|
||||||
bool dump_disasm;
|
bool dump_disasm;
|
||||||
|
// Verify context objects
|
||||||
|
bool verify_ctx;
|
||||||
// [experimental] Do not start RJIT until RJIT.resume is called.
|
// [experimental] Do not start RJIT until RJIT.resume is called.
|
||||||
bool pause;
|
bool pause;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1316,6 +1316,7 @@ module RubyVM::RJIT # :nodoc: all
|
||||||
stats: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_options *)NULL)), stats)")],
|
stats: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_options *)NULL)), stats)")],
|
||||||
trace_exits: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_options *)NULL)), trace_exits)")],
|
trace_exits: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_options *)NULL)), trace_exits)")],
|
||||||
dump_disasm: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_options *)NULL)), dump_disasm)")],
|
dump_disasm: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_options *)NULL)), dump_disasm)")],
|
||||||
|
verify_ctx: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_options *)NULL)), verify_ctx)")],
|
||||||
pause: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_options *)NULL)), pause)")],
|
pause: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_options *)NULL)), pause)")],
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
Загрузка…
Ссылка в новой задаче