8000 Merge pull request #151 from jhawthorn/verify_ctx · eileencodes/ruby@f04d502 · GitHub
[go: up one dir, main page]

Skip to content

Commit f04d502

Browse files
authored
Merge pull request ruby#151 from jhawthorn/verify_ctx
Add verify_ctx to validate learned types against received types
2 parents 839f5b7 + 858f6c4 commit f04d502

File tree

3 files changed

+162
-29
lines changed

3 files changed

+162
-29
lines changed

yjit_codegen.c

Lines changed: 76 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -118,32 +118,16 @@ jit_peek_at_self(jitstate_t *jit, ctx_t *ctx)
118118
return jit->ec->cfp->self;
119119
}
120120

121-
// When we know a VALUE to be static, this returns an appropriate val_type_t
122-
static val_type_t
123-
jit_type_of_value(VALUE val)
124-
{
125-
if (SPECIAL_CONST_P(val)) {
126-
if (FIXNUM_P(val)) {
127-
return TYPE_FIXNUM;
128-
} else if (NIL_P(val)) {
129-
return TYPE_NIL;
130-
} else {
131-
// generic immediate
132-
return TYPE_IMM;
133-
}
134-
} else {
135-
switch (BUILTIN_TYPE(val)) {
136-
case T_ARRAY:
137-
return TYPE_ARRAY;
138-
case T_HASH:
139-
return TYPE_HASH;
140-
case T_STRING:
141-
return TYPE_STRING;
142-
default:
143-
// generic heap object
144-
return TYPE_HEAP;
145-
}
146-
}
121+
static VALUE
122+
jit_peek_at_local(jitstate_t *jit, ctx_t *ctx, int n)
123+
{
124+
RUBY_ASSERT(jit_at_current_insn(jit));
125+
126+
int32_t local_table_size = jit->iseq->body->local_table_size;
127+
RUBY_ASSERT(n < (int)jit->iseq->body->local_table_size);
128+
129+
const VALUE *ep = jit->ec->cfp->ep;
130+
return ep[-VM_ENV_DATA_SIZE - local_table_size + n + 1];
147131
}
148132

149133
// Save the incremented PC on the CFP
@@ -223,11 +207,70 @@ _add_comment(codeblock_t* cb, const char* comment_str)
223207
#define ADD_COMMENT(cb, comment) _add_comment((cb), (comment))
224208
yjit_comment_array_t yjit_code_comments;
225209

210+
// Verify the ctx's types and mappings against the compile-time stack, self,
211+
// and locals.
212+
static void
213+
verify_ctx(jitstate_t *jit, ctx_t *ctx)
214+
{
215+
// Only able to check types when at current insn
216+
RUBY_ASSERT(jit_at_current_insn(jit));
217+
218+
VALUE self_val = jit_peek_at_self(jit, ctx);
219+
if (type_diff(yjit_type_of_value(self_val), ctx->self_type) == INT_MAX) {
220+
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));
221+
}
222+
223+
for (int i = 0; i < ctx->stack_size && i < MAX_TEMP_TYPES; i++) {
224+
temp_type_mapping_t learned = ctx_get_opnd_mapping(ctx, OPND_STACK(i));
225+
VALUE val = jit_peek_at_stack(jit, ctx, i);
226+
val_type_t detected = yjit_type_of_value(val);
227+
228+
if (learned.mapping.kind == TEMP_SELF) {
229+
if (self_val != val) {
230+
rb_bug("verify_ctx: stack value was mapped to self, but values did not match\n"
231+
" stack: %s\n"
232+
" self: %s",
233+
rb_obj_info(val),
234+
rb_obj_info(self_val));
235+
}
236+
}
237+
238+
if (learned.mapping.kind == TEMP_LOCAL) {
239+
int local_idx = learned.mapping.idx;
240+
VALUE local_val = jit_peek_at_local(jit, ctx, local_idx);
241+
if (local_val != val) {
242+
rb_bug("verify_ctx: stack value was mapped to local, but values did not match\n"
243+
" stack: %s\n"
244+
" local %i: %s",
245+
rb_obj_info(val),
246+
local_idx,
247+
rb_obj_info(local_val));
248+
}
249+
}
250+
251+
if (type_diff(detected, learned.type) == INT_MAX) {
252+
rb_bug("verify_ctx: ctx type (%s) incompatible with actual value on stack: %s", yjit_type_name(learned.type), rb_obj_info(val));
253+
}
254+
}
255+
256+
int32_t local_table_size = jit->iseq->body->local_table_size;
257+
for (int i = 0; i < local_table_size && i < MAX_TEMP_TYPES; i++) {
258+
val_type_t learned = ctx->local_types[i];
259+
VALUE val = jit_peek_at_local(jit, ctx, i);
260+
val_type_t detected = yjit_type_of_value(val);
261+
262+
if (type_diff(detected, learned) == INT_MAX) {
263+
rb_bug("verify_ctx: ctx type (%s) incompatible with actual value of local: %s", yjit_type_name(learned), rb_obj_info(val));
264+
}
265+
}
266+
}
267+
226268
#else
227269

