implement array_len_greater_than+jmp fusion
[ajla.git] / c1-loong.inc
blob0be44b2b2d0f6bcba55862bccf79279751cffbdc
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 #define OP_SIZE_NATIVE                  OP_SIZE_8
20 #define OP_SIZE_ADDRESS                 OP_SIZE_NATIVE
22 #define JMP_LIMIT                       JMP_LONG
24 #define UNALIGNED_TRAP                  (!cpu_test_feature(CPU_FEATURE_unaligned))
26 #define ALU_WRITES_FLAGS(alu, im)       0
27 #define ALU1_WRITES_FLAGS(alu)          0
28 #define ROT_WRITES_FLAGS(alu, size, im) 0
29 #define COND_IS_LOGICAL(cond)           0
31 #define ARCH_PARTIAL_ALU(size)          0
32 #define ARCH_IS_3ADDRESS(alu, f)        1
33 #define ARCH_IS_3ADDRESS_IMM(alu, f)    1
34 #define ARCH_IS_3ADDRESS_ROT(alu, size) 1
35 #define ARCH_IS_3ADDRESS_ROT_IMM(alu)   1
36 #define ARCH_IS_2ADDRESS(alu)           1
37 #define ARCH_IS_3ADDRESS_FP             1
38 #define ARCH_HAS_JMP_2REGS(cond)        ((cond) == COND_E || (cond) == COND_NE)
39 #define ARCH_HAS_FLAGS                  0
40 #define ARCH_PREFERS_SX(size)           0
41 #define ARCH_HAS_BWX                    1
42 #define ARCH_HAS_MUL                    1
43 #define ARCH_HAS_DIV                    1
44 #define ARCH_HAS_ANDN                   1
45 #define ARCH_HAS_SHIFTED_ADD(bits)      0
46 #define ARCH_HAS_BTX(btx, size, cnst)   (((btx) == BTX_BTR || (btx) == BTX_BTEXT) && (cnst))
47 #define ARCH_SHIFT_SIZE                 OP_SIZE_4
48 #define ARCH_HAS_FP_GP_MOV              1
49 #define ARCH_NEEDS_BARRIER              0
51 #define i_size(size)                    OP_SIZE_NATIVE
52 #define i_size_rot(size)                maximum(size, OP_SIZE_4)
53 #define i_size_cmp(size)                OP_SIZE_NATIVE
55 /*#define TIMESTAMP_IN_REGISTER*/
57 #define R_ZERO          0x00
58 #define R_RA            0x01
59 #define R_TP            0x02
60 #define R_SP            0x03
61 #define R_A0            0x04
62 #define R_A1            0x05
63 #define R_A2            0x06
64 #define R_A3            0x07
65 #define R_A4            0x08
66 #define R_A5            0x09
67 #define R_A6            0x0a
68 #define R_A7            0x0b
69 #define R_T0            0x0c
70 #define R_T1            0x0d
71 #define R_T2            0x0e
72 #define R_T3            0x0f
73 #define R_T4            0x10
74 #define R_T5            0x11
75 #define R_T6            0x12
76 #define R_T7            0x13
77 #define R_T8            0x14
78 #define R_RESERVED      0x15
79 #define R_FP            0x16
80 #define R_S0            0x17
81 #define R_S1            0x18
82 #define R_S2            0x19
83 #define R_S3            0x1a
84 #define R_S4            0x1b
85 #define R_S5            0x1c
86 #define R_S6            0x1d
87 #define R_S7            0x1e
88 #define R_S8            0x1f
90 #define R_FA0           0x20
91 #define R_FA1           0x21
92 #define R_FA2           0x22
93 #define R_FA3           0x23
94 #define R_FA4           0x24
95 #define R_FA5           0x25
96 #define R_FA6           0x26
97 #define R_FA7           0x27
98 #define R_FT0           0x28
99 #define R_FT1           0x29
100 #define R_FT2           0x2a
101 #define R_FT3           0x2b
102 #define R_FT4           0x2c
103 #define R_FT5           0x2d
104 #define R_FT6           0x2e
105 #define R_FT7           0x2f
106 #define R_FT8           0x30
107 #define R_FT9           0x31
108 #define R_FT10          0x32
109 #define R_FT11          0x33
110 #define R_FT12          0x34
111 #define R_FT13          0x35
112 #define R_FT14          0x36
113 #define R_FT15          0x37
114 #define R_FS0           0x38
115 #define R_FS1           0x39
116 #define R_FS2           0x3a
117 #define R_FS3           0x3b
118 #define R_FS4           0x3c
119 #define R_FS5           0x3d
120 #define R_FS6           0x3e
121 #define R_FS7           0x3f
123 #define R_FRAME         R_S0
124 #define R_UPCALL        R_S1
125 #ifdef TIMESTAMP_IN_REGISTER
126 #define R_TIMESTAMP     R_S4
127 #endif
129 #define R_SCRATCH_1     R_A0
130 #define R_SCRATCH_2     R_A1
131 #define R_SCRATCH_3     R_A2
132 #define R_SCRATCH_4     R_SAVED_2
133 #define R_SCRATCH_NA_1  R_A4
134 #define R_SCRATCH_NA_2  R_A5
135 #define R_SCRATCH_NA_3  R_A6
137 #define R_SAVED_1       R_S2
138 #define R_SAVED_2       R_S3
140 #define R_ARG0          R_A0
141 #define R_ARG1          R_A1
142 #define R_ARG2          R_A2
143 #define R_ARG3          R_A3
144 #define R_RET0          R_A0
145 #define R_RET1          R_A1
147 #define R_OFFSET_IMM    R_T0
148 #define R_CONST_IMM     R_T1
149 #define R_CMP_RESULT    R_T2
151 #define FR_SCRATCH_1    R_FA0
152 #define FR_SCRATCH_2    R_FA1
154 #define SUPPORTED_FP    0x6
156 #define FRAME_SIZE      0x60
158 static bool reg_is_fp(unsigned reg)
160         return reg >= 0x20 && reg < 0x40;
163 static const uint8_t regs_saved[] = {
164 #ifndef TIMESTAMP_IN_REGISTER
165         R_S4,
166 #endif
167         R_S5, R_S6, R_S7, R_S8, R_FP };
168 static const uint8_t regs_volatile[] = { R_RA, R_A3, R_A7, R_T3, R_T4, R_T5, R_T6, R_T7, R_T8 };
169 static const uint8_t fp_saved[] = { 0 };
170 #define n_fp_saved 0U
171 static const uint8_t fp_volatile[] = { R_FA2, R_FA3, R_FA4, R_FA5, R_FA6, R_FA7, R_FT0, R_FT1, R_FT2, R_FT3, R_FT4, R_FT5, R_FT6, R_FT7, R_FT8, R_FT9, R_FT10, R_FT11, R_FT12, R_FT13, R_FT14, R_FT15 };
172 #define reg_is_saved(r) ((r) >= R_FP && (r) <= R_S8)
174 static bool attr_w gen_load_constant(struct codegen_context *ctx, unsigned reg, uint64_t c)
176         uint64_t c0 = c & 0x0000000000000fffULL;
177         uint64_t c1 = c & 0x00000000fffff000ULL;
178         uint64_t c2 = c & 0x000fffff00000000ULL;
179         uint64_t c3 = c & 0xfff0000000000000ULL;
180         uint64_t top_bits = 0;
181         if (!(c0 | c1 | c2)) {
182                 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
183                 gen_one(reg);
184                 gen_one(ARG_IMM);
185                 gen_eight(c3);
186                 return true;
187         }
188         if (c0 & 0x800ULL && c1 == 0xfffff000ULL) {
189                 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
190                 gen_one(reg);
191                 gen_one(ARG_IMM);
192                 gen_eight(c0 | 0xfffffffffffff000ULL);
193                 top_bits = 0xffffffff00000000ULL;
194         } else {
195                 bool have_reg = false;
196                 if (c1) {
197                         gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
198                         gen_one(reg);
199                         gen_one(ARG_IMM);
200                         gen_eight((uint64_t)(int32_t)c1);
201                         top_bits = (uint64_t)(int32_t)c1 & 0xffffffff00000000ULL;
202                         have_reg = true;
203                 }
204                 if (!have_reg || c0) {
205                         if (!have_reg) {
206                                 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
207                                 gen_one(reg);
208                                 gen_one(ARG_IMM);
209                                 gen_eight(c0);
210                         } else {
211                                 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
212                                 gen_one(reg);
213                                 gen_one(reg);
214                                 gen_one(ARG_IMM);
215                                 gen_eight(c0);
216                         }
217                 }
218         }
219         if (top_bits != (c2 | c3)) {
220                 uint64_t c2x = c2;
221                 if (c2 & 0x0008000000000000ULL)
222                         c2x |= 0xfff0000000000000ULL;
223                 if (top_bits != c2x) {
224                         gen_insn(INSN_MOV_MASK, OP_SIZE_NATIVE, MOV_MASK_32_64, 0);
225                         gen_one(reg);
226                         gen_one(reg);
227                         gen_one(ARG_IMM);
228                         gen_eight(c2x >> 32);
229                 }
230                 top_bits = c2x & 0xfff0000000000000ULL;
231                 if (top_bits != c3) {
232                         gen_insn(INSN_MOV_MASK, OP_SIZE_NATIVE, MOV_MASK_52_64, 0);
233                         gen_one(reg);
234                         gen_one(reg);
235                         gen_one(ARG_IMM);
236                         gen_eight(c3 >> 52);
237                 }
238         }
239         return true;
242 static bool attr_w gen_address(struct codegen_context *ctx, unsigned base, int64_t imm, unsigned purpose, unsigned size)
244         ctx->base_reg = base;
245         ctx->offset_imm = imm;
246         ctx->offset_reg = false;
247         switch (purpose) {
248                 case IMM_PURPOSE_LDR_OFFSET:
249                 case IMM_PURPOSE_LDR_SX_OFFSET:
250                 case IMM_PURPOSE_STR_OFFSET:
251                 case IMM_PURPOSE_VLDR_VSTR_OFFSET:
252                 case IMM_PURPOSE_MVI_CLI_OFFSET:
253                         if (likely(imm >= -0x800) && likely(imm < 0x800)) {
254                                 return true;
255                         }
256                         if (imm >= -0x8000 && imm < 0x8000 && !(imm & 3)) {
257                                 if (size == OP_SIZE_NATIVE)
258                                         return true;
259                                 if (purpose == IMM_PURPOSE_LDR_SX_OFFSET && size == OP_SIZE_4)
260                                         return true;
261                                 if (purpose == IMM_PURPOSE_STR_OFFSET && size == OP_SIZE_4)
262                                         return true;
263                         }
264                         break;
265                 default:
266                         internal(file_line, "gen_address: invalid purpose %u (imm %"PRIxMAX", size %u)", purpose, (uintmax_t)imm, size);
267         }
268         g(gen_load_constant(ctx, R_OFFSET_IMM, imm));
269         ctx->offset_reg = true;
270         return true;
273 static bool is_direct_const(int64_t imm, unsigned purpose, unsigned size)
275         switch (purpose) {
276                 case IMM_PURPOSE_STORE_VALUE:
277                         if (!imm)
278                                 return true;
279                         break;
280                 case IMM_PURPOSE_ADD:
281                 case IMM_PURPOSE_CMP:
282                 case IMM_PURPOSE_CMP_LOGICAL:
283                         if (likely(imm >= -0x800) && likely(imm < 0x800))
284                                 return true;
285                         break;
286                 case IMM_PURPOSE_SUB:
287                         if (likely(imm > -0x800) && likely(imm <= 0x800))
288                                 return true;
289                         break;
290                 case IMM_PURPOSE_AND:
291                 case IMM_PURPOSE_OR:
292                 case IMM_PURPOSE_XOR:
293                         if (likely(imm >= 0) && likely(imm < 0x1000))
294                                 return true;
295                         break;
296                 case IMM_PURPOSE_ANDN:
297                         break;
298                 case IMM_PURPOSE_TEST:
299                         break;
300                 case IMM_PURPOSE_JMP_2REGS:
301                         break;
302                 case IMM_PURPOSE_MUL:
303                         break;
304                 case IMM_PURPOSE_BITWISE:
305                         return true;
306                 default:
307                         internal(file_line, "is_direct_const: invalid purpose %u (imm %"PRIxMAX", size %u)", purpose, (uintmax_t)imm, size);
308         }
309         return false;
312 static bool attr_w gen_imm(struct codegen_context *ctx, int64_t imm, unsigned purpose, unsigned size)
314         if (is_direct_const(imm, purpose, size)) {
315                 ctx->const_imm = imm;
316                 ctx->const_reg = false;
317         } else {
318                 g(gen_load_constant(ctx, R_CONST_IMM, imm));
319                 ctx->const_reg = true;
320         }
321         return true;
324 static bool attr_w gen_entry(struct codegen_context *ctx)
326         int offset, i;
328         g(gen_imm(ctx, -FRAME_SIZE, IMM_PURPOSE_ADD, OP_SIZE_NATIVE));
329         gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
330         gen_one(R_SP);
331         gen_one(R_SP);
332         gen_imm_offset();
334         offset = FRAME_SIZE - (1 << OP_SIZE_NATIVE);
336         g(gen_address(ctx, R_SP, FRAME_SIZE - 0x08, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
337         gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
338         gen_address_offset();
339         gen_one(R_RA);
340         offset -= 1 << OP_SIZE_NATIVE;
342         for (i = R_FP; i <= R_S8; i++) {
343                 g(gen_address(ctx, R_SP, offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
344                 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
345                 gen_address_offset();
346                 gen_one(i);
347                 offset -= 1 << OP_SIZE_NATIVE;
348         }
350 #ifndef TIMESTAMP_IN_REGISTER
351         g(gen_address(ctx, R_SP, offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
352         gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
353         gen_address_offset();
354         gen_one(R_ARG2);
355 #endif
357         gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
358         gen_one(R_FRAME);
359         gen_one(R_ARG0);
361         gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
362         gen_one(R_UPCALL);
363         gen_one(R_ARG1);
365 #ifdef TIMESTAMP_IN_REGISTER
366         gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
367         gen_one(R_TIMESTAMP);
368         gen_one(R_ARG2);
369 #endif
371         gen_insn(INSN_JMP_INDIRECT, 0, 0, 0);
372         gen_one(R_ARG3);
374         return true;
377 static bool attr_w gen_escape_arg(struct codegen_context *ctx, ip_t ip, uint32_t escape_label)
379         g(gen_load_constant(ctx, R_RET1, ip));
381         gen_insn(INSN_JMP, 0, 0, 0);
382         gen_four(escape_label);
384         return true;
387 static bool attr_w gen_escape(struct codegen_context *ctx)
389         int offset, i;
391         gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
392         gen_one(R_RET0);
393         gen_one(R_FRAME);
395         offset = FRAME_SIZE - (1 << OP_SIZE_NATIVE);
397         g(gen_address(ctx, R_SP, FRAME_SIZE - 0x08, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
398         gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
399         gen_one(R_RA);
400         gen_address_offset();
401         offset -= 1 << OP_SIZE_NATIVE;
403         for (i = R_FP; i <= R_S8; i++) {
404                 g(gen_address(ctx, R_SP, offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
405                 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
406                 gen_one(i);
407                 gen_address_offset();
408                 offset -= 1 << OP_SIZE_NATIVE;
409         }
411         g(gen_imm(ctx, FRAME_SIZE, IMM_PURPOSE_ADD, OP_SIZE_NATIVE));
412         gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
413         gen_one(R_SP);
414         gen_one(R_SP);
415         gen_imm_offset();
417         gen_insn(INSN_RET, 0, 0, 0);
419         return true;
422 static bool attr_w gen_upcall_argument(struct codegen_context attr_unused *ctx, unsigned attr_unused arg)
424         return true;
427 static bool attr_w gen_upcall(struct codegen_context *ctx, unsigned offset, unsigned n_args)
429         g(gen_address(ctx, R_UPCALL, offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_ADDRESS));
430         gen_insn(INSN_MOV, OP_SIZE_ADDRESS, 0, 0);
431         gen_one(R_SCRATCH_NA_1);
432         gen_address_offset();
434         gen_insn(INSN_CALL_INDIRECT, OP_SIZE_ADDRESS, 0, 0);
435         gen_one(R_SCRATCH_NA_1);
437         g(gen_upcall_end(ctx, n_args));
439         return true;
442 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);
444 static bool attr_w gen_timestamp_test(struct codegen_context *ctx, uint32_t escape_label)
446         g(gen_address(ctx, R_UPCALL, offsetof(struct cg_upcall_vector_s, ts), IMM_PURPOSE_LDR_SX_OFFSET, OP_SIZE_4));
447         gen_insn(INSN_MOVSX, OP_SIZE_4, 0, 0);
448         gen_one(R_SCRATCH_1);
449         gen_address_offset();
451 #ifdef TIMESTAMP_IN_REGISTER
452         g(gen_cmp_test_jmp(ctx, INSN_CMP, OP_SIZE_NATIVE, R_SCRATCH_1, R_TIMESTAMP, COND_NE, escape_label));
453 #else
454         g(gen_address(ctx, R_SP, 0, IMM_PURPOSE_LDR_SX_OFFSET, OP_SIZE_4));
455         gen_insn(INSN_MOVSX, OP_SIZE_4, 0, 0);
456         gen_one(R_SCRATCH_2);
457         gen_address_offset();
459         g(gen_cmp_test_jmp(ctx, INSN_CMP, OP_SIZE_NATIVE, R_SCRATCH_1, R_SCRATCH_2, COND_NE, escape_label));
460 #endif
462         return true;