2021-03-07 02:46:56 +03:00
|
|
|
#ifndef YJIT_CORE_H
|
|
|
|
#define YJIT_CORE_H 1
|
2020-12-09 00:54:41 +03:00
|
|
|
|
|
|
|
#include "stddef.h"
|
2021-03-07 02:46:56 +03:00
|
|
|
#include "yjit_asm.h"
|
2020-12-09 00:54:41 +03:00
|
|
|
|
2021-03-07 02:46:56 +03:00
|
|
|
// Register YJIT receives the CFP and EC into
|
2021-07-15 23:35:19 +03:00
|
|
|
#define REG_CFP RSI
|
|
|
|
#define REG_EC RDI
|
2020-12-09 00:54:41 +03:00
|
|
|
|
2021-03-07 02:46:56 +03:00
|
|
|
// Register YJIT loads the SP into
|
2020-12-09 00:54:41 +03:00
|
|
|
#define REG_SP RDX
|
|
|
|
|
2021-03-07 02:46:56 +03:00
|
|
|
// Scratch registers used by YJIT
|
2020-12-09 00:54:41 +03:00
|
|
|
#define REG0 RAX
|
|
|
|
#define REG1 RCX
|
|
|
|
#define REG0_32 EAX
|
|
|
|
#define REG1_32 ECX
|
|
|
|
|
2021-05-11 00:05:12 +03:00
|
|
|
#define REG0_8 AL
|
|
|
|
|
2021-01-20 20:44:24 +03:00
|
|
|
// Maximum number of temp value types we keep track of
|
|
|
|
#define MAX_TEMP_TYPES 8
|
|
|
|
|
2021-03-30 22:37:20 +03:00
|
|
|
// Maximum number of local variable types we keep track of
|
|
|
|
#define MAX_LOCAL_TYPES 8
|
|
|
|
|
2021-03-05 23:45:44 +03:00
|
|
|
// Default versioning context (no type information)
|
|
|
|
#define DEFAULT_CTX ( (ctx_t){ 0 } )
|
|
|
|
|
2021-04-09 17:14:06 +03:00
|
|
|
enum yjit_type_enum
|
|
|
|
{
|
|
|
|
ETYPE_UNKNOWN = 0,
|
|
|
|
ETYPE_NIL,
|
2021-06-02 18:15:39 +03:00
|
|
|
ETYPE_TRUE,
|
|
|
|
ETYPE_FALSE,
|
2021-04-09 17:14:06 +03:00
|
|
|
ETYPE_FIXNUM,
|
2021-06-24 18:15:53 +03:00
|
|
|
ETYPE_FLONUM,
|
2021-04-09 17:14:06 +03:00
|
|
|
ETYPE_ARRAY,
|
|
|
|
ETYPE_HASH,
|
|
|
|
ETYPE_SYMBOL,
|
|
|
|
ETYPE_STRING
|
|
|
|
};
|
|
|
|
|
2021-04-08 23:40:08 +03:00
|
|
|
// Represent the type of a value (local/stack/self) in YJIT
|
2021-03-31 22:54:46 +03:00
|
|
|
typedef struct yjit_type_struct
|
2021-03-30 17:59:53 +03:00
|
|
|
{
|
|
|
|
// Value is definitely a heap object
|
|
|
|
uint8_t is_heap : 1;
|
|
|
|
|
|
|
|
// Value is definitely an immediate
|
|
|
|
uint8_t is_imm : 1;
|
|
|
|
|
2021-03-31 22:54:46 +03:00
|
|
|
// Specific value type, if known
|
2021-06-02 18:15:39 +03:00
|
|
|
uint8_t type : 4;
|
2021-03-30 17:59:53 +03:00
|
|
|
|
|
|
|
} val_type_t;
|
|
|
|
STATIC_ASSERT(val_type_size, sizeof(val_type_t) == 1);
|
|
|
|
|
|
|
|
// Unknown type, could be anything, all zeroes
|
|
|
|
#define TYPE_UNKNOWN ( (val_type_t){ 0 } )
|
|
|
|
|
|
|
|
// Could be any heap object
|
|
|
|
#define TYPE_HEAP ( (val_type_t){ .is_heap = 1 } )
|
|
|
|
|
|
|
|
// Could be any immediate
|
|
|
|
#define TYPE_IMM ( (val_type_t){ .is_imm = 1 } )
|
|
|
|
|
2021-03-31 22:54:46 +03:00
|
|
|
#define TYPE_NIL ( (val_type_t){ .is_imm = 1, .type = ETYPE_NIL } )
|
2021-06-02 18:15:39 +03:00
|
|
|
#define TYPE_TRUE ( (val_type_t){ .is_imm = 1, .type = ETYPE_TRUE } )
|
|
|
|
#define TYPE_FALSE ( (val_type_t){ .is_imm = 1, .type = ETYPE_FALSE } )
|
2021-03-31 22:54:46 +03:00
|
|
|
#define TYPE_FIXNUM ( (val_type_t){ .is_imm = 1, .type = ETYPE_FIXNUM } )
|
2021-06-24 18:15:53 +03:00
|
|
|
#define TYPE_FLONUM ( (val_type_t){ .is_imm = 1, .type = ETYPE_FLONUM } )
|
2021-06-23 05:22:30 +03:00
|
|
|
#define TYPE_STATIC_SYMBOL ( (val_type_t){ .is_imm = 1, .type = ETYPE_SYMBOL } )
|
2021-04-01 23:56:20 +03:00
|
|
|
#define TYPE_ARRAY ( (val_type_t){ .is_heap = 1, .type = ETYPE_ARRAY } )
|
|
|
|
#define TYPE_HASH ( (val_type_t){ .is_heap = 1, .type = ETYPE_HASH } )
|
2021-05-25 23:12:48 +03:00
|
|
|
#define TYPE_STRING ( (val_type_t){ .is_heap = 1, .type = ETYPE_STRING } )
|
2021-03-30 17:59:53 +03:00
|
|
|
|
2021-04-09 17:14:06 +03:00
|
|
|
enum yjit_temp_loc
|
|
|
|
{
|
|
|
|
TEMP_STACK = 0,
|
|
|
|
TEMP_SELF,
|
|
|
|
TEMP_LOCAL, // Local with index
|
|
|
|
//TEMP_CONST, // Small constant (0, 1, 2, Qnil, Qfalse, Qtrue)
|
|
|
|
};
|
|
|
|
|
2021-04-08 23:40:08 +03:00
|
|
|
// Potential mapping of a value on the temporary stack to
|
|
|
|
// self, a local variable or constant so that we can track its type
|
2021-03-30 17:59:53 +03:00
|
|
|
typedef struct yjit_temp_mapping
|
|
|
|
{
|
2021-04-08 23:40:08 +03:00
|
|
|
// Where/how is the value stored?
|
2021-03-30 17:59:53 +03:00
|
|
|
uint8_t kind: 2;
|
|
|
|
|
|
|
|
// Index of the local variale,
|
2021-03-30 22:37:20 +03:00
|
|
|
// or small non-negative constant in [0, 63]
|
2021-03-30 17:59:53 +03:00
|
|
|
uint8_t idx : 6;
|
|
|
|
|
|
|
|
} temp_mapping_t;
|
|
|
|
STATIC_ASSERT(temp_mapping_size, sizeof(temp_mapping_t) == 1);
|
|
|
|
|
|
|
|
// By default, temps are just temps on the stack
|
|
|
|
#define MAP_STACK ( (temp_mapping_t) { 0 } )
|
|
|
|
|
|
|
|
// Temp value is actually self
|
|
|
|
#define MAP_SELF ( (temp_mapping_t) { .kind = TEMP_SELF } )
|
|
|
|
|
2021-07-14 21:36:33 +03:00
|
|
|
// Represents both the type and mapping
|
|
|
|
typedef struct {
|
|
|
|
temp_mapping_t mapping;
|
|
|
|
val_type_t type;
|
|
|
|
} temp_type_mapping_t;
|
|
|
|
STATIC_ASSERT(temp_type_mapping_size, sizeof(temp_type_mapping_t) == 2);
|
|
|
|
|
2021-04-08 23:40:08 +03:00
|
|
|
// 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 } )
|
|
|
|
|
2021-01-22 20:22:34 +03:00
|
|
|
/**
|
|
|
|
Code generation context
|
|
|
|
Contains information we can use to optimize code
|
|
|
|
*/
|
2021-03-17 18:00:36 +03:00
|
|
|
typedef struct yjit_context
|
2020-12-09 00:54:41 +03:00
|
|
|
{
|
2021-02-10 00:24:06 +03:00
|
|
|
// Number of values currently on the temporary stack
|
2021-01-27 00:51:44 +03:00
|
|
|
uint16_t stack_size;
|
2020-12-10 08:06:10 +03:00
|
|
|
|
2021-02-10 00:24:06 +03:00
|
|
|
// Offset of the JIT SP relative to the interpreter SP
|
2021-03-03 22:58:42 +03:00
|
|
|
// This represents how far the JIT's SP is from the "real" SP
|
2021-02-10 00:24:06 +03:00
|
|
|
int16_t sp_offset;
|
|
|
|
|
2021-03-31 22:54:46 +03:00
|
|
|
// Depth of this block in the sidechain (eg: inline-cache chain)
|
|
|
|
uint8_t chain_depth;
|
2021-03-30 22:37:20 +03:00
|
|
|
|
2021-03-31 22:54:46 +03:00
|
|
|
// Local variable types we keepp track of
|
|
|
|
val_type_t local_types[MAX_LOCAL_TYPES];
|
2021-03-30 22:37:20 +03:00
|
|
|
|
2021-03-31 22:54:46 +03:00
|
|
|
// Temporary variable types we keep track of
|
|
|
|
val_type_t temp_types[MAX_TEMP_TYPES];
|
2021-03-30 22:37:20 +03:00
|
|
|
|
2021-03-31 22:54:46 +03:00
|
|
|
// Type we track for self
|
|
|
|
val_type_t self_type;
|
2021-03-30 22:37:20 +03:00
|
|
|
|
2021-03-31 22:54:46 +03:00
|
|
|
// Mapping of temp stack entries to types we track
|
|
|
|
temp_mapping_t temp_mapping[MAX_TEMP_TYPES];
|
2021-03-30 22:37:20 +03:00
|
|
|
|
2020-12-09 00:54:41 +03:00
|
|
|
} ctx_t;
|
2021-03-31 22:54:46 +03:00
|
|
|
STATIC_ASSERT(yjit_ctx_size, sizeof(ctx_t) <= 32);
|
2020-12-09 00:54:41 +03:00
|
|
|
|
2020-12-17 01:07:18 +03:00
|
|
|
// Tuple of (iseq, idx) used to idenfity basic blocks
|
|
|
|
typedef struct BlockId
|
|
|
|
{
|
|
|
|
// Instruction sequence
|
|
|
|
const rb_iseq_t *iseq;
|
|
|
|
|
2021-01-19 01:03:04 +03:00
|
|
|
// Index in the iseq where the block starts
|
|
|
|
uint32_t idx;
|
2020-12-17 01:07:18 +03:00
|
|
|
|
|
|
|
} blockid_t;
|
|
|
|
|
2021-01-08 23:18:03 +03:00
|
|
|
// Null block id constant
|
|
|
|
static const blockid_t BLOCKID_NULL = { 0, 0 };
|
|
|
|
|
2020-12-17 01:07:18 +03:00
|
|
|
/// Branch code shape enumeration
|
2021-03-05 23:54:21 +03:00
|
|
|
typedef enum branch_shape
|
2020-12-17 01:07:18 +03:00
|
|
|
{
|
2020-12-17 05:45:51 +03:00
|
|
|
SHAPE_NEXT0, // Target 0 is next
|
|
|
|
SHAPE_NEXT1, // Target 1 is next
|
|
|
|
SHAPE_DEFAULT // Neither target is next
|
2021-03-05 23:45:44 +03:00
|
|
|
} branch_shape_t;
|
2020-12-17 01:07:18 +03:00
|
|
|
|
|
|
|
// Branch code generation function signature
|
|
|
|
typedef void (*branchgen_fn)(codeblock_t* cb, uint8_t* target0, uint8_t* target1, uint8_t shape);
|
|
|
|
|
2021-01-22 20:22:34 +03:00
|
|
|
/**
|
|
|
|
Store info about an outgoing branch in a code segment
|
|
|
|
Note: care must be taken to minimize the size of branch_t objects
|
|
|
|
*/
|
2021-04-06 00:59:25 +03:00
|
|
|
typedef struct yjit_branch_entry
|
2020-12-17 01:07:18 +03:00
|
|
|
{
|
2021-04-20 00:07:27 +03:00
|
|
|
// Block this is attached to
|
|
|
|
struct yjit_block_version *block;
|
|
|
|
|
2020-12-17 01:07:18 +03:00
|
|
|
// Positions where the generated code starts and ends
|
|
|
|
uint32_t start_pos;
|
|
|
|
uint32_t end_pos;
|
|
|
|
|
2021-01-08 23:18:03 +03:00
|
|
|
// Context right after the branch instruction
|
|
|
|
ctx_t src_ctx;
|
|
|
|
|
|
|
|
// Branch target blocks and their contexts
|
2020-12-17 01:07:18 +03:00
|
|
|
blockid_t targets[2];
|
2021-01-08 23:18:03 +03:00
|
|
|
ctx_t target_ctxs[2];
|
2021-04-20 00:07:27 +03:00
|
|
|
struct yjit_block_version *blocks[2];
|
2020-12-17 01:07:18 +03:00
|
|
|
|
2020-12-17 05:45:51 +03:00
|
|
|
// Jump target addresses
|
|
|
|
uint8_t* dst_addrs[2];
|
|
|
|
|
2020-12-17 01:07:18 +03:00
|
|
|
// Branch code generation function
|
|
|
|
branchgen_fn gen_fn;
|
|
|
|
|
2020-12-17 05:45:51 +03:00
|
|
|
// Shape of the branch
|
2021-04-07 22:36:02 +03:00
|
|
|
branch_shape_t shape : 2;
|
|
|
|
|
2020-12-17 01:07:18 +03:00
|
|
|
} branch_t;
|
2020-12-14 23:57:55 +03:00
|
|
|
|
2021-04-20 00:07:27 +03:00
|
|
|
typedef rb_darray(branch_t*) branch_array_t;
|
|
|
|
|
2021-02-20 00:04:23 +03:00
|
|
|
typedef rb_darray(uint32_t) int32_array_t;
|
2021-02-19 23:03:12 +03:00
|
|
|
|
2021-01-22 20:22:34 +03:00
|
|
|
/**
|
|
|
|
Basic block version
|
|
|
|
Represents a portion of an iseq compiled with a given context
|
|
|
|
Note: care must be taken to minimize the size of block_t objects
|
|
|
|
*/
|
2021-03-07 02:46:56 +03:00
|
|
|
typedef struct yjit_block_version
|
2021-01-13 23:18:35 +03:00
|
|
|
{
|
2021-01-14 21:33:19 +03:00
|
|
|
// Bytecode sequence (iseq, idx) this is a version of
|
2021-01-13 23:18:35 +03:00
|
|
|
blockid_t blockid;
|
|
|
|
|
|
|
|
// Context at the start of the block
|
|
|
|
ctx_t ctx;
|
|
|
|
|
|
|
|
// Positions where the generated code starts and ends
|
|
|
|
uint32_t start_pos;
|
|
|
|
uint32_t end_pos;
|
|
|
|
|
2021-04-20 00:07:27 +03:00
|
|
|
// List of incoming branches (from predecessors)
|
|
|
|
branch_array_t incoming;
|
|
|
|
|
|
|
|
// List of outgoing branches (to successors)
|
|
|
|
// Note: these are owned by this block version
|
|
|
|
branch_array_t outgoing;
|
2021-01-13 23:18:35 +03:00
|
|
|
|
2021-02-25 23:10:38 +03:00
|
|
|
// Offsets for GC managed objects in the mainline code block
|
|
|
|
int32_array_t gc_object_offsets;
|
|
|
|
|
2021-03-18 02:07:20 +03:00
|
|
|
// In case this block is invalidated, these two pieces of info
|
|
|
|
// help to remove all pointers to this block in the system.
|
|
|
|
VALUE receiver_klass;
|
|
|
|
VALUE callee_cme;
|
2021-02-25 23:10:38 +03:00
|
|
|
|
2021-07-13 21:56:02 +03:00
|
|
|
// Code page this block lives on
|
|
|
|
VALUE code_page;
|
|
|
|
|
2021-02-25 23:10:38 +03:00
|
|
|
// Index one past the last instruction in the iseq
|
|
|
|
uint32_t end_idx;
|
2021-07-13 21:56:02 +03:00
|
|
|
|
2021-01-14 21:33:19 +03:00
|
|
|
} block_t;
|
2021-01-13 23:18:35 +03:00
|
|
|
|
2020-12-11 00:59:13 +03:00
|
|
|
// Context object methods
|
2020-12-09 00:54:41 +03:00
|
|
|
x86opnd_t ctx_sp_opnd(ctx_t* ctx, int32_t offset_bytes);
|
2021-07-14 21:36:33 +03:00
|
|
|
x86opnd_t ctx_stack_push_mapping(ctx_t* ctx, temp_type_mapping_t mapping);
|
2021-03-31 22:54:46 +03:00
|
|
|
x86opnd_t ctx_stack_push(ctx_t* ctx, val_type_t type);
|
2021-04-01 23:56:20 +03:00
|
|
|
x86opnd_t ctx_stack_push_self(ctx_t* ctx);
|
2021-04-06 19:00:09 +03:00
|
|
|
x86opnd_t ctx_stack_push_local(ctx_t* ctx, size_t local_idx);
|
2020-12-09 00:54:41 +03:00
|
|
|
x86opnd_t ctx_stack_pop(ctx_t* ctx, size_t n);
|
|
|
|
x86opnd_t ctx_stack_opnd(ctx_t* ctx, int32_t idx);
|
2021-04-08 23:40:08 +03:00
|
|
|
val_type_t ctx_get_opnd_type(const ctx_t* ctx, insn_opnd_t opnd);
|
2021-07-14 21:52:00 +03:00
|
|
|
void ctx_upgrade_opnd_type(ctx_t* ctx, insn_opnd_t opnd, val_type_t type);
|
2021-04-06 21:44:28 +03:00
|
|
|
void ctx_set_local_type(ctx_t* ctx, size_t idx, val_type_t type);
|
2021-04-09 18:44:35 +03:00
|
|
|
void ctx_clear_local_types(ctx_t* ctx);
|
2021-01-22 22:57:44 +03:00
|
|
|
int ctx_diff(const ctx_t* src, const ctx_t* dst);
|
2020-12-09 00:54:41 +03:00
|
|
|
|
2021-07-14 21:36:33 +03:00
|
|
|
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);
|
|
|
|
|
2021-01-14 21:33:19 +03:00
|
|
|
block_t* find_block_version(blockid_t blockid, const ctx_t* ctx);
|
2021-03-02 04:43:58 +03:00
|
|
|
block_t* gen_block_version(blockid_t blockid, const ctx_t* ctx, rb_execution_context_t *ec);
|
|
|
|
uint8_t* gen_entry_point(const rb_iseq_t *iseq, uint32_t insn_idx, rb_execution_context_t *ec);
|
2021-03-07 02:46:56 +03:00
|
|
|
void yjit_free_block(block_t *block);
|
2021-03-25 01:07:26 +03:00
|
|
|
rb_yjit_block_array_t yjit_get_version_array(const rb_iseq_t *iseq, unsigned idx);
|
2021-01-08 23:18:03 +03:00
|
|
|
|
|
|
|
void gen_branch(
|
2021-04-20 00:07:27 +03:00
|
|
|
block_t* block,
|
2021-01-08 23:18:03 +03:00
|
|
|
const ctx_t* src_ctx,
|
|
|
|
blockid_t target0,
|
|
|
|
const ctx_t* ctx0,
|
|
|
|
blockid_t target1,
|
|
|
|
const ctx_t* ctx1,
|
|
|
|
branchgen_fn gen_fn
|
|
|
|
);
|
2020-12-17 01:07:18 +03:00
|
|
|
|
2021-01-19 19:11:11 +03:00
|
|
|
void gen_direct_jump(
|
2021-04-20 00:07:27 +03:00
|
|
|
block_t* block,
|
2021-01-19 19:11:11 +03:00
|
|
|
const ctx_t* ctx,
|
|
|
|
blockid_t target0
|
|
|
|
);
|
|
|
|
|
2021-03-05 23:45:44 +03:00
|
|
|
void defer_compilation(
|
|
|
|
block_t* block,
|
|
|
|
uint32_t insn_idx,
|
|
|
|
ctx_t* cur_ctx
|
|
|
|
);
|
|
|
|
|
2021-01-29 20:07:18 +03:00
|
|
|
void invalidate_block_version(block_t* block);
|
2021-01-13 01:03:54 +03:00
|
|
|
|
2021-03-07 02:46:56 +03:00
|
|
|
void yjit_init_core(void);
|
2020-12-11 00:59:13 +03:00
|
|
|
|
2021-03-07 02:46:56 +03:00
|
|
|
#endif // #ifndef YJIT_CORE_H
|