mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Introduce concept of YJIT instruction operands
This commit is contained in:
parent
40b3290966
commit
f6e3f75c2b
3 changed files with 74 additions and 55 deletions
|
@ -406,7 +406,7 @@ static codegen_status_t
|
|||
gen_dup(jitstate_t* jit, ctx_t* ctx)
|
||||
{
|
||||
// Get the top value and its type
|
||||
val_type_t dup_type = ctx_get_temp_type(ctx, 0);
|
||||
val_type_t dup_type = ctx_get_opnd_type(ctx, OPND_STACK(0));
|
||||
x86opnd_t dup_val = ctx_stack_pop(ctx, 0);
|
||||
|
||||
// Push the same value on top
|
||||
|
@ -596,7 +596,7 @@ gen_setlocal_wc0(jitstate_t* jit, ctx_t* ctx)
|
|||
// for dealing with how blocks/closures can affect local types
|
||||
//
|
||||
// Set the type of the local variable in the context
|
||||
//val_type_t temp_type = ctx_get_temp_type(ctx, 0);
|
||||
//val_type_t temp_type = ctx_get_opnd_type(ctx, OPND_STACK(0));
|
||||
//ctx_set_local_type(ctx, local_idx, temp_type);
|
||||
|
||||
// Pop the value to write from the stack
|
||||
|
@ -912,9 +912,9 @@ gen_fixnum_cmp(jitstate_t* jit, ctx_t* ctx, cmov_fn cmov_op)
|
|||
}
|
||||
|
||||
// Get the operands and destination from the stack
|
||||
val_type_t arg1_type = ctx_get_temp_type(ctx, 0);
|
||||
val_type_t arg1_type = ctx_get_opnd_type(ctx, OPND_STACK(0));
|
||||
x86opnd_t arg1 = ctx_stack_pop(ctx, 1);
|
||||
val_type_t arg0_type = ctx_get_temp_type(ctx, 0);
|
||||
val_type_t arg0_type = ctx_get_opnd_type(ctx, OPND_STACK(0));
|
||||
x86opnd_t arg0 = ctx_stack_pop(ctx, 1);
|
||||
|
||||
// If not fixnums, fall back
|
||||
|
@ -1122,9 +1122,9 @@ gen_opt_and(jitstate_t* jit, ctx_t* ctx)
|
|||
}
|
||||
|
||||
// Get the operands and destination from the stack
|
||||
val_type_t arg1_type = ctx_get_temp_type(ctx, 0);
|
||||
val_type_t arg1_type = ctx_get_opnd_type(ctx, OPND_STACK(0));
|
||||
x86opnd_t arg1 = ctx_stack_pop(ctx, 1);
|
||||
val_type_t arg0_type = ctx_get_temp_type(ctx, 0);
|
||||
val_type_t arg0_type = ctx_get_opnd_type(ctx, OPND_STACK(0));
|
||||
x86opnd_t arg0 = ctx_stack_pop(ctx, 1);
|
||||
|
||||
// If not fixnums, fall back
|
||||
|
@ -1160,9 +1160,9 @@ gen_opt_minus(jitstate_t* jit, ctx_t* ctx)
|
|||
}
|
||||
|
||||
// Get the operands and destination from the stack
|
||||
val_type_t arg1_type = ctx_get_temp_type(ctx, 0);
|
||||
val_type_t arg1_type = ctx_get_opnd_type(ctx, OPND_STACK(0));
|
||||
x86opnd_t arg1 = ctx_stack_pop(ctx, 1);
|
||||
val_type_t arg0_type = ctx_get_temp_type(ctx, 0);
|
||||
val_type_t arg0_type = ctx_get_opnd_type(ctx, OPND_STACK(0));
|
||||
x86opnd_t arg0 = ctx_stack_pop(ctx, 1);
|
||||
|
||||
// If not fixnums, fall back
|
||||
|
@ -1200,9 +1200,9 @@ gen_opt_plus(jitstate_t* jit, ctx_t* ctx)
|
|||
}
|
||||
|
||||
// Get the operands and destination from the stack
|
||||
val_type_t arg1_type = ctx_get_temp_type(ctx, 0);
|
||||
val_type_t arg1_type = ctx_get_opnd_type(ctx, OPND_STACK(0));
|
||||
x86opnd_t arg1 = ctx_stack_pop(ctx, 1);
|
||||
val_type_t arg0_type = ctx_get_temp_type(ctx, 0);
|
||||
val_type_t arg0_type = ctx_get_opnd_type(ctx, OPND_STACK(0));
|
||||
x86opnd_t arg0 = ctx_stack_pop(ctx, 1);
|
||||
|
||||
// If not fixnums, fall back
|
||||
|
@ -1360,7 +1360,7 @@ Guard that a stack operand has the same class as known_klass.
|
|||
Recompile as contingency if possible, or take side exit a last resort.
|
||||
*/
|
||||
static bool
|
||||
jit_guard_known_klass(jitstate_t *jit, ctx_t* ctx, VALUE known_klass, uint32_t stack_idx, const int max_chain_depth, uint8_t *side_exit)
|
||||
jit_guard_known_klass(jitstate_t *jit, ctx_t* ctx, VALUE known_klass, insn_opnd_t insn_opnd, const int max_chain_depth, uint8_t *side_exit)
|
||||
{
|
||||
// Can't guard for for these classes because some of they are sometimes immediate (special const).
|
||||
// Can remove this by adding appropriate dynamic checks.
|
||||
|
@ -1373,10 +1373,10 @@ jit_guard_known_klass(jitstate_t *jit, ctx_t* ctx, VALUE known_klass, uint32_t s
|
|||
return false;
|
||||
}
|
||||
|
||||
val_type_t temp_type = ctx_get_temp_type(ctx, stack_idx);
|
||||
val_type_t val_type = ctx_get_opnd_type(ctx, insn_opnd);
|
||||
|
||||
// Check that the receiver is a heap object
|
||||
if (!temp_type.is_heap)
|
||||
if (!val_type.is_heap)
|
||||
{
|
||||
test(cb, REG0, imm_opnd(RUBY_IMMEDIATE_MASK));
|
||||
jnz_ptr(cb, side_exit);
|
||||
|
@ -1385,7 +1385,7 @@ jit_guard_known_klass(jitstate_t *jit, ctx_t* ctx, VALUE known_klass, uint32_t s
|
|||
cmp(cb, REG0, imm_opnd(Qnil));
|
||||
je_ptr(cb, side_exit);
|
||||
|
||||
ctx_set_temp_type(ctx, stack_idx, TYPE_HEAP);
|
||||
ctx_set_opnd_type(ctx, insn_opnd, TYPE_HEAP);
|
||||
}
|
||||
|
||||
// Pointer to the klass field of the receiver &(recv->klass)
|
||||
|
@ -1796,7 +1796,7 @@ gen_opt_send_without_block(jitstate_t* jit, ctx_t* ctx)
|
|||
// Points to the receiver operand on the stack
|
||||
x86opnd_t recv = ctx_stack_opnd(ctx, argc);
|
||||
mov(cb, REG0, recv);
|
||||
if (!jit_guard_known_klass(jit, ctx, comptime_recv_klass, argc, OSWB_MAX_DEPTH, side_exit)) {
|
||||
if (!jit_guard_known_klass(jit, ctx, comptime_recv_klass, OPND_STACK(argc), OSWB_MAX_DEPTH, side_exit)) {
|
||||
return YJIT_CANT_COMPILE;
|
||||
}
|
||||
|
||||
|
|
33
yjit_core.c
33
yjit_core.c
|
@ -132,18 +132,20 @@ ctx_stack_opnd(ctx_t* ctx, int32_t idx)
|
|||
}
|
||||
|
||||
/**
|
||||
Get the type of a value on the temp stack
|
||||
Returns T_NONE if unknown
|
||||
Get the type of an instruction operand
|
||||
*/
|
||||
val_type_t
|
||||
ctx_get_temp_type(const ctx_t* ctx, size_t idx)
|
||||
ctx_get_opnd_type(const ctx_t* ctx, insn_opnd_t opnd)
|
||||
{
|
||||
RUBY_ASSERT(idx < ctx->stack_size);
|
||||
RUBY_ASSERT(opnd.idx < ctx->stack_size);
|
||||
|
||||
if (opnd.is_self)
|
||||
return ctx->self_type;
|
||||
|
||||
if (ctx->stack_size > MAX_TEMP_TYPES)
|
||||
return TYPE_UNKNOWN;
|
||||
|
||||
temp_mapping_t mapping = ctx->temp_mapping[ctx->stack_size - 1 - idx];
|
||||
temp_mapping_t mapping = ctx->temp_mapping[ctx->stack_size - 1 - opnd.idx];
|
||||
|
||||
switch (mapping.kind)
|
||||
{
|
||||
|
@ -151,7 +153,7 @@ ctx_get_temp_type(const ctx_t* ctx, size_t idx)
|
|||
return ctx->self_type;
|
||||
|
||||
case TEMP_STACK:
|
||||
return ctx->temp_types[ctx->stack_size - 1 - idx];
|
||||
return ctx->temp_types[ctx->stack_size - 1 - opnd.idx];
|
||||
|
||||
case TEMP_LOCAL:
|
||||
RUBY_ASSERT(mapping.idx < MAX_LOCAL_TYPES);
|
||||
|
@ -162,16 +164,21 @@ ctx_get_temp_type(const ctx_t* ctx, size_t idx)
|
|||
}
|
||||
|
||||
/**
|
||||
Set the type of a value in the temporary stack
|
||||
Set the type of an instruction operand
|
||||
*/
|
||||
void ctx_set_temp_type(ctx_t* ctx, size_t idx, val_type_t type)
|
||||
void ctx_set_opnd_type(ctx_t* ctx, insn_opnd_t opnd, val_type_t type)
|
||||
{
|
||||
RUBY_ASSERT(idx < ctx->stack_size);
|
||||
RUBY_ASSERT(opnd.idx < ctx->stack_size);
|
||||
|
||||
if (opnd.is_self) {
|
||||
ctx->self_type = type;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx->stack_size > MAX_TEMP_TYPES)
|
||||
return;
|
||||
|
||||
temp_mapping_t mapping = ctx->temp_mapping[ctx->stack_size - 1 - idx];
|
||||
temp_mapping_t mapping = ctx->temp_mapping[ctx->stack_size - 1 - opnd.idx];
|
||||
|
||||
switch (mapping.kind)
|
||||
{
|
||||
|
@ -180,7 +187,7 @@ void ctx_set_temp_type(ctx_t* ctx, size_t idx, val_type_t type)
|
|||
break;
|
||||
|
||||
case TEMP_STACK:
|
||||
ctx->temp_types[ctx->stack_size - 1 - idx] = type;
|
||||
ctx->temp_types[ctx->stack_size - 1 - opnd.idx] = type;
|
||||
break;
|
||||
|
||||
case TEMP_LOCAL:
|
||||
|
@ -275,8 +282,8 @@ int ctx_diff(const ctx_t* src, const ctx_t* dst)
|
|||
// 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);
|
||||
val_type_t t_src = ctx_get_opnd_type(src, OPND_STACK(i));
|
||||
val_type_t t_dst = ctx_get_opnd_type(dst, OPND_STACK(i));
|
||||
int temp_diff = type_diff(t_src, t_dst);
|
||||
|
||||
if (temp_diff == INT_MAX)
|
||||
|
|
66
yjit_core.h
66
yjit_core.h
|
@ -26,23 +26,20 @@
|
|||
// Default versioning context (no type information)
|
||||
#define DEFAULT_CTX ( (ctx_t){ 0 } )
|
||||
|
||||
typedef enum yjit_type_enum
|
||||
{
|
||||
ETYPE_UNKNOWN = 0,
|
||||
ETYPE_NIL,
|
||||
ETYPE_FIXNUM,
|
||||
ETYPE_ARRAY,
|
||||
ETYPE_HASH
|
||||
//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_type_struct
|
||||
{
|
||||
enum
|
||||
{
|
||||
ETYPE_UNKNOWN = 0,
|
||||
ETYPE_NIL,
|
||||
ETYPE_FIXNUM,
|
||||
ETYPE_ARRAY,
|
||||
ETYPE_HASH
|
||||
//ETYPE_SYMBOL
|
||||
//ETYPE_STRING
|
||||
};
|
||||
|
||||
// Value is definitely a heap object
|
||||
uint8_t is_heap : 1;
|
||||
|
||||
|
@ -69,18 +66,19 @@ STATIC_ASSERT(val_type_size, sizeof(val_type_t) == 1);
|
|||
#define TYPE_ARRAY ( (val_type_t){ .is_heap = 1, .type = ETYPE_ARRAY } )
|
||||
#define TYPE_HASH ( (val_type_t){ .is_heap = 1, .type = ETYPE_HASH } )
|
||||
|
||||
typedef enum yjit_temp_loc
|
||||
{
|
||||
TEMP_STACK = 0,
|
||||
TEMP_SELF,
|
||||
TEMP_LOCAL, // Local with index
|
||||
//TEMP_CONST, // Small constant (0, 1, 2, Qnil, Qfalse, Qtrue)
|
||||
|
||||
} temp_loc_t;
|
||||
|
||||
// Potential mapping of a value on the temporary stack to
|
||||
// self, a local variable or constant so that we can track its type
|
||||
typedef struct yjit_temp_mapping
|
||||
{
|
||||
// Where/how is the local stored?
|
||||
enum
|
||||
{
|
||||
TEMP_STACK = 0,
|
||||
TEMP_SELF,
|
||||
TEMP_LOCAL, // Local with index
|
||||
//TEMP_CONST, // Small constant (0, 1, 2, Qnil, Qfalse, Qtrue)
|
||||
};
|
||||
|
||||
// Where/how is the value stored?
|
||||
uint8_t kind: 2;
|
||||
|
||||
// Index of the local variale,
|
||||
|
@ -96,6 +94,20 @@ STATIC_ASSERT(temp_mapping_size, sizeof(temp_mapping_t) == 1);
|
|||
// Temp value is actually self
|
||||
#define MAP_SELF ( (temp_mapping_t) { .kind = TEMP_SELF } )
|
||||
|
||||
// Operand to a bytecode instruction
|
||||
typedef struct yjit_insn_opnd
|
||||
{
|
||||
// Indicates if the value is self
|
||||
bool is_self;
|
||||
|
||||
// Index on the temporary stack (for stack operands only)
|
||||
uint16_t idx;
|
||||
|
||||
} insn_opnd_t;
|
||||
|
||||
#define OPND_SELF ( (insn_opnd_t){ .is_self = true } )
|
||||
#define OPND_STACK(stack_idx) ( (insn_opnd_t){ .is_self = false, .idx = stack_idx } )
|
||||
|
||||
/**
|
||||
Code generation context
|
||||
Contains information we can use to optimize code
|
||||
|
@ -225,8 +237,8 @@ x86opnd_t ctx_stack_push_self(ctx_t* ctx);
|
|||
x86opnd_t ctx_stack_push_local(ctx_t* ctx, size_t local_idx);
|
||||
x86opnd_t ctx_stack_pop(ctx_t* ctx, size_t n);
|
||||
x86opnd_t ctx_stack_opnd(ctx_t* ctx, int32_t idx);
|
||||
val_type_t ctx_get_temp_type(const ctx_t* ctx, size_t idx);
|
||||
void ctx_set_temp_type(ctx_t* ctx, size_t idx, val_type_t type);
|
||||
val_type_t ctx_get_opnd_type(const ctx_t* ctx, insn_opnd_t opnd);
|
||||
void ctx_set_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);
|
||||
int ctx_diff(const ctx_t* src, const ctx_t* dst);
|
||||
|
||||
|
|
Loading…
Reference in a new issue