Ajla 0.1.5
[ajla.git] / c1-mips.inc
blob5607a63154b9c646367b330ed10bf253e236de87
1 /*
2  * Copyright (C) 2024 Mikulas Patocka
3  *
4  * This file is part of Ajla.
5  *
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
9  * version.
10  *
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.
14  *
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/>.
17  */
19 #ifdef ARCH_MIPS_O32
20 #define OP_SIZE_NATIVE  OP_SIZE_4
21 #else
22 #define OP_SIZE_NATIVE  OP_SIZE_8
23 #endif
25 #ifdef ARCH_MIPS32
26 #define OP_SIZE_ADDRESS OP_SIZE_4
27 #else
28 #define OP_SIZE_ADDRESS OP_SIZE_8
29 #endif
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)
57 #if __mips < 2
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
62 #else
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
67 #endif
69 #if __mips < 4
70 #define MIPS_R4000_ERRATA               1
71 #define MIPS_FCMP_DELAY_SLOTS           1
72 #define MIPS_HAS_MOVT                   0
73 #else
74 #define MIPS_R4000_ERRATA               0
75 #define MIPS_FCMP_DELAY_SLOTS           0
76 #define MIPS_HAS_MOVT                   1
77 #endif
79 #if __mips < 32
80 #define MIPS_HAS_CLZ                    0
81 #define MIPS_HAS_MUL                    0
82 #else
83 #define MIPS_HAS_CLZ                    1
84 #define MIPS_HAS_MUL                    1
85 #endif
87 #if __mips < 32 || !defined(__mips_isa_rev) || __mips_isa_rev < 2
88 #define MIPS_HAS_ROT                    0
89 #else
90 #define MIPS_HAS_ROT                    1
91 #endif
93 #if __mips < 32 || !defined(__mips_isa_rev) || __mips_isa_rev < 6
94 #define MIPS_R6                         0
95 #else
96 #define MIPS_R6                         1
97 #endif
99 #define R_ZERO          0x00
100 #define R_AT            0x01
101 #define R_V0            0x02
102 #define R_V1            0x03
103 #define R_A0            0x04
104 #define R_A1            0x05
105 #define R_A2            0x06
106 #define R_A3            0x07
107 #define R_T0            0x08
108 #define R_T1            0x09
109 #define R_T2            0x0a
110 #define R_T3            0x0b
111 #define R_T4            0x0c
112 #define R_T5            0x0d
113 #define R_T6            0x0e
114 #define R_T7            0x0f
115 #define R_S0            0x10
116 #define R_S1            0x11
117 #define R_S2            0x12
118 #define R_S3            0x13
119 #define R_S4            0x14
120 #define R_S5            0x15
121 #define R_S6            0x16
122 #define R_S7            0x17
123 #define R_T8            0x18
124 #define R_T9            0x19
125 #define R_K0            0x1a
126 #define R_K1            0x1b
127 #define R_GP            0x1c
128 #define R_SP            0x1d
129 #define R_S8            0x1e
130 #define R_RA            0x1f
132 #define R_F0            0x20
133 #define R_F1            0x21
134 #define R_F2            0x22
135 #define R_F3            0x23
136 #define R_F4            0x24
137 #define R_F5            0x25
138 #define R_F6            0x26
139 #define R_F7            0x27
140 #define R_F8            0x28
141 #define R_F9            0x29
142 #define R_F10           0x2a
143 #define R_F11           0x2b
144 #define R_F12           0x2c
145 #define R_F13           0x2d
146 #define R_F14           0x2e
147 #define R_F15           0x2f
148 #define R_F16           0x30
149 #define R_F17           0x31
150 #define R_F18           0x32
151 #define R_F19           0x33
152 #define R_F20           0x34
153 #define R_F21           0x35
154 #define R_F22           0x36
155 #define R_F23           0x37
156 #define R_F24           0x38
157 #define R_F25           0x39
158 #define R_F26           0x3a
159 #define R_F27           0x3b
160 #define R_F28           0x3c
161 #define R_F29           0x3d
162 #define R_F30           0x3e
163 #define R_F31           0x3f
165 #define R_FRAME         R_S0
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
180 #define R_ARG0          R_A0
181 #define R_ARG1          R_A1
182 #define R_ARG2          R_A2
183 #define R_ARG3          R_A3
185 #define R_RET0          R_V0
186 #define R_RET1          R_V1
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
199 #ifdef ARCH_MIPS_O32
200 #define FRAME_SIZE      48
201 #define SAVE_OFFSET     24
202 #else
203 #define FRAME_SIZE      64
204 #define SAVE_OFFSET     8
205 #endif
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)
215                 c = (int32_t)c;
216         if ((int64_t)c != (int32_t)c) {
217                 if (MIPS_R6) {
218                         g(gen_load_constant(ctx, reg, (int32_t)c));
219                         if ((int32_t)c < 0) {
220                                 c += 0x100000000ULL;
221                         }
222                         c &= ~0xFFFFFFFFULL;
223                         c >>= 32;
224                         if (c & 0xFFFFULL) {
225                                 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
226                                 gen_one(reg);
227                                 gen_one(reg);
228                                 gen_one(ARG_IMM);
229                                 gen_eight((uint64_t)(int16_t)c << 32);
230                         }
231                         if ((int16_t)c < 0) {
232                                 c += 0x10000ULL;
233                         }
234                         c &= ~0xFFFFULL;
235                         c >>= 16;
236                         if (c & 0xFFFFULL) {
237                                 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
238                                 gen_one(reg);
239                                 gen_one(reg);
240                                 gen_one(ARG_IMM);
241                                 gen_eight(c << 48);
242                         }
243                         return true;
244                 }
245                 if (c && !(c & (c - 1))) {
246                         int bit = -1;
247                         uint64_t cc = c;
248                         do {
249                                 cc >>= 1;
250                                 bit++;
251                         } while (cc);
252                         gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
253                         gen_one(reg);
254                         gen_one(R_ZERO);
255                         gen_one(ARG_IMM);
256                         gen_eight(1);
258                         gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
259                         gen_one(reg);
260                         gen_one(reg);
261                         gen_one(ARG_IMM);
262                         gen_eight(bit);
264                         return true;
265                 }
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);
270                         gen_one(reg);
271                         gen_one(reg);
273                         return true;
274                 }
275                 g(gen_load_constant(ctx, reg, (int32_t)((c & 0xFFFFFFFF00000000ULL) >> 32)));
276                 c &= 0xFFFFFFFFULL;
277                 if (c & 0xFFFF0000ULL) {
278                         gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
279                         gen_one(reg);
280                         gen_one(reg);
281                         gen_one(ARG_IMM);
282                         gen_eight(16);
284                         gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
285                         gen_one(reg);
286                         gen_one(reg);
287                         gen_one(ARG_IMM);
288                         gen_eight(c >> 16);
290                         gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
291                         gen_one(reg);
292                         gen_one(reg);
293                         gen_one(ARG_IMM);
294                         gen_eight(16);
295                 } else {
296                         gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
297                         gen_one(reg);
298                         gen_one(reg);
299                         gen_one(ARG_IMM);
300                         gen_eight(32);
301                 }
302                 if (c & 0xFFFFULL) {
303                         gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
304                         gen_one(reg);
305                         gen_one(reg);
306                         gen_one(ARG_IMM);
307                         gen_eight(c & 0xFFFFULL);
308                 }
309                 return true;
310         }
311         if (c == (uint16_t)c) {
312                 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
313                 gen_one(reg);
314                 gen_one(R_ZERO);
315                 gen_one(ARG_IMM);
316                 gen_eight(c);
317                 return true;
318         }
319         if ((int64_t)c == (int16_t)c) {
320                 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
321                 gen_one(reg);
322                 gen_one(R_ZERO);
323                 gen_one(ARG_IMM);
324                 gen_eight(c);
325                 return true;
326         }
327         gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
328         gen_one(reg);
329         gen_one(ARG_IMM);
330         gen_eight(c & ~0xffffULL);
331         if (c & 0xFFFFULL) {
332                 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
333                 gen_one(reg);
334                 gen_one(reg);
335                 gen_one(ARG_IMM);
336                 gen_eight(c & 0xFFFFULL);
337         }
338         return true;
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;
346         switch (purpose) {
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))
353                                 return true;
354                         break;
355                 default:
356                         internal(file_line, "gen_address: invalid purpose %u (imm %"PRIxMAX", size %u)", purpose, (uintmax_t)imm, size);
357         }
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);
362         gen_one(base);
363         ctx->base_reg = R_OFFSET_IMM;
364         ctx->offset_imm = 0;
365         return true;
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)
371                 return false;
372         switch (purpose) {
373                 case IMM_PURPOSE_STORE_VALUE:
374                         if (!imm)
375                                 return true;
376                         break;
377                 case IMM_PURPOSE_ADD:
378                 case IMM_PURPOSE_CMP:
379                 case IMM_PURPOSE_CMP_LOGICAL:
380                         if (likely(imm == (int16_t)imm))
381                                 return true;
382                         break;
383                 case IMM_PURPOSE_SUB:
384                         if (likely(imm > -0x8000) && likely(imm <= 0x8000))
385                                 return true;
386                         break;
387                 case IMM_PURPOSE_AND:
388                 case IMM_PURPOSE_OR:
389                 case IMM_PURPOSE_XOR:
390                 case IMM_PURPOSE_TEST:
391                         if (likely((uint64_t)imm == (uint16_t)imm))
392                                 return true;
393                         break;
394                 case IMM_PURPOSE_MUL:
395                         break;
396                 default:
397                         internal(file_line, "is_direct_const: invalid purpose %u (imm %"PRIxMAX", size %u)", purpose, (uintmax_t)imm, size);
398         }
399         return false;
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;
407         } else {
408                 g(gen_load_constant(ctx, R_CONST_IMM, imm));
409                 ctx->const_reg = true;
410         }
411         return true;
414 static bool attr_w gen_entry(struct codegen_context *ctx)
416         int save_offset;
417         unsigned reg;
419         gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_SUB, 0);
420         gen_one(R_SP);
421         gen_one(R_SP);
422         gen_one(ARG_IMM);
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();
430                 gen_one(reg);
431                 save_offset += 1 << OP_SIZE_NATIVE;
432         }
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();
436         gen_one(R_RA);
438         gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
439         gen_one(R_FRAME);
440         gen_one(R_ARG0);
442         gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
443         gen_one(R_UPCALL);
444         gen_one(R_ARG1);
446         gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
447         gen_one(R_TIMESTAMP);
448         gen_one(R_ARG2);
450         gen_insn(INSN_JMP_INDIRECT, 0, 0, 0);
451         gen_one(R_ARG3);
453         return true;
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);
463         return true;
466 static bool attr_w gen_escape(struct codegen_context *ctx)
468         int save_offset;
469         unsigned reg;
471 #if defined(ARCH_MIPS_N32)
472         gen_insn(INSN_ROT, OP_SIZE_NATIVE, ROT_SHL, 0);
473         gen_one(R_RET1);
474         gen_one(R_RET1);
475         gen_one(ARG_IMM);
476         gen_eight(32);
478         gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
479         gen_one(R_RET0);
480         gen_one(R_FRAME);
481         gen_one(R_RET1);
482 #else
483         gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
484         gen_one(R_RET0);
485         gen_one(R_FRAME);
486 #endif
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);
492                 gen_one(reg);
493                 gen_address_offset();
494                 save_offset += 1 << OP_SIZE_NATIVE;
495         }
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);
498         gen_one(R_RA);
499         gen_address_offset();
501         gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
502         gen_one(R_SP);
503         gen_one(R_SP);
504         gen_one(ARG_IMM);
505         gen_eight(FRAME_SIZE);
507         gen_insn(INSN_RET, 0, 0, 0);
509         return true;
512 static bool attr_w gen_upcall_argument(struct codegen_context attr_unused *ctx, unsigned attr_unused arg)
514         return true;
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);
521         gen_one(R_T9);
522         gen_address_offset();
524         gen_insn(INSN_CALL_INDIRECT, OP_SIZE_ADDRESS, 0, 0);
525         gen_one(R_T9);
527         return true;
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));
541         return true;