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)
120 #define R_UPCALL R_R15
121 #define R_TIMESTAMP R_R14
122 #define R_OFFSET_IMM R_R10
123 #define R_CONST_IMM R_R11
125 #define R_OFFSET_IMM 255 /* this should not be used */
126 #define R_CONST_IMM 255
129 #if defined(ARCH_X86_64) && defined(ARCH_X86_WIN_ABI)
130 #define R_SCRATCH_1 R_AX
131 #define R_SCRATCH_2 R_DX
132 #define R_SCRATCH_3 R_CX
133 #define R_SCRATCH_4 R_R8
134 #define R_SAVED_1 R_SI
135 #define R_SAVED_2 R_DI
136 #elif defined(ARCH_X86_32)
137 #define R_SCRATCH_1 R_AX
138 #define R_SCRATCH_2 R_DX
139 #define R_SCRATCH_3 R_CX
140 #define R_SCRATCH_4 R_SI
141 #define R_SAVED_1 R_BP
142 #define R_SAVED_2 R_DI
144 #define R_SCRATCH_1 R_AX
145 #define R_SCRATCH_2 R_DX
146 #define R_SCRATCH_3 R_CX
147 #define R_SCRATCH_4 R_SI
148 #define R_SAVED_1 R_BP
149 #define R_SAVED_2 R_R12
152 #define FR_SCRATCH_1 R_XMM0
153 #define FR_SCRATCH_2 R_XMM1
155 #if defined(ARCH_X86_32)
160 #elif defined(ARCH_X86_WIN_ABI)
173 #if defined(ARCH_X86_32)
174 #define ARG_SPACE 0x1c /* must be 0xc modulo 0x10 */
175 #define ARG_OFFSET 0x14
176 #elif defined(ARCH_X86_WIN_ABI)
177 #define ARG_SPACE 0x28
180 #define SUPPORTED_FP (cpu_test_feature(CPU_FEATURE_sse) * 0x2 + cpu_test_feature(CPU_FEATURE_sse2) * 0x4)
181 #define SUPPORTED_FP_X87 0xe
182 #define SUPPORTED_FP_HALF_CVT (cpu_test_feature(CPU_FEATURE_f16c) * 0x1)
184 static bool reg_is_fp(unsigned reg)
186 return reg >= 0x20 && reg < 0x40;
189 static bool attr_w imm_is_8bit(int64_t imm)
191 return imm >= -0x80 && imm < 0x80;
194 static bool attr_w imm_is_32bit(int64_t attr_unused imm)
199 return imm >= -0x80000000LL && imm < 0x80000000LL;
203 static bool attr_w gen_load_constant(struct codegen_context *ctx, unsigned reg, uint64_t c)
205 if (OP_SIZE_NATIVE == OP_SIZE_4)
207 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
214 static bool attr_w gen_address(struct codegen_context *ctx, unsigned base, int64_t imm, unsigned purpose, unsigned attr_unused size)
216 ctx->offset_imm = imm;
217 ctx->offset_reg = false;
218 ctx->base_reg = base;
220 case IMM_PURPOSE_LDR_OFFSET:
221 case IMM_PURPOSE_LDR_SX_OFFSET:
222 case IMM_PURPOSE_STR_OFFSET:
223 case IMM_PURPOSE_VLDR_VSTR_OFFSET:
224 case IMM_PURPOSE_MVI_CLI_OFFSET:
227 internal(file_line, "gen_address: invalid purpose %d", purpose);
230 if (likely(imm_is_32bit(imm)))
232 gen_insn(INSN_MOV, OP_SIZE_8, 0, 0);
233 gen_one(R_OFFSET_IMM);
236 ctx->offset_reg = true;
241 static bool is_direct_const(int64_t attr_unused imm, unsigned attr_unused purpose, unsigned attr_unused size)
246 return imm_is_32bit(imm);
250 static bool attr_w gen_imm(struct codegen_context *ctx, int64_t imm, unsigned purpose, unsigned size)
253 if (size == OP_SIZE_1 && (unlikely(imm < -0x80LL) || unlikely(imm >= 0x80LL)))
254 internal(file_line, "invalid imm for size 1: %016llx", (long long)imm);
255 if (size == OP_SIZE_2 && (unlikely(imm < -0x8000LL) || unlikely(imm >= 0x8000LL)))
256 internal(file_line, "invalid imm for size 2 : %016llx", (long long)imm);
257 if (size == OP_SIZE_4 && (unlikely(imm < -0x80000000LL) || unlikely(imm >= 0x80000000LL)))
258 internal(file_line, "invalid imm for size 3: %016llx", (long long)imm);
260 if (is_direct_const(imm, purpose, size)) {
261 ctx->const_imm = imm;
262 ctx->const_reg = false;
264 gen_insn(INSN_MOV, OP_SIZE_8, 0, 0);
265 gen_one(R_CONST_IMM);
268 ctx->const_reg = true;
273 static bool attr_w gen_entry(struct codegen_context *ctx)
275 #if defined(ARCH_X86_32)
276 gen_insn(INSN_PUSH, OP_SIZE_4, 0, 0);
279 gen_insn(INSN_PUSH, OP_SIZE_4, 0, 0);
282 gen_insn(INSN_PUSH, OP_SIZE_4, 0, 0);
285 gen_insn(INSN_PUSH, OP_SIZE_4, 0, 0);
288 gen_insn(INSN_ALU, OP_SIZE_4, ALU_SUB, 1);
292 gen_eight(ARG_SPACE);
294 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
296 gen_one(ARG_ADDRESS_1);
298 gen_eight(ARG_SPACE + ARG_OFFSET);
300 gen_insn(INSN_PUSH, OP_SIZE_8, 0, 0);
303 gen_insn(INSN_PUSH, OP_SIZE_8, 0, 0);
306 gen_insn(INSN_PUSH, OP_SIZE_8, 0, 0);
309 gen_insn(INSN_PUSH, OP_SIZE_8, 0, 0);
310 gen_one(R_TIMESTAMP);
312 gen_insn(INSN_PUSH, OP_SIZE_8, 0, 0);
314 #if defined(ARCH_X86_WIN_ABI)
315 gen_insn(INSN_PUSH, OP_SIZE_8, 0, 0);
318 gen_insn(INSN_ALU, OP_SIZE_8, ALU_SUB, 1);
322 gen_eight(ARG_SPACE);
324 gen_insn(INSN_MOV, OP_SIZE_ADDRESS, 0, 0);
328 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
329 gen_one(R_TIMESTAMP);
332 gen_insn(INSN_MOV, OP_SIZE_ADDRESS, 0, 0);
336 gen_insn(INSN_MOV, OP_SIZE_ADDRESS, 0, 0);
340 gen_insn(INSN_MOV, OP_SIZE_ADDRESS, 0, 0);
344 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
345 gen_one(R_TIMESTAMP);
352 static bool attr_w gen_escape_arg(struct codegen_context *ctx, ip_t ip, uint32_t escape_label)
354 #if defined(ARCH_X86_32) || defined(ARCH_X86_64)
355 gen_insn(INSN_MOV, OP_SIZE_ADDRESS, 0, 0);
360 gen_insn(INSN_MOV, OP_SIZE_8, 0, 0);
363 gen_eight((uint64_t)ip << 32);
365 gen_insn(INSN_JMP, 0, 0, 0);
366 gen_four(escape_label);
371 static bool attr_w gen_escape(struct codegen_context *ctx)
373 #if defined(ARCH_X86_32)
374 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
378 gen_insn(INSN_ALU, OP_SIZE_4, ALU_ADD, 1);
382 gen_eight(ARG_SPACE);
384 gen_insn(INSN_POP, OP_SIZE_4, 0, 0);
387 gen_insn(INSN_POP, OP_SIZE_4, 0, 0);
390 gen_insn(INSN_POP, OP_SIZE_4, 0, 0);
393 gen_insn(INSN_POP, OP_SIZE_4, 0, 0);
396 gen_insn(INSN_RET, 0, 0, 0);
397 #elif defined(ARCH_X86_WIN_ABI)
398 gen_insn(INSN_ALU, OP_SIZE_8, ALU_ADD, 1);
402 gen_eight(ARG_SPACE);
404 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
407 gen_insn(INSN_MOV, OP_SIZE_8, 0, 0);
408 gen_one(ARG_ADDRESS_1);
413 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
414 gen_one(ARG_ADDRESS_1);
419 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
422 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
423 gen_one(R_TIMESTAMP);
425 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
428 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
431 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
434 gen_insn(INSN_RET, 0, 0, 0);
436 #if defined(ARCH_X86_X32)
437 gen_insn(INSN_ALU, OP_SIZE_8, ALU_ADD, 1);
442 gen_insn(INSN_MOV, OP_SIZE_ADDRESS, 0, 0);
446 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
449 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
450 gen_one(R_TIMESTAMP);
452 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
455 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
458 gen_insn(INSN_POP, OP_SIZE_8, 0, 0);
461 gen_insn(INSN_RET, 0, 0, 0);
466 static bool attr_w gen_upcall_argument(struct codegen_context attr_unused *ctx, unsigned attr_unused arg)
468 #if defined(ARCH_X86_32)
469 ajla_assert_lo(arg * 4 < ARG_SPACE, (file_line, "gen_upcall_argument: argument %u", arg));
470 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
471 gen_one(ARG_ADDRESS_1);
479 static bool attr_w gen_upcall(struct codegen_context *ctx, unsigned offset, unsigned attr_unused n_args)
481 #if defined(ARCH_X86_32)
482 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
484 gen_one(ARG_ADDRESS_1);
486 gen_eight(ARG_SPACE + ARG_OFFSET + 4);
488 gen_insn(INSN_CALL_INDIRECT, OP_SIZE_4, 0, 0);
489 gen_one(ARG_ADDRESS_1);
492 #elif defined(ARCH_X86_X32)
493 gen_insn(INSN_MOV, OP_SIZE_ADDRESS, 0, 0);
495 gen_one(ARG_ADDRESS_1);
499 gen_insn(INSN_CALL_INDIRECT, OP_SIZE_8, 0, 0);
502 gen_insn(INSN_CALL_INDIRECT, OP_SIZE_8, 0, 0);
503 gen_one(ARG_ADDRESS_1);
510 static bool attr_w gen_timestamp_test(struct codegen_context *ctx, uint32_t label_id)
512 #if defined(ARCH_X86_32)
513 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
515 gen_one(ARG_ADDRESS_1);
517 gen_eight(ARG_SPACE + ARG_OFFSET + 4);
519 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
521 gen_one(ARG_ADDRESS_1);
523 gen_eight(ARG_SPACE + ARG_OFFSET + 8);
525 gen_insn(INSN_CMP, OP_SIZE_4, 0, 1);
527 gen_one(ARG_ADDRESS_1);
529 gen_eight(offsetof(struct cg_upcall_vector_s, ts));
531 gen_insn(INSN_CMP, OP_SIZE_4, 0, 1);
532 gen_one(R_TIMESTAMP);
533 gen_one(ARG_ADDRESS_1);
535 gen_eight(offsetof(struct cg_upcall_vector_s, ts));
537 gen_insn(INSN_JMP_COND, OP_SIZE_4, COND_E, 0);