Ruby  3.1.4p223 (2023-03-30 revision HEAD)
yjit_core.h
1 #ifndef YJIT_CORE_H
2 #define YJIT_CORE_H 1
3 
4 #include <stddef.h>
5 #include <stdint.h>
6 #include "yjit_asm.h"
7 
8 // Callee-saved regs
9 #define REG_CFP R13
10 #define REG_EC R12
11 #define REG_SP RBX
12 
13 // Scratch registers used by YJIT
14 #define REG0 RAX
15 #define REG0_32 EAX
16 #define REG0_8 AL
17 #define REG1 RCX
18 #define REG1_32 ECX
19 
20 // Maximum number of temp value types we keep track of
21 #define MAX_TEMP_TYPES 8
22 
23 // Maximum number of local variable types we keep track of
24 #define MAX_LOCAL_TYPES 8
25 
26 // Default versioning context (no type information)
27 #define DEFAULT_CTX ( (ctx_t){ 0 } )
28 
29 enum yjit_type_enum
30 {
31  ETYPE_UNKNOWN = 0,
32  ETYPE_NIL,
33  ETYPE_TRUE,
34  ETYPE_FALSE,
35  ETYPE_FIXNUM,
36  ETYPE_FLONUM,
37  ETYPE_ARRAY,
38  ETYPE_HASH,
39  ETYPE_SYMBOL,
40  ETYPE_STRING
41 };
42 
43 // Represent the type of a value (local/stack/self) in YJIT
44 typedef struct yjit_type_struct
45 {
46  // Value is definitely a heap object
47  uint8_t is_heap : 1;
48 
49  // Value is definitely an immediate
50  uint8_t is_imm : 1;
51 
52  // Specific value type, if known
53  uint8_t type : 4;
54 
55 } val_type_t;
56 STATIC_ASSERT(val_type_size, sizeof(val_type_t) == 1);
57 
58 // Unknown type, could be anything, all zeroes
59 #define TYPE_UNKNOWN ( (val_type_t){ 0 } )
60 
61 // Could be any heap object
62 #define TYPE_HEAP ( (val_type_t){ .is_heap = 1 } )
63 
64 // Could be any immediate
65 #define TYPE_IMM ( (val_type_t){ .is_imm = 1 } )
66 
67 #define TYPE_NIL ( (val_type_t){ .is_imm = 1, .type = ETYPE_NIL } )
68 #define TYPE_TRUE ( (val_type_t){ .is_imm = 1, .type = ETYPE_TRUE } )
69 #define TYPE_FALSE ( (val_type_t){ .is_imm = 1, .type = ETYPE_FALSE } )
70 #define TYPE_FIXNUM ( (val_type_t){ .is_imm = 1, .type = ETYPE_FIXNUM } )
71 #define TYPE_FLONUM ( (val_type_t){ .is_imm = 1, .type = ETYPE_FLONUM } )
72 #define TYPE_STATIC_SYMBOL ( (val_type_t){ .is_imm = 1, .type = ETYPE_SYMBOL } )
73 #define TYPE_ARRAY ( (val_type_t){ .is_heap = 1, .type = ETYPE_ARRAY } )
74 #define TYPE_HASH ( (val_type_t){ .is_heap = 1, .type = ETYPE_HASH } )
75 #define TYPE_STRING ( (val_type_t){ .is_heap = 1, .type = ETYPE_STRING } )
76 
77 enum yjit_temp_loc
78 {
79  TEMP_STACK = 0,
80  TEMP_SELF,
81  TEMP_LOCAL, // Local with index
82  //TEMP_CONST, // Small constant (0, 1, 2, Qnil, Qfalse, Qtrue)
83 };
84 
85 // Potential mapping of a value on the temporary stack to
86 // self, a local variable or constant so that we can track its type
87 typedef struct yjit_temp_mapping
88 {
89  // Where/how is the value stored?
90  uint8_t kind: 2;
91 
92  // Index of the local variale,
93  // or small non-negative constant in [0, 63]
94  uint8_t idx : 6;
95 
97 STATIC_ASSERT(temp_mapping_size, sizeof(temp_mapping_t) == 1);
98 
99 // By default, temps are just temps on the stack.
100 // Name conflict with an mmap flag. This is a struct instance,
101 // so the compiler will check for wrong usage.
102 #undef MAP_STACK
103 #define MAP_STACK ( (temp_mapping_t) { 0 } )
104 
105 // Temp value is actually self
106 #define MAP_SELF ( (temp_mapping_t) { .kind = TEMP_SELF } )
107 
108 // Represents both the type and mapping
109 typedef struct {
110  temp_mapping_t mapping;
113 STATIC_ASSERT(temp_type_mapping_size, sizeof(temp_type_mapping_t) == 2);
114 
115 // Operand to a bytecode instruction
116 typedef struct yjit_insn_opnd
117 {
118  // Indicates if the value is self
119  bool is_self;
120 
121  // Index on the temporary stack (for stack operands only)
122  uint16_t idx;
123 
124 } insn_opnd_t;
125 
126 #define OPND_SELF ( (insn_opnd_t){ .is_self = true } )
127 #define OPND_STACK(stack_idx) ( (insn_opnd_t){ .is_self = false, .idx = stack_idx } )
128 
133 typedef struct yjit_context
134 {
135  // Number of values currently on the temporary stack
136  uint16_t stack_size;
137 
138  // Offset of the JIT SP relative to the interpreter SP
139  // This represents how far the JIT's SP is from the "real" SP
140  int16_t sp_offset;
141 
142  // Depth of this block in the sidechain (eg: inline-cache chain)
143  uint8_t chain_depth;
144 
145  // Local variable types we keepp track of
146  val_type_t local_types[MAX_LOCAL_TYPES];
147 
148  // Temporary variable types we keep track of
149  val_type_t temp_types[MAX_TEMP_TYPES];
150 
151  // Type we track for self
152  val_type_t self_type;
153 
154  // Mapping of temp stack entries to types we track
155  temp_mapping_t temp_mapping[MAX_TEMP_TYPES];
156 
157 } ctx_t;
158 STATIC_ASSERT(yjit_ctx_size, sizeof(ctx_t) <= 32);
159 
160 // Tuple of (iseq, idx) used to identify basic blocks
161 typedef struct BlockId
162 {
163  // Instruction sequence
164  const rb_iseq_t *iseq;
165 
166  // Index in the iseq where the block starts
167  uint32_t idx;
168 
169 } blockid_t;
170 
171 // Null block id constant
172 static const blockid_t BLOCKID_NULL = { 0, 0 };
173 
175 typedef enum branch_shape
176 {
177  SHAPE_NEXT0, // Target 0 is next
178  SHAPE_NEXT1, // Target 1 is next
179  SHAPE_DEFAULT // Neither target is next
180 } branch_shape_t;
181 
182 // Branch code generation function signature
183 typedef void (*branchgen_fn)(codeblock_t* cb, uint8_t* target0, uint8_t* target1, uint8_t shape);
184 
189 typedef struct yjit_branch_entry
190 {
191  // Block this is attached to
192  struct yjit_block_version *block;
193 
194  // Positions where the generated code starts and ends
195  uint8_t *start_addr;
196  uint8_t *end_addr;
197 
198  // Context right after the branch instruction
199  // Unused for now.
200  // ctx_t src_ctx;
201 
202  // Branch target blocks and their contexts
203  blockid_t targets[2];
204  ctx_t target_ctxs[2];
205  struct yjit_block_version *blocks[2];
206 
207  // Jump target addresses
208  uint8_t *dst_addrs[2];
209 
210  // Branch code generation function
211  branchgen_fn gen_fn;
212 
213  // Shape of the branch
214  branch_shape_t shape : 2;
215 
216 } branch_t;
217 
218 // In case this block is invalidated, these two pieces of info
219 // help to remove all pointers to this block in the system.
220 typedef struct {
221  VALUE receiver_klass;
222  VALUE callee_cme;
224 
225 typedef rb_darray(cme_dependency_t) cme_dependency_array_t;
226 
227 typedef rb_darray(branch_t*) branch_array_t;
228 
229 typedef rb_darray(uint32_t) int32_array_t;
230 
236 typedef struct yjit_block_version
237 {
238  // Bytecode sequence (iseq, idx) this is a version of
239  blockid_t blockid;
240 
241  // Context at the start of the block
242  ctx_t ctx;
243 
244  // Positions where the generated code starts and ends
245  uint8_t *start_addr;
246  uint8_t *end_addr;
247 
248  // List of incoming branches (from predecessors)
249  branch_array_t incoming;
250 
251  // List of outgoing branches (to successors)
252  // Note: these are owned by this block version
253  branch_array_t outgoing;
254 
255  // Offsets for GC managed objects in the mainline code block
256  int32_array_t gc_object_offsets;
257 
258  // CME dependencies of this block, to help to remove all pointers to this
259  // block in the system.
260  cme_dependency_array_t cme_dependencies;
261 
262  // Code address of an exit for `ctx` and `blockid`. Used for block
263  // invalidation.
264  uint8_t *entry_exit;
265 
266  // Index one past the last instruction in the iseq
267  uint32_t end_idx;
268 
269 } block_t;
270 
271 // Code generation state
272 typedef struct JITState
273 {
274  // Inline and outlined code blocks we are
275  // currently generating code into
276  codeblock_t* cb;
277  codeblock_t* ocb;
278 
279  // Block version being compiled
280  block_t *block;
281 
282  // Instruction sequence this is associated with
283  const rb_iseq_t *iseq;
284 
285  // Index of the current instruction being compiled
286  uint32_t insn_idx;
287 
288  // Opcode for the instruction being compiled
289  int opcode;
290 
291  // PC of the instruction being compiled
292  VALUE *pc;
293 
294  // Side exit to the instruction being compiled. See :side-exit:.
295  uint8_t *side_exit_for_pc;
296 
297  // Execution context when compilation started
298  // This allows us to peek at run-time values
300 
301  // Whether we need to record the code address at
302  // the end of this bytecode instruction for global invalidation
303  bool record_boundary_patch_point;
304 
305 } jitstate_t;
306 
307 #endif // #ifndef YJIT_CORE_H
VALUE type(ANYARGS)
ANYARGS-ed function type.
Definition: cxxanyargs.hpp:56
Basic block version Represents a portion of an iseq compiled with a given context Note: care must be ...
Definition: yjit_core.h:237
Store info about an outgoing branch in a code segment Note: care must be taken to minimize the size o...
Definition: yjit_core.h:190
Code generation context Contains information we can use to optimize code.
Definition: yjit_core.h:134
uintptr_t VALUE
Type that represents a Ruby object.
Definition: value.h:40