Ajla 0.1.0
[ajla.git] / c1-arm.inc
blobf7bd370c661e8143f9bb0ddedd661ee8ada77d0f
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_4
20 #define OP_SIZE_ADDRESS                 OP_SIZE_4
22 #define JMP_LIMIT                       JMP_SHORTEST
24 #ifndef __ARM_FEATURE_UNALIGNED
25 #define UNALIGNED_TRAP                  1
26 #else
27 #define UNALIGNED_TRAP                  0
28 #endif
30 #define ALU_WRITES_FLAGS(alu, im)       0
31 #define ALU1_WRITES_FLAGS(alu)          0
32 #define ROT_WRITES_FLAGS(alu)           0
33 #define COND_IS_LOGICAL(cond)           0
35 #define ARCH_PARTIAL_ALU(size)          0
36 #define ARCH_IS_3ADDRESS                1
37 #define ARCH_HAS_FLAGS                  1
38 #define ARCH_PREFERS_SX(size)           0
39 #define ARCH_HAS_BWX                    1
40 #define ARCH_HAS_MUL                    1
41 #define ARCH_HAS_DIV                    cpu_test_feature(CPU_FEATURE_idiv)
42 #define ARCH_HAS_ANDN                   1
43 #define ARCH_HAS_SHIFTED_ADD(bits)      1
44 #define ARCH_HAS_BTX(btx, size, cnst)   0
45 #define ARCH_SHIFT_SIZE                 32
46 #define ARCH_NEEDS_BARRIER              0
48 #define i_size(size)                    OP_SIZE_4
49 #define i_size_rot(size)                OP_SIZE_4
51 #define R_0             0x00
52 #define R_1             0x01
53 #define R_2             0x02
54 #define R_3             0x03
55 #define R_4             0x04
56 #define R_5             0x05
57 #define R_6             0x06
58 #define R_7             0x07
59 #define R_8             0x08
60 #define R_9             0x09
61 #define R_10            0x0a
62 #define R_FP            0x0b
63 #define R_IP            0x0c
64 #define R_SP            0x0d
65 #define R_LR            0x0e
66 #define R_PC            0x0f
68 #define FSR_0           0x20
69 #define FSR_1           0x21
70 #define FSR_2           0x22
71 #define FSR_3           0x23
72 #define FSR_4           0x24
73 #define FSR_5           0x25
74 #define FSR_6           0x26
75 #define FSR_7           0x27
76 #define FSR_8           0x28
77 #define FSR_9           0x29
78 #define FSR_10          0x2a
79 #define FSR_11          0x2b
80 #define FSR_12          0x2c
81 #define FSR_13          0x2d
82 #define FSR_14          0x2e
83 #define FSR_15          0x2f
84 #define FSR_16          0x30
85 #define FSR_17          0x31
86 #define FSR_18          0x32
87 #define FSR_19          0x33
88 #define FSR_20          0x34
89 #define FSR_21          0x35
90 #define FSR_22          0x36
91 #define FSR_23          0x37
92 #define FSR_24          0x38
93 #define FSR_25          0x39
94 #define FSR_26          0x3a
95 #define FSR_27          0x3b
96 #define FSR_28          0x3c
97 #define FSR_29          0x3d
98 #define FSR_30          0x3e
99 #define FSR_31          0x3f
101 #define FDR_0           0x20
102 #define FDR_2           0x22
103 #define FDR_4           0x24
104 #define FDR_6           0x26
105 #define FDR_8           0x28
106 #define FDR_10          0x2a
107 #define FDR_12          0x2c
108 #define FDR_14          0x2e
109 #define FDR_16          0x30
110 #define FDR_18          0x32
111 #define FDR_20          0x34
112 #define FDR_22          0x36
113 #define FDR_24          0x38
114 #define FDR_26          0x3a
115 #define FDR_28          0x3c
116 #define FDR_30          0x3e
118 #define FQR_0           0x20
119 #define FQR_2           0x22
120 #define FQR_4           0x24
121 #define FQR_6           0x26
122 #define FQR_8           0x28
123 #define FQR_10          0x2a
124 #define FQR_12          0x2c
125 #define FQR_14          0x2e
126 #define FQR_16          0x30
127 #define FQR_18          0x32
128 #define FQR_20          0x34
129 #define FQR_22          0x36
130 #define FQR_24          0x38
131 #define FQR_26          0x3a
132 #define FQR_28          0x3c
133 #define FQR_30          0x3e
135 #define R_FRAME         R_4
136 #define R_UPCALL        R_5
138 #define R_SCRATCH_1     R_0
139 #define R_SCRATCH_2     R_1
140 #define R_SCRATCH_3     R_2
141 #define R_SCRATCH_4     R_3
143 #define R_SAVED_1       R_6
144 #define R_SAVED_2       R_7
146 #define R_OFFSET_IMM    R_8
147 #define R_CONST_IMM     R_IP
149 #define R_SCRATCH_NA_1  R_10
150 #define R_SCRATCH_NA_2  R_FP
151 #define R_SCRATCH_NA_3  R_LR
153 #define R_ARG0          R_0
154 #define R_ARG1          R_1
155 #define R_ARG2          R_2
156 #define R_ARG3          R_3
157 #define R_RET0          R_0
159 #define FR_SCRATCH_1    FQR_0
160 #define FR_SCRATCH_2    FQR_4
162 #define SUPPORTED_FP            (cpu_test_feature(CPU_FEATURE_vfp) * 0x6)
163 #define SUPPORTED_FP_HALF_CVT   (cpu_test_feature(CPU_FEATURE_half) * 0x1)
165 static bool reg_is_fp(unsigned reg)
167         return reg >= 0x20 && reg < 0x40;
170 static int gen_imm12(uint32_t c)
172         int rot;
173         for (rot = 0; rot < 32; rot += 2) {
174                 uint32_t val = c << rot | c >> (-rot & 31);
175                 if (val < 0x100)
176                         return val | (rot << 7);
177         }
178         return -1;
181 static bool attr_w gen_load_constant(struct codegen_context *ctx, unsigned reg, uint32_t c)
183         if (gen_imm12(c) >= 0 || gen_imm12(~c) >= 0) {
184                 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
185                 gen_one(reg);
186                 gen_one(ARG_IMM);
187                 gen_eight(c);
188                 return true;
189         }
190         if (likely(cpu_test_feature(CPU_FEATURE_armv6t2))) {
191                 gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
192                 gen_one(reg);
193                 gen_one(ARG_IMM);
194                 gen_eight(c & 0xffff);
195                 if (c >> 16) {
196                         gen_insn(INSN_MOV_MASK, OP_SIZE_4, MOV_MASK_16_32, 0);
197                         gen_one(reg);
198                         gen_one(reg);
199                         gen_one(ARG_IMM);
200                         gen_eight(c >> 16);
201                 }
202         } else {
203                 bool need_init = true;
204                 int p;
205                 for (p = 0; p < 32; p += 8) {
206                         if ((c >> p) & 0xff) {
207                                 if (need_init) {
208                                         gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
209                                         gen_one(reg);
210                                         gen_one(ARG_IMM);
211                                         gen_eight(c & (0xff << p));
212                                         need_init = false;
213                                 } else {
214                                         gen_insn(INSN_ALU, OP_SIZE_4, ALU_OR, 0);
215                                         gen_one(reg);
216                                         gen_one(reg);
217                                         gen_one(ARG_IMM);
218                                         gen_eight(c & (0xff << p));
219                                 }
220                         }
221                 }
222                 if (need_init) {
223                         gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
224                         gen_one(reg);
225                         gen_one(ARG_IMM);
226                         gen_eight(0);
227                 }
228         }
229         return true;
232 static bool attr_w gen_address(struct codegen_context *ctx, unsigned base, int64_t imm, unsigned purpose, unsigned size)
234         ctx->base_reg = base;
235         ctx->offset_imm = imm;
236         ctx->offset_reg = false;
237         switch (purpose) {
238                 case IMM_PURPOSE_LDR_OFFSET:
239                 case IMM_PURPOSE_STR_OFFSET:
240                 case IMM_PURPOSE_MVI_CLI_OFFSET:
241                         if (size == OP_SIZE_2) {
242                                 if (imm >= -255 && imm <= 255)
243                                         return true;
244                         } else {
245                                 if (imm >= -4095 && imm <= 4095)
246                                         return true;
247                         }
248                         break;
249                 case IMM_PURPOSE_LDR_SX_OFFSET:
250                 case IMM_PURPOSE_LDP_STP_OFFSET:
251                         if (imm >= -255 && imm <= 255)
252                                 return true;
253                         break;
254                 case IMM_PURPOSE_VLDR_VSTR_OFFSET:
255                         if (size < OP_SIZE_4 && imm != 0)
256                                 break;
257                         if (unlikely((imm & 3) != 0))
258                                 break;
259                         if (imm >= -1023 && imm <= 1023)
260                                 return true;
261                         break;
262                 default:
263                         internal(file_line, "gen_address: invalid purpose %d", purpose);
264         }
265         if (purpose == IMM_PURPOSE_VLDR_VSTR_OFFSET) {
266                 if (gen_imm12(imm) >= 0) {
267                         gen_insn(INSN_ALU, OP_SIZE_ADDRESS, ALU_ADD, 0);
268                         gen_one(R_OFFSET_IMM);
269                         gen_one(base);
270                         gen_one(ARG_IMM);
271                         gen_eight(imm);
272                 } else {
273                         g(gen_load_constant(ctx, R_OFFSET_IMM, imm));
274                         gen_insn(INSN_ALU, OP_SIZE_ADDRESS, ALU_ADD, 0);
275                         gen_one(R_OFFSET_IMM);
276                         gen_one(R_OFFSET_IMM);
277                         gen_one(base);
278                 }
279                 ctx->base_reg = R_OFFSET_IMM;
280                 ctx->offset_imm = 0;
281                 return true;
282         }
283         g(gen_load_constant(ctx, R_OFFSET_IMM, imm));
284         ctx->offset_reg = true;
285         return true;
288 static bool is_direct_const(int64_t imm, unsigned purpose, unsigned size)
290         int imm12;
291         switch (purpose) {
292                 case IMM_PURPOSE_STORE_VALUE:
293                         break;
294                 case IMM_PURPOSE_ADD:
295                 case IMM_PURPOSE_SUB:
296                 case IMM_PURPOSE_CMP:
297                 case IMM_PURPOSE_CMP_LOGICAL:
298                 case IMM_PURPOSE_AND:
299                 case IMM_PURPOSE_OR:
300                 case IMM_PURPOSE_XOR:
301                 case IMM_PURPOSE_ANDN:
302                 case IMM_PURPOSE_TEST:
303                         imm12 = gen_imm12(imm);
304                         if (unlikely(imm12 == -1))
305                                 break;
306                         return true;
307                 case IMM_PURPOSE_CMOV:
308                         if (gen_imm12(imm) >= 0 || gen_imm12(~imm) >= 0)
309                                 return true;
310                         if ((uint32_t)imm < 0x10000 && likely(cpu_test_feature(CPU_FEATURE_armv6t2)))
311                                 return true;
312                         break;
313                 case IMM_PURPOSE_MUL:
314                         break;
315                 default:
316                         internal(file_line, "is_direct_const: invalid purpose %u (imm %"PRIxMAX", size %u)", purpose, (uintmax_t)imm, size);
317         }
318         return false;
321 static bool attr_w gen_imm(struct codegen_context *ctx, int64_t imm, unsigned purpose, unsigned size)
323         if (is_direct_const(imm, purpose, size)) {
324                 ctx->const_imm = imm;
325                 ctx->const_reg = false;
326         } else {
327                 g(gen_load_constant(ctx, R_CONST_IMM, imm));
328                 ctx->const_reg = true;
329         }
330         return true;
333 static bool attr_w gen_entry(struct codegen_context *ctx)
335         gen_insn(INSN_ARM_PUSH, OP_SIZE_NATIVE, 0, 0);
337         gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
338         gen_one(R_FRAME);
339         gen_one(R_ARG0);
341         gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
342         gen_one(R_UPCALL);
343         gen_one(R_ARG1);
345         return true;
348 static bool attr_w gen_escape_arg(struct codegen_context *ctx, ip_t ip, uint32_t escape_label)
350         g(gen_load_constant(ctx, R_ARG1, ip));
352         gen_insn(INSN_JMP, 0, 0, 0);
353         gen_four(escape_label);
355         return true;
358 static bool attr_w gen_escape(struct codegen_context *ctx)
360         gen_insn(INSN_MOV, OP_SIZE_NATIVE, 0, 0);
361         gen_one(R_ARG0);
362         gen_one(R_FRAME);
364         gen_insn(INSN_ARM_POP, OP_SIZE_NATIVE, 0, 0);
366         return true;
369 static bool attr_w gen_upcall_argument(struct codegen_context attr_unused *ctx, unsigned attr_unused arg)
371         if (unlikely(arg >= 4))
372                 internal(file_line, "gen_upcall_argument: only 4 arguments supported");
373         return true;
376 static bool attr_w gen_upcall(struct codegen_context *ctx, unsigned offset, unsigned attr_unused n_args)
378         g(gen_address(ctx, R_UPCALL, offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_4));
379         gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
380         gen_one(R_SCRATCH_NA_1);
381         gen_address_offset();
383         gen_insn(INSN_CALL_INDIRECT, OP_SIZE_4, 0, 0);
384         gen_one(R_SCRATCH_NA_1);
386         return true;
389 static bool attr_w gen_timestamp_test(struct codegen_context *ctx, uint32_t label_id)
391         g(gen_address(ctx, R_UPCALL, offsetof(struct cg_upcall_vector_s, ts), IMM_PURPOSE_LDR_OFFSET, OP_SIZE_4));
392         gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
393         gen_one(R_SCRATCH_1);
394         gen_address_offset();
396         gen_insn(INSN_MOV, OP_SIZE_4, 0, 0);
397         gen_one(R_SCRATCH_2);
398         gen_one(ARG_ADDRESS_1);
399         gen_one(R_SP);
400         gen_eight(0);
402         gen_insn(INSN_CMP, OP_SIZE_4, 0, 1);
403         gen_one(R_SCRATCH_1);
404         gen_one(R_SCRATCH_2);
406         gen_insn(INSN_JMP_COND, OP_SIZE_4, COND_E, 0);
407         gen_four(label_id);
409         return true;