228270
#define GEN_COUNTER_INC(cb, counter_name) ((void)0)
229271
#define COUNTED_EXIT(side_exit, counter_name) side_exit
230272
#define ADD_COMMENT(cb, comment) ((void)0)
273+
#define verify_ctx(jit, ctx) ((void)0)
231274

232275
#endif // if RUBY_DEBUG
233276

@@ -475,6 +518,11 @@ yjit_gen_block(block_t *block, rb_execution_context_t *ec)
475518
jit.pc = pc;
476519
jit.opcode = opcode;
477520

521+
// Verify our existing assumption (DEBUG)
522+
if (jit_at_current_insn(&jit)) {
523+
verify_ctx(&jit, ctx);
524+
}
525+
478526
// Lookup the codegen function for this instruction
479527
codegen_fn gen_fn = gen_fns[opcode];
480528
if (!gen_fn) {
@@ -907,7 +955,7 @@ gen_putobject(jitstate_t* jit, ctx_t* ctx)
907955
VALUE put_val = jit_get_arg(jit, 0);
908956
jit_mov_gc_ptr(jit, cb, REG0, put_val);
909957

910-
val_type_t val_type = jit_type_of_value(put_val);
958+
val_type_t val_type = yjit_type_of_value(put_val);
911959

912960
// Write argument at SP
913961
x86opnd_t stack_top = ctx_stack_push(ctx, val_type);
@@ -3546,7 +3594,7 @@ gen_opt_getinlinecache(jitstate_t *jit, ctx_t *ctx)
35463594
// FIXME: This leaks when st_insert raises NoMemoryError
35473595
assume_stable_global_constant_state(jit->block);
35483596

3549-
val_type_t type = jit_type_of_value(ice->value);
3597+
val_type_t type = yjit_type_of_value(ice->value);
35503598
x86opnd_t stack_top = ctx_stack_push(ctx, type);
35513599
jit_mov_gc_ptr(jit, cb, REG0, ice->value);
35523600
mov(cb, stack_top, REG0);

yjit_core.c

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ ctx_stack_push_mapping(ctx_t* ctx, temp_type_mapping_t mapping)
3434
ctx->temp_types[ctx->stack_size] = mapping.type;
3535

3636
RUBY_ASSERT(mapping.mapping.kind != TEMP_LOCAL || mapping.mapping.idx < MAX_LOCAL_TYPES);
37+
RUBY_ASSERT(mapping.mapping.kind != TEMP_STACK || mapping.mapping.idx == 0);
38+
RUBY_ASSERT(mapping.mapping.kind != TEMP_SELF || mapping.mapping.idx == 0);
3739
}
3840

3941
ctx->stack_size += 1;
@@ -156,7 +158,6 @@ ctx_get_opnd_type(const ctx_t* ctx, insn_opnd_t opnd)
156158
rb_bug("unreachable");
157159
}
158160

159-
int type_diff(val_type_t src, val_type_t dst);
160161
#define UPGRADE_TYPE(dest, src) do { \
161162
RUBY_ASSERT(type_diff((src), (dest)) != INT_MAX); \
162163
(dest) = (src); \
@@ -284,6 +285,81 @@ void ctx_clear_local_types(ctx_t* ctx)
284285
memset(&ctx->local_types, 0, sizeof(ctx->local_types));
285286
}
286287

