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) 0
38 #define COND_IS_LOGICAL(cond) 0
40 #define ARCH_PARTIAL_ALU(size) 0
41 #define ARCH_IS_3ADDRESS 1
42 #define ARCH_HAS_FLAGS 0
43 #define ARCH_SUPPORTS_TRAPS OS_SUPPORTS_TRAPS
44 #define ARCH_PREFERS_SX(size) 0
45 #define ARCH_HAS_BWX 1
46 #define ARCH_HAS_MUL 1
47 #define ARCH_HAS_DIV 1
48 #define ARCH_HAS_ANDN 0
49 #define ARCH_HAS_SHIFTED_ADD(bits) (MIPS_R6 && (bits) >= 1 && (bits) <= 4)
50 #define ARCH_HAS_BTX(btx, size, cnst) 0
51 #define ARCH_SHIFT_SIZE OP_SIZE_4
52 #define ARCH_NEEDS_BARRIER 0
54 #define i_size(size) OP_SIZE_NATIVE
55 #define i_size_rot(size) maximum(size, OP_SIZE_4)
58 #define MIPS_LOAD_DELAY_SLOTS 1
59 #define MIPS_HAS_LS_DOUBLE 0
60 #define MIPS_HAS_SQRT 0
61 #define MIPS_HAS_TRUNC 0
63 #define MIPS_LOAD_DELAY_SLOTS 0
64 #define MIPS_HAS_LS_DOUBLE 1
65 #define MIPS_HAS_SQRT 1
66 #define MIPS_HAS_TRUNC 1
70 #define MIPS_R4000_ERRATA 1
71 #define MIPS_FCMP_DELAY_SLOTS 1
72 #define MIPS_HAS_MOVT 0
74 #define MIPS_R4000_ERRATA 0
75 #define MIPS_FCMP_DELAY_SLOTS 0
76 #define MIPS_HAS_MOVT 1
80 #define MIPS_HAS_CLZ 0
81 #define MIPS_HAS_MUL 0
83 #define MIPS_HAS_CLZ 1
84 #define MIPS_HAS_MUL 1
87 #if __mips < 32 || !defined(__mips_isa_rev) || __mips_isa_rev < 2
88 #define MIPS_HAS_ROT 0
90 #define MIPS_HAS_ROT 1
93 #if __mips < 32 || !defined(__mips_isa_rev) || __mips_isa_rev < 6
166 #define R_UPCALL R_S1
167 #define R_TIMESTAMP R_S2
169 #define R_SCRATCH_1 R_A0
170 #define R_SCRATCH_2 R_A1
171 #define R_SCRATCH_3 R_A2
172 #define R_SCRATCH_4 R_A3
173 #define R_SCRATCH_NA_1 R_T0
174 #define R_SCRATCH_NA_2 R_T1
175 #define R_SCRATCH_NA_3 R_T2
177 #define R_SAVED_1 R_S3
178 #define R_SAVED_2 R_S4
188 #define R_OFFSET_IMM R_T3
189 #define R_CONST_IMM R_T4
190 #define R_CMP_RESULT R_T5
192 #define FR_SCRATCH_1 R_F0
193 #define FR_SCRATCH_2 R_F2
194 #define FR_SCRATCH_3 R_F4
195 #define FR_CMP_RESULT R_F6
197 #define SUPPORTED_FP 0x6
200 #define FRAME_SIZE 48
201 #define SAVE_OFFSET 24
203 #define FRAME_SIZE 64
204 #define SAVE_OFFSET 8
207 static bool reg_is_fp(unsigned reg)
209 return reg >= 0x20 && reg < 0x40;
212 static bool attr_w gen_load_constant(struct codegen_context *ctx, unsigned reg, uint64_t c)
214 if (OP_SIZE_NATIVE == OP_SIZE_4)
216 if ((int64_t)c != (int32_t)c) {
218 g(gen_load_constant(ctx, reg, (int32_t)c));
219 if ((int32_t)c < 0) {
225 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
229 gen_eight((uint64_t)(int16_t)c << 32);
231 if ((int16_t)c < 0) {
237 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
245 if (c && !(c & (c - 1))) {
252 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
258 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
266 if (~c && !(~c & (~c - 1))) {
267 g(gen_load_constant(ctx, reg, ~c));
269 gen_insn(INSN_ALU1, OP_SIZE_NATIVE, ALU1_NOT, 0);
275 g(gen_load_constant(ctx, reg, (int32_t)((c & 0xFFFFFFFF00000000ULL) >> 32)));
277 if (c & 0xFFFF0000ULL) {
278 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
284 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
290 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
296 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
303 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
307 gen_eight(c & 0xFFFFULL);
311 if (c == (uint16_t)c) {
312 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
319 if ((int64_t)c == (int16_t)c) {
320 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
327 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
330 gen_eight(c & ~0xffffULL);
332 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
336 gen_eight(c & 0xFFFFULL);
341 static bool attr_w gen_address(struct codegen_context *ctx, unsigned base, int64_t imm, unsigned purpose, unsigned size)
343 ctx->base_reg = base;
344 ctx->offset_imm = imm;
345 ctx->offset_reg = false;
347 case IMM_PURPOSE_LDR_OFFSET:
348 case IMM_PURPOSE_LDR_SX_OFFSET:
349 case IMM_PURPOSE_STR_OFFSET:
350 case IMM_PURPOSE_VLDR_VSTR_OFFSET:
351 case IMM_PURPOSE_MVI_CLI_OFFSET:
352 if (likely(imm == (int16_t)imm))
356 internal(file_line, "gen_address: invalid purpose %u (imm %"PRIxMAX", size %u)", purpose, (uintmax_t)imm, size);
358 g(gen_load_constant(ctx, R_OFFSET_IMM, imm));
359 gen_insn(INSN_ALU, OP_SIZE_ADDRESS, ALU_ADD, 0);
360 gen_one(R_OFFSET_IMM);
361 gen_one(R_OFFSET_IMM);
363 ctx->base_reg = R_OFFSET_IMM;
368 static bool is_direct_const(int64_t imm, unsigned purpose, unsigned size)
370 if (MIPS_R4000_ERRATA && (purpose == IMM_PURPOSE_ADD || purpose == IMM_PURPOSE_SUB) && size == OP_SIZE_8)
373 case IMM_PURPOSE_STORE_VALUE:
377 case IMM_PURPOSE_ADD:
378 case IMM_PURPOSE_CMP:
379 case IMM_PURPOSE_CMP_LOGICAL:
380 if (likely(imm == (int16_t)imm))
383 case IMM_PURPOSE_SUB:
384 if (likely(imm > -0x8000) && likely(imm <= 0x8000))
387 case IMM_PURPOSE_AND:
389 case IMM_PURPOSE_XOR:
390 case IMM_PURPOSE_TEST:
391 if (likely((uint64_t)imm == (uint16_t)imm))
394 case IMM_PURPOSE_MUL:
397 internal(file_line, "is_direct_const: invalid purpose %u (imm %"PRIxMAX", size %u)", purpose, (uintmax_t)imm, size);
402 static bool attr_w gen_imm(struct codegen_context *ctx, int64_t imm, unsigned purpose, unsigned size)
404 if (is_direct_const(imm, purpose, size)) {
405 ctx->const_imm = imm;
406 ctx->const_reg = false;
408 g(gen_load_constant(ctx, R_CONST_IMM, imm));
409 ctx->const_reg = true;
414 static bool attr_w gen_entry(struct codegen_context *ctx)
419 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_SUB, 0);
423 gen_eight(FRAME_SIZE);
425 save_offset = SAVE_OFFSET;
426 for (reg = R_S0; reg <= R_S4; reg++) {
427 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
428 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
429 gen_address_offset();
431 save_offset += 1 << OP_SIZE_NATIVE;
433 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
434 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
435 gen_address_offset();
438 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
442 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
446 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
447 gen_one(R_TIMESTAMP);
450 gen_insn(INSN_JMP_INDIRECT, 0, 0, 0);
456 static bool attr_w gen_escape_arg(struct codegen_context *ctx, ip_t ip, uint32_t escape_label)
458 g(gen_load_constant(ctx, R_RET1, (int32_t)ip));
460 gen_insn(INSN_JMP, 0, 0, 0);
461 gen_four(escape_label);
466 static bool attr_w gen_escape(struct codegen_context *ctx)
471 #if defined(ARCH_MIPS_N32)
472 gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
478 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
483 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
488 save_offset = SAVE_OFFSET;
489 for (reg = R_S0; reg <= R_S4; reg++) {
490 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_NATIVE));
491 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
493 gen_address_offset();
494 save_offset += 1 << OP_SIZE_NATIVE;
496 g(gen_address(ctx, R_SP, save_offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_NATIVE));
497 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
499 gen_address_offset();
501 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
505 gen_eight(FRAME_SIZE);
507 gen_insn(INSN_RET, 0, 0, 0);
512 static bool attr_w gen_upcall_argument(struct codegen_context attr_unused *ctx, unsigned attr_unused arg)
517 static bool attr_w gen_upcall(struct codegen_context *ctx, unsigned offset, unsigned attr_unused n_args)
519 g(gen_address(ctx, R_UPCALL, offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_ADDRESS));
520 gen_insn(INSN_MOV, OP_SIZE_ADDRESS, 0, 0);
522 gen_address_offset();
524 gen_insn(INSN_CALL_INDIRECT, OP_SIZE_ADDRESS, 0, 0);
530 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);
532 static bool attr_w gen_timestamp_test(struct codegen_context *ctx, uint32_t escape_label)
534 g(gen_address(ctx, R_UPCALL, offsetof(struct cg_upcall_vector_s, ts), IMM_PURPOSE_LDR_OFFSET, OP_SIZE_4));
535 gen_insn(INSN_MOVSX, OP_SIZE_4, 0, 0);
536 gen_one(R_SCRATCH_1);
537 gen_address_offset();
539 g(gen_cmp_test_jmp(ctx, INSN_CMP, OP_SIZE_NATIVE, R_SCRATCH_1, R_TIMESTAMP, COND_NE, escape_label));