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_PREFERS_SX(size) 0
50 #define ARCH_HAS_BWX 1
51 #define ARCH_HAS_MUL 1
52 #define ARCH_HAS_DIV 1
53 #define ARCH_HAS_ANDN 0
54 #define ARCH_HAS_SHIFTED_ADD(bits) (MIPS_R6 && (bits) >= 1 && (bits) <= 4)
55 #define ARCH_HAS_BTX(btx, size, cnst) 0
56 #define ARCH_SHIFT_SIZE OP_SIZE_4
57 #define ARCH_NEEDS_BARRIER 0
59 #define i_size(size) OP_SIZE_NATIVE
60 #define i_size_rot(size) maximum(size, OP_SIZE_4)
61 #define i_size_cmp(size) OP_SIZE_NATIVE
64 #define MIPS_LOAD_DELAY_SLOTS 1
65 #define MIPS_HAS_LS_DOUBLE 0
66 #define MIPS_HAS_SQRT 0
67 #define MIPS_HAS_TRUNC 0
69 #define MIPS_LOAD_DELAY_SLOTS 0
70 #define MIPS_HAS_LS_DOUBLE 1
71 #define MIPS_HAS_SQRT 1
72 #define MIPS_HAS_TRUNC 1
76 #define MIPS_R4000_ERRATA 1
77 #define MIPS_FCMP_DELAY_SLOTS 1
78 #define MIPS_HAS_MOVT 0
80 #define MIPS_R4000_ERRATA 0
81 #define MIPS_FCMP_DELAY_SLOTS 0
82 #define MIPS_HAS_MOVT 1
86 #define MIPS_HAS_CLZ 0
87 #define MIPS_HAS_MUL 0
89 #define MIPS_HAS_CLZ 1
90 #define MIPS_HAS_MUL 1
93 #if __mips < 32 || !defined(__mips_isa_rev) || __mips_isa_rev < 2
94 #define MIPS_HAS_ROT 0
96 #define MIPS_HAS_ROT 1
99 #if __mips < 32 || !defined(__mips_isa_rev) || __mips_isa_rev < 6
172 #define R_UPCALL R_S1
174 #define R_SCRATCH_1 R_A0
175 #define R_SCRATCH_2 R_A1
176 #define R_SCRATCH_3 R_A2
177 #define R_SCRATCH_4 R_SAVED_2
178 #define R_SCRATCH_NA_1 R_T0
179 #define R_SCRATCH_NA_2 R_T1
180 #define R_SCRATCH_NA_3 R_T2
182 #define R_SAVED_1 R_S2
183 #define R_SAVED_2 R_S3
193 #define R_OFFSET_IMM R_T3
194 #define R_CONST_IMM R_T4
195 #define R_CMP_RESULT R_T5
197 #define FR_SCRATCH_1 R_F0
198 #define FR_SCRATCH_2 R_F2
199 #define FR_SCRATCH_3 R_F4
200 #define FR_CMP_RESULT R_F6
202 #define SUPPORTED_FP 0x6
205 #define FRAME_SIZE 64
206 #define SAVE_OFFSET 20
208 #define FRAME_SIZE 96
209 #define SAVE_OFFSET 0
212 static bool reg_is_fp(unsigned reg)
214 return reg >= 0x20 && reg < 0x40;
217 static const uint8_t regs_saved[] = { R_S4, R_S5, R_S6, R_S7,
218 #ifndef ARCH_MIPS_O32
222 static const uint8_t regs_volatile[] = { R_A3, R_T6, R_T7, R_T8, R_T9, R_RA };
223 static const uint8_t fp_saved[] = { 0 };
224 #define n_fp_saved 0U
225 static const uint8_t fp_volatile[] = { R_F8, R_F10, R_F12, R_F14, R_F16, R_F18 };
226 #define reg_is_saved(r) (((r) >= R_S0 && (r) <= R_S7) || (r) == R_GP || (r) == R_FP)
228 static bool attr_w gen_load_constant(struct codegen_context *ctx, unsigned reg, uint64_t c)
230 if (OP_SIZE_NATIVE == OP_SIZE_4)
232 if ((int64_t)c != (int32_t)c) {
234 g(gen_load_constant(ctx, reg, (int32_t)c));
235 if ((int32_t)c < 0) {
241 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
245 gen_eight((uint64_t)(int16_t)c << 32);
247 if ((int16_t)c < 0) {
253 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
261 if (c && !(c & (c - 1))) {
268 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
274 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
282 if (~c && !(~c & (~c - 1))) {
283 g(gen_load_constant(ctx, reg, ~c));
285 gen_insn(INSN_ALU1, OP_SIZE_NATIVE, ALU1_NOT, 0);
291 g(gen_load_constant(ctx, reg, (int32_t)((c & 0xFFFFFFFF00000000ULL) >> 32)));
293 if (c & 0xFFFF0000ULL) {
294 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
300 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
306 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
312 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
319 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
323 gen_eight(c & 0xFFFFULL);
327 if (c == (uint16_t)c) {
328 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
335 if ((int64_t)c == (int16_t)c) {
336 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
343 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
346 gen_eight(c & ~0xffffULL);
348 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
352 gen_eight(c & 0xFFFFULL);
357 static bool attr_w gen_address(struct codegen_context *ctx, unsigned base, int64_t imm, unsigned purpose, unsigned size)
359 ctx->base_reg = base;
360 ctx->offset_imm = imm;
361 ctx->offset_reg = false;
363 case IMM_PURPOSE_LDR_OFFSET:
364 case IMM_PURPOSE_LDR_SX_OFFSET:
365 case IMM_PURPOSE_STR_OFFSET:
366 case IMM_PURPOSE_VLDR_VSTR_OFFSET:
367 case IMM_PURPOSE_MVI_CLI_OFFSET:
368 if (likely(imm == (int16_t)imm))
372 internal(file_line, "gen_address: invalid purpose %u (imm %"PRIxMAX", size %u)", purpose, (uintmax_t)imm, size);
374 g(gen_load_constant(ctx, R_OFFSET_IMM, imm));
375 gen_insn(INSN_ALU, OP_SIZE_ADDRESS, ALU_ADD, 0);
376 gen_one(R_OFFSET_IMM);
377 gen_one(R_OFFSET_IMM);
379 ctx->base_reg = R_OFFSET_IMM;
384 static bool is_direct_const(int64_t imm, unsigned purpose, unsigned size)
386 if (MIPS_R4000_ERRATA && (purpose == IMM_PURPOSE_ADD || purpose == IMM_PURPOSE_SUB) && size == OP_SIZE_8)
389 case IMM_PURPOSE_STORE_VALUE:
393 case IMM_PURPOSE_ADD:
394 case IMM_PURPOSE_CMP:
395 case IMM_PURPOSE_CMP_LOGICAL:
396 if (likely(imm == (int16_t)imm))
399 case IMM_PURPOSE_SUB:
400 if (likely(imm > -0x8000) && likely(imm <= 0x8000))
403 case IMM_PURPOSE_AND:
405 case IMM_PURPOSE_XOR:
406 case IMM_PURPOSE_TEST:
407 if (likely((uint64_t)imm == (uint16_t)imm))
410 case IMM_PURPOSE_MUL:
413 internal(file_line, "is_direct_const: invalid purpose %u (imm %"PRIxMAX", size %u)", purpose, (uintmax_t)imm, size);
418 static bool attr_w gen_imm(struct codegen_context *ctx, int64_t imm, unsigned purpose, unsigned size)
420 if (is_direct_const(imm, purpose, size)) {
421 ctx->const_imm = imm;
422 ctx->const_reg = false;
424 g(gen_load_constant(ctx, R_CONST_IMM, imm));
425 ctx->const_reg = true;
430 static bool attr_w gen_entry(struct codegen_context *ctx)
435 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_SUB, 0);
439 gen_eight(FRAME_SIZE);
441 save_offset = SAVE_OFFSET;
443 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
444 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
445 gen_address_offset();
447 save_offset += 1 << OP_SIZE_NATIVE;
449 for (reg = R_S0; reg <= R_S7; reg++) {
450 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
451 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
452 gen_address_offset();
454 save_offset += 1 << OP_SIZE_NATIVE;
456 #ifndef ARCH_MIPS_O32
457 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
458 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
459 gen_address_offset();
461 save_offset += 1 << OP_SIZE_NATIVE;
463 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
464 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
465 gen_address_offset();
467 save_offset += 1 << OP_SIZE_NATIVE;
469 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
470 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
471 gen_address_offset();
474 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
478 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
482 gen_insn(INSN_JMP_INDIRECT, 0, 0, 0);
488 static bool attr_w gen_escape_arg(struct codegen_context *ctx, ip_t ip, uint32_t escape_label)
490 g(gen_load_constant(ctx, R_RET1, (int32_t)ip));
492 gen_insn(INSN_JMP, 0, 0, 0);
493 gen_four(escape_label);
498 static bool attr_w gen_escape(struct codegen_context *ctx)
503 #if defined(ARCH_MIPS_N32)
504 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
510 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
515 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
520 save_offset = SAVE_OFFSET + (1 << OP_SIZE_NATIVE);
521 for (reg = R_S0; reg <= R_S7; reg++) {
522 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_NATIVE));
523 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
525 gen_address_offset();
526 save_offset += 1 << OP_SIZE_NATIVE;
528 #ifndef ARCH_MIPS_O32
529 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
530 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
532 gen_address_offset();
533 save_offset += 1 << OP_SIZE_NATIVE;
535 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_NATIVE));
536 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
538 gen_address_offset();
539 save_offset += 1 << OP_SIZE_NATIVE;
541 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_NATIVE));
542 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
544 gen_address_offset();
546 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
550 gen_eight(FRAME_SIZE);
552 gen_insn(INSN_RET, 0, 0, 0);
557 static bool attr_w gen_upcall_argument(struct codegen_context attr_unused *ctx, unsigned attr_unused arg)
562 static bool attr_w gen_upcall(struct codegen_context *ctx, unsigned offset, unsigned n_args)
564 g(gen_address(ctx, R_UPCALL, offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_ADDRESS));
565 gen_insn(INSN_MOV, OP_SIZE_ADDRESS, 0, 0);
567 gen_address_offset();
569 gen_insn(INSN_CALL_INDIRECT, OP_SIZE_ADDRESS, 0, 0);
572 g(gen_upcall_end(ctx, n_args));
577 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);
579 static bool attr_w gen_timestamp_test(struct codegen_context *ctx, uint32_t escape_label)
581 g(gen_address(ctx, R_UPCALL, offsetof(struct cg_upcall_vector_s, ts), IMM_PURPOSE_LDR_OFFSET, OP_SIZE_4));
582 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
583 gen_one(R_SCRATCH_1);
584 gen_address_offset();
586 g(gen_address(ctx, R_SP, SAVE_OFFSET, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
587 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
588 gen_one(R_SCRATCH_2);
589 gen_address_offset();
591 g(gen_cmp_test_jmp(ctx, INSN_CMP, OP_SIZE_NATIVE, R_SCRATCH_1, R_SCRATCH_2, COND_NE, escape_label));