mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Part 1 of improved type tracking logic
This commit is contained in:
parent
e47dd8bb88
commit
aee44e4f2b
3 changed files with 172 additions and 110 deletions
|
@ -369,8 +369,8 @@ static codegen_status_t
|
||||||
gen_dup(jitstate_t* jit, ctx_t* ctx)
|
gen_dup(jitstate_t* jit, ctx_t* ctx)
|
||||||
{
|
{
|
||||||
// Get the top value and its type
|
// Get the top value and its type
|
||||||
|
val_type_t dup_type = ctx_get_temp_type(ctx, 0);
|
||||||
x86opnd_t dup_val = ctx_stack_pop(ctx, 0);
|
x86opnd_t dup_val = ctx_stack_pop(ctx, 0);
|
||||||
int dup_type = ctx_get_top_type(ctx);
|
|
||||||
|
|
||||||
// Push the same value on top
|
// Push the same value on top
|
||||||
x86opnd_t loc0 = ctx_stack_push(ctx, dup_type);
|
x86opnd_t loc0 = ctx_stack_push(ctx, dup_type);
|
||||||
|
@ -399,7 +399,7 @@ static codegen_status_t
|
||||||
gen_putnil(jitstate_t* jit, ctx_t* ctx)
|
gen_putnil(jitstate_t* jit, ctx_t* ctx)
|
||||||
{
|
{
|
||||||
// Write constant at SP
|
// Write constant at SP
|
||||||
x86opnd_t stack_top = ctx_stack_push(ctx, T_NIL);
|
x86opnd_t stack_top = ctx_stack_push(ctx, TYPE_NIL);
|
||||||
mov(cb, stack_top, imm_opnd(Qnil));
|
mov(cb, stack_top, imm_opnd(Qnil));
|
||||||
return YJIT_KEEP_COMPILING;
|
return YJIT_KEEP_COMPILING;
|
||||||
}
|
}
|
||||||
|
@ -412,7 +412,7 @@ gen_putobject(jitstate_t* jit, ctx_t* ctx)
|
||||||
if (FIXNUM_P(arg))
|
if (FIXNUM_P(arg))
|
||||||
{
|
{
|
||||||
// Keep track of the fixnum type tag
|
// Keep track of the fixnum type tag
|
||||||
x86opnd_t stack_top = ctx_stack_push(ctx, T_FIXNUM);
|
x86opnd_t stack_top = ctx_stack_push(ctx, TYPE_FIXNUM);
|
||||||
|
|
||||||
x86opnd_t imm = imm_opnd((int64_t)arg);
|
x86opnd_t imm = imm_opnd((int64_t)arg);
|
||||||
|
|
||||||
|
@ -429,7 +429,7 @@ gen_putobject(jitstate_t* jit, ctx_t* ctx)
|
||||||
}
|
}
|
||||||
else if (arg == Qtrue || arg == Qfalse)
|
else if (arg == Qtrue || arg == Qfalse)
|
||||||
{
|
{
|
||||||
x86opnd_t stack_top = ctx_stack_push(ctx, T_NONE);
|
x86opnd_t stack_top = ctx_stack_push(ctx, TYPE_UNKNOWN);
|
||||||
mov(cb, stack_top, imm_opnd((int64_t)arg));
|
mov(cb, stack_top, imm_opnd((int64_t)arg));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -441,7 +441,7 @@ gen_putobject(jitstate_t* jit, ctx_t* ctx)
|
||||||
mov(cb, RAX, mem_opnd(64, RAX, 0));
|
mov(cb, RAX, mem_opnd(64, RAX, 0));
|
||||||
|
|
||||||
// Write argument at SP
|
// Write argument at SP
|
||||||
x86opnd_t stack_top = ctx_stack_push(ctx, T_NONE);
|
x86opnd_t stack_top = ctx_stack_push(ctx, TYPE_UNKNOWN);
|
||||||
mov(cb, stack_top, RAX);
|
mov(cb, stack_top, RAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -455,7 +455,7 @@ gen_putobject_int2fix(jitstate_t* jit, ctx_t* ctx)
|
||||||
int cst_val = (opcode == BIN(putobject_INT2FIX_0_))? 0:1;
|
int cst_val = (opcode == BIN(putobject_INT2FIX_0_))? 0:1;
|
||||||
|
|
||||||
// Write constant at SP
|
// Write constant at SP
|
||||||
x86opnd_t stack_top = ctx_stack_push(ctx, T_FIXNUM);
|
x86opnd_t stack_top = ctx_stack_push(ctx, TYPE_FIXNUM);
|
||||||
mov(cb, stack_top, imm_opnd(INT2FIX(cst_val)));
|
mov(cb, stack_top, imm_opnd(INT2FIX(cst_val)));
|
||||||
|
|
||||||
return YJIT_KEEP_COMPILING;
|
return YJIT_KEEP_COMPILING;
|
||||||
|
@ -468,7 +468,7 @@ gen_putself(jitstate_t* jit, ctx_t* ctx)
|
||||||
mov(cb, RAX, member_opnd(REG_CFP, rb_control_frame_t, self));
|
mov(cb, RAX, member_opnd(REG_CFP, rb_control_frame_t, self));
|
||||||
|
|
||||||
// Write it on the stack
|
// Write it on the stack
|
||||||
x86opnd_t stack_top = ctx_stack_push(ctx, T_NONE);
|
x86opnd_t stack_top = ctx_stack_push(ctx, TYPE_UNKNOWN);
|
||||||
mov(cb, stack_top, RAX);
|
mov(cb, stack_top, RAX);
|
||||||
|
|
||||||
return YJIT_KEEP_COMPILING;
|
return YJIT_KEEP_COMPILING;
|
||||||
|
@ -488,7 +488,7 @@ gen_getlocal_wc0(jitstate_t* jit, ctx_t* ctx)
|
||||||
mov(cb, REG0, mem_opnd(64, REG0, offs));
|
mov(cb, REG0, mem_opnd(64, REG0, offs));
|
||||||
|
|
||||||
// Write the local at SP
|
// Write the local at SP
|
||||||
x86opnd_t stack_top = ctx_stack_push(ctx, T_NONE);
|
x86opnd_t stack_top = ctx_stack_push(ctx, TYPE_UNKNOWN);
|
||||||
mov(cb, stack_top, REG0);
|
mov(cb, stack_top, REG0);
|
||||||
|
|
||||||
return YJIT_KEEP_COMPILING;
|
return YJIT_KEEP_COMPILING;
|
||||||
|
@ -515,7 +515,7 @@ gen_getlocal_wc1(jitstate_t* jit, ctx_t* ctx)
|
||||||
mov(cb, REG0, mem_opnd(64, REG0, offs));
|
mov(cb, REG0, mem_opnd(64, REG0, offs));
|
||||||
|
|
||||||
// Write the local at SP
|
// Write the local at SP
|
||||||
x86opnd_t stack_top = ctx_stack_push(ctx, T_NONE);
|
x86opnd_t stack_top = ctx_stack_push(ctx, TYPE_UNKNOWN);
|
||||||
mov(cb, stack_top, REG0);
|
mov(cb, stack_top, REG0);
|
||||||
|
|
||||||
return YJIT_KEEP_COMPILING;
|
return YJIT_KEEP_COMPILING;
|
||||||
|
@ -564,10 +564,10 @@ gen_setlocal_wc0(jitstate_t* jit, ctx_t* ctx)
|
||||||
|
|
||||||
// Check that `self` is a pointer to an object on the GC heap
|
// Check that `self` is a pointer to an object on the GC heap
|
||||||
static void
|
static void
|
||||||
guard_self_is_object(codeblock_t *cb, x86opnd_t self_opnd, uint8_t *side_exit, ctx_t *ctx)
|
guard_self_is_heap(codeblock_t *cb, x86opnd_t self_opnd, uint8_t *side_exit, ctx_t *ctx)
|
||||||
{
|
{
|
||||||
// `self` is constant throughout the entire region, so we only need to do this check once.
|
// `self` is constant throughout the entire region, so we only need to do this check once.
|
||||||
if (!ctx->self_is_object) {
|
if (!ctx->self_type.is_heap) {
|
||||||
test(cb, self_opnd, imm_opnd(RUBY_IMMEDIATE_MASK));
|
test(cb, self_opnd, imm_opnd(RUBY_IMMEDIATE_MASK));
|
||||||
jnz_ptr(cb, side_exit);
|
jnz_ptr(cb, side_exit);
|
||||||
cmp(cb, self_opnd, imm_opnd(Qfalse));
|
cmp(cb, self_opnd, imm_opnd(Qfalse));
|
||||||
|
@ -580,11 +580,10 @@ guard_self_is_object(codeblock_t *cb, x86opnd_t self_opnd, uint8_t *side_exit, c
|
||||||
// cmp(cb, self_opnd, imm_opnd(Qnil));
|
// cmp(cb, self_opnd, imm_opnd(Qnil));
|
||||||
// jbe(cb, side_exit);
|
// jbe(cb, side_exit);
|
||||||
|
|
||||||
ctx->self_is_object = true;
|
ctx->self_type.is_heap = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Generate a stubbed unconditional jump to the next bytecode instruction.
|
// Generate a stubbed unconditional jump to the next bytecode instruction.
|
||||||
// Blocks that are part of a guard chain can use this to share the same successor.
|
// Blocks that are part of a guard chain can use this to share the same successor.
|
||||||
static void
|
static void
|
||||||
|
@ -726,7 +725,7 @@ gen_getinstancevariable(jitstate_t* jit, ctx_t* ctx)
|
||||||
// Load self from CFP
|
// Load self from CFP
|
||||||
mov(cb, REG0, member_opnd(REG_CFP, rb_control_frame_t, self));
|
mov(cb, REG0, member_opnd(REG_CFP, rb_control_frame_t, self));
|
||||||
|
|
||||||
guard_self_is_object(cb, REG0, COUNTED_EXIT(side_exit, getivar_se_self_not_heap), ctx);
|
guard_self_is_heap(cb, REG0, COUNTED_EXIT(side_exit, getivar_se_self_not_heap), ctx);
|
||||||
|
|
||||||
// Guard that self has a known class
|
// Guard that self has a known class
|
||||||
x86opnd_t klass_opnd = mem_opnd(64, REG0, offsetof(struct RBasic, klass));
|
x86opnd_t klass_opnd = mem_opnd(64, REG0, offsetof(struct RBasic, klass));
|
||||||
|
@ -754,7 +753,7 @@ gen_getinstancevariable(jitstate_t* jit, ctx_t* ctx)
|
||||||
je_ptr(cb, COUNTED_EXIT(side_exit, getivar_undef));
|
je_ptr(cb, COUNTED_EXIT(side_exit, getivar_undef));
|
||||||
|
|
||||||
// Push the ivar on the stack
|
// Push the ivar on the stack
|
||||||
x86opnd_t out_opnd = ctx_stack_push(ctx, T_NONE);
|
x86opnd_t out_opnd = ctx_stack_push(ctx, TYPE_UNKNOWN);
|
||||||
mov(cb, out_opnd, REG1);
|
mov(cb, out_opnd, REG1);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -787,7 +786,7 @@ gen_getinstancevariable(jitstate_t* jit, ctx_t* ctx)
|
||||||
je_ptr(cb, COUNTED_EXIT(side_exit, getivar_undef));
|
je_ptr(cb, COUNTED_EXIT(side_exit, getivar_undef));
|
||||||
|
|
||||||
// Push the ivar on the stack
|
// Push the ivar on the stack
|
||||||
x86opnd_t out_opnd = ctx_stack_push(ctx, T_NONE);
|
x86opnd_t out_opnd = ctx_stack_push(ctx, TYPE_UNKNOWN);
|
||||||
mov(cb, out_opnd, REG0);
|
mov(cb, out_opnd, REG0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -825,7 +824,7 @@ gen_setinstancevariable(jitstate_t* jit, ctx_t* ctx)
|
||||||
// Load self from CFP
|
// Load self from CFP
|
||||||
mov(cb, REG0, member_opnd(REG_CFP, rb_control_frame_t, self));
|
mov(cb, REG0, member_opnd(REG_CFP, rb_control_frame_t, self));
|
||||||
|
|
||||||
guard_self_is_object(cb, REG0, side_exit, ctx);
|
guard_self_is_heap(cb, REG0, side_exit, ctx);
|
||||||
|
|
||||||
// Bail if receiver class is different from compiled time call cache class
|
// Bail if receiver class is different from compiled time call cache class
|
||||||
x86opnd_t klass_opnd = mem_opnd(64, REG0, offsetof(struct RBasic, klass));
|
x86opnd_t klass_opnd = mem_opnd(64, REG0, offsetof(struct RBasic, klass));
|
||||||
|
@ -882,17 +881,17 @@ gen_fixnum_cmp(jitstate_t* jit, ctx_t* ctx, cmov_fn cmov_op)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the operands and destination from the stack
|
// Get the operands and destination from the stack
|
||||||
int arg1_type = ctx_get_top_type(ctx);
|
val_type_t arg1_type = ctx_get_temp_type(ctx, 0);
|
||||||
x86opnd_t arg1 = ctx_stack_pop(ctx, 1);
|
x86opnd_t arg1 = ctx_stack_pop(ctx, 1);
|
||||||
int arg0_type = ctx_get_top_type(ctx);
|
val_type_t arg0_type = ctx_get_temp_type(ctx, 0);
|
||||||
x86opnd_t arg0 = ctx_stack_pop(ctx, 1);
|
x86opnd_t arg0 = ctx_stack_pop(ctx, 1);
|
||||||
|
|
||||||
// If not fixnums, fall back
|
// If not fixnums, fall back
|
||||||
if (arg0_type != T_FIXNUM) {
|
if (arg0_type.type != ETYPE_FIXNUM) {
|
||||||
test(cb, arg0, imm_opnd(RUBY_FIXNUM_FLAG));
|
test(cb, arg0, imm_opnd(RUBY_FIXNUM_FLAG));
|
||||||
jz_ptr(cb, side_exit);
|
jz_ptr(cb, side_exit);
|
||||||
}
|
}
|
||||||
if (arg1_type != T_FIXNUM) {
|
if (arg1_type.type != ETYPE_FIXNUM) {
|
||||||
test(cb, arg1, imm_opnd(RUBY_FIXNUM_FLAG));
|
test(cb, arg1, imm_opnd(RUBY_FIXNUM_FLAG));
|
||||||
jz_ptr(cb, side_exit);
|
jz_ptr(cb, side_exit);
|
||||||
}
|
}
|
||||||
|
@ -905,7 +904,7 @@ gen_fixnum_cmp(jitstate_t* jit, ctx_t* ctx, cmov_fn cmov_op)
|
||||||
cmov_op(cb, REG0, REG1);
|
cmov_op(cb, REG0, REG1);
|
||||||
|
|
||||||
// Push the output on the stack
|
// Push the output on the stack
|
||||||
x86opnd_t dst = ctx_stack_push(ctx, T_NONE);
|
x86opnd_t dst = ctx_stack_push(ctx, TYPE_UNKNOWN);
|
||||||
mov(cb, dst, REG0);
|
mov(cb, dst, REG0);
|
||||||
|
|
||||||
return YJIT_KEEP_COMPILING;
|
return YJIT_KEEP_COMPILING;
|
||||||
|
@ -1007,7 +1006,7 @@ gen_opt_aref(jitstate_t *jit, ctx_t *ctx)
|
||||||
yjit_load_regs(cb);
|
yjit_load_regs(cb);
|
||||||
|
|
||||||
// Push the return value onto the stack
|
// Push the return value onto the stack
|
||||||
x86opnd_t stack_ret = ctx_stack_push(ctx, T_NONE);
|
x86opnd_t stack_ret = ctx_stack_push(ctx, TYPE_UNKNOWN);
|
||||||
mov(cb, stack_ret, RAX);
|
mov(cb, stack_ret, RAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1068,7 +1067,7 @@ gen_opt_aref(jitstate_t *jit, ctx_t *ctx)
|
||||||
yjit_load_regs(cb);
|
yjit_load_regs(cb);
|
||||||
|
|
||||||
// Push the return value onto the stack
|
// Push the return value onto the stack
|
||||||
x86opnd_t stack_ret = ctx_stack_push(ctx, T_NONE);
|
x86opnd_t stack_ret = ctx_stack_push(ctx, TYPE_UNKNOWN);
|
||||||
mov(cb, stack_ret, RAX);
|
mov(cb, stack_ret, RAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1092,17 +1091,17 @@ gen_opt_and(jitstate_t* jit, ctx_t* ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the operands and destination from the stack
|
// Get the operands and destination from the stack
|
||||||
int arg1_type = ctx_get_top_type(ctx);
|
val_type_t arg1_type = ctx_get_temp_type(ctx, 0);
|
||||||
x86opnd_t arg1 = ctx_stack_pop(ctx, 1);
|
x86opnd_t arg1 = ctx_stack_pop(ctx, 1);
|
||||||
int arg0_type = ctx_get_top_type(ctx);
|
val_type_t arg0_type = ctx_get_temp_type(ctx, 0);
|
||||||
x86opnd_t arg0 = ctx_stack_pop(ctx, 1);
|
x86opnd_t arg0 = ctx_stack_pop(ctx, 1);
|
||||||
|
|
||||||
// If not fixnums, fall back
|
// If not fixnums, fall back
|
||||||
if (arg0_type != T_FIXNUM) {
|
if (arg0_type.type != ETYPE_FIXNUM) {
|
||||||
test(cb, arg0, imm_opnd(RUBY_FIXNUM_FLAG));
|
test(cb, arg0, imm_opnd(RUBY_FIXNUM_FLAG));
|
||||||
jz_ptr(cb, side_exit);
|
jz_ptr(cb, side_exit);
|
||||||
}
|
}
|
||||||
if (arg1_type != T_FIXNUM) {
|
if (arg1_type.type != ETYPE_FIXNUM) {
|
||||||
test(cb, arg1, imm_opnd(RUBY_FIXNUM_FLAG));
|
test(cb, arg1, imm_opnd(RUBY_FIXNUM_FLAG));
|
||||||
jz_ptr(cb, side_exit);
|
jz_ptr(cb, side_exit);
|
||||||
}
|
}
|
||||||
|
@ -1112,7 +1111,7 @@ gen_opt_and(jitstate_t* jit, ctx_t* ctx)
|
||||||
and(cb, REG0, arg1);
|
and(cb, REG0, arg1);
|
||||||
|
|
||||||
// Push the output on the stack
|
// Push the output on the stack
|
||||||
x86opnd_t dst = ctx_stack_push(ctx, T_FIXNUM);
|
x86opnd_t dst = ctx_stack_push(ctx, TYPE_FIXNUM);
|
||||||
mov(cb, dst, REG0);
|
mov(cb, dst, REG0);
|
||||||
|
|
||||||
return YJIT_KEEP_COMPILING;
|
return YJIT_KEEP_COMPILING;
|
||||||
|
@ -1130,14 +1129,20 @@ gen_opt_minus(jitstate_t* jit, ctx_t* ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the operands and destination from the stack
|
// Get the operands and destination from the stack
|
||||||
|
val_type_t arg1_type = ctx_get_temp_type(ctx, 0);
|
||||||
x86opnd_t arg1 = ctx_stack_pop(ctx, 1);
|
x86opnd_t arg1 = ctx_stack_pop(ctx, 1);
|
||||||
|
val_type_t arg0_type = ctx_get_temp_type(ctx, 0);
|
||||||
x86opnd_t arg0 = ctx_stack_pop(ctx, 1);
|
x86opnd_t arg0 = ctx_stack_pop(ctx, 1);
|
||||||
|
|
||||||
// If not fixnums, fall back
|
// If not fixnums, fall back
|
||||||
test(cb, arg0, imm_opnd(RUBY_FIXNUM_FLAG));
|
if (arg0_type.type != ETYPE_FIXNUM) {
|
||||||
jz_ptr(cb, side_exit);
|
test(cb, arg0, imm_opnd(RUBY_FIXNUM_FLAG));
|
||||||
test(cb, arg1, imm_opnd(RUBY_FIXNUM_FLAG));
|
jz_ptr(cb, side_exit);
|
||||||
jz_ptr(cb, side_exit);
|
}
|
||||||
|
if (arg1_type.type != ETYPE_FIXNUM) {
|
||||||
|
test(cb, arg1, imm_opnd(RUBY_FIXNUM_FLAG));
|
||||||
|
jz_ptr(cb, side_exit);
|
||||||
|
}
|
||||||
|
|
||||||
// Subtract arg0 - arg1 and test for overflow
|
// Subtract arg0 - arg1 and test for overflow
|
||||||
mov(cb, REG0, arg0);
|
mov(cb, REG0, arg0);
|
||||||
|
@ -1146,7 +1151,7 @@ gen_opt_minus(jitstate_t* jit, ctx_t* ctx)
|
||||||
add(cb, REG0, imm_opnd(1));
|
add(cb, REG0, imm_opnd(1));
|
||||||
|
|
||||||
// Push the output on the stack
|
// Push the output on the stack
|
||||||
x86opnd_t dst = ctx_stack_push(ctx, T_FIXNUM);
|
x86opnd_t dst = ctx_stack_push(ctx, TYPE_FIXNUM);
|
||||||
mov(cb, dst, REG0);
|
mov(cb, dst, REG0);
|
||||||
|
|
||||||
return YJIT_KEEP_COMPILING;
|
return YJIT_KEEP_COMPILING;
|
||||||
|
@ -1164,17 +1169,17 @@ gen_opt_plus(jitstate_t* jit, ctx_t* ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the operands and destination from the stack
|
// Get the operands and destination from the stack
|
||||||
int arg1_type = ctx_get_top_type(ctx);
|
val_type_t arg1_type = ctx_get_temp_type(ctx, 0);
|
||||||
x86opnd_t arg1 = ctx_stack_pop(ctx, 1);
|
x86opnd_t arg1 = ctx_stack_pop(ctx, 1);
|
||||||
int arg0_type = ctx_get_top_type(ctx);
|
val_type_t arg0_type = ctx_get_temp_type(ctx, 0);
|
||||||
x86opnd_t arg0 = ctx_stack_pop(ctx, 1);
|
x86opnd_t arg0 = ctx_stack_pop(ctx, 1);
|
||||||
|
|
||||||
// If not fixnums, fall back
|
// If not fixnums, fall back
|
||||||
if (arg0_type != T_FIXNUM) {
|
if (arg0_type.type != ETYPE_FIXNUM) {
|
||||||
test(cb, arg0, imm_opnd(RUBY_FIXNUM_FLAG));
|
test(cb, arg0, imm_opnd(RUBY_FIXNUM_FLAG));
|
||||||
jz_ptr(cb, side_exit);
|
jz_ptr(cb, side_exit);
|
||||||
}
|
}
|
||||||
if (arg1_type != T_FIXNUM) {
|
if (arg1_type.type != ETYPE_FIXNUM) {
|
||||||
test(cb, arg1, imm_opnd(RUBY_FIXNUM_FLAG));
|
test(cb, arg1, imm_opnd(RUBY_FIXNUM_FLAG));
|
||||||
jz_ptr(cb, side_exit);
|
jz_ptr(cb, side_exit);
|
||||||
}
|
}
|
||||||
|
@ -1186,7 +1191,7 @@ gen_opt_plus(jitstate_t* jit, ctx_t* ctx)
|
||||||
jo_ptr(cb, side_exit);
|
jo_ptr(cb, side_exit);
|
||||||
|
|
||||||
// Push the output on the stack
|
// Push the output on the stack
|
||||||
x86opnd_t dst = ctx_stack_push(ctx, T_FIXNUM);
|
x86opnd_t dst = ctx_stack_push(ctx, TYPE_FIXNUM);
|
||||||
mov(cb, dst, REG0);
|
mov(cb, dst, REG0);
|
||||||
|
|
||||||
return YJIT_KEEP_COMPILING;
|
return YJIT_KEEP_COMPILING;
|
||||||
|
@ -1529,7 +1534,7 @@ gen_oswb_cfunc(jitstate_t *jit, ctx_t *ctx, const struct rb_callinfo *ci, const
|
||||||
yjit_load_regs(cb);
|
yjit_load_regs(cb);
|
||||||
|
|
||||||
// Push the return value on the Ruby stack
|
// Push the return value on the Ruby stack
|
||||||
x86opnd_t stack_ret = ctx_stack_push(ctx, T_NONE);
|
x86opnd_t stack_ret = ctx_stack_push(ctx, TYPE_UNKNOWN);
|
||||||
mov(cb, stack_ret, RAX);
|
mov(cb, stack_ret, RAX);
|
||||||
|
|
||||||
// If this function needs a Ruby stack frame
|
// If this function needs a Ruby stack frame
|
||||||
|
@ -1677,7 +1682,7 @@ gen_oswb_iseq(jitstate_t *jit, ctx_t *ctx, const struct rb_callinfo *ci, const r
|
||||||
// After the return, the JIT and interpreter SP will match up
|
// After the return, the JIT and interpreter SP will match up
|
||||||
ctx_t return_ctx = *ctx;
|
ctx_t return_ctx = *ctx;
|
||||||
ctx_stack_pop(&return_ctx, argc + 1);
|
ctx_stack_pop(&return_ctx, argc + 1);
|
||||||
ctx_stack_push(&return_ctx, T_NONE);
|
ctx_stack_push(&return_ctx, TYPE_UNKNOWN);
|
||||||
return_ctx.sp_offset = 0;
|
return_ctx.sp_offset = 0;
|
||||||
return_ctx.chain_depth = 0;
|
return_ctx.chain_depth = 0;
|
||||||
|
|
||||||
|
@ -1912,7 +1917,7 @@ gen_opt_getinlinecache(jitstate_t *jit, ctx_t *ctx)
|
||||||
// FIXME: This leaks when st_insert raises NoMemoryError
|
// FIXME: This leaks when st_insert raises NoMemoryError
|
||||||
if (!assume_stable_global_constant_state(jit->block)) return YJIT_CANT_COMPILE;
|
if (!assume_stable_global_constant_state(jit->block)) return YJIT_CANT_COMPILE;
|
||||||
|
|
||||||
x86opnd_t stack_top = ctx_stack_push(ctx, T_NONE);
|
x86opnd_t stack_top = ctx_stack_push(ctx, TYPE_UNKNOWN);
|
||||||
jit_mov_gc_ptr(jit, cb, REG0, ice->value);
|
jit_mov_gc_ptr(jit, cb, REG0, ice->value);
|
||||||
mov(cb, stack_top, REG0);
|
mov(cb, stack_top, REG0);
|
||||||
|
|
||||||
|
|
121
yjit_core.c
121
yjit_core.c
|
@ -34,12 +34,33 @@ Push one new value on the temp stack
|
||||||
Return a pointer to the new stack top
|
Return a pointer to the new stack top
|
||||||
*/
|
*/
|
||||||
x86opnd_t
|
x86opnd_t
|
||||||
ctx_stack_push(ctx_t* ctx, int type)
|
ctx_stack_push(ctx_t* ctx, val_type_t type)
|
||||||
{
|
{
|
||||||
// Keep track of the type of the value
|
// Keep track of the type of the value
|
||||||
RUBY_ASSERT(type <= RUBY_T_MASK);
|
if (ctx->stack_size < MAX_TEMP_TYPES) {
|
||||||
if (ctx->stack_size < MAX_TEMP_TYPES)
|
ctx->temp_mapping[ctx->stack_size] = MAP_STACK;
|
||||||
ctx->temp_types[ctx->stack_size] = type;
|
ctx->temp_types[ctx->stack_size] = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->stack_size += 1;
|
||||||
|
ctx->sp_offset += 1;
|
||||||
|
|
||||||
|
// SP points just above the topmost value
|
||||||
|
int32_t offset = (ctx->sp_offset - 1) * sizeof(VALUE);
|
||||||
|
return mem_opnd(64, REG_SP, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Push the self value on the stack
|
||||||
|
*/
|
||||||
|
x86opnd_t
|
||||||
|
ctx_stack_push_self(ctx_t* ctx)
|
||||||
|
{
|
||||||
|
// Keep track of the type of the value
|
||||||
|
if (ctx->stack_size < MAX_TEMP_TYPES) {
|
||||||
|
ctx->temp_mapping[ctx->stack_size] = MAP_SELF;
|
||||||
|
ctx->temp_types[ctx->stack_size] = ctx->self_type;
|
||||||
|
}
|
||||||
|
|
||||||
ctx->stack_size += 1;
|
ctx->stack_size += 1;
|
||||||
ctx->sp_offset += 1;
|
ctx->sp_offset += 1;
|
||||||
|
@ -66,8 +87,10 @@ ctx_stack_pop(ctx_t* ctx, size_t n)
|
||||||
for (size_t i = 0; i < n; ++i)
|
for (size_t i = 0; i < n; ++i)
|
||||||
{
|
{
|
||||||
size_t idx = ctx->stack_size - i - 1;
|
size_t idx = ctx->stack_size - i - 1;
|
||||||
if (idx < MAX_TEMP_TYPES)
|
if (idx < MAX_TEMP_TYPES) {
|
||||||
ctx->temp_types[idx] = T_NONE;
|
ctx->temp_types[idx] = TYPE_UNKNOWN;
|
||||||
|
ctx->temp_mapping[idx] = MAP_STACK;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->stack_size -= n;
|
ctx->stack_size -= n;
|
||||||
|
@ -90,18 +113,58 @@ ctx_stack_opnd(ctx_t* ctx, int32_t idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Get the type of the topmost value on the temp stack
|
Get the type of a value on the temp stack
|
||||||
Returns T_NONE if unknown
|
Returns T_NONE if unknown
|
||||||
*/
|
*/
|
||||||
int
|
val_type_t
|
||||||
ctx_get_top_type(ctx_t* ctx)
|
ctx_get_temp_type(const ctx_t* ctx, size_t idx)
|
||||||
{
|
{
|
||||||
RUBY_ASSERT(ctx->stack_size > 0);
|
RUBY_ASSERT(idx < ctx->stack_size);
|
||||||
|
|
||||||
if (ctx->stack_size > MAX_TEMP_TYPES)
|
if (ctx->stack_size > MAX_TEMP_TYPES)
|
||||||
return T_NONE;
|
return TYPE_UNKNOWN;
|
||||||
|
|
||||||
return ctx->temp_types[ctx->stack_size - 1];
|
temp_mapping_t mapping = ctx->temp_mapping[ctx->stack_size - 1 - idx];
|
||||||
|
|
||||||
|
if (mapping.kind == TEMP_SELF)
|
||||||
|
return ctx->self_type;
|
||||||
|
else if (mapping.kind == TEMP_STACK)
|
||||||
|
return ctx->temp_types[ctx->stack_size - 1 - idx];
|
||||||
|
|
||||||
|
RUBY_ASSERT(false);
|
||||||
|
return TYPE_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Compute a difference between two value types
|
||||||
|
Returns 0 if the two are the same
|
||||||
|
Returns > 0 if different but compatible
|
||||||
|
Returns INT_MAX if incompatible
|
||||||
|
*/
|
||||||
|
int type_diff(val_type_t src, val_type_t dst)
|
||||||
|
{
|
||||||
|
RUBY_ASSERT(!src.is_heap || !src.is_imm);
|
||||||
|
RUBY_ASSERT(!dst.is_heap || !dst.is_imm);
|
||||||
|
|
||||||
|
if (src.type != dst.type && dst.type != ETYPE_UNKNOWN)
|
||||||
|
return INT_MAX;
|
||||||
|
|
||||||
|
if (src.is_heap && !dst.is_heap)
|
||||||
|
return INT_MAX;
|
||||||
|
|
||||||
|
if (src.is_imm && !dst.is_imm)
|
||||||
|
return INT_MAX;
|
||||||
|
|
||||||
|
if (src.is_heap != dst.is_heap)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (src.is_imm != dst.is_imm)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (src.type != dst.type)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -127,26 +190,30 @@ int ctx_diff(const ctx_t* src, const ctx_t* dst)
|
||||||
if (dst->sp_offset != src->sp_offset)
|
if (dst->sp_offset != src->sp_offset)
|
||||||
return INT_MAX;
|
return INT_MAX;
|
||||||
|
|
||||||
if (dst->self_is_object != src->self_is_object)
|
|
||||||
return INT_MAX;
|
|
||||||
|
|
||||||
// Difference sum
|
// Difference sum
|
||||||
int diff = 0;
|
int diff = 0;
|
||||||
|
|
||||||
// For each temporary variable
|
// Check the type of self
|
||||||
for (size_t i = 0; i < MAX_TEMP_TYPES; ++i)
|
int self_diff = type_diff(src->self_type, dst->self_type);
|
||||||
{
|
|
||||||
int t_src = src->temp_types[i];
|
|
||||||
int t_dst = dst->temp_types[i];
|
|
||||||
|
|
||||||
if (t_dst != t_src)
|
if (self_diff == INT_MAX)
|
||||||
{
|
return INT_MAX;
|
||||||
// It's OK to lose some type information
|
|
||||||
if (t_dst == T_NONE)
|
diff += self_diff;
|
||||||
diff += 1;
|
|
||||||
else
|
// TODO: when we track local types, need to check them too
|
||||||
return INT_MAX;
|
|
||||||
}
|
// For each value on the temp stack
|
||||||
|
for (size_t i = 0; i < src->stack_size; ++i)
|
||||||
|
{
|
||||||
|
val_type_t t_src = ctx_get_temp_type(src, i);
|
||||||
|
val_type_t t_dst = ctx_get_temp_type(dst, i);
|
||||||
|
int temp_diff = type_diff(t_src, t_dst);
|
||||||
|
|
||||||
|
if (temp_diff == INT_MAX)
|
||||||
|
return INT_MAX;
|
||||||
|
|
||||||
|
diff += temp_diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
return diff;
|
return diff;
|
||||||
|
|
72
yjit_core.h
72
yjit_core.h
|
@ -26,10 +26,21 @@
|
||||||
// Default versioning context (no type information)
|
// Default versioning context (no type information)
|
||||||
#define DEFAULT_CTX ( (ctx_t){ 0 } )
|
#define DEFAULT_CTX ( (ctx_t){ 0 } )
|
||||||
|
|
||||||
|
typedef enum yjit_type_enum
|
||||||
|
{
|
||||||
|
ETYPE_UNKNOWN = 0,
|
||||||
|
ETYPE_NIL,
|
||||||
|
ETYPE_FIXNUM,
|
||||||
|
//ETYPE_ARRAY
|
||||||
|
//ETYPE_SYMBOL
|
||||||
|
//ETYPE_STRING
|
||||||
|
|
||||||
|
} type_enum_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Represent the type of a value (local/stack/self) in YJIT
|
Represent the type of a value (local/stack/self) in YJIT
|
||||||
*/
|
*/
|
||||||
typedef struct yjit_val_type
|
typedef struct yjit_type_struct
|
||||||
{
|
{
|
||||||
// Value is definitely a heap object
|
// Value is definitely a heap object
|
||||||
uint8_t is_heap : 1;
|
uint8_t is_heap : 1;
|
||||||
|
@ -37,17 +48,8 @@ typedef struct yjit_val_type
|
||||||
// Value is definitely an immediate
|
// Value is definitely an immediate
|
||||||
uint8_t is_imm : 1;
|
uint8_t is_imm : 1;
|
||||||
|
|
||||||
// Not Qfalse or Qnil
|
// Specific value type, if known
|
||||||
// Is this useful?
|
uint8_t type : 3;
|
||||||
//uint8_t is_truthy: 1;
|
|
||||||
|
|
||||||
// NOTE: we could switch to using an enum to track multiple types
|
|
||||||
// but then we also need a value for "unknown type"
|
|
||||||
uint8_t is_fixnum : 1;
|
|
||||||
//uint8_t is_array : 1; // for opt_aref
|
|
||||||
//uint8_t is_hash : 1; // for opt_aref
|
|
||||||
//uint8_t is_symbol : 1;
|
|
||||||
//uint8_t is_string : 1;
|
|
||||||
|
|
||||||
} val_type_t;
|
} val_type_t;
|
||||||
STATIC_ASSERT(val_type_size, sizeof(val_type_t) == 1);
|
STATIC_ASSERT(val_type_size, sizeof(val_type_t) == 1);
|
||||||
|
@ -61,15 +63,16 @@ STATIC_ASSERT(val_type_size, sizeof(val_type_t) == 1);
|
||||||
// Could be any immediate
|
// Could be any immediate
|
||||||
#define TYPE_IMM ( (val_type_t){ .is_imm = 1 } )
|
#define TYPE_IMM ( (val_type_t){ .is_imm = 1 } )
|
||||||
|
|
||||||
// Immediate integer
|
// Immediate types
|
||||||
#define TYPE_FIXNUM ( (val_type_t){ .is_imm = 1, .is_fixnum = 1 } )
|
#define TYPE_NIL ( (val_type_t){ .is_imm = 1, .type = ETYPE_NIL } )
|
||||||
|
#define TYPE_FIXNUM ( (val_type_t){ .is_imm = 1, .type = ETYPE_FIXNUM } )
|
||||||
|
|
||||||
typedef enum yjit_temp_loc
|
typedef enum yjit_temp_loc
|
||||||
{
|
{
|
||||||
TEMP_STACK = 0,
|
TEMP_STACK = 0,
|
||||||
TEMP_SELF,
|
TEMP_SELF,
|
||||||
//TEMP_LOCAL, // Local with index
|
//TEMP_LOCAL, // Local with index
|
||||||
//TEMP_CONST, // Small constant
|
//TEMP_CONST, // Small constant (0, 1, 2, Qnil, Qfalse, Qtrue)
|
||||||
|
|
||||||
} temp_loc_t;
|
} temp_loc_t;
|
||||||
|
|
||||||
|
@ -91,26 +94,12 @@ STATIC_ASSERT(temp_mapping_size, sizeof(temp_mapping_t) == 1);
|
||||||
// Temp value is actually self
|
// Temp value is actually self
|
||||||
#define MAP_SELF ( (temp_mapping_t) { .kind = TEMP_SELF } )
|
#define MAP_SELF ( (temp_mapping_t) { .kind = TEMP_SELF } )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Code generation context
|
Code generation context
|
||||||
Contains information we can use to optimize code
|
Contains information we can use to optimize code
|
||||||
*/
|
*/
|
||||||
typedef struct yjit_context
|
typedef struct yjit_context
|
||||||
{
|
{
|
||||||
// Depth of this block in the sidechain (eg: inline-cache chain)
|
|
||||||
uint8_t chain_depth;
|
|
||||||
|
|
||||||
// Temporary variable types we keep track of
|
|
||||||
// Values are `ruby_value_type`
|
|
||||||
// T_NONE==0 is the unknown type
|
|
||||||
uint8_t temp_types[MAX_TEMP_TYPES];
|
|
||||||
|
|
||||||
// Number of values currently on the temporary stack
|
// Number of values currently on the temporary stack
|
||||||
uint16_t stack_size;
|
uint16_t stack_size;
|
||||||
|
|
||||||
|
@ -118,22 +107,23 @@ typedef struct yjit_context
|
||||||
// This represents how far the JIT's SP is from the "real" SP
|
// This represents how far the JIT's SP is from the "real" SP
|
||||||
int16_t sp_offset;
|
int16_t sp_offset;
|
||||||
|
|
||||||
|
// Depth of this block in the sidechain (eg: inline-cache chain)
|
||||||
|
uint8_t chain_depth;
|
||||||
|
|
||||||
|
// Local variable types we keepp track of
|
||||||
|
val_type_t local_types[MAX_LOCAL_TYPES];
|
||||||
|
|
||||||
|
// Temporary variable types we keep track of
|
||||||
|
val_type_t temp_types[MAX_TEMP_TYPES];
|
||||||
|
|
||||||
|
// Type we track for self
|
||||||
|
val_type_t self_type;
|
||||||
|
|
||||||
|
// Mapping of temp stack entries to types we track
|
||||||
// FIXME: no longer need this bit after type mapping refactoring
|
temp_mapping_t temp_mapping[MAX_TEMP_TYPES];
|
||||||
// Whether we know self is a heap object
|
|
||||||
bool self_is_object : 1;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} ctx_t;
|
} ctx_t;
|
||||||
|
STATIC_ASSERT(yjit_ctx_size, sizeof(ctx_t) <= 32);
|
||||||
|
|
||||||
// Tuple of (iseq, idx) used to idenfity basic blocks
|
// Tuple of (iseq, idx) used to idenfity basic blocks
|
||||||
typedef struct BlockId
|
typedef struct BlockId
|
||||||
|
@ -224,10 +214,10 @@ typedef struct yjit_block_version
|
||||||
|
|
||||||
// Context object methods
|
// Context object methods
|
||||||
x86opnd_t ctx_sp_opnd(ctx_t* ctx, int32_t offset_bytes);
|
x86opnd_t ctx_sp_opnd(ctx_t* ctx, int32_t offset_bytes);
|
||||||
x86opnd_t ctx_stack_push(ctx_t* ctx, int type);
|
x86opnd_t ctx_stack_push(ctx_t* ctx, val_type_t type);
|
||||||
x86opnd_t ctx_stack_pop(ctx_t* ctx, size_t n);
|
x86opnd_t ctx_stack_pop(ctx_t* ctx, size_t n);
|
||||||
x86opnd_t ctx_stack_opnd(ctx_t* ctx, int32_t idx);
|
x86opnd_t ctx_stack_opnd(ctx_t* ctx, int32_t idx);
|
||||||
int ctx_get_top_type(ctx_t* ctx);
|
val_type_t ctx_get_temp_type(const ctx_t* ctx, size_t idx);
|
||||||
int ctx_diff(const ctx_t* src, const ctx_t* dst);
|
int ctx_diff(const ctx_t* src, const ctx_t* dst);
|
||||||
|
|
||||||
block_t* find_block_version(blockid_t blockid, const ctx_t* ctx);
|
block_t* find_block_version(blockid_t blockid, const ctx_t* ctx);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue