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_JMP_2REGS(cond) ((cond) == COND_E || (cond) == COND_NE || MIPS_R6)
48 #define ARCH_HAS_FLAGS 0
49 #define ARCH_SUPPORTS_TRAPS OS_SUPPORTS_TRAPS
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_HAS_FP_GP_MOV 1
60 #define ARCH_NEEDS_BARRIER 0
62 #define i_size(size) OP_SIZE_NATIVE
63 #define i_size_rot(size) maximum(size, OP_SIZE_4)
64 #define i_size_cmp(size) OP_SIZE_NATIVE
67 #define MIPS_LOAD_DELAY_SLOTS 1
68 #define MIPS_HAS_LS_DOUBLE 0
69 #define MIPS_HAS_SQRT 0
70 #define MIPS_HAS_TRUNC 0
72 #define MIPS_LOAD_DELAY_SLOTS 0
73 #define MIPS_HAS_LS_DOUBLE 1
74 #define MIPS_HAS_SQRT 1
75 #define MIPS_HAS_TRUNC 1
79 #define MIPS_R4000_ERRATA 1
80 #define MIPS_FCMP_DELAY_SLOTS 1
81 #define MIPS_HAS_MOVT 0
83 #define MIPS_R4000_ERRATA 0
84 #define MIPS_FCMP_DELAY_SLOTS 0
85 #define MIPS_HAS_MOVT 1
89 #define MIPS_HAS_CLZ 0
90 #define MIPS_HAS_MUL 0
92 #define MIPS_HAS_CLZ 1
93 #define MIPS_HAS_MUL 1
96 #if __mips < 32 || !defined(__mips_isa_rev) || __mips_isa_rev < 2
97 #define MIPS_HAS_ROT 0
99 #define MIPS_HAS_ROT 1
102 #if __mips < 32 || !defined(__mips_isa_rev) || __mips_isa_rev < 6
175 #define R_UPCALL R_S1
177 #define R_SCRATCH_1 R_A0
178 #define R_SCRATCH_2 R_A1
179 #define R_SCRATCH_3 R_A2
180 #define R_SCRATCH_4 R_SAVED_2
181 #define R_SCRATCH_NA_1 R_T0
182 #define R_SCRATCH_NA_2 R_T1
183 #define R_SCRATCH_NA_3 R_T2
185 #define R_SAVED_1 R_S2
186 #define R_SAVED_2 R_S3
196 #define R_OFFSET_IMM R_T3
197 #define R_CONST_IMM R_T4
198 #define R_CMP_RESULT R_T5
200 #define FR_SCRATCH_1 R_F0
201 #define FR_SCRATCH_2 R_F2
202 #define FR_SCRATCH_3 R_F4
203 #define FR_CMP_RESULT R_F6
205 #define SUPPORTED_FP 0x6
208 #define FRAME_SIZE 64
209 #define SAVE_OFFSET 20
211 #define FRAME_SIZE 96
212 #define SAVE_OFFSET 0
215 static bool reg_is_fp(unsigned reg)
217 return reg >= 0x20 && reg < 0x40;
220 static const uint8_t regs_saved[] = { R_S4, R_S5, R_S6, R_S7,
221 #ifndef ARCH_MIPS_O32
225 static const uint8_t regs_volatile[] = { R_A3, R_T6, R_T7, R_T8, R_T9 };
226 static const uint8_t fp_saved[] = { 0 };
227 #define n_fp_saved 0U
228 static const uint8_t fp_volatile[] = { R_F8, R_F10, R_F12, R_F14, R_F16, R_F18 };
229 #define reg_is_saved(r) (((r) >= R_S0 && (r) <= R_S7) || (r) == R_GP || (r) == R_FP)
231 static bool attr_w gen_load_constant(struct codegen_context *ctx, unsigned reg, uint64_t c)
233 if (OP_SIZE_NATIVE == OP_SIZE_4)
235 if ((int64_t)c != (int32_t)c) {
237 g(gen_load_constant(ctx, reg, (int32_t)c));
238 if ((int32_t)c < 0) {
244 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
248 gen_eight((uint64_t)(int16_t)c << 32);
250 if ((int16_t)c < 0) {
256 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
264 if (c && !(c & (c - 1))) {
271 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
277 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
285 if (~c && !(~c & (~c - 1))) {
286 g(gen_load_constant(ctx, reg, ~c));
288 gen_insn(INSN_ALU1, OP_SIZE_NATIVE, ALU1_NOT, 0);
294 g(gen_load_constant(ctx, reg, (int32_t)((c & 0xFFFFFFFF00000000ULL) >> 32)));
296 if (c & 0xFFFF0000ULL) {
297 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
303 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
309 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
315 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
322 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
326 gen_eight(c & 0xFFFFULL);
330 if (c == (uint16_t)c) {
331 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
338 if ((int64_t)c == (int16_t)c) {
339 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
346 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
349 gen_eight(c & ~0xffffULL);
351 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
355 gen_eight(c & 0xFFFFULL);
360 static bool attr_w gen_address(struct codegen_context *ctx, unsigned base, int64_t imm, unsigned purpose, unsigned size)
362 ctx->base_reg = base;
363 ctx->offset_imm = imm;
364 ctx->offset_reg = false;
366 case IMM_PURPOSE_LDR_OFFSET:
367 case IMM_PURPOSE_LDR_SX_OFFSET:
368 case IMM_PURPOSE_STR_OFFSET:
369 case IMM_PURPOSE_VLDR_VSTR_OFFSET:
370 case IMM_PURPOSE_MVI_CLI_OFFSET:
371 if (likely(imm == (int16_t)imm))
375 internal(file_line, "gen_address: invalid purpose %u (imm %"PRIxMAX", size %u)", purpose, (uintmax_t)imm, size);
377 g(gen_load_constant(ctx, R_OFFSET_IMM, imm));
378 gen_insn(INSN_ALU, OP_SIZE_ADDRESS, ALU_ADD, 0);
379 gen_one(R_OFFSET_IMM);
380 gen_one(R_OFFSET_IMM);
382 ctx->base_reg = R_OFFSET_IMM;
387 static bool is_direct_const(int64_t imm, unsigned purpose, unsigned size)
389 if (MIPS_R4000_ERRATA && (purpose == IMM_PURPOSE_ADD || purpose == IMM_PURPOSE_SUB) && size == OP_SIZE_8)
392 case IMM_PURPOSE_STORE_VALUE:
396 case IMM_PURPOSE_ADD:
397 case IMM_PURPOSE_CMP:
398 case IMM_PURPOSE_CMP_LOGICAL:
399 if (likely(imm == (int16_t)imm))
402 case IMM_PURPOSE_SUB:
403 if (likely(imm > -0x8000) && likely(imm <= 0x8000))
406 case IMM_PURPOSE_AND:
408 case IMM_PURPOSE_XOR:
409 case IMM_PURPOSE_TEST:
410 if (likely((uint64_t)imm == (uint16_t)imm))
413 case IMM_PURPOSE_MUL:
416 internal(file_line, "is_direct_const: invalid purpose %u (imm %"PRIxMAX", size %u)", purpose, (uintmax_t)imm, size);
421 static bool attr_w gen_imm(struct codegen_context *ctx, int64_t imm, unsigned purpose, unsigned size)
423 if (is_direct_const(imm, purpose, size)) {
424 ctx->const_imm = imm;
425 ctx->const_reg = false;
427 g(gen_load_constant(ctx, R_CONST_IMM, imm));
428 ctx->const_reg = true;
433 static bool attr_w gen_entry(struct codegen_context *ctx)
438 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_SUB, 0);
442 gen_eight(FRAME_SIZE);
444 save_offset = SAVE_OFFSET;
446 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
447 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
448 gen_address_offset();
450 save_offset += 1 << OP_SIZE_NATIVE;
452 for (reg = R_S0; reg <= R_S7; reg++) {
453 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
454 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
455 gen_address_offset();
457 save_offset += 1 << OP_SIZE_NATIVE;
459 #ifndef ARCH_MIPS_O32
460 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
461 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
462 gen_address_offset();
464 save_offset += 1 << OP_SIZE_NATIVE;
466 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
467 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
468 gen_address_offset();
470 save_offset += 1 << OP_SIZE_NATIVE;
472 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
473 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
474 gen_address_offset();
477 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
481 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
485 gen_insn(INSN_JMP_INDIRECT, 0, 0, 0);
491 static bool attr_w gen_escape_arg(struct codegen_context *ctx, ip_t ip, uint32_t escape_label)
493 g(gen_load_constant(ctx, R_RET1, (int32_t)ip));
495 gen_insn(INSN_JMP, 0, 0, 0);
496 gen_four(escape_label);
501 static bool attr_w gen_escape(struct codegen_context *ctx)
506 #if defined(ARCH_MIPS_N32)
507 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
513 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
518 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
523 save_offset = SAVE_OFFSET + (1 << OP_SIZE_NATIVE);
524 for (reg = R_S0; reg <= R_S7; reg++) {
525 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_NATIVE));
526 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
528 gen_address_offset();
529 save_offset += 1 << OP_SIZE_NATIVE;
531 #ifndef ARCH_MIPS_O32
532 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
533 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
535 gen_address_offset();
536 save_offset += 1 << OP_SIZE_NATIVE;
538 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_NATIVE));
539 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
541 gen_address_offset();
542 save_offset += 1 << OP_SIZE_NATIVE;
544 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_NATIVE));
545 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
547 gen_address_offset();
549 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
553 gen_eight(FRAME_SIZE);
555 gen_insn(INSN_RET, 0, 0, 0);
560 static bool attr_w gen_upcall_argument(struct codegen_context attr_unused *ctx, unsigned attr_unused arg)
565 static bool attr_w gen_upcall(struct codegen_context *ctx, unsigned offset, unsigned n_args)
567 g(gen_address(ctx, R_UPCALL, offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_ADDRESS));
568 gen_insn(INSN_MOV, OP_SIZE_ADDRESS, 0, 0);
570 gen_address_offset();
572 gen_insn(INSN_CALL_INDIRECT, OP_SIZE_ADDRESS, 0, 0);
575 g(gen_upcall_end(ctx, n_args));
580 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);
582 static bool attr_w gen_timestamp_test(struct codegen_context *ctx, uint32_t escape_label)
584 g(gen_address(ctx, R_UPCALL, offsetof(struct cg_upcall_vector_s, ts), IMM_PURPOSE_LDR_OFFSET, OP_SIZE_4));
585 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
586 gen_one(R_SCRATCH_1);
587 gen_address_offset();
589 g(gen_address(ctx, R_SP, SAVE_OFFSET, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
590 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
591 gen_one(R_SCRATCH_2);
592 gen_address_offset();
594 g(gen_cmp_test_jmp(ctx, INSN_CMP, OP_SIZE_NATIVE, R_SCRATCH_1, R_SCRATCH_2, COND_NE, escape_label));