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_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_imm(struct codegen_context *ctx, int64_t imm, unsigned purpose, unsigned size)
436 if (is_direct_const(imm, purpose, size)) {
437 ctx->const_imm = imm;
438 ctx->const_reg = false;
440 g(gen_load_constant(ctx, R_CONST_IMM, imm));
441 ctx->const_reg = true;
446 static bool attr_w gen_entry(struct codegen_context *ctx)
451 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_SUB, 0);
455 gen_eight(FRAME_SIZE);
457 save_offset = SAVE_OFFSET;
459 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
460 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
461 gen_address_offset();
463 save_offset += 1 << OP_SIZE_NATIVE;
465 for (reg = R_S0; reg <= R_S7; reg++) {
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 #ifndef ARCH_MIPS_O32
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();
477 save_offset += 1 << OP_SIZE_NATIVE;
479 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
480 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
481 gen_address_offset();
483 save_offset += 1 << OP_SIZE_NATIVE;
485 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
486 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
487 gen_address_offset();
490 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
494 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
498 gen_insn(INSN_JMP_INDIRECT, 0, 0, 0);
504 static bool attr_w gen_escape_arg(struct codegen_context *ctx, ip_t ip, uint32_t escape_label)
506 g(gen_load_constant(ctx, R_RET1, (int32_t)ip));
508 gen_insn(INSN_JMP, 0, 0, 0);
509 gen_four(escape_label);
514 static bool attr_w gen_escape(struct codegen_context *ctx)
519 #if defined(ARCH_MIPS_N32)
520 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
526 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
531 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
536 save_offset = SAVE_OFFSET + (1 << OP_SIZE_NATIVE);
537 for (reg = R_S0; reg <= R_S7; reg++) {
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 #ifndef ARCH_MIPS_O32
545 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
546 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
548 gen_address_offset();
549 save_offset += 1 << OP_SIZE_NATIVE;
551 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_NATIVE));
552 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
554 gen_address_offset();
555 save_offset += 1 << OP_SIZE_NATIVE;
557 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_NATIVE));
558 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
560 gen_address_offset();
562 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
566 gen_eight(FRAME_SIZE);
568 gen_insn(INSN_RET, 0, 0, 0);
573 static bool attr_w gen_upcall_argument(struct codegen_context attr_unused *ctx, unsigned attr_unused arg)
578 static bool attr_w gen_get_upcall_pointer(struct codegen_context *ctx, unsigned offset, unsigned reg)
580 g(gen_address(ctx, R_UPCALL, offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_ADDRESS));
581 gen_insn(INSN_MOV, OP_SIZE_ADDRESS, 0, 0);
583 gen_address_offset();
588 static bool attr_w gen_upcall(struct codegen_context *ctx, unsigned offset, unsigned n_args)
590 g(gen_get_upcall_pointer(ctx, offset, R_T9));
592 gen_insn(INSN_CALL_INDIRECT, OP_SIZE_ADDRESS, 0, 0);
595 g(gen_upcall_end(ctx, n_args));
600 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);
602 static bool attr_w gen_timestamp_test(struct codegen_context *ctx, uint32_t escape_label)
604 g(gen_address(ctx, R_UPCALL, offsetof(struct cg_upcall_vector_s, ts), IMM_PURPOSE_LDR_OFFSET, OP_SIZE_4));
605 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
606 gen_one(R_SCRATCH_1);
607 gen_address_offset();
609 g(gen_address(ctx, R_SP, SAVE_OFFSET, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
610 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
611 gen_one(R_SCRATCH_2);
612 gen_address_offset();
614 g(gen_cmp_test_jmp(ctx, INSN_CMP, OP_SIZE_NATIVE, R_SCRATCH_1, R_SCRATCH_2, COND_NE, escape_label));