Implement verify_ctx for debugging

This commit is contained in:
John Hawthorn 2021-07-27 23:41:29 -07:00 committed by Alan Wu
parent a02002dc4f
commit d78ea4abec
3 changed files with 118 additions and 1 deletions

View file

@ -119,6 +119,18 @@ jit_peek_at_self(jitstate_t *jit, ctx_t *ctx)
return jit->ec->cfp->self; return jit->ec->cfp->self;
} }
static VALUE
jit_peek_at_local(jitstate_t *jit, ctx_t *ctx, int n)
{
RUBY_ASSERT(jit_at_current_insn(jit));
int32_t local_table_size = jit->iseq->body->local_table_size;
RUBY_ASSERT(n < (int)jit->iseq->body->local_table_size);
const VALUE *ep = jit->ec->cfp->ep;
return ep[-VM_ENV_DATA_SIZE - local_table_size + n + 1];
}
// When we know a VALUE to be static, this returns an appropriate val_type_t // When we know a VALUE to be static, this returns an appropriate val_type_t
static val_type_t static val_type_t
jit_type_of_value(VALUE val) jit_type_of_value(VALUE val)
@ -232,11 +244,70 @@ _add_comment(codeblock_t* cb, const char* comment_str)
#define ADD_COMMENT(cb, comment) _add_comment((cb), (comment)) #define ADD_COMMENT(cb, comment) _add_comment((cb), (comment))
yjit_comment_array_t yjit_code_comments; yjit_comment_array_t yjit_code_comments;
// Verify the ctx's types and mappings against the compile-time stack, self,
// and locals.
static void
verify_ctx(jitstate_t *jit, ctx_t *ctx)
{
// Only able to check types when at current insn
RUBY_ASSERT(jit_at_current_insn(jit));
VALUE self_val = jit_peek_at_self(jit, ctx);
if (type_diff(jit_type_of_value(self_val), ctx->self_type) == INT_MAX) {
rb_bug("verify_ctx: ctx type (%s) incompatible with actual value of self: %s", yjit_type_name(ctx->self_type), rb_obj_info(self_val));
}
for (int i = 0; i < ctx->stack_size && i < MAX_TEMP_TYPES; i++) {
temp_type_mapping_t learned = ctx_get_opnd_mapping(ctx, OPND_STACK(i));
VALUE val = jit_peek_at_stack(jit, ctx, i);
val_type_t detected = jit_type_of_value(val);
if (learned.mapping.kind == TEMP_SELF) {
if (self_val != val) {
rb_bug("verify_ctx: stack value was mapped to self, but values did not match\n"
" stack: %s\n"
" self: %s",
rb_obj_info(val),
rb_obj_info(self_val));
}
}
if (learned.mapping.kind == TEMP_LOCAL) {
int local_idx = learned.mapping.idx;
VALUE local_val = jit_peek_at_local(jit, ctx, local_idx);
if (local_val != val) {
rb_bug("verify_ctx: stack value was mapped to local, but values did not match\n"
" stack: %s\n"
" local %i: %s",
rb_obj_info(val),
local_idx,
rb_obj_info(local_val));
}
}
if (type_diff(detected, learned.type) == INT_MAX) {
rb_bug("verify_ctx: ctx type (%s) incompatible with actual value on stack: %s", yjit_type_name(learned.type), rb_obj_info(val));
}
}
int32_t local_table_size = jit->iseq->body->local_table_size;
for (int i = 0; i < local_table_size && i < MAX_TEMP_TYPES; i++) {
val_type_t learned = ctx->local_types[i];
VALUE val = jit_peek_at_local(jit, ctx, i);
val_type_t detected = jit_type_of_value(val);
if (type_diff(detected, learned) == INT_MAX) {
rb_bug("verify_ctx: ctx type (%s) incompatible with actual value of local: %s", yjit_type_name(learned), rb_obj_info(val));
}
}
}
#else #else
#define GEN_COUNTER_INC(cb, counter_name) ((void)0) #define GEN_COUNTER_INC(cb, counter_name) ((void)0)
#define COUNTED_EXIT(side_exit, counter_name) side_exit #define COUNTED_EXIT(side_exit, counter_name) side_exit
#define ADD_COMMENT(cb, comment) ((void)0) #define ADD_COMMENT(cb, comment) ((void)0)
#define verify_ctx(jit, ctx) ((void)0)
#endif // if RUBY_DEBUG #endif // if RUBY_DEBUG
@ -484,6 +555,11 @@ yjit_gen_block(block_t *block, rb_execution_context_t *ec)
jit.pc = pc; jit.pc = pc;
jit.opcode = opcode; jit.opcode = opcode;
// Verify our existing assumption (DEBUG)
if (jit_at_current_insn(&jit)) {
verify_ctx(&jit, ctx);
}
// Lookup the codegen function for this instruction // Lookup the codegen function for this instruction
codegen_fn gen_fn = gen_fns[opcode]; codegen_fn gen_fn = gen_fns[opcode];
if (!gen_fn) { if (!gen_fn) {

View file

@ -33,6 +33,8 @@ ctx_stack_push_mapping(ctx_t* ctx, temp_type_mapping_t mapping)
ctx->temp_types[ctx->stack_size] = mapping.type; ctx->temp_types[ctx->stack_size] = mapping.type;
RUBY_ASSERT(mapping.mapping.kind != TEMP_LOCAL || mapping.mapping.idx < MAX_LOCAL_TYPES); RUBY_ASSERT(mapping.mapping.kind != TEMP_LOCAL || mapping.mapping.idx < MAX_LOCAL_TYPES);
RUBY_ASSERT(mapping.mapping.kind != TEMP_STACK || mapping.mapping.idx == 0);
RUBY_ASSERT(mapping.mapping.kind != TEMP_SELF || mapping.mapping.idx == 0);
} }
ctx->stack_size += 1; ctx->stack_size += 1;
@ -155,7 +157,6 @@ ctx_get_opnd_type(const ctx_t* ctx, insn_opnd_t opnd)
rb_bug("unreachable"); rb_bug("unreachable");
} }
int type_diff(val_type_t src, val_type_t dst);
#define UPGRADE_TYPE(dest, src) do { \ #define UPGRADE_TYPE(dest, src) do { \
RUBY_ASSERT(type_diff((src), (dest)) != INT_MAX); \ RUBY_ASSERT(type_diff((src), (dest)) != INT_MAX); \
(dest) = (src); \ (dest) = (src); \
@ -283,6 +284,44 @@ void ctx_clear_local_types(ctx_t* ctx)
memset(&ctx->local_types, 0, sizeof(ctx->local_types)); memset(&ctx->local_types, 0, sizeof(ctx->local_types));
} }
/* The name of a type, for debugging */
const char *
yjit_type_name(val_type_t type)
{
RUBY_ASSERT(!(type.is_imm && type.is_heap));
switch (type.type) {
case ETYPE_UNKNOWN:
if (type.is_imm) {
return "unknown immediate";
} else if (type.is_heap) {
return "unknown heap";
} else {
return "unknown";
}
case ETYPE_NIL:
return "nil";
case ETYPE_TRUE:
return "true";
case ETYPE_FALSE:
return "false";
case ETYPE_FIXNUM:
return "fixnum";
case ETYPE_FLONUM:
return "flonum";
case ETYPE_ARRAY:
return "array";
case ETYPE_HASH:
return "hash";
case ETYPE_SYMBOL:
return "symbol";
case ETYPE_STRING:
return "string";
}
UNREACHABLE_RETURN("");
}
/* /*
Compute a difference between two value types Compute a difference between two value types
Returns 0 if the two are the same Returns 0 if the two are the same

View file

@ -270,6 +270,8 @@ void ctx_upgrade_opnd_type(ctx_t* ctx, insn_opnd_t opnd, val_type_t type);
void ctx_set_local_type(ctx_t* ctx, size_t idx, val_type_t type); void ctx_set_local_type(ctx_t* ctx, size_t idx, val_type_t type);
void ctx_clear_local_types(ctx_t* ctx); void ctx_clear_local_types(ctx_t* ctx);
int ctx_diff(const ctx_t* src, const ctx_t* dst); int ctx_diff(const ctx_t* src, const ctx_t* dst);
int type_diff(val_type_t src, val_type_t dst);
const char *yjit_type_name(val_type_t type);
temp_type_mapping_t ctx_get_opnd_mapping(const ctx_t* ctx, insn_opnd_t opnd); temp_type_mapping_t ctx_get_opnd_mapping(const ctx_t* ctx, insn_opnd_t opnd);
void ctx_set_opnd_mapping(ctx_t* ctx, insn_opnd_t opnd, temp_type_mapping_t type_mapping); void ctx_set_opnd_mapping(ctx_t* ctx, insn_opnd_t opnd, temp_type_mapping_t type_mapping);