Ruby 3.1.3p185 (2022-11-24 revision 1a6b16756e0ba6b95ab71a441357ed5484e33498)
yjit_asm.h
1#ifndef YJIT_ASM_H
2#define YJIT_ASM_H 1
3
4#include <stdint.h>
5#include <stddef.h>
6#include <stdbool.h>
7
8// Maximum number of labels to link
9#define MAX_LABELS 32
10
11// Maximum number of label references
12#define MAX_LABEL_REFS 32
13
14// Reference to an ASM label
15typedef struct LabelRef
16{
17 // Position in the code block where the label reference exists
18 uint32_t pos;
19
20 // Label which this refers to
21 uint32_t label_idx;
22
24
25// Block of executable memory into which instructions can be written
26typedef struct CodeBlock
27{
28 // Memory block
29 // Users are advised to not use this directly.
30 uint8_t *mem_block_;
31
32 // Memory block size
33 uint32_t mem_size;
34
35 // Current writing position
36 uint32_t write_pos;
37
38 // Table of registered label addresses
39 uint32_t label_addrs[MAX_LABELS];
40
41 // Table of registered label names
42 // Note that these should be constant strings only
43 const char *label_names[MAX_LABELS];
44
45 // References to labels
46 labelref_t label_refs[MAX_LABEL_REFS];
47
48 // Number of labels registeered
49 uint32_t num_labels;
50
51 // Number of references to labels
52 uint32_t num_refs;
53
54
55 // Keep track of the current aligned write position.
56 // Used for changing protection when writing to the JIT buffer
57 uint32_t current_aligned_write_pos;
58
59 // Set if the assembler is unable to output some instructions,
60 // for example, when there is not enough space or when a jump
61 // target is too far away.
62 bool dropped_bytes;
63
64 // Flag to enable or disable comments
65 bool has_asm;
66
67
69
70// 1 is not aligned so this won't match any pages
71#define ALIGNED_WRITE_POSITION_NONE 1
72
73enum OpndType
74{
75 OPND_NONE,
76 OPND_REG,
77 OPND_IMM,
78 OPND_MEM
79};
80
81enum RegType
82{
83 REG_GP,
84 REG_FP,
85 REG_XMM,
86 REG_IP
87};
88
89typedef struct X86Reg
90{
91 // Register type
92 uint8_t reg_type;
93
94 // Register index number
95 uint8_t reg_no;
96
97} x86reg_t;
98
99typedef struct X86Mem
100{
102 uint8_t base_reg_no;
103
105 uint8_t idx_reg_no;
106
108 uint8_t scale_exp;
109
112
113 // TODO: should this be here, or should we have an extra operand type?
116
118 int32_t disp;
119
120} x86mem_t;
121
122typedef struct X86Opnd
123{
124 // Operand type
125 uint8_t type;
126
127 // Size in bits
128 uint16_t num_bits;
129
130 union
131 {
132 // Register operand
133 x86reg_t reg;
134
135 // Memory operand
136 x86mem_t mem;
137
138 // Signed immediate value
139 int64_t imm;
140
141 // Unsigned immediate value
142 uint64_t unsig_imm;
143 } as;
144
145} x86opnd_t;
146
147// Dummy none/null operand
148static const x86opnd_t NO_OPND = { OPND_NONE, 0, .as.imm = 0 };
149
150// Instruction pointer
151static const x86opnd_t RIP = { OPND_REG, 64, .as.reg = { REG_IP, 5 }};
152
153// 64-bit GP registers
154static const x86opnd_t RAX = { OPND_REG, 64, .as.reg = { REG_GP, 0 }};
155static const x86opnd_t RCX = { OPND_REG, 64, .as.reg = { REG_GP, 1 }};
156static const x86opnd_t RDX = { OPND_REG, 64, .as.reg = { REG_GP, 2 }};
157static const x86opnd_t RBX = { OPND_REG, 64, .as.reg = { REG_GP, 3 }};
158static const x86opnd_t RSP = { OPND_REG, 64, .as.reg = { REG_GP, 4 }};
159static const x86opnd_t RBP = { OPND_REG, 64, .as.reg = { REG_GP, 5 }};
160static const x86opnd_t RSI = { OPND_REG, 64, .as.reg = { REG_GP, 6 }};
161static const x86opnd_t RDI = { OPND_REG, 64, .as.reg = { REG_GP, 7 }};
162static const x86opnd_t R8 = { OPND_REG, 64, .as.reg = { REG_GP, 8 }};
163static const x86opnd_t R9 = { OPND_REG, 64, .as.reg = { REG_GP, 9 }};
164static const x86opnd_t R10 = { OPND_REG, 64, .as.reg = { REG_GP, 10 }};
165static const x86opnd_t R11 = { OPND_REG, 64, .as.reg = { REG_GP, 11 }};
166static const x86opnd_t R12 = { OPND_REG, 64, .as.reg = { REG_GP, 12 }};
167static const x86opnd_t R13 = { OPND_REG, 64, .as.reg = { REG_GP, 13 }};
168static const x86opnd_t R14 = { OPND_REG, 64, .as.reg = { REG_GP, 14 }};
169static const x86opnd_t R15 = { OPND_REG, 64, .as.reg = { REG_GP, 15 }};
170
171// 32-bit GP registers
172static const x86opnd_t EAX = { OPND_REG, 32, .as.reg = { REG_GP, 0 }};
173static const x86opnd_t ECX = { OPND_REG, 32, .as.reg = { REG_GP, 1 }};
174static const x86opnd_t EDX = { OPND_REG, 32, .as.reg = { REG_GP, 2 }};
175static const x86opnd_t EBX = { OPND_REG, 32, .as.reg = { REG_GP, 3 }};
176static const x86opnd_t ESP = { OPND_REG, 32, .as.reg = { REG_GP, 4 }};
177static const x86opnd_t EBP = { OPND_REG, 32, .as.reg = { REG_GP, 5 }};
178static const x86opnd_t ESI = { OPND_REG, 32, .as.reg = { REG_GP, 6 }};
179static const x86opnd_t EDI = { OPND_REG, 32, .as.reg = { REG_GP, 7 }};
180static const x86opnd_t R8D = { OPND_REG, 32, .as.reg = { REG_GP, 8 }};
181static const x86opnd_t R9D = { OPND_REG, 32, .as.reg = { REG_GP, 9 }};
182static const x86opnd_t R10D = { OPND_REG, 32, .as.reg = { REG_GP, 10 }};
183static const x86opnd_t R11D = { OPND_REG, 32, .as.reg = { REG_GP, 11 }};
184static const x86opnd_t R12D = { OPND_REG, 32, .as.reg = { REG_GP, 12 }};
185static const x86opnd_t R13D = { OPND_REG, 32, .as.reg = { REG_GP, 13 }};
186static const x86opnd_t R14D = { OPND_REG, 32, .as.reg = { REG_GP, 14 }};
187static const x86opnd_t R15D = { OPND_REG, 32, .as.reg = { REG_GP, 15 }};
188
189// 16-bit GP registers
190static const x86opnd_t AX = { OPND_REG, 16, .as.reg = { REG_GP, 0 }};
191static const x86opnd_t CX = { OPND_REG, 16, .as.reg = { REG_GP, 1 }};
192static const x86opnd_t DX = { OPND_REG, 16, .as.reg = { REG_GP, 2 }};
193static const x86opnd_t BX = { OPND_REG, 16, .as.reg = { REG_GP, 3 }};
194static const x86opnd_t SP = { OPND_REG, 16, .as.reg = { REG_GP, 4 }};
195static const x86opnd_t BP = { OPND_REG, 16, .as.reg = { REG_GP, 5 }};
196static const x86opnd_t SI = { OPND_REG, 16, .as.reg = { REG_GP, 6 }};
197static const x86opnd_t DI = { OPND_REG, 16, .as.reg = { REG_GP, 7 }};
198static const x86opnd_t R8W = { OPND_REG, 16, .as.reg = { REG_GP, 8 }};
199static const x86opnd_t R9W = { OPND_REG, 16, .as.reg = { REG_GP, 9 }};
200static const x86opnd_t R10W = { OPND_REG, 16, .as.reg = { REG_GP, 10 }};
201static const x86opnd_t R11W = { OPND_REG, 16, .as.reg = { REG_GP, 11 }};
202static const x86opnd_t R12W = { OPND_REG, 16, .as.reg = { REG_GP, 12 }};
203static const x86opnd_t R13W = { OPND_REG, 16, .as.reg = { REG_GP, 13 }};
204static const x86opnd_t R14W = { OPND_REG, 16, .as.reg = { REG_GP, 14 }};
205static const x86opnd_t R15W = { OPND_REG, 16, .as.reg = { REG_GP, 15 }};
206
207// 8-bit GP registers
208static const x86opnd_t AL = { OPND_REG, 8, .as.reg = { REG_GP, 0 }};
209static const x86opnd_t CL = { OPND_REG, 8, .as.reg = { REG_GP, 1 }};
210static const x86opnd_t DL = { OPND_REG, 8, .as.reg = { REG_GP, 2 }};
211static const x86opnd_t BL = { OPND_REG, 8, .as.reg = { REG_GP, 3 }};
212static const x86opnd_t SPL = { OPND_REG, 8, .as.reg = { REG_GP, 4 }};
213static const x86opnd_t BPL = { OPND_REG, 8, .as.reg = { REG_GP, 5 }};
214static const x86opnd_t SIL = { OPND_REG, 8, .as.reg = { REG_GP, 6 }};
215static const x86opnd_t DIL = { OPND_REG, 8, .as.reg = { REG_GP, 7 }};
216static const x86opnd_t R8B = { OPND_REG, 8, .as.reg = { REG_GP, 8 }};
217static const x86opnd_t R9B = { OPND_REG, 8, .as.reg = { REG_GP, 9 }};
218static const x86opnd_t R10B = { OPND_REG, 8, .as.reg = { REG_GP, 10 }};
219static const x86opnd_t R11B = { OPND_REG, 8, .as.reg = { REG_GP, 11 }};
220static const x86opnd_t R12B = { OPND_REG, 8, .as.reg = { REG_GP, 12 }};
221static const x86opnd_t R13B = { OPND_REG, 8, .as.reg = { REG_GP, 13 }};
222static const x86opnd_t R14B = { OPND_REG, 8, .as.reg = { REG_GP, 14 }};
223static const x86opnd_t R15B = { OPND_REG, 8, .as.reg = { REG_GP, 15 }};
224
225// C argument registers
226#define NUM_C_ARG_REGS 6
227#define C_ARG_REGS ( (x86opnd_t[]){ RDI, RSI, RDX, RCX, R8, R9 } )
228
229// Compute the number of bits needed to store a signed or unsigned value
230static inline uint32_t sig_imm_size(int64_t imm);
231static inline uint32_t unsig_imm_size(uint64_t imm);
232
233// Memory operand with base register and displacement/offset
234static inline x86opnd_t mem_opnd(uint32_t num_bits, x86opnd_t base_reg, int32_t disp);
235
236// Scale-index-base memory operand
237static inline x86opnd_t mem_opnd_sib(uint32_t num_bits, x86opnd_t base_reg, x86opnd_t index_reg, int32_t scale, int32_t disp);
238
239// Immediate number operand
240static inline x86opnd_t imm_opnd(int64_t val);
241
242// Constant pointer operand
243static inline x86opnd_t const_ptr_opnd(const void *ptr);
244
245// Struct member operand
246#define member_opnd(base_reg, struct_type, member_name) mem_opnd( \
247 8 * sizeof(((struct_type*)0)->member_name), \
248 base_reg, \
249 offsetof(struct_type, member_name) \
250)
251
252// Struct member operand with an array index
253#define member_opnd_idx(base_reg, struct_type, member_name, idx) mem_opnd( \
254 8 * sizeof(((struct_type*)0)->member_name[0]), \
255 base_reg, \
256 (offsetof(struct_type, member_name) + \
257 sizeof(((struct_type*)0)->member_name[0]) * idx) \
258)
259
260// Allocate executable memory
261static uint8_t *alloc_exec_mem(uint32_t mem_size);
262
263// Code block functions
264static inline void cb_init(codeblock_t *cb, uint8_t *mem_block, uint32_t mem_size);
265static inline void cb_align_pos(codeblock_t *cb, uint32_t multiple);
266static inline void cb_set_pos(codeblock_t *cb, uint32_t pos);
267static inline void cb_set_write_ptr(codeblock_t *cb, uint8_t *code_ptr);
268static inline uint8_t *cb_get_ptr(const codeblock_t *cb, uint32_t index);
269static inline uint8_t *cb_get_write_ptr(const codeblock_t *cb);
270static inline void cb_write_byte(codeblock_t *cb, uint8_t byte);
271static inline void cb_write_bytes(codeblock_t *cb, uint32_t num_bytes, ...);
272static inline void cb_write_int(codeblock_t *cb, uint64_t val, uint32_t num_bits);
273static inline uint32_t cb_new_label(codeblock_t *cb, const char *name);
274static inline void cb_write_label(codeblock_t *cb, uint32_t label_idx);
275static inline void cb_label_ref(codeblock_t *cb, uint32_t label_idx);
276static inline void cb_link_labels(codeblock_t *cb);
277static inline void cb_mark_all_writeable(codeblock_t *cb);
278static inline void cb_mark_position_writeable(codeblock_t *cb, uint32_t write_pos);
279static inline void cb_mark_all_executable(codeblock_t *cb);
280
281// Encode individual instructions into a code block
282static inline void add(codeblock_t *cb, x86opnd_t opnd0, x86opnd_t opnd1);
283static inline void and(codeblock_t *cb, x86opnd_t opnd0, x86opnd_t opnd1);
284static inline void call_ptr(codeblock_t *cb, x86opnd_t scratch_reg, uint8_t *dst_ptr);
285static inline void call_label(codeblock_t *cb, uint32_t label_idx);
286static inline void call(codeblock_t *cb, x86opnd_t opnd);
287static inline void cmova(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
288static inline void cmovae(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
289static inline void cmovb(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
290static inline void cmovbe(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
291static inline void cmovc(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
292static inline void cmove(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
293static inline void cmovg(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
294static inline void cmovge(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
295static inline void cmovl(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
296static inline void cmovle(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
297static inline void cmovna(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
298static inline void cmovnae(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
299static inline void cmovnb(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
300static inline void cmovnbe(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
301static inline void cmovnc(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
302static inline void cmovne(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
303static inline void cmovng(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
304static inline void cmovnge(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
305static inline void cmovnl(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
306static inline void cmovnle(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
307static inline void cmovno(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
308static inline void cmovnp(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
309static inline void cmovns(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
310static inline void cmovnz(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
311static inline void cmovo(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
312static inline void cmovp(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
313static inline void cmovpe(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
314static inline void cmovpo(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
315static inline void cmovs(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
316static inline void cmovz(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
317static inline void cmp(codeblock_t *cb, x86opnd_t opnd0, x86opnd_t opnd1);
318static inline void cdq(codeblock_t *cb);
319static inline void cqo(codeblock_t *cb);
320static inline void int3(codeblock_t *cb);
321static inline void ja_label(codeblock_t *cb, uint32_t label_idx);
322static inline void jae_label(codeblock_t *cb, uint32_t label_idx);
323static inline void jb_label(codeblock_t *cb, uint32_t label_idx);
324static inline void jbe_label(codeblock_t *cb, uint32_t label_idx);
325static inline void jc_label(codeblock_t *cb, uint32_t label_idx);
326static inline void je_label(codeblock_t *cb, uint32_t label_idx);
327static inline void jg_label(codeblock_t *cb, uint32_t label_idx);
328static inline void jge_label(codeblock_t *cb, uint32_t label_idx);
329static inline void jl_label(codeblock_t *cb, uint32_t label_idx);
330static inline void jle_label(codeblock_t *cb, uint32_t label_idx);
331static inline void jna_label(codeblock_t *cb, uint32_t label_idx);
332static inline void jnae_label(codeblock_t *cb, uint32_t label_idx);
333static inline void jnb_label(codeblock_t *cb, uint32_t label_idx);
334static inline void jnbe_label(codeblock_t *cb, uint32_t label_idx);
335static inline void jnc_label(codeblock_t *cb, uint32_t label_idx);
336static inline void jne_label(codeblock_t *cb, uint32_t label_idx);
337static inline void jng_label(codeblock_t *cb, uint32_t label_idx);
338static inline void jnge_label(codeblock_t *cb, uint32_t label_idx);
339static inline void jnl_label(codeblock_t *cb, uint32_t label_idx);
340static inline void jnle_label(codeblock_t *cb, uint32_t label_idx);
341static inline void jno_label(codeblock_t *cb, uint32_t label_idx);
342static inline void jnp_label(codeblock_t *cb, uint32_t label_idx);
343static inline void jns_label(codeblock_t *cb, uint32_t label_idx);
344static inline void jnz_label(codeblock_t *cb, uint32_t label_idx);
345static inline void jo_label(codeblock_t *cb, uint32_t label_idx);
346static inline void jp_label(codeblock_t *cb, uint32_t label_idx);
347static inline void jpe_label(codeblock_t *cb, uint32_t label_idx);
348static inline void jpo_label(codeblock_t *cb, uint32_t label_idx);
349static inline void js_label(codeblock_t *cb, uint32_t label_idx);
350static inline void jz_label(codeblock_t *cb, uint32_t label_idx);
351static inline void ja_ptr(codeblock_t *cb, uint8_t *ptr);
352static inline void jae_ptr(codeblock_t *cb, uint8_t *ptr);
353static inline void jb_ptr(codeblock_t *cb, uint8_t *ptr);
354static inline void jbe_ptr(codeblock_t *cb, uint8_t *ptr);
355static inline void jc_ptr(codeblock_t *cb, uint8_t *ptr);
356static inline void je_ptr(codeblock_t *cb, uint8_t *ptr);
357static inline void jg_ptr(codeblock_t *cb, uint8_t *ptr);
358static inline void jge_ptr(codeblock_t *cb, uint8_t *ptr);
359static inline void jl_ptr(codeblock_t *cb, uint8_t *ptr);
360static inline void jle_ptr(codeblock_t *cb, uint8_t *ptr);
361static inline void jna_ptr(codeblock_t *cb, uint8_t *ptr);
362static inline void jnae_ptr(codeblock_t *cb, uint8_t *ptr);
363static inline void jnb_ptr(codeblock_t *cb, uint8_t *ptr);
364static inline void jnbe_ptr(codeblock_t *cb, uint8_t *ptr);
365static inline void jnc_ptr(codeblock_t *cb, uint8_t *ptr);
366static inline void jne_ptr(codeblock_t *cb, uint8_t *ptr);
367static inline void jng_ptr(codeblock_t *cb, uint8_t *ptr);
368static inline void jnge_ptr(codeblock_t *cb, uint8_t *ptr);
369static inline void jnl_ptr(codeblock_t *cb, uint8_t *ptr);
370static inline void jnle_ptr(codeblock_t *cb, uint8_t *ptr);
371static inline void jno_ptr(codeblock_t *cb, uint8_t *ptr);
372static inline void jnp_ptr(codeblock_t *cb, uint8_t *ptr);
373static inline void jns_ptr(codeblock_t *cb, uint8_t *ptr);
374static inline void jnz_ptr(codeblock_t *cb, uint8_t *ptr);
375static inline void jo_ptr(codeblock_t *cb, uint8_t *ptr);
376static inline void jp_ptr(codeblock_t *cb, uint8_t *ptr);
377static inline void jpe_ptr(codeblock_t *cb, uint8_t *ptr);
378static inline void jpo_ptr(codeblock_t *cb, uint8_t *ptr);
379static inline void js_ptr(codeblock_t *cb, uint8_t *ptr);
380static inline void jz_ptr(codeblock_t *cb, uint8_t *ptr);
381static inline void jmp_label(codeblock_t *cb, uint32_t label_idx);
382static inline void jmp_ptr(codeblock_t *cb, uint8_t *ptr);
383static inline void jmp_rm(codeblock_t *cb, x86opnd_t opnd);
384static inline void jmp32(codeblock_t *cb, int32_t offset);
385static inline void lea(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
386static inline void mov(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
387static inline void movsx(codeblock_t *cb, x86opnd_t dst, x86opnd_t src);
388static inline void neg(codeblock_t *cb, x86opnd_t opnd);
389static inline void nop(codeblock_t *cb, uint32_t length);
390static inline void not(codeblock_t *cb, x86opnd_t opnd);
391static inline void or(codeblock_t *cb, x86opnd_t opnd0, x86opnd_t opnd1);
392static inline void pop(codeblock_t *cb, x86opnd_t reg);
393static inline void popfq(codeblock_t *cb);
394static inline void push(codeblock_t *cb, x86opnd_t opnd);
395static inline void pushfq(codeblock_t *cb);
396static inline void ret(codeblock_t *cb);
397static inline void sal(codeblock_t *cb, x86opnd_t opnd0, x86opnd_t opnd1);
398static inline void sar(codeblock_t *cb, x86opnd_t opnd0, x86opnd_t opnd1);
399static inline void shl(codeblock_t *cb, x86opnd_t opnd0, x86opnd_t opnd1);
400static inline void shr(codeblock_t *cb, x86opnd_t opnd0, x86opnd_t opnd1);
401static inline void sub(codeblock_t *cb, x86opnd_t opnd0, x86opnd_t opnd1);
402static inline void test(codeblock_t *cb, x86opnd_t rm_opnd, x86opnd_t test_opnd);
403static inline void ud2(codeblock_t *cb);
404static inline void xchg(codeblock_t *cb, x86opnd_t rm_opnd, x86opnd_t r_opnd);
405static inline void xor(codeblock_t *cb, x86opnd_t opnd0, x86opnd_t opnd1);
406static inline void cb_write_lock_prefix(codeblock_t *cb);
407
408#endif
C99 shim for <stdbool.h>
uint8_t base_reg_no
Base register number.
Definition: yjit_asm.h:102
bool has_idx
Has index register flag.
Definition: yjit_asm.h:111
bool is_iprel
IP-relative addressing flag.
Definition: yjit_asm.h:115
uint8_t scale_exp
SIB scale exponent value (power of two, two bits)
Definition: yjit_asm.h:108
int32_t disp
Constant displacement from the base, not scaled.
Definition: yjit_asm.h:118
uint8_t idx_reg_no
Index register number.
Definition: yjit_asm.h:105