288+
289+
/* This returns an appropriate val_type_t based on a known value */
290+
val_type_t
291+
yjit_type_of_value(VALUE val)
292+
{
293+
if (SPECIAL_CONST_P(val)) {
294+
if (FIXNUM_P(val)) {
295+
return TYPE_FIXNUM;
296+
} else if (NIL_P(val)) {
297+
return TYPE_NIL;
298+
} else if (val == Qtrue) {
299+
return TYPE_TRUE;
300+
} else if (val == Qfalse) {
301+
return TYPE_FALSE;
302+
} else if (STATIC_SYM_P(val)) {
303+
return TYPE_STATIC_SYMBOL;
304+
} else if (FLONUM_P(val)) {
305+
return TYPE_FLONUM;
306+
} else {
307+
RUBY_ASSERT(false);
308+
UNREACHABLE_RETURN(TYPE_IMM);
309+
}
310+
} else {
311+
switch (BUILTIN_TYPE(val)) {
312+
case T_ARRAY:
313+
return TYPE_ARRAY;
314+
case T_HASH:
315+
return TYPE_HASH;
316+
case T_STRING:
317+
return TYPE_STRING;
318+
default:
319+
// generic heap object
320+
return TYPE_HEAP;
321+
}
322+
}
323+
}
324+
325+
/* The name of a type, for debugging */
326+
const char *
327+
yjit_type_name(val_type_t type)
328+
{
329+
RUBY_ASSERT(!(type.is_imm && type.is_heap));
330+
331+
switch (type.type) {
332+
case ETYPE_UNKNOWN:
333+
if (type.is_imm) {
334+
return "unknown immediate";
335+
} else if (type.is_heap) {
336+
return "unknown heap";
337+
} else {
338+
return "unknown";
339+
}
340+
case ETYPE_NIL:
341+
return "nil";
342+
case ETYPE_TRUE:
343+
return "true";
344+
case ETYPE_FALSE:
345+
return "false";
346+
case ETYPE_FIXNUM:
347+
return "fixnum";
348+
case ETYPE_FLONUM:
349+
return "flonum";
350+
case ETYPE_ARRAY:
351+
return "array";
352+
case ETYPE_HASH:
353+
return "hash";
354+
case ETYPE_SYMBOL:
355+
return "symbol";
356+
case ETYPE_STRING:
357+
return "string";
358+
}
359+
360+
UNREACHABLE_RETURN("");
361+
}
362+
287363
/*
288364
Compute a difference between two value types
289365
Returns 0 if the two are the same
@@ -614,6 +690,12 @@ block_t* gen_block_version(blockid_t blockid, const ctx_t* start_ctx, rb_executi
614690
// Generate a block version that is an entry point inserted into an iseq
615691
uint8_t* gen_entry_point(const rb_iseq_t *iseq, uint32_t insn_idx, rb_execution_context_t *ec)
616692
{
693+
// If we aren't at PC 0, don't generate code
694+
// See yjit_pc_guard
695+
if (iseq->body->iseq_encoded != ec->cfp->pc) {
696+
return NULL;
697+
}
698+
617699
// The entry context makes no assumptions about types
618700
blockid_t blockid = { iseq, insn_idx };
619701

yjit_core.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,9 @@ void ctx_upgrade_opnd_type(ctx_t* ctx, insn_opnd_t opnd, val_type_t type);
271271
void ctx_set_local_type(ctx_t* ctx, size_t idx, val_type_t type);
272272
void ctx_clear_local_types(ctx_t* ctx);
273273
int ctx_diff(const ctx_t* src, const ctx_t* dst);
274+
int type_diff(val_type_t src, val_type_t dst);
275+
val_type_t yjit_type_of_value(VALUE val);
276+
const char *yjit_type_name(val_type_t type);
274277

275278
temp_type_mapping_t ctx_get_opnd_mapping(const ctx_t* ctx, insn_opnd_t opnd);
276279
void ctx_set_opnd_mapping(ctx_t* ctx, insn_opnd_t opnd, temp_type_mapping_t type_mapping);

0 commit comments

Comments
 (0)
0