2 * Copyright (C) 2024 Mikulas Patocka
4 * This file is part of Ajla.
6 * Ajla is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
11 * Ajla is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along with
16 * Ajla. If not, see <https://www.gnu.org/licenses/>.
20 #define OP_SIZE_NATIVE OP_SIZE_4
22 #define OP_SIZE_NATIVE OP_SIZE_8
26 #define OP_SIZE_ADDRESS OP_SIZE_4
28 #define OP_SIZE_ADDRESS OP_SIZE_8
31 #define JMP_LIMIT JMP_LONG
33 #define UNALIGNED_TRAP 0
35 #define ALU_WRITES_FLAGS(alu, im) ((alu) != ALU_ADD ? 3 : 0)
36 #define ALU1_WRITES_FLAGS(alu) ((alu) == ALU1_INC || (alu) == ALU1_DEC ? 1 : (alu) == ALU1_NOT || (alu) == ALU1_BSWAP ? 0 : 3)
37 #define ROT_WRITES_FLAGS(alu) 1
38 #define COND_IS_LOGICAL(cond) 0
40 #define ARCH_PARTIAL_ALU(size) ((size) <= OP_SIZE_2)
41 #define ARCH_IS_3ADDRESS 0
42 #define ARCH_HAS_FLAGS 1
43 #define ARCH_PREFERS_SX(size) 0
44 #define ARCH_HAS_BWX 1
45 #define ARCH_HAS_MUL 1
46 #define ARCH_HAS_DIV 1
47 #define ARCH_HAS_ANDN 0
48 #define ARCH_HAS_BTX(btx, size, cnst) ((btx) != BTX_BTEXT && (size) >= OP_SIZE_2)
49 #define ARCH_HAS_SHIFTED_ADD(bits) ((bits) <= 3)
50 #define ARCH_SHIFT_SIZE OP_SIZE_4
51 #define ARCH_NEEDS_BARRIER 0
53 #define i_size(size) (size)
54 #define i_size_rot(size) (size)
115 #define R_IS_GPR(r) ((r) < 16)
116 #define R_IS_XMM(r) ((r) >= R_XMM0 && (r) <= R_XMM31)
118 /*#define TIMESTAMP_IN_REGISTER*/
121 #define R_UPCALL R_R15
122 #ifdef TIMESTAMP_IN_REGISTER
123 #define R_TIMESTAMP R_R14
125 #define R_CONST_IMM R_R11
127 #define R_CONST_IMM 255 /* this should not be used */
129 #define R_OFFSET_IMM 255 /* this should not be used */
131 #if defined(ARCH_X86_32)
133 #define R_SCRATCH_1 R_AX
134 #define R_SCRATCH_2 R_DX
135 #define R_SCRATCH_3 R_CX
136 #define R_SCRATCH_4 R_SAVED_2
137 #define R_SAVED_1 R_SI
138 #define R_SAVED_2 R_DI
139 #elif defined(ARCH_X86_WIN_ABI)
141 #define R_SCRATCH_1 R_AX
142 #define R_SCRATCH_2 R_DX
143 #define R_SCRATCH_3 R_CX
144 #define R_SCRATCH_4 R_SAVED_2
145 #define R_SAVED_1 R_SI
146 #define R_SAVED_2 R_DI
149 #define R_SCRATCH_1 R_AX
150 #define R_SCRATCH_2 R_DX
151 #define R_SCRATCH_3 R_CX
152 #define R_SCRATCH_4 R_SAVED_2
153 #define R_SAVED_1 R_BP
154 #define R_SAVED_2 R_R12
157 #define FR_SCRATCH_1 R_XMM0
158 #define FR_SCRATCH_2 R_XMM1
160 #if defined(ARCH_X86_32)
165 #elif defined(ARCH_X86_WIN_ABI)
178 #if defined(ARCH_X86_32)
179 #define ARG_SPACE 0x1c /* must be 0xc modulo 0x10 */
180 #define ARG_OFFSET 0x14
181 #elif defined(ARCH_X86_WIN_ABI) && !defined(TIMESTAMP_IN_REGISTER)
182 #define ARG_SPACE 0x28 /* must be 0x8 modulo 0x10 */
183 #define ARG_OFFSET 0xa0
184 #elif defined(ARCH_X86_WIN_ABI)
185 #define ARG_SPACE 0x20 /* must be 0x0 modulo 0x10 */
186 #define ARG_OFFSET 0x90
189 #define SUPPORTED_FP (cpu_test_feature(CPU_FEATURE_sse) * 0x2 + cpu_test_feature(CPU_FEATURE_sse2) * 0x4)
190 #define SUPPORTED_FP_X87 0xe
191 #define SUPPORTED_FP_HALF_CVT (cpu_test_feature(CPU_FEATURE_f16c) * 0x1)
193 static bool reg_is_fp(unsigned reg)
195 return reg >= 0x20 && reg < 0x40;
198 #if defined(ARCH_X86_32)
200 static const uint8_t regs_saved[] = { R_BX };
201 static const uint8_t regs_volatile[] = { 0 };
202 #define n_regs_volatile 0U
203 static const uint8_t fp_saved[] = { 0 };
204 #define n_fp_saved 0U
205 static const uint8_t fp_volatile[] = { R_XMM2, R_XMM3, R_XMM4, R_XMM5, R_XMM6, R_XMM7 };
206 #define reg_is_saved(r) ((r) == R_BX)
208 #elif defined(ARCH_X86_WIN_ABI)
210 static const uint8_t regs_saved[] = { R_BX, R_R12, R_R13,
211 #ifndef TIMESTAMP_IN_REGISTER
215 static const uint8_t regs_volatile[] = { R_R8, R_R9, R_R10 };
216 static const uint8_t fp_saved[] = { 0 };
217 #define n_fp_saved 0U
218 static const uint8_t fp_volatile[] = { R_XMM2, R_XMM3, R_XMM4, R_XMM5 };
219 #define reg_is_saved(r) ((r) == R_BX || ((r) >= R_R12 && ((r) <= R_R15)))
223 static const uint8_t regs_saved[] = { R_R13,
224 #ifndef TIMESTAMP_IN_REGISTER
228 static const uint8_t regs_volatile[] = { R_SI, R_DI, R_R8, R_R9, R_R10 };
229 static const uint8_t fp_saved[] = { 0 };
230 #define n_fp_saved 0U
231 static const uint8_t fp_volatile[] = { R_XMM2, R_XMM3, R_XMM4, R_XMM5, R_XMM6, R_XMM7, R_XMM8, R_XMM9, R_XMM10, R_XMM11, R_XMM12, R_XMM13, R_XMM14, R_XMM15 };
232 #define reg_is_saved(r) ((r) == R_R13 || (r) == R_R14)
235 static bool attr_w imm_is_8bit(int64_t imm)
237 return imm >= -0x80 && imm < 0x80;
240 static bool attr_w imm_is_32bit(int64_t attr_unused imm)
245 return imm >= -0x80000000LL && imm < 0x80000000LL;
249 static bool attr_w gen_load_constant(struct codegen_context *ctx, unsigned reg, uint64_t c)
251 if (OP_SIZE_NATIVE == OP_SIZE_4)
253 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
260 static bool attr_w gen_address(struct codegen_context *ctx, unsigned base, int64_t imm, unsigned purpose, unsigned attr_unused size)
262 ctx->offset_imm = imm;
263 ctx->offset_reg = false;
264 ctx->base_reg = base;
266 case IMM_PURPOSE_LDR_OFFSET:
267 case IMM_PURPOSE_LDR_SX_OFFSET:
268 case IMM_PURPOSE_STR_OFFSET:
269 case IMM_PURPOSE_VLDR_VSTR_OFFSET:
270 case IMM_PURPOSE_MVI_CLI_OFFSET:
273 internal(file_line, "gen_address: invalid purpose %d", purpose);
276 if (unlikely(!imm_is_32bit(imm)))
282 static bool is_direct_const(int64_t attr_unused imm, unsigned attr_unused purpose, unsigned attr_unused size)
287 return imm_is_32bit(imm);
291 static bool attr_w gen_imm(struct codegen_context *ctx, int64_t imm, unsigned purpose, unsigned size)
294 if (size == OP_SIZE_1 && (unlikely(imm < -0x80LL) || unlikely(imm >= 0x80LL)))
295 internal(file_line, "invalid imm for size 1: %016llx", (long long)imm);
296 if (size == OP_SIZE_2 && (unlikely(imm < -0x8000LL) || unlikely(imm >= 0x8000LL)))
297 internal(file_line, "invalid imm for size 2 : %016llx", (long long)imm);
298 if (size == OP_SIZE_4 && (unlikely(imm < -0x80000000LL) || unlikely(imm >= 0x80000000LL)))
299 internal(file_line, "invalid imm for size 3: %016llx", (long long)imm);
301 if (is_direct_const(imm, purpose, size)) {
302 ctx->const_imm = imm;
303 ctx->const_reg = false;
305 gen_insn(INSN_MOV, OP_SIZE_8, 0, 0);
306 gen_one(R_CONST_IMM);
309 ctx->const_reg = true;
314 static bool attr_w gen_entry(struct codegen_context *ctx)
316 #if defined(ARCH_X86_32)
317 gen_insn(INSN_PUSH, OP_SIZE_4, 0, 0);
320 gen_insn(INSN_PUSH, OP_SIZE_4, 0, 0);
323 gen_insn(INSN_PUSH, OP_SIZE_4, 0, 0);
326 gen_insn(INSN_PUSH, OP_SIZE_4, 0, 0);
329 gen_insn(INSN_ALU, OP_SIZE_4, ALU_SUB, 1);
333 gen_eight(ARG_SPACE);
335 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
337 gen_one(ARG_ADDRESS_1);
339 gen_eight(ARG_SPACE + ARG_OFFSET);
341 gen_insn(INSN_JMP_INDIRECT, 0, 0, 0);
342 gen_one(ARG_ADDRESS_1);
344 gen_eight(ARG_SPACE + ARG_OFFSET + 12);
345 #elif defined(ARCH_X86_WIN_ABI)
346 gen_insn(INSN_PUSH, OP_SIZE_8, 0, 0);
349 gen_insn(INSN_PUSH, OP_SIZE_8, 0, 0);
352 gen_insn(INSN_PUSH, OP_SIZE_8, 0, 0);
355 gen_insn(INSN_PUSH, OP_SIZE_8, 0, 0);
358 gen_insn(INSN_PUSH, OP_SIZE_8, 0, 0);
361 gen_insn(INSN_PUSH, OP_SIZE_8, 0, 0);
364 gen_insn(INSN_PUSH, OP_SIZE_8, 0, 0);
367 gen_insn(INSN_PUSH, OP_SIZE_8, 0, 0);
370 gen_insn(INSN_PUSH, OP_SIZE_8, 0, 0);
372 #ifndef TIMESTAMP_IN_REGISTER
373 gen_insn(INSN_PUSH, OP_SIZE_8, 0, 0);
376 gen_insn(INSN_ALU, OP_SIZE_8, ALU_SUB, 1);
380 gen_eight(ARG_SPACE);
382 gen_insn(INSN_MOV, OP_SIZE_ADDRESS, 0, 0);
386 gen_insn(INSN_MOV, OP_SIZE_ADDRESS, 0, 0);
389 #ifdef TIMESTAMP_IN_REGISTER
390 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
391 gen_one(R_TIMESTAMP);
394 gen_insn(INSN_JMP_INDIRECT, 0, 0, 0);
395 gen_one(ARG_ADDRESS_1);
397 gen_eight(ARG_OFFSET);
399 gen_insn(INSN_PUSH, OP_SIZE_8, 0, 0);
402 gen_insn(INSN_PUSH, OP_SIZE_8, 0, 0);
405 gen_insn(INSN_PUSH, OP_SIZE_8, 0, 0);
408 gen_insn(INSN_PUSH, OP_SIZE_8, 0, 0);
411 gen_insn(INSN_PUSH, OP_SIZE_8, 0, 0);
414 gen_insn(INSN_PUSH, OP_SIZE_8, 0, 0);
417 gen_insn(INSN_PUSH, OP_SIZE_8, 0, 0);
419 gen_insn(INSN_MOV, OP_SIZE_ADDRESS, 0, 0);
423 gen_insn(INSN_MOV, OP_SIZE_ADDRESS, 0, 0);
426 #ifdef TIMESTAMP_IN_REGISTER
427 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
428 gen_one(R_TIMESTAMP);
431 gen_insn(INSN_JMP_INDIRECT, 0, 0, 0);
437 static bool attr_w gen_escape_arg(struct codegen_context *ctx, ip_t ip, uint32_t escape_label)
439 #if defined(ARCH_X86_32) || defined(ARCH_X86_64)
440 gen_insn(INSN_MOV, OP_SIZE_ADDRESS, 0, 0);
445 gen_insn(INSN_MOV, OP_SIZE_8, 0, 0);
448 gen_eight((uint64_t)ip << 32);
450 gen_insn(INSN_JMP, 0, 0, 0);
451 gen_four(escape_label);
456 static bool attr_w gen_escape(struct codegen_context *ctx)
458 #if defined(ARCH_X86_32)
459 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
463 gen_insn(INSN_ALU, OP_SIZE_4, ALU_ADD, 1);
467 gen_eight(ARG_SPACE);
469 gen_insn(INSN_POP, OP_SIZE_4, 0, 0);
472 gen_insn(INSN_POP, OP_SIZE_4, 0, 0);
475 gen_insn(INSN_POP, OP_SIZE_4, 0, 0);
478 gen_insn(INSN_POP, OP_SIZE_4, 0, 0);
481 gen_insn(INSN_RET, 0, 0, 0);
482 #elif defined(ARCH_X86_WIN_ABI)
483 gen_insn(INSN_ALU, OP_SIZE_8, ALU_ADD, 1);
487 #if defined(TIMESTAMP_IN_REGISTER)
488 gen_eight(ARG_SPACE);
490 gen_eight(ARG_SPACE + 8);
492 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
495 gen_insn(INSN_MOV, OP_SIZE_8, 0, 0);
496 gen_one(ARG_ADDRESS_1);
501 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
502 gen_one(ARG_ADDRESS_1);
507 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
510 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
513 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
516 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
519 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
522 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
525 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
528 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
531 gen_insn(INSN_RET, 0, 0, 0);
533 #if defined(ARCH_X86_X32)
534 gen_insn(INSN_ALU, OP_SIZE_8, ALU_ADD, 1);
539 gen_insn(INSN_MOV, OP_SIZE_ADDRESS, 0, 0);
543 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
546 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
549 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
552 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
555 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
558 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
561 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
564 gen_insn(INSN_RET, 0, 0, 0);
569 static bool attr_w gen_upcall_argument(struct codegen_context attr_unused *ctx, unsigned attr_unused arg)
571 #if defined(ARCH_X86_32)
572 ajla_assert_lo(arg * 4 < ARG_SPACE, (file_line, "gen_upcall_argument: argument %u", arg));
573 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
574 gen_one(ARG_ADDRESS_1);
582 static bool attr_w gen_upcall(struct codegen_context *ctx, unsigned offset, unsigned n_args)
584 #if defined(ARCH_X86_32)
585 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
587 gen_one(ARG_ADDRESS_1);
589 gen_eight(ARG_SPACE + ARG_OFFSET + 4);
591 gen_insn(INSN_CALL_INDIRECT, OP_SIZE_4, 0, 0);
592 gen_one(ARG_ADDRESS_1);
595 #elif defined(ARCH_X86_X32)
596 gen_insn(INSN_MOV, OP_SIZE_ADDRESS, 0, 0);
598 gen_one(ARG_ADDRESS_1);
602 gen_insn(INSN_CALL_INDIRECT, OP_SIZE_8, 0, 0);
605 gen_insn(INSN_CALL_INDIRECT, OP_SIZE_8, 0, 0);
606 gen_one(ARG_ADDRESS_1);
610 g(gen_upcall_end(ctx, n_args));
615 static bool attr_w gen_timestamp_test(struct codegen_context *ctx, uint32_t escape_label)
617 #if defined(ARCH_X86_32)
618 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
620 gen_one(ARG_ADDRESS_1);
622 gen_eight(ARG_SPACE + ARG_OFFSET + 4);
624 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
626 gen_one(ARG_ADDRESS_1);
628 gen_eight(ARG_SPACE + ARG_OFFSET + 8);
630 gen_insn(INSN_CMP, OP_SIZE_4, 0, 1);
632 gen_one(ARG_ADDRESS_1);
634 gen_eight(offsetof(struct cg_upcall_vector_s, ts));
635 #elif defined(TIMESTAMP_IN_REGISTER)
636 gen_insn(INSN_CMP, OP_SIZE_4, 0, 1);
637 gen_one(R_TIMESTAMP);
638 gen_one(ARG_ADDRESS_1);
640 gen_eight(offsetof(struct cg_upcall_vector_s, ts));
642 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
644 gen_one(ARG_ADDRESS_1);
646 #if defined(ARCH_X86_WIN_ABI)
647 gen_eight(ARG_SPACE);
651 gen_insn(INSN_CMP, OP_SIZE_4, 0, 1);
653 gen_one(ARG_ADDRESS_1);
655 gen_eight(offsetof(struct cg_upcall_vector_s, ts));
657 gen_insn(INSN_JMP_COND, OP_SIZE_4, COND_NE, 0);
658 gen_four(escape_label);