From f6e3f75c2b7503eb89f517c57ac4ea97dc2752b4 Mon Sep 17 00:00:00 2001 From: Maxime Chevalier-Boisvert Date: Thu, 8 Apr 2021 16:40:08 -0400 Subject: Introduce concept of YJIT instruction operands --- yjit_codegen.c | 30 +++++++++++++------------- yjit_core.c | 33 +++++++++++++++++------------ yjit_core.h | 66 ++++++++++++++++++++++++++++++++++------------------------ 3 files changed, 74 insertions(+), 55 deletions(-) diff --git a/yjit_codegen.c b/yjit_codegen.c index ce9e56a157..5b759f6c6f 100644 --- a/yjit_codegen.c +++ b/yjit_codegen.c @@ -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; } diff --git a/yjit_core.c b/yjit_core.c index 3be45c12e5..f0b7c31984 100644 --- a/yjit_core.c +++ b/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) diff --git a/yjit_core.h b/yjit_core.h index e264d89ffa..3830146f7e 100644 --- a/yjit_core.h +++ b/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); -- cgit v1.2.3