ssa: move optimizations from P_BinaryOp to P_BinaryConstOp
[ajla.git] / c1-loong.inc
blob23f712b65d11dec417308ff798b32fbc24e9e574
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_BOOL_SIZE                  OP_SIZE_NATIVE
49 #define ARCH_HAS_FP_GP_MOV              1
50 #define ARCH_NEEDS_BARRIER              0
52 #define i_size(size)                    OP_SIZE_NATIVE
53 #define i_size_rot(size)                maximum(size, OP_SIZE_4)
54 #define i_size_cmp(size)                OP_SIZE_NATIVE
56 /*#define TIMESTAMP_IN_REGISTER*/
58 #define R_ZERO          0x00
59 #define R_RA            0x01
60 #define R_TP            0x02
61 #define R_SP            0x03
62 #define R_A0            0x04
63 #define R_A1            0x05
64 #define R_A2            0x06
65 #define R_A3            0x07
66 #define R_A4            0x08
67 #define R_A5            0x09
68 #define R_A6            0x0a
69 #define R_A7            0x0b
70 #define R_T0            0x0c
71 #define R_T1            0x0d
72 #define R_T2            0x0e
73 #define R_T3            0x0f
74 #define R_T4            0x10
75 #define R_T5            0x11
76 #define R_T6            0x12
77 #define R_T7            0x13
78 #define R_T8            0x14
79 #define R_RESERVED      0x15
80 #define R_FP            0x16
81 #define R_S0            0x17
82 #define R_S1            0x18
83 #define R_S2            0x19
84 #define R_S3            0x1a
85 #define R_S4            0x1b
86 #define R_S5            0x1c
87 #define R_S6            0x1d
88 #define R_S7            0x1e
89 #define R_S8            0x1f
91 #define R_FA0           0x20
92 #define R_FA1           0x21
93 #define R_FA2           0x22
94 #define R_FA3           0x23
95 #define R_FA4           0x24
96 #define R_FA5           0x25
97 #define R_FA6           0x26
98 #define R_FA7           0x27
99 #define R_FT0           0x28
100 #define R_FT1           0x29
101 #define R_FT2           0x2a
102 #define R_FT3           0x2b
103 #define R_FT4           0x2c
104 #define R_FT5           0x2d
105 #define R_FT6           0x2e
106 #define R_FT7           0x2f
107 #define R_FT8           0x30
108 #define R_FT9           0x31
109 #define R_FT10          0x32
110 #define R_FT11          0x33
111 #define R_FT12          0x34
112 #define R_FT13          0x35
113 #define R_FT14          0x36
114 #define R_FT15          0x37
115 #define R_FS0           0x38
116 #define R_FS1           0x39
117 #define R_FS2           0x3a
118 #define R_FS3           0x3b
119 #define R_FS4           0x3c
120 #define R_FS5           0x3d
121 #define R_FS6           0x3e
122 #define R_FS7           0x3f
124 #define R_FRAME         R_S0
125 #define R_UPCALL        R_S1
126 #ifdef TIMESTAMP_IN_REGISTER
127 #define R_TIMESTAMP     R_S4
128 #endif
130 #define R_SCRATCH_1     R_A0
131 #define R_SCRATCH_2     R_A1
132 #define R_SCRATCH_3     R_A2
133 #define R_SCRATCH_4     R_SAVED_2
134 #define R_SCRATCH_NA_1  R_A4
135 #define R_SCRATCH_NA_2  R_A5
136 #define R_SCRATCH_NA_3  R_A6
138 #define R_SAVED_1       R_S2
139 #define R_SAVED_2       R_S3
141 #define R_ARG0          R_A0
142 #define R_ARG1          R_A1
143 #define R_ARG2          R_A2
144 #define R_ARG3          R_A3
145 #define R_RET0          R_A0
146 #define R_RET1          R_A1
148 #define R_OFFSET_IMM    R_T0
149 #define R_CONST_IMM     R_T1
150 #define R_CMP_RESULT    R_T2
152 #define FR_SCRATCH_1    R_FA0
153 #define FR_SCRATCH_2    R_FA1
155 #define SUPPORTED_FP    0x6
157 #define FRAME_SIZE      0x60
159 static bool reg_is_fp(unsigned reg)
161         return reg >= 0x20 && reg < 0x40;
164 static const uint8_t regs_saved[] = {
165 #ifndef TIMESTAMP_IN_REGISTER
166         R_S4,
167 #endif
168         R_S5, R_S6, R_S7, R_S8, R_FP };
169 static const uint8_t regs_volatile[] = { R_RA, R_A3, R_A7, R_T3, R_T4, R_T5, R_T6, R_T7, R_T8 };
170 static const uint8_t fp_saved[] = { 0 };
171 #define n_fp_saved 0U
172 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 };
173 #define reg_is_saved(r) ((r) >= R_FP && (r) <= R_S8)
175 static bool attr_w gen_load_constant(struct codegen_context *ctx, unsigned reg, uint64_t c)
177         uint64_t c0 = c & 0x0000000000000fffULL;
178         uint64_t c1 = c & 0x00000000fffff000ULL;
179         uint64_t c2 = c & 0x000fffff00000000ULL;
180         uint64_t c3 = c & 0xfff0000000000000ULL;
181         uint64_t top_bits = 0;
182         if (!(c0 | c1 | c2)) {
183                 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
184                 gen_one(reg);
185                 gen_one(ARG_IMM);
186                 gen_eight(c3);
187                 return true;
188         }
189         if (c0 & 0x800ULL && c1 == 0xfffff000ULL) {
190                 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
191                 gen_one(reg);
192                 gen_one(ARG_IMM);
193                 gen_eight(c0 | 0xfffffffffffff000ULL);
194                 top_bits = 0xffffffff00000000ULL;
195         } else {
196                 bool have_reg = false;
197                 if (c1) {
198                         gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
199                         gen_one(reg);
200                         gen_one(ARG_IMM);
201                         gen_eight((uint64_t)(int32_t)c1);
202                         top_bits = (uint64_t)(int32_t)c1 & 0xffffffff00000000ULL;
203                         have_reg = true;
204                 }
205                 if (!have_reg || c0) {
206                         if (!have_reg) {
207                                 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
208                                 gen_one(reg);
209                                 gen_one(ARG_IMM);
210                                 gen_eight(c0);
211                         } else {
212                                 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_OR, 0);
213                                 gen_one(reg);
214                                 gen_one(reg);
215                                 gen_one(ARG_IMM);
216                                 gen_eight(c0);
217                         }
218                 }
219         }
220         if (top_bits != (c2 | c3)) {
221                 uint64_t c2x = c2;
222                 if (c2 & 0x0008000000000000ULL)
223                         c2x |= 0xfff0000000000000ULL;
224                 if (top_bits != c2x) {
225                         gen_insn(INSN_MOV_MASK, OP_SIZE_NATIVE, MOV_MASK_32_64, 0);
226                         gen_one(reg);
227                         gen_one(reg);
228                         gen_one(ARG_IMM);
229                         gen_eight(c2x >> 32);
230                 }
231                 top_bits = c2x & 0xfff0000000000000ULL;
232                 if (top_bits != c3) {
233                         gen_insn(INSN_MOV_MASK, OP_SIZE_NATIVE, MOV_MASK_52_64, 0);
234                         gen_one(reg);
235                         gen_one(reg);
236                         gen_one(ARG_IMM);
237                         gen_eight(c3 >> 52);
238                 }
239         }
240         return true;
243 static bool attr_w gen_address(struct codegen_context *ctx, unsigned base, int64_t imm, unsigned purpose, unsigned size)
245         ctx->base_reg = base;
246         ctx->offset_imm = imm;
247         ctx->offset_reg = false;
248         switch (purpose) {
249                 case IMM_PURPOSE_LDR_OFFSET:
250                 case IMM_PURPOSE_LDR_SX_OFFSET:
251                 case IMM_PURPOSE_STR_OFFSET:
252                 case IMM_PURPOSE_VLDR_VSTR_OFFSET:
253                 case IMM_PURPOSE_MVI_CLI_OFFSET:
254                         if (likely(imm >= -0x800) && likely(imm < 0x800)) {
255                                 return true;
256                         }
257                         if (imm >= -0x8000 && imm < 0x8000 && !(imm & 3)) {
258                                 if (size == OP_SIZE_NATIVE)
259                                         return true;
260                                 if (purpose == IMM_PURPOSE_LDR_SX_OFFSET && size == OP_SIZE_4)
261                                         return true;
262                                 if (purpose == IMM_PURPOSE_STR_OFFSET && size == OP_SIZE_4)
263                                         return true;
264                         }
265                         break;
266                 default:
267                         internal(file_line, "gen_address: invalid purpose %u (imm %"PRIxMAX", size %u)", purpose, (uintmax_t)imm, size);
268         }
269         g(gen_load_constant(ctx, R_OFFSET_IMM, imm));
270         ctx->offset_reg = true;
271         return true;
274 static bool is_direct_const(int64_t imm, unsigned purpose, unsigned size)
276         switch (purpose) {
277                 case IMM_PURPOSE_STORE_VALUE:
278                         if (!imm)
279                                 return true;
280                         break;
281                 case IMM_PURPOSE_ADD:
282                 case IMM_PURPOSE_CMP:
283                 case IMM_PURPOSE_CMP_LOGICAL:
284                         if (likely(imm >= -0x800) && likely(imm < 0x800))
285                                 return true;
286                         break;
287                 case IMM_PURPOSE_SUB:
288                         if (likely(imm > -0x800) && likely(imm <= 0x800))
289                                 return true;
290                         break;
291                 case IMM_PURPOSE_AND:
292                 case IMM_PURPOSE_OR:
293                 case IMM_PURPOSE_XOR:
294                         if (likely(imm >= 0) && likely(imm < 0x1000))
295                                 return true;
296                         break;
297                 case IMM_PURPOSE_ANDN:
298                         break;
299                 case IMM_PURPOSE_TEST:
300                         break;
301                 case IMM_PURPOSE_JMP_2REGS:
302                         break;
303                 case IMM_PURPOSE_MUL:
304                         break;
305                 case IMM_PURPOSE_BITWISE:
306                         return true;
307                 default:
308                         internal(file_line, "is_direct_const: invalid purpose %u (imm %"PRIxMAX", size %u)", purpose, (uintmax_t)imm, size);
309         }
310         return false;
313 static bool attr_w gen_imm(struct codegen_context *ctx, int64_t imm, unsigned purpose, unsigned size)
315         if (is_direct_const(imm, purpose, size)) {
316                 ctx->const_imm = imm;
317                 ctx->const_reg = false;
318         } else {
319                 g(gen_load_constant(ctx, R_CONST_IMM, imm));
320                 ctx->const_reg = true;
321         }
322         return true;
325 static bool attr_w gen_entry(struct codegen_context *ctx)
327         int offset, i;
329         g(gen_imm(ctx, -FRAME_SIZE, IMM_PURPOSE_ADD, OP_SIZE_NATIVE));
330         gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
331         gen_one(R_SP);
332         gen_one(R_SP);
333         gen_imm_offset();
335         offset = FRAME_SIZE - (1 << OP_SIZE_NATIVE);
337         g(gen_address(ctx, R_SP, FRAME_SIZE - 0x08, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
338         gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
339         gen_address_offset();
340         gen_one(R_RA);
341         offset -= 1 << OP_SIZE_NATIVE;
343         for (i = R_FP; i <= R_S8; i++) {
344                 g(gen_address(ctx, R_SP, offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
345                 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
346                 gen_address_offset();
347                 gen_one(i);
348                 offset -= 1 << OP_SIZE_NATIVE;
349         }
351 #ifndef TIMESTAMP_IN_REGISTER
352         g(gen_address(ctx, R_SP, offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
353         gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
354         gen_address_offset();
355         gen_one(R_ARG2);
356 #endif
358         gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
359         gen_one(R_FRAME);
360         gen_one(R_ARG0);
362         gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
363         gen_one(R_UPCALL);
364         gen_one(R_ARG1);
366 #ifdef TIMESTAMP_IN_REGISTER
367         gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
368         gen_one(R_TIMESTAMP);
369         gen_one(R_ARG2);
370 #endif
372         gen_insn(INSN_JMP_INDIRECT, 0, 0, 0);
373         gen_one(R_ARG3);
375         return true;
378 static bool attr_w gen_escape_arg(struct codegen_context *ctx, ip_t ip, uint32_t escape_label)
380         g(gen_load_constant(ctx, R_RET1, ip));
382         gen_insn(INSN_JMP, 0, 0, 0);
383         gen_four(escape_label);
385         return true;
388 static bool attr_w gen_escape(struct codegen_context *ctx)
390         int offset, i;
392         gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
393         gen_one(R_RET0);
394         gen_one(R_FRAME);
396         offset = FRAME_SIZE - (1 << OP_SIZE_NATIVE);
398         g(gen_address(ctx, R_SP, FRAME_SIZE - 0x08, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
399         gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
400         gen_one(R_RA);
401         gen_address_offset();
402         offset -= 1 << OP_SIZE_NATIVE;
404         for (i = R_FP; i <= R_S8; i++) {
405                 g(gen_address(ctx, R_SP, offset, IMM_PURPOSE_STR_OFFSET, OP_SIZE_NATIVE));
406                 gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
407                 gen_one(i);
408                 gen_address_offset();
409                 offset -= 1 << OP_SIZE_NATIVE;
410         }
412         g(gen_imm(ctx, FRAME_SIZE, IMM_PURPOSE_ADD, OP_SIZE_NATIVE));
413         gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_ADD, 0);
414         gen_one(R_SP);
415         gen_one(R_SP);
416         gen_imm_offset();
418         gen_insn(INSN_RET, 0, 0, 0);
420         return true;
423 static bool attr_w gen_upcall_argument(struct codegen_context attr_unused *ctx, unsigned attr_unused arg)
425         return true;
428 static bool attr_w gen_get_upcall_pointer(struct codegen_context *ctx, unsigned offset, unsigned reg)
430         g(gen_address(ctx, R_UPCALL, offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_ADDRESS));
431         gen_insn(INSN_MOV, OP_SIZE_ADDRESS, 0, 0);
432         gen_one(reg);
433         gen_address_offset();
435         return true;
438 static bool attr_w gen_upcall(struct codegen_context *ctx, unsigned offset, unsigned n_args)
440         g(gen_get_upcall_pointer(ctx, offset, R_SCRATCH_NA_1));
442         gen_insn(INSN_CALL_INDIRECT, OP_SIZE_ADDRESS, 0, 0);
443         gen_one(R_SCRATCH_NA_1);
445         g(gen_upcall_end(ctx, n_args));
447         return true;
450 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);
452 static bool attr_w gen_timestamp_test(struct codegen_context *ctx, uint32_t escape_label)
454         g(gen_address(ctx, R_UPCALL, offsetof(struct cg_upcall_vector_s, ts), IMM_PURPOSE_LDR_SX_OFFSET, OP_SIZE_4));
455         gen_insn(INSN_MOVSX, OP_SIZE_4, 0, 0);
456         gen_one(R_SCRATCH_1);
457         gen_address_offset();
459 #ifdef TIMESTAMP_IN_REGISTER
460         g(gen_cmp_test_jmp(ctx, INSN_CMP, OP_SIZE_NATIVE, R_SCRATCH_1, R_TIMESTAMP, COND_NE, escape_label));
461 #else
462         g(gen_address(ctx, R_SP, 0, IMM_PURPOSE_LDR_SX_OFFSET, OP_SIZE_4));
463         gen_insn(INSN_MOVSX, OP_SIZE_4, 0, 0);
464         gen_one(R_SCRATCH_2);
465         gen_address_offset();
467         g(gen_cmp_test_jmp(ctx, INSN_CMP, OP_SIZE_NATIVE, R_SCRATCH_1, R_SCRATCH_2, COND_NE, escape_label));
468 #endif
470         return true;