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_EXTRA_LONG
33 #define UNALIGNED_TRAP (!MIPS_R6)
35 #define ALU_WRITES_FLAGS(size, alu, is_mem, is_imm, imm) 0
36 #define ALU1_WRITES_FLAGS(alu) 0
37 #define ROT_WRITES_FLAGS(alu, size, im) 0
38 #define COND_IS_LOGICAL(cond) 0
40 #define ARCH_PARTIAL_ALU(size) 0
41 #define ARCH_IS_3ADDRESS(alu, f) 1
42 #define ARCH_IS_3ADDRESS_IMM(alu, f) 1
43 #define ARCH_IS_3ADDRESS_ROT(alu, size) 1
44 #define ARCH_IS_3ADDRESS_ROT_IMM(alu) 1
45 #define ARCH_IS_2ADDRESS(alu) 1
46 #define ARCH_IS_3ADDRESS_FP 1
47 #define ARCH_HAS_JMP_2REGS(cond) ((cond) == COND_E || (cond) == COND_NE || MIPS_R6)
48 #define ARCH_HAS_FLAGS 0
49 #define ARCH_SUPPORTS_TRAPS(size) (OS_SUPPORTS_TRAPS && ((size) == OP_SIZE_4 || (size) == OP_SIZE_8))
50 #define ARCH_TRAP_BEFORE 1
51 #define ARCH_PREFERS_SX(size) 0
52 #define ARCH_HAS_BWX 1
53 #define ARCH_HAS_MUL 1
54 #define ARCH_HAS_DIV 1
55 #define ARCH_HAS_ANDN 0
56 #define ARCH_HAS_SHIFTED_ADD(bits) (MIPS_R6 && (bits) >= 1 && (bits) <= 4)
57 #define ARCH_HAS_BTX(btx, size, cnst) 0
58 #define ARCH_SHIFT_SIZE OP_SIZE_4
59 #define ARCH_BOOL_SIZE OP_SIZE_NATIVE
60 #define ARCH_HAS_FP_GP_MOV 1
61 #define ARCH_NEEDS_BARRIER 0
63 #define i_size(size) OP_SIZE_NATIVE
64 #define i_size_rot(size) maximum(size, OP_SIZE_4)
65 #define i_size_cmp(size) OP_SIZE_NATIVE
68 #define MIPS_LOAD_DELAY_SLOTS 1
69 #define MIPS_HAS_LS_DOUBLE 0
70 #define MIPS_HAS_SQRT 0
71 #define MIPS_HAS_TRUNC 0
73 #define MIPS_LOAD_DELAY_SLOTS 0
74 #define MIPS_HAS_LS_DOUBLE 1
75 #define MIPS_HAS_SQRT 1
76 #define MIPS_HAS_TRUNC 1
80 #define MIPS_R4000_ERRATA 1
81 #define MIPS_FCMP_DELAY_SLOTS 1
82 #define MIPS_HAS_MOVT 0
84 #define MIPS_R4000_ERRATA 0
85 #define MIPS_FCMP_DELAY_SLOTS 0
86 #define MIPS_HAS_MOVT 1
90 #define MIPS_HAS_CLZ 0
91 #define MIPS_HAS_MUL 0
93 #define MIPS_HAS_CLZ 1
94 #define MIPS_HAS_MUL 1
97 #if __mips < 32 || !defined(__mips_isa_rev) || __mips_isa_rev < 2
98 #define MIPS_HAS_ROT 0
100 #define MIPS_HAS_ROT 1
103 #if __mips < 32 || !defined(__mips_isa_rev) || __mips_isa_rev < 6
176 #define R_UPCALL R_S1
178 #define R_SCRATCH_1 R_A0
179 #define R_SCRATCH_2 R_A1
180 #define R_SCRATCH_3 R_A2
181 #define R_SCRATCH_4 R_SAVED_2
182 #define R_SCRATCH_NA_1 R_T0
183 #define R_SCRATCH_NA_2 R_T1
184 #define R_SCRATCH_NA_3 R_T2
186 #define R_SAVED_1 R_S2
187 #define R_SAVED_2 R_S3
197 #define R_OFFSET_IMM R_T3
198 #define R_CONST_IMM R_T4
199 #define R_CMP_RESULT R_T5
201 #define FR_SCRATCH_1 R_F0
202 #define FR_SCRATCH_2 R_F2
203 #define FR_SCRATCH_3 R_F4
204 #define FR_CMP_RESULT R_F6
206 #define SUPPORTED_FP 0x6
209 #define FRAME_SIZE 64
210 #define SAVE_OFFSET 20
212 #define FRAME_SIZE 96
213 #define SAVE_OFFSET 0
216 static bool reg_is_fp(unsigned reg)
218 return reg >= 0x20 && reg < 0x40;
221 static const uint8_t regs_saved[] = { R_S4, R_S5, R_S6, R_S7,
222 #ifndef ARCH_MIPS_O32
226 static const uint8_t regs_volatile[] = { R_A3, R_T6, R_T7, R_T8, R_T9 };
227 static const uint8_t fp_saved[] = { 0 };
228 #define n_fp_saved 0U
229 static const uint8_t fp_volatile[] = { R_F8, R_F10, R_F12, R_F14, R_F16, R_F18 };
230 #define reg_is_saved(r) (((r) >= R_S0 && (r) <= R_S7) || (r) == R_GP || (r) == R_FP)
232 static bool attr_w gen_load_constant(struct codegen_context *ctx, unsigned reg, uint64_t c)
234 if (OP_SIZE_NATIVE == OP_SIZE_4)
236 if ((int64_t)c != (int32_t)c) {
238 g(gen_load_constant(ctx, reg, (int32_t)c));
239 if ((int32_t)c < 0) {
245 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
249 gen_eight((uint64_t)(int16_t)c << 32);
251 if ((int16_t)c < 0) {
257 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
265 if (c && !(c & (c - 1))) {
272 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
278 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
286 if (~c && !(~c & (~c - 1))) {
287 g(gen_load_constant(ctx, reg, ~c));
289 gen_insn(INSN_ALU1, OP_SIZE_NATIVE, ALU1_NOT, 0);
295 g(gen_load_constant(ctx, reg, (int32_t)((c & 0xFFFFFFFF00000000ULL) >> 32)));
297 if (c & 0xFFFF0000ULL) {
298 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
304 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
310 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
316 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
323 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
327 gen_eight(c & 0xFFFFULL);
331 if (c == (uint16_t)c) {
332 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
339 if ((int64_t)c == (int16_t)c) {
340 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
347 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
350 gen_eight(c & ~0xffffULL);
352 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
356 gen_eight(c & 0xFFFFULL);
361 static bool attr_w gen_address(struct codegen_context *ctx, unsigned base, int64_t imm, unsigned purpose, unsigned size)
363 ctx->base_reg = base;
364 ctx->offset_imm = imm;
365 ctx->offset_reg = false;
367 case IMM_PURPOSE_LDR_OFFSET:
368 case IMM_PURPOSE_LDR_SX_OFFSET:
369 case IMM_PURPOSE_STR_OFFSET:
370 case IMM_PURPOSE_VLDR_VSTR_OFFSET:
371 case IMM_PURPOSE_MVI_CLI_OFFSET:
372 if (likely(imm == (int16_t)imm))
376 internal(file_line, "gen_address: invalid purpose %u (imm %"PRIxMAX", size %u)", purpose, (uintmax_t)imm, size);
378 g(gen_load_constant(ctx, R_OFFSET_IMM, imm));
379 gen_insn(INSN_ALU, OP_SIZE_ADDRESS, ALU_ADD, 0);
380 gen_one(R_OFFSET_IMM);
381 gen_one(R_OFFSET_IMM);
383 ctx->base_reg = R_OFFSET_IMM;
388 static bool is_direct_const(int64_t imm, unsigned purpose, unsigned size)
390 if (MIPS_R4000_ERRATA && (purpose == IMM_PURPOSE_ADD_TRAP || purpose == IMM_PURPOSE_SUB_TRAP) && size == OP_SIZE_8)
393 case IMM_PURPOSE_STORE_VALUE:
397 case IMM_PURPOSE_ADD:
398 case IMM_PURPOSE_CMP:
399 case IMM_PURPOSE_CMP_LOGICAL:
400 if (likely(imm == (int16_t)imm))
403 case IMM_PURPOSE_SUB:
404 if (likely(imm > -0x8000) && likely(imm <= 0x8000))
407 case IMM_PURPOSE_AND:
409 case IMM_PURPOSE_XOR:
410 case IMM_PURPOSE_TEST:
411 if (likely((uint64_t)imm == (uint16_t)imm))
414 case IMM_PURPOSE_MUL:
416 case IMM_PURPOSE_ADD_TRAP:
419 if (likely(imm == (int16_t)imm))
422 case IMM_PURPOSE_SUB_TRAP:
425 if (likely(imm > -0x8000) && likely(imm <= 0x8000))
429 internal(file_line, "is_direct_const: invalid purpose %u (imm %"PRIxMAX", size %u)", purpose, (uintmax_t)imm, size);
434 static bool attr_w gen_entry(struct codegen_context *ctx)
439 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_SUB, 0);
443 gen_eight(FRAME_SIZE);
445 save_offset = SAVE_OFFSET;
447 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
448 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
449 gen_address_offset();
451 save_offset += 1 << OP_SIZE_NATIVE;
453 for (reg = R_S0; reg <= R_S7; reg++) {
454 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
455 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
456 gen_address_offset();
458 save_offset += 1 << OP_SIZE_NATIVE;
460 #ifndef ARCH_MIPS_O32
461 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
462 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
463 gen_address_offset();
465 save_offset += 1 << OP_SIZE_NATIVE;
467 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
468 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
469 gen_address_offset();
471 save_offset += 1 << OP_SIZE_NATIVE;
473 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
474 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
475 gen_address_offset();
478 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
482 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
486 gen_insn(INSN_JMP_INDIRECT, 0, 0, 0);
492 static bool attr_w gen_escape_arg(struct codegen_context *ctx, ip_t ip, uint32_t escape_label)
494 g(gen_load_constant(ctx, R_RET1, (int32_t)ip));
496 gen_insn(INSN_JMP, 0, 0, 0);
497 gen_four(escape_label);
502 static bool attr_w gen_escape(struct codegen_context *ctx)
507 #if defined(ARCH_MIPS_N32)
508 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
514 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
519 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
524 save_offset = SAVE_OFFSET + (1 << OP_SIZE_NATIVE);
525 for (reg = R_S0; reg <= R_S7; reg++) {
526 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_NATIVE));
527 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
529 gen_address_offset();
530 save_offset += 1 << OP_SIZE_NATIVE;
532 #ifndef ARCH_MIPS_O32
533 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
534 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
536 gen_address_offset();
537 save_offset += 1 << OP_SIZE_NATIVE;
539 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_NATIVE));
540 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
542 gen_address_offset();
543 save_offset += 1 << OP_SIZE_NATIVE;
545 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_NATIVE));
546 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
548 gen_address_offset();
550 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
554 gen_eight(FRAME_SIZE);
556 gen_insn(INSN_RET, 0, 0, 0);
561 static bool attr_w gen_upcall_argument(struct codegen_context attr_unused *ctx, unsigned attr_unused arg)
566 static bool attr_w gen_get_upcall_pointer(struct codegen_context *ctx, unsigned offset, unsigned reg)
568 g(gen_address(ctx, R_UPCALL, offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_ADDRESS));
569 gen_insn(INSN_MOV, OP_SIZE_ADDRESS, 0, 0);
571 gen_address_offset();
576 static bool attr_w gen_upcall(struct codegen_context *ctx, unsigned offset, unsigned n_args)
578 g(gen_get_upcall_pointer(ctx, offset, R_T9));
580 gen_insn(INSN_CALL_INDIRECT, OP_SIZE_ADDRESS, 0, 0);
583 g(gen_upcall_end(ctx, offset, n_args));
588 static bool attr_w gen_cmp_test_jmp(struct codegen_context *ctx, unsigned insn, unsigned op_size, unsigned reg1, unsigned reg2, unsigned cond, uint32_t label);
590 static bool attr_w gen_timestamp_test(struct codegen_context *ctx, uint32_t escape_label)
592 g(gen_address(ctx, R_UPCALL, offsetof(struct cg_upcall_vector_s, ts), IMM_PURPOSE_LDR_OFFSET, OP_SIZE_4));
593 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
594 gen_one(R_SCRATCH_1);
595 gen_address_offset();
597 g(gen_address(ctx, R_SP, SAVE_OFFSET, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
598 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
599 gen_one(R_SCRATCH_2);
600 gen_address_offset();
602 g(gen_cmp_test_jmp(ctx, INSN_CMP, OP_SIZE_NATIVE, R_SCRATCH_1, R_SCRATCH_2, COND_NE, escape_label));