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(alu, im) 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_FLAGS 0
48 #define ARCH_SUPPORTS_TRAPS OS_SUPPORTS_TRAPS
49 #define ARCH_TRAP_BEFORE 1
50 #define ARCH_PREFERS_SX(size) 0
51 #define ARCH_HAS_BWX 1
52 #define ARCH_HAS_MUL 1
53 #define ARCH_HAS_DIV 1
54 #define ARCH_HAS_ANDN 0
55 #define ARCH_HAS_SHIFTED_ADD(bits) (MIPS_R6 && (bits) >= 1 && (bits) <= 4)
56 #define ARCH_HAS_BTX(btx, size, cnst) 0
57 #define ARCH_SHIFT_SIZE OP_SIZE_4
58 #define ARCH_HAS_FP_GP_MOV 1
59 #define ARCH_NEEDS_BARRIER 0
61 #define i_size(size) OP_SIZE_NATIVE
62 #define i_size_rot(size) maximum(size, OP_SIZE_4)
63 #define i_size_cmp(size) OP_SIZE_NATIVE
66 #define MIPS_LOAD_DELAY_SLOTS 1
67 #define MIPS_HAS_LS_DOUBLE 0
68 #define MIPS_HAS_SQRT 0
69 #define MIPS_HAS_TRUNC 0
71 #define MIPS_LOAD_DELAY_SLOTS 0
72 #define MIPS_HAS_LS_DOUBLE 1
73 #define MIPS_HAS_SQRT 1
74 #define MIPS_HAS_TRUNC 1
78 #define MIPS_R4000_ERRATA 1
79 #define MIPS_FCMP_DELAY_SLOTS 1
80 #define MIPS_HAS_MOVT 0
82 #define MIPS_R4000_ERRATA 0
83 #define MIPS_FCMP_DELAY_SLOTS 0
84 #define MIPS_HAS_MOVT 1
88 #define MIPS_HAS_CLZ 0
89 #define MIPS_HAS_MUL 0
91 #define MIPS_HAS_CLZ 1
92 #define MIPS_HAS_MUL 1
95 #if __mips < 32 || !defined(__mips_isa_rev) || __mips_isa_rev < 2
96 #define MIPS_HAS_ROT 0
98 #define MIPS_HAS_ROT 1
101 #if __mips < 32 || !defined(__mips_isa_rev) || __mips_isa_rev < 6
174 #define R_UPCALL R_S1
176 #define R_SCRATCH_1 R_A0
177 #define R_SCRATCH_2 R_A1
178 #define R_SCRATCH_3 R_A2
179 #define R_SCRATCH_4 R_SAVED_2
180 #define R_SCRATCH_NA_1 R_T0
181 #define R_SCRATCH_NA_2 R_T1
182 #define R_SCRATCH_NA_3 R_T2
184 #define R_SAVED_1 R_S2
185 #define R_SAVED_2 R_S3
195 #define R_OFFSET_IMM R_T3
196 #define R_CONST_IMM R_T4
197 #define R_CMP_RESULT R_T5
199 #define FR_SCRATCH_1 R_F0
200 #define FR_SCRATCH_2 R_F2
201 #define FR_SCRATCH_3 R_F4
202 #define FR_CMP_RESULT R_F6
204 #define SUPPORTED_FP 0x6
207 #define FRAME_SIZE 64
208 #define SAVE_OFFSET 20
210 #define FRAME_SIZE 96
211 #define SAVE_OFFSET 0
214 static bool reg_is_fp(unsigned reg)
216 return reg >= 0x20 && reg < 0x40;
219 static const uint8_t regs_saved[] = { R_S4, R_S5, R_S6, R_S7,
220 #ifndef ARCH_MIPS_O32
224 static const uint8_t regs_volatile[] = { R_A3, R_T6, R_T7, R_T8, R_T9, R_RA };
225 static const uint8_t fp_saved[] = { 0 };
226 #define n_fp_saved 0U
227 static const uint8_t fp_volatile[] = { R_F8, R_F10, R_F12, R_F14, R_F16, R_F18 };
228 #define reg_is_saved(r) (((r) >= R_S0 && (r) <= R_S7) || (r) == R_GP || (r) == R_FP)
230 static bool attr_w gen_load_constant(struct codegen_context *ctx, unsigned reg, uint64_t c)
232 if (OP_SIZE_NATIVE == OP_SIZE_4)
234 if ((int64_t)c != (int32_t)c) {
236 g(gen_load_constant(ctx, reg, (int32_t)c));
237 if ((int32_t)c < 0) {
243 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
247 gen_eight((uint64_t)(int16_t)c << 32);
249 if ((int16_t)c < 0) {
255 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
263 if (c && !(c & (c - 1))) {
270 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
276 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
284 if (~c && !(~c & (~c - 1))) {
285 g(gen_load_constant(ctx, reg, ~c));
287 gen_insn(INSN_ALU1, OP_SIZE_NATIVE, ALU1_NOT, 0);
293 g(gen_load_constant(ctx, reg, (int32_t)((c & 0xFFFFFFFF00000000ULL) >> 32)));
295 if (c & 0xFFFF0000ULL) {
296 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
302 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
308 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
314 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
321 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
325 gen_eight(c & 0xFFFFULL);
329 if (c == (uint16_t)c) {
330 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
337 if ((int64_t)c == (int16_t)c) {
338 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
345 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
348 gen_eight(c & ~0xffffULL);
350 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
354 gen_eight(c & 0xFFFFULL);
359 static bool attr_w gen_address(struct codegen_context *ctx, unsigned base, int64_t imm, unsigned purpose, unsigned size)
361 ctx->base_reg = base;
362 ctx->offset_imm = imm;
363 ctx->offset_reg = false;
365 case IMM_PURPOSE_LDR_OFFSET:
366 case IMM_PURPOSE_LDR_SX_OFFSET:
367 case IMM_PURPOSE_STR_OFFSET:
368 case IMM_PURPOSE_VLDR_VSTR_OFFSET:
369 case IMM_PURPOSE_MVI_CLI_OFFSET:
370 if (likely(imm == (int16_t)imm))
374 internal(file_line, "gen_address: invalid purpose %u (imm %"PRIxMAX", size %u)", purpose, (uintmax_t)imm, size);
376 g(gen_load_constant(ctx, R_OFFSET_IMM, imm));
377 gen_insn(INSN_ALU, OP_SIZE_ADDRESS, ALU_ADD, 0);
378 gen_one(R_OFFSET_IMM);
379 gen_one(R_OFFSET_IMM);
381 ctx->base_reg = R_OFFSET_IMM;
386 static bool is_direct_const(int64_t imm, unsigned purpose, unsigned size)
388 if (MIPS_R4000_ERRATA && (purpose == IMM_PURPOSE_ADD || purpose == IMM_PURPOSE_SUB) && size == OP_SIZE_8)
391 case IMM_PURPOSE_STORE_VALUE:
395 case IMM_PURPOSE_ADD:
396 case IMM_PURPOSE_CMP:
397 case IMM_PURPOSE_CMP_LOGICAL:
398 if (likely(imm == (int16_t)imm))
401 case IMM_PURPOSE_SUB:
402 if (likely(imm > -0x8000) && likely(imm <= 0x8000))
405 case IMM_PURPOSE_AND:
407 case IMM_PURPOSE_XOR:
408 case IMM_PURPOSE_TEST:
409 if (likely((uint64_t)imm == (uint16_t)imm))
412 case IMM_PURPOSE_MUL:
415 internal(file_line, "is_direct_const: invalid purpose %u (imm %"PRIxMAX", size %u)", purpose, (uintmax_t)imm, size);
420 static bool attr_w gen_imm(struct codegen_context *ctx, int64_t imm, unsigned purpose, unsigned size)
422 if (is_direct_const(imm, purpose, size)) {
423 ctx->const_imm = imm;
424 ctx->const_reg = false;
426 g(gen_load_constant(ctx, R_CONST_IMM, imm));
427 ctx->const_reg = true;
432 static bool attr_w gen_entry(struct codegen_context *ctx)
437 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_SUB, 0);
441 gen_eight(FRAME_SIZE);
443 save_offset = SAVE_OFFSET;
445 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
446 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
447 gen_address_offset();
449 save_offset += 1 << OP_SIZE_NATIVE;
451 for (reg = R_S0; reg <= R_S7; reg++) {
452 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
453 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
454 gen_address_offset();
456 save_offset += 1 << OP_SIZE_NATIVE;
458 #ifndef ARCH_MIPS_O32
459 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
460 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
461 gen_address_offset();
463 save_offset += 1 << OP_SIZE_NATIVE;
465 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
466 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
467 gen_address_offset();
469 save_offset += 1 << OP_SIZE_NATIVE;
471 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
472 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
473 gen_address_offset();
476 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
480 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
484 gen_insn(INSN_JMP_INDIRECT, 0, 0, 0);
490 static bool attr_w gen_escape_arg(struct codegen_context *ctx, ip_t ip, uint32_t escape_label)
492 g(gen_load_constant(ctx, R_RET1, (int32_t)ip));
494 gen_insn(INSN_JMP, 0, 0, 0);
495 gen_four(escape_label);
500 static bool attr_w gen_escape(struct codegen_context *ctx)
505 #if defined(ARCH_MIPS_N32)
506 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
512 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
517 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
522 save_offset = SAVE_OFFSET + (1 << OP_SIZE_NATIVE);
523 for (reg = R_S0; reg <= R_S7; reg++) {
524 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_NATIVE));
525 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
527 gen_address_offset();
528 save_offset += 1 << OP_SIZE_NATIVE;
530 #ifndef ARCH_MIPS_O32
531 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
532 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
534 gen_address_offset();
535 save_offset += 1 << OP_SIZE_NATIVE;
537 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_NATIVE));
538 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
540 gen_address_offset();
541 save_offset += 1 << OP_SIZE_NATIVE;
543 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_NATIVE));
544 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
546 gen_address_offset();
548 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
552 gen_eight(FRAME_SIZE);
554 gen_insn(INSN_RET, 0, 0, 0);
559 static bool attr_w gen_upcall_argument(struct codegen_context attr_unused *ctx, unsigned attr_unused arg)
564 static bool attr_w gen_upcall(struct codegen_context *ctx, unsigned offset, unsigned n_args)
566 g(gen_address(ctx, R_UPCALL, offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_ADDRESS));
567 gen_insn(INSN_MOV, OP_SIZE_ADDRESS, 0, 0);
569 gen_address_offset();
571 gen_insn(INSN_CALL_INDIRECT, OP_SIZE_ADDRESS, 0, 0);
574 g(gen_upcall_end(ctx, n_args));
579 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);
581 static bool attr_w gen_timestamp_test(struct codegen_context *ctx, uint32_t escape_label)
583 g(gen_address(ctx, R_UPCALL, offsetof(struct cg_upcall_vector_s, ts), IMM_PURPOSE_LDR_OFFSET, OP_SIZE_4));
584 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
585 gen_one(R_SCRATCH_1);
586 gen_address_offset();
588 g(gen_address(ctx, R_SP, SAVE_OFFSET, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
589 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
590 gen_one(R_SCRATCH_2);
591 gen_address_offset();
593 g(gen_cmp_test_jmp(ctx, INSN_CMP, OP_SIZE_NATIVE, R_SCRATCH_1, R_SCRATCH_2, COND_NE, escape_label));