codegen: introduce gen_frame_decompress_slot and use it in
[ajla.git] / c2-arm.inc
bloba327ee9f101be4f880aa00b3a2166817802d35f4
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  ARM_ALWAYS                     0xe0000000U
20 #define  ARM_WRITE_FLAGS                0x00100000U
22 #define  ARM_LDR_STR_LD                 0x00100000U
23 #define  ARM_LDR_STR_W                  0x00200000U
24 #define  ARM_LDR_STR_U                  0x00800000U
25 #define  ARM_LDR_STR_P                  0x01000000U
26 #define ARM_STRH_REG            0x000000b0U
27 #define ARM_LDRD_REG            0x000000d0U
28 #define ARM_STRD_REG            0x000000f0U
29 #define ARM_LDRSB_REG           0x001000d0U
30 #define ARM_LDRSH_REG           0x001000f0U
31 #define ARM_STRH_IMM            0x004000b0U
32 #define ARM_LDRD_IMM            0x004000d0U
33 #define ARM_STRD_IMM            0x004000f0U
34 #define ARM_LDRSB_IMM           0x005000d0U
35 #define ARM_LDRSH_IMM           0x005000f0U
36 #define ARM_STR_IMM             0x04000000U
37 #define ARM_STRB_IMM            0x04400000U
38 #define ARM_STR_REG             0x06000000U
39 #define ARM_STRB_REG            0x06400000U
41 #define  ARM_ALU_IMM                    0x02000000U
42 #define  ARM_ALU_REG_SHIFTED            0x00000010U
43 #define  ARM_ROT_LSL                    0x00000000U
44 #define  ARM_ROT_LSR                    0x00000020U
45 #define  ARM_ROT_ASR                    0x00000040U
46 #define  ARM_ROT_ROR                    0x00000060U
47 #define ARM_AND                 0x00000000U
48 #define ARM_EOR                 0x00200000U
49 #define ARM_SUB                 0x00400000U
50 #define ARM_RSB                 0x00600000U
51 #define ARM_ADD                 0x00800000U
52 #define ARM_ADC                 0x00a00000U
53 #define ARM_SBC                 0x00c00000U
54 #define ARM_RSC                 0x00e00000U
55 #define ARM_TST                 0x01100000U
56 #define ARM_TEQ                 0x01300000U
57 #define ARM_CMP                 0x01500000U
58 #define ARM_CMN                 0x01700000U
59 #define ARM_ORR                 0x01800000U
60 #define ARM_MOV                 0x01a00000U
61 #define ARM_BIC                 0x01c00000U
62 #define ARM_MVN                 0x01e00000U
64 #define ARM_MUL                 0x00000090U
65 #define ARM_MLA                 0x00200090U
66 #define ARM_MLS                 0x00600090U
67 #define ARM_UMULL               0x00800090U
68 #define ARM_SMULL               0x00c00090U
69 #define ARM_BX                  0x012fff10U
70 #define ARM_BLX_REG             0x012fff30U
71 #define ARM_CLZ                 0x016f0f10U
72 #define ARM_MOV_IMM16           0x03000000U
73 #define ARM_MOVT                0x03400000U
74 #define ARM_SXTB                0x06af0070U
75 #define ARM_SXTH                0x06bf0070U
76 #define ARM_REV                 0x06bf0f30U
77 #define ARM_REV16               0x06bf0fb0U
78 #define ARM_UXTB                0x06ef0070U
79 #define ARM_UXTH                0x06ff0070U
80 #define ARM_RBIT                0x06ff0f30U
81 #define ARM_SDIV                0x0710f010U
82 #define ARM_UDIV                0x0730f010U
83 #define ARM_POP                 0x08bd0000U
84 #define ARM_PUSH                0x092d0000U
85 #define ARM_B                   0x0a000000U
87 #define  ARM_V_D                        0x00000100U
88 #define ARM_VSTR                0x0d000a00U
89 #define ARM_VLDR                0x0d100a00U
90 #define ARM_VMOV_S32_R          0x0e000a10U
91 #define ARM_VMOV_R_S32          0x0e100a10U
92 #define ARM_VMUL                0x0e200a00U
93 #define ARM_VADD                0x0e300a00U
94 #define ARM_VSUB                0x0e300a40U
95 #define ARM_VDIV                0x0e800a00U
96 #define ARM_VMOV                0x0eb00a40U
97 #define ARM_VNEG                0x0eb10a40U
98 #define ARM_VSQRT               0x0eb10ac0U
99 #define ARM_VCVT_F32_F16        0x0eb20a40U
100 #define ARM_VCVT_F16_F32        0x0eb30a40U
101 #define ARM_VCMP                0x0eb40a40U
102 #define ARM_VCVT_F_S32          0x0eb80ac0U
103 #define ARM_VCVT_S32_F          0x0ebd0ac0U
104 #define ARM_VMRS_NZCV_FPSCR     0x0ef1fa10U
106 #define  ARM_V_BYTE                     0x00000000U
107 #define  ARM_V_HALF                     0x00000400U
108 #define ARM_VST1                0xf480000fU
109 #define ARM_VLD1                0xf4a0000fU
110 #define  ARM_VCNT_8_Q                   0x00000040U
111 #define ARM_VCNT_8              0xf3b00500U
112 #define ARM_VPADDL_U8           0xf3b00280U
113 #define ARM_VPADDL_U16          0xf3b40280U
114 #define ARM_VPADDL_U32          0xf3b80280U
116 static const uint32_t alu_codes[7] = {
117         ARM_ADD,
118         ARM_ORR,
119         ARM_ADC,
120         ARM_SBC,
121         ARM_AND,
122         ARM_SUB,
123         ARM_EOR,
126 static const int8_t jmp_cond[48] = {
127         0x6, 0x7, 0x3, 0x2, 0x0, 0x1, 0x9, 0x8,
128         0x4, 0x5,  -1,  -1, 0xb, 0xa, 0xd, 0xc,
129          -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
130          -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
131          -1,  -1, 0x3, 0x2, 0x0, 0x1, 0x9, 0x8,
132          -1,  -1, 0x6, 0x7,  -1,  -1,  -1,  -1,
135 static const int8_t rot_codes[8] = {
136         -1,
137         ARM_ROT_ROR,
138         -1,
139         -1,
140         ARM_ROT_LSL,
141         ARM_ROT_LSR,
142         -1,
143         ARM_ROT_ASR,
146 static bool attr_w cgen_arm_push_pop(struct codegen_context *ctx, bool pop)
148         bool need_bx = false;
149         uint32_t mc = ARM_ALWAYS;
150         mc |= pop ? ARM_POP : ARM_PUSH;
151 #if defined(__thumb__) || defined(__thumb2__)
152         if (pop) {
153                 if (unlikely(!cpu_test_feature(CPU_FEATURE_armv5)))
154                         need_bx = true;
155         }
156 #endif
157         mc |= 1U << R_2;
158         mc |= 1U << R_4;
159         mc |= 1U << R_5;
160         mc |= 1U << R_6;
161         mc |= 1U << R_7;
162         mc |= 1U << R_8;
163         mc |= 1U << R_9;
164         mc |= 1U << R_10;
165         mc |= 1U << R_FP;
166         mc |= 1U << (pop && !need_bx ? R_PC : R_LR);
167         cgen_four(mc);
168         if (need_bx) {
169                 mc = ARM_ALWAYS | ARM_BX;
170                 mc |= R_LR;
171                 cgen_four(mc);
172         }
173         return true;
176 static bool attr_w cgen_call_indirect(struct codegen_context *ctx)
178         uint32_t mc;
179         bool blx = false;
180         uint8_t reg = cget_one(ctx);
182 #if defined(__thumb__) || defined(__thumb2__)
183         blx = true;
184 #else
185         if (likely(cpu_test_feature(CPU_FEATURE_armv6)))
186                 blx = true;
187 #endif
189         if (blx) {
190                 mc = ARM_ALWAYS | ARM_BLX_REG;
191                 mc |= reg;
192                 cgen_four(mc);
193         } else {
194                 mc = ARM_ALWAYS | ARM_MOV;
195                 mc |= (uint32_t)R_LR << 12;
196                 mc |= R_PC;
197                 cgen_four(mc);
199                 mc = ARM_ALWAYS | ARM_MOV;
200                 mc |= (uint32_t)R_PC << 12;
201                 mc |= reg;
202                 cgen_four(mc);
203         }
205         return true;
208 static bool attr_w cgen_ldr_str(struct codegen_context *ctx, bool ld, uint32_t cond, unsigned size, bool sx, uint8_t reg, uint8_t *address)
210         int64_t imm;
211         uint32_t mc = cond;
212         if (ld)
213                 mc |= ARM_LDR_STR_LD;
214         if (size == OP_SIZE_NATIVE)
215                 sx = false;
216         if (address[0] >= ARG_ADDRESS_2 && address[0] <= ARG_ADDRESS_2_8) {
217                 imm = get_imm(&address[3]);
218                 if (unlikely(imm != 0))
219                         goto invalid;
220                 if (sx || size == OP_SIZE_2) {
221                         if (unlikely(address[0] != ARG_ADDRESS_2))
222                                 goto invalid;
223                         if (sx)
224                                 mc |= size == OP_SIZE_2 ? ARM_LDRSH_REG : ARM_LDRSB_REG;
225                         else
226                                 mc |= ARM_STRH_REG;
227                 } else {
228                         mc |= size == OP_SIZE_1 ? ARM_STRB_REG : ARM_STR_REG;
229                 }
230                 mc |= ARM_LDR_STR_U;
231                 mc |= ARM_LDR_STR_P;
232                 mc |= (uint32_t)address[1] << 16;
233                 mc |= address[2];
234                 mc |= (uint32_t)reg << 12;
235                 mc |= (uint32_t)(address[0] - ARG_ADDRESS_2) << 7;
236                 cgen_four(mc);
237                 return true;
238         }
239         if (address[0] == ARG_ADDRESS_1 || address[0] == ARG_ADDRESS_1_PRE_I || address[0] == ARG_ADDRESS_1_POST_I) {
240                 imm = get_imm(&address[2]);
241                 if (!(imm >= -4095 && imm <= 4095))
242                         goto invalid;
243                 if (imm < 0) {
244                         imm = -imm;
245                 } else {
246                         mc |= ARM_LDR_STR_U;
247                 }
248                 if (sx || size == OP_SIZE_2) {
249                         if (unlikely(imm >= 256))
250                                 goto invalid;
251                         if (sx)
252                                 mc |= size == OP_SIZE_2 ? ARM_LDRSH_IMM : ARM_LDRSB_IMM;
253                         else
254                                 mc |= ARM_STRH_IMM;
255                         mc |= imm & 0xf;
256                         mc |= (imm & 0xf0) << 4;
257                 } else {
258                         mc |= size == OP_SIZE_1 ? ARM_STRB_IMM : ARM_STR_IMM;
259                         mc |= imm;
260                 }
261                 if (address[0] == ARG_ADDRESS_1) {
262                         mc |= ARM_LDR_STR_P;
263                 } else if (address[0] == ARG_ADDRESS_1_PRE_I) {
264                         mc |= ARM_LDR_STR_P;
265                         mc |= ARM_LDR_STR_W;
266                 }
267                 mc |= (uint32_t)address[1] << 16;
268                 mc |= (uint32_t)reg << 12;
269                 cgen_four(mc);
270                 return true;
271         }
273         imm = 0;
274 invalid:
275         internal(file_line, "cgen_ldr_str: invalid address: %02x, %02x, %"PRIxMAX"", reg, address[0], (uintmax_t)imm);
276         return false;
279 static bool attr_w cgen_vldr_vstr(struct codegen_context *ctx, bool ld, uint32_t cond, unsigned size, uint8_t reg, uint8_t *address)
281         int64_t imm = 0;
282         uint32_t mc;
284         if (size < OP_SIZE_4) {
285                 mc = ld ? ARM_VLD1 : ARM_VST1;
286                 mc |= size == OP_SIZE_1 ? ARM_V_BYTE : ARM_V_HALF;
287                 mc |= (uint32_t)address[1] << 16;
288                 mc |= (uint32_t)(reg & 0x1e) << 11;
289                 mc |= (uint32_t)(reg & 1) << 22;
290                 cgen_four(mc);
291                 return true;
292         }
294         mc = cond;
295         mc |= ld ? ARM_VLDR : ARM_VSTR;
297         if (unlikely(address[0] != ARG_ADDRESS_1))
298                 goto invalid;
300         imm = get_imm(&address[2]);
301         if (!(imm >= -1023 && imm <= 1023))
302                 goto invalid;
304         if (imm < 0) {
305                 imm = -imm;
306         } else {
307                 mc |= ARM_LDR_STR_U;
308         }
310         mc |= (uint32_t)address[1] << 16;
311         mc |= (uint32_t)(reg & 0x1e) << 11;
312         mc |= (uint32_t)(reg & 1) << 22;
313         mc |= (imm >> 2) & 0xff;
314         if (size == OP_SIZE_8)
315                 mc |= ARM_V_D;
316         cgen_four(mc);
317         return true;
319 invalid:
320         internal(file_line, "cgen_vldr_vstr: invalid address: %02x, %02x, %"PRIxMAX"", reg, address[0], (uintmax_t)imm);
321         return false;
324 static bool attr_w cgen_mov_args(struct codegen_context *ctx, uint32_t cond, unsigned size, bool sx, uint8_t *arg1, uint8_t *arg2)
326         int64_t imm;
327         uint32_t mc;
328         if (arg1[0] < 16) {
329                 if (arg2[0] < 16) {
330                         if (unlikely(sx)) {
331                                 switch (size) {
332                                         case OP_SIZE_1: mc = cond | ARM_SXTB; break;
333                                         case OP_SIZE_2: mc = cond | ARM_SXTH; break;
334                                         default:        internal(file_line, "cgen_mov_args: invalid sign extend size");
335                                 }
336                         } else {
337                                 switch (size) {
338                                         case OP_SIZE_1: mc = cond | ARM_UXTB; break;
339                                         case OP_SIZE_2: mc = cond | ARM_UXTH; break;
340                                         case OP_SIZE_4: mc = cond | ARM_MOV; break;
341                                         default:        internal(file_line, "cgen_mov_args: invalid zero extend size");
342                                 }
343                         }
344                         mc |= arg2[0];
345                         mc |= (uint32_t)arg1[0] << 12;
346                         cgen_four(mc);
347                         return true;
348                 }
349                 if (reg_is_fp(arg2[0])) {
350                         if (unlikely(sx))
351                                 internal(file_line, "cgen_mov_args: unsupported sign extension");
352                         if (unlikely(size != OP_SIZE_NATIVE))
353                                 internal(file_line, "cgen_mov_args: unsupported size %u", size);
354                         mc = cond | ARM_VMOV_R_S32;
355                         mc |= (uint32_t)(arg2[0] & 0x1e) << 15;
356                         mc |= (uint32_t)(arg2[0] & 1) << 7;
357                         mc |= (uint32_t)arg1[0] << 12;
358                         cgen_four(mc);
359                         return true;
360                 }
361                 if (arg2[0] == ARG_IMM) {
362                         int imm12;
363                         imm = get_imm(&arg2[1]);
364                         imm12 = gen_imm12(imm);
365                         if (imm12 >= 0) {
366                                 mc = cond | ARM_MOV | ARM_ALU_IMM;
367                                 mc |= (uint32_t)arg1[0] << 12;
368                                 mc |= imm12;
369                                 cgen_four(mc);
370                                 return true;
371                         }
372                         imm12 = gen_imm12(~imm);
373                         if (imm12 >= 0) {
374                                 mc = cond | ARM_MVN | ARM_ALU_IMM;
375                                 mc |= (uint32_t)arg1[0] << 12;
376                                 mc |= imm12;
377                                 cgen_four(mc);
378                                 return true;
379                         }
380                         if ((uint32_t)imm >= 0x10000)
381                                 goto invalid;
382                         mc = cond | ARM_MOV_IMM16;
383                         mc |= (uint32_t)arg1[0] << 12;
384                         mc |= imm & 0xfff;
385                         mc |= (imm & 0xf000) << 4;
386                         cgen_four(mc);
387                         return true;
388                 }
389                 return cgen_ldr_str(ctx, true, cond, size, sx, arg1[0], arg2);
390         }
391         if (reg_is_fp(arg1[0])) {
392                 if (arg2[0] < 16) {
393                         if (unlikely(sx))
394                                 internal(file_line, "cgen_mov_args: unsupported sign extension");
395                         if (unlikely(size != OP_SIZE_NATIVE))
396                                 internal(file_line, "cgen_mov_args: unsupported size %u", size);
397                         mc = cond | ARM_VMOV_S32_R;
398                         mc |= (uint32_t)(arg1[0] & 0x1e) << 15;
399                         mc |= (uint32_t)(arg1[0] & 1) << 7;
400                         mc |= (uint32_t)arg2[0] << 12;
401                         cgen_four(mc);
402                         return true;
403                 }
404                 if (reg_is_fp(arg2[0])) {
405                         mc = cond | ARM_VMOV;
406                         switch (size) {
407                                 case OP_SIZE_4:         break;
408                                 case OP_SIZE_8:         mc |= ARM_V_D; break;
409                                 default:                internal(file_line, "cgen_mov_args: invalid size %u", size);
410                         }
411                         mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
412                         mc |= (uint32_t)(arg1[0] & 1) << 22;
413                         mc |= (uint32_t)(arg2[0] & 0x1e) >> 1;
414                         mc |= (uint32_t)(arg2[0] & 1) << 5;
415                         cgen_four(mc);
416                         return true;
417                 }
418                 return cgen_vldr_vstr(ctx, true, cond, size, arg1[0] & 31, arg2);
419         }
420         if (arg2[0] < 16) {
421                 return cgen_ldr_str(ctx, false, cond, size, false, arg2[0], arg1);
422         }
423         if (reg_is_fp(arg2[0])) {
424                 return cgen_vldr_vstr(ctx, false, cond, size, arg2[0] & 31, arg1);
425         }
426 invalid:
427         internal(file_line, "cgen_mov_args: invalid arguments %02x, %02x", arg1[0], arg2[0]);
428         return false;
431 static bool attr_w cgen_mov(struct codegen_context *ctx, unsigned size, bool sx)
433         uint8_t *arg1 = ctx->code_position;
434         uint8_t *arg2 = arg1 + arg_size(*arg1);
435         ctx->code_position = arg2 + arg_size(*arg2);
436         g(cgen_mov_args(ctx, ARM_ALWAYS, size, sx, arg1, arg2));
437         return true;
440 static bool attr_w cgen_alu_args(struct codegen_context *ctx, unsigned writes_flags, uint32_t mc, uint8_t *arg1, uint8_t *arg2, uint8_t *arg3)
442         int64_t imm;
443         int imm12;
444         if (unlikely(arg1[0] >= 16))
445                 goto invalid;
446         if (unlikely(arg2[0] >= 16))
447                 goto invalid;
448         mc |= ARM_ALWAYS;
449         if (writes_flags)
450                 mc |= ARM_WRITE_FLAGS;
451         mc |= (uint32_t)arg1[0] << 12;
452         mc |= (uint32_t)arg2[0] << 16;
453         if (arg3[0] == ARG_IMM) {
454                 imm = get_imm(&arg3[1]);
455                 imm12 = gen_imm12(imm);
456                 if (unlikely(imm12 < 0))
457                         goto invalid;
458                 mc |= ARM_ALU_IMM;
459                 mc |= imm12;
460                 cgen_four(mc);
461                 return true;
462         }
463         if (likely(arg3[0] < 16)) {
464                 mc |= arg3[0];
465                 cgen_four(mc);
466                 return true;
467         }
468         if (arg3[0] == ARG_SHIFTED_REGISTER) {
469                 unsigned mode = arg3[1] >> 6;
470                 unsigned amount = arg3[1] & ARG_SHIFT_AMOUNT;
471                 if (!amount)
472                         mode = 0;
473                 mc |= arg3[2];
474                 mc |= (uint32_t)mode << 5;
475                 mc |= ((uint32_t)amount & 0x1f) << 7;
476                 cgen_four(mc);
477                 return true;
478         }
480 invalid:
481         internal(file_line, "cgen_alu_args: invalid arguments %02x, %02x, %02x, %08x, %u", arg1[0], arg2[0], arg3[0], (unsigned)mc, writes_flags);
482         return false;
485 static bool attr_w cgen_cmp(struct codegen_context *ctx, bool cmn)
487         uint8_t z = 0;
488         uint8_t *arg1 = ctx->code_position;
489         uint8_t *arg2 = arg1 + arg_size(*arg1);
490         ctx->code_position = arg2 + arg_size(*arg2);
491         return cgen_alu_args(ctx, true, cmn ? ARM_CMN : ARM_CMP, &z, arg1, arg2);
494 static bool attr_w cgen_test(struct codegen_context *ctx)
496         uint8_t z = 0;
497         uint8_t *arg1 = ctx->code_position;
498         uint8_t *arg2 = arg1 + arg_size(*arg1);
499         ctx->code_position = arg2 + arg_size(*arg2);
500         return cgen_alu_args(ctx, true, ARM_TST, &z, arg1, arg2);
503 static bool attr_w cgen_alu(struct codegen_context *ctx, unsigned writes_flags, unsigned alu)
505         uint8_t *arg1 = ctx->code_position;
506         uint8_t *arg2 = arg1 + arg_size(*arg1);
507         uint8_t *arg3 = arg2 + arg_size(*arg2);
508         ctx->code_position = arg3 + arg_size(*arg3);
509         if (unlikely(alu >= 7)) {
510                 uint32_t mc = ARM_ALWAYS;
511                 if (alu == ALU_ANDN) {
512                         return cgen_alu_args(ctx, writes_flags, ARM_BIC, arg1, arg2, arg3);
513                 } else if (alu == ALU_MUL) {
514                         mc |= ARM_MUL;
515                 } else if (alu == ALU_UDIV) {
516                         mc |= ARM_UDIV;
517                 } else if (alu == ALU_SDIV) {
518                         mc |= ARM_SDIV;
519                 } else {
520                         internal(file_line, "cgen_alu: invalid alu %u", alu);
521                 }
522                 mc |= (uint32_t)arg1[0] << 16;
523                 mc |= arg2[0];
524                 mc |= (uint32_t)arg3[0] << 8;
525                 cgen_four(mc);
526                 return true;
527         }
528         return cgen_alu_args(ctx, writes_flags, alu_codes[alu], arg1, arg2, arg3);
531 static bool attr_w cgen_alu1(struct codegen_context *ctx, unsigned writes_flags, unsigned alu)
533         uint32_t mc;
534         uint8_t z = 0;
535         uint8_t z_imm[9] = { ARG_IMM, 0, 0, 0, 0, 0, 0, 0, 0 };
536         uint8_t one_imm[9] = { ARG_IMM, 1, 0, 0, 0, 0, 0, 0, 0 };
537         uint8_t *arg1 = ctx->code_position;
538         uint8_t *arg2 = arg1 + arg_size(*arg1);
539         ctx->code_position = arg2 + arg_size(*arg2);
540         switch (alu) {
541                 case ALU1_NOT:
542                         return cgen_alu_args(ctx, writes_flags, ARM_MVN, arg1, &z, arg2);
543                 case ALU1_NEG:
544                         return cgen_alu_args(ctx, writes_flags, ARM_RSB, arg1, arg2, z_imm);
545                 case ALU1_NGC:
546                         return cgen_alu_args(ctx, writes_flags, ARM_RSC, arg1, arg2, z_imm);
547                 case ALU1_INC:
548                         return cgen_alu_args(ctx, writes_flags, ARM_ADD, arg1, arg2, one_imm);
549                 case ALU1_DEC:
550                         return cgen_alu_args(ctx, writes_flags, ARM_SUB, arg1, arg2, one_imm);
551                 case ALU1_BSWAP:
552                         mc = ARM_ALWAYS | ARM_REV;
553                         break;
554                 case ALU1_BSWAP16:
555                         mc = ARM_ALWAYS | ARM_REV16;
556                         break;
557                 case ALU1_BREV:
558                         mc = ARM_ALWAYS | ARM_RBIT;
559                         break;
560                 case ALU1_LZCNT:
561                         mc = ARM_ALWAYS | ARM_CLZ;
562                         break;
563                 default:
564                         internal(file_line, "cgen_alu1: invalid alu %u", alu);
565                         return false;
566         }
567         mc |= (uint32_t)arg1[0] << 12;
568         mc |= arg2[0];
569         cgen_four(mc);
570         return true;
573 static bool attr_w cgen_rot(struct codegen_context *ctx, unsigned writes_flags, unsigned rot)
575         int8_t arm_rot;
576         uint32_t mc;
577         uint8_t *arg1 = ctx->code_position;
578         uint8_t *arg2 = arg1 + arg_size(*arg1);
579         uint8_t *arg3 = arg2 + arg_size(*arg2);
580         ctx->code_position = arg3 + arg_size(*arg3);
581         mc = ARM_ALWAYS | ARM_MOV;
582         if (writes_flags)
583                 mc |= ARM_WRITE_FLAGS;
584         arm_rot = rot_codes[rot];
585         if (unlikely(arm_rot < 0))
586                 internal(file_line, "cgen_rot: invalid rotation %u", rot);
587         if (arg3[0] == ARG_IMM) {
588                 uint8_t imm = arg3[1];
589                 if (!imm)
590                         arm_rot = ARM_ROT_LSL;
591                 mc |= (uint32_t)imm << 7;
592         } else {
593                 mc |= ARM_ALU_REG_SHIFTED;
594                 mc |= (uint32_t)arg3[0] << 8;
595         }
596         mc |= arm_rot;
597         mc |= (uint32_t)arg1[0] << 12;
598         mc |= arg2[0];
599         cgen_four(mc);
600         return true;
603 static bool attr_w cgen_mul_l(struct codegen_context *ctx, bool sgn)
605         uint32_t mc;
606         uint8_t *arg1 = ctx->code_position;
607         uint8_t *arg2 = arg1 + arg_size(*arg1);
608         uint8_t *arg3 = arg2 + arg_size(*arg2);
609         uint8_t *arg4 = arg3 + arg_size(*arg3);
610         ctx->code_position = arg4 + arg_size(*arg4);
611         if (unlikely(arg1[0] >= 16) || unlikely(arg2[0] >= 16) || unlikely(arg3[0] >= 16) || unlikely(arg4[0] >= 16))
612                 internal(file_line, "cgen_madd: invalid arguments");
613         mc = ARM_ALWAYS;
614         mc |= sgn ? ARM_SMULL : ARM_UMULL;
615         mc |= (uint32_t)arg1[0] << 12;
616         mc |= (uint32_t)arg2[0] << 16;
617         mc |= arg3[0];
618         mc |= (uint32_t)arg4[0] << 8;
619         cgen_four(mc);
620         return true;
623 static bool attr_w cgen_madd(struct codegen_context *ctx, bool sub)
625         uint32_t mc;
626         uint8_t *arg1 = ctx->code_position;
627         uint8_t *arg2 = arg1 + arg_size(*arg1);
628         uint8_t *arg3 = arg2 + arg_size(*arg2);
629         uint8_t *arg4 = arg3 + arg_size(*arg3);
630         ctx->code_position = arg4 + arg_size(*arg4);
631         if (unlikely(arg1[0] >= 16) || unlikely(arg2[0] >= 16) || unlikely(arg3[0] >= 16) || unlikely(arg4[0] >= 16))
632                 internal(file_line, "cgen_madd: invalid arguments");
633         mc = ARM_ALWAYS;
634         mc |= sub ? ARM_MLS : ARM_MLA;
635         mc |= (uint32_t)arg1[0] << 16;
636         mc |= arg2[0];
637         mc |= (uint32_t)arg3[0] << 8;
638         mc |= (uint32_t)arg4[0] << 12;
639         cgen_four(mc);
640         return true;
643 static bool attr_w cgen_cmov(struct codegen_context *ctx, unsigned size, unsigned aux)
645         int8_t cond;
646         uint8_t *arg1 = ctx->code_position;
647         uint8_t *arg2 = arg1 + arg_size(*arg1);
648         uint8_t *arg3 = arg2 + arg_size(*arg2);
649         ctx->code_position = arg3 + arg_size(*arg3);
650         if (unlikely(arg1[0] != arg2[0]))
651                 internal(file_line, "cgen_cmov: invalid arguments");
652         cond = jmp_cond[aux];
653         if (unlikely(cond < 0))
654                 internal(file_line, "cgen_cmov: invalid condition %u", aux);
655         g(cgen_mov_args(ctx, (uint32_t)cond << 28, size, false, arg1, arg3));
656         return true;
659 static bool attr_w cgen_ldp_stp(struct codegen_context *ctx, bool ldr)
661         uint8_t *arg1, *arg2, *arg3;
662         int64_t imm;
663         uint32_t mc = ARM_ALWAYS;
664         if (!ldr) {
665                 arg1 = ctx->code_position;
666                 arg2 = arg1 + arg_size(*arg1);
667                 arg3 = arg2 + arg_size(*arg2);
668                 ctx->code_position = arg3 + arg_size(*arg3);
669         } else {
670                 arg2 = ctx->code_position;
671                 arg3 = arg2 + arg_size(*arg2);
672                 arg1 = arg3 + arg_size(*arg3);
673                 ctx->code_position = arg1 + arg_size(*arg1);
674         }
675         if (unlikely(arg2[0] >= 16) || unlikely(arg3[0] >= 16))
676                 goto invalid;
677         if (unlikely((arg2[0] & 1) != 0) || unlikely(arg3[0] != arg2[0] + 1))
678                 goto invalid;
679         if (arg1[0] == ARG_ADDRESS_1 || arg1[0] == ARG_ADDRESS_1_PRE_I || arg1[0] == ARG_ADDRESS_1_POST_I) {
680                 mc |= !ldr ? ARM_STRD_IMM : ARM_LDRD_IMM;
681                 if (arg1[0] == ARG_ADDRESS_1) {
682                         mc |= ARM_LDR_STR_P;
683                 } else if (arg1[0] == ARG_ADDRESS_1_PRE_I) {
684                         mc |= ARM_LDR_STR_P;
685                         mc |= ARM_LDR_STR_W;
686                 }
687                 imm = get_imm(&arg1[2]);
688                 if (!(imm >= -255 && imm <= 255))
689                         goto invalid;
690                 if (imm < 0) {
691                         imm = -imm;
692                 } else {
693                         mc |= ARM_LDR_STR_U;
694                 }
695                 mc |= imm & 0xf;
696                 mc |= (imm & 0xf0) << 4;
697         } else if (arg1[0] == ARG_ADDRESS_2) {
698                 imm = get_imm(&arg1[3]);
699                 if (unlikely(imm != 0))
700                         goto invalid;
701                 mc |= !ldr ? ARM_STRD_REG : ARM_LDRD_REG;
702                 mc |= ARM_LDR_STR_P;
703                 mc |= ARM_LDR_STR_U;
704                 mc |= arg1[2];
705         } else {
706                 goto invalid;
707         }
708         mc |= (uint32_t)arg1[1] << 16;
709         mc |= (uint32_t)arg2[0] << 12;
710         cgen_four(mc);
711         return true;
713 invalid:
714         internal(file_line, "cgen_ldp_stp: invalid arguments %02x, %02x, %02x", arg1[0], arg2[0], arg3[0]);
715         return false;
718 static bool attr_w cgen_mov_mask(struct codegen_context *ctx, unsigned aux)
720         uint32_t mc;
721         uint64_t imm;
722         uint8_t *arg1 = ctx->code_position;
723         uint8_t *arg2 = arg1 + arg_size(*arg1);
724         uint8_t *arg3 = arg2 + arg_size(*arg2);
725         ctx->code_position = arg3 + arg_size(*arg3);
726         if (unlikely(arg1[0] >= 16) || unlikely(arg2[0] >= 16) || unlikely(arg3[0] != ARG_IMM) || unlikely(aux != MOV_MASK_16_32))
727                 internal(file_line, "cgen_mov_mask: bad arguments");
728         imm = get_imm(&arg3[1]);
729         if (unlikely(imm >= 0x10000))
730                 internal(file_line, "cgen_mov_mask: bad number");
731         mc = ARM_ALWAYS | ARM_MOVT;
732         mc |= (uint32_t)arg1[0] << 12;
733         mc |= imm & 0xfff;
734         mc |= (imm & 0xf000) << 4;
735         cgen_four(mc);
736         return true;
739 static bool attr_w cgen_fp_cmp(struct codegen_context *ctx, unsigned op_size)
741         uint32_t mc;
742         uint8_t *arg1 = ctx->code_position;
743         uint8_t *arg2 = arg1 + arg_size(*arg1);
744         ctx->code_position = arg2 + arg_size(*arg2);
745         mc = ARM_ALWAYS;
746         mc |= ARM_VCMP;
747         switch (op_size) {
748                 case OP_SIZE_4:         break;
749                 case OP_SIZE_8:         mc |= ARM_V_D; break;
750                 default:                internal(file_line, "cgen_fp_cmp: invalid size %u", op_size);
751         }
752         mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
753         mc |= (uint32_t)(arg1[0] & 1) << 22;
754         mc |= (uint32_t)(arg2[0] & 0x1e) >> 1;
755         mc |= (uint32_t)(arg2[0] & 1) << 5;
756         cgen_four(mc);
757         return true;
760 static bool attr_w cgen_fp_alu(struct codegen_context *ctx, unsigned op_size, unsigned aux)
762         uint32_t mc;
763         uint8_t *arg1 = ctx->code_position;
764         uint8_t *arg2 = arg1 + arg_size(*arg1);
765         uint8_t *arg3 = arg2 + arg_size(*arg2);
766         ctx->code_position = arg3 + arg_size(*arg3);
767         mc = ARM_ALWAYS;
768         switch (aux) {
769                 case FP_ALU_ADD:        mc |= ARM_VADD; break;
770                 case FP_ALU_SUB:        mc |= ARM_VSUB; break;
771                 case FP_ALU_MUL:        mc |= ARM_VMUL; break;
772                 case FP_ALU_DIV:        mc |= ARM_VDIV; break;
773                 default:                internal(file_line, "cgen_fp_alu: invalid alu %u", aux);
774         }
775         switch (op_size) {
776                 case OP_SIZE_4:         break;
777                 case OP_SIZE_8:         mc |= ARM_V_D; break;
778                 default:                internal(file_line, "cgen_fp_alu: invalid size %u", op_size);
779         }
780         mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
781         mc |= (uint32_t)(arg1[0] & 1) << 22;
782         mc |= (uint32_t)(arg2[0] & 0x1e) << 15;
783         mc |= (uint32_t)(arg2[0] & 1) << 7;
784         mc |= (uint32_t)(arg3[0] & 0x1e) >> 1;
785         mc |= (uint32_t)(arg3[0] & 1) << 5;
786         cgen_four(mc);
787         return true;
790 static bool attr_w cgen_fp_alu1(struct codegen_context *ctx, unsigned op_size, unsigned aux)
792         uint32_t mc;
793         uint8_t *arg1 = ctx->code_position;
794         uint8_t *arg2 = arg1 + arg_size(*arg1);
795         ctx->code_position = arg2 + arg_size(*arg2);
796         switch (aux) {
797                 case FP_ALU1_NEG:       mc = ARM_ALWAYS | ARM_VNEG; break;
798                 case FP_ALU1_SQRT:      mc = ARM_ALWAYS | ARM_VSQRT; break;
799                 case FP_ALU1_VCNT8:     mc = ARM_VCNT_8; goto do_regs;
800                 case FP_ALU1_VPADDL:    mc = op_size == OP_SIZE_1 ? ARM_VPADDL_U8 : op_size == OP_SIZE_2 ? ARM_VPADDL_U16 : op_size == OP_SIZE_4 ? ARM_VPADDL_U32 : 0;
801                                         if (!mc)
802                                                 goto invalid_size;
803                                         goto do_regs;
804                 default:                internal(file_line, "cgen_fp_alu1: invalid alu %u", aux);
805         }
806         switch (op_size) {
807                 case OP_SIZE_4:         break;
808                 case OP_SIZE_8:         mc |= ARM_V_D; break;
809 invalid_size:
810                 default:                internal(file_line, "cgen_fp_alu1: invalid size %u", op_size);
811         }
812 do_regs:
813         mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
814         mc |= (uint32_t)(arg1[0] & 1) << 22;
815         mc |= (uint32_t)(arg2[0] & 0x1e) >> 1;
816         mc |= (uint32_t)(arg2[0] & 1) << 5;
817         cgen_four(mc);
818         return true;
821 static bool attr_w cgen_fp_to_int(struct codegen_context *ctx, unsigned fp_op_size)
823         uint32_t mc;
824         uint8_t *arg1 = ctx->code_position;
825         uint8_t *arg2 = arg1 + arg_size(*arg1);
826         ctx->code_position = arg2 + arg_size(*arg2);
827         mc = ARM_ALWAYS;
828         mc |= ARM_VCVT_S32_F;
829         switch (fp_op_size) {
830                 case OP_SIZE_4:         break;
831                 case OP_SIZE_8:         mc |= ARM_V_D; break;
832                 default:                internal(file_line, "cgen_fp_to_int: invalid size %u", fp_op_size);
833         }
834         mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
835         mc |= (uint32_t)(arg1[0] & 1) << 22;
836         mc |= (uint32_t)(arg2[0] & 0x1e) >> 1;
837         mc |= (uint32_t)(arg2[0] & 1) << 5;
838         cgen_four(mc);
839         return true;
842 static bool attr_w cgen_fp_from_int(struct codegen_context *ctx, unsigned fp_op_size)
844         uint32_t mc;
845         uint8_t *arg1 = ctx->code_position;
846         uint8_t *arg2 = arg1 + arg_size(*arg1);
847         ctx->code_position = arg2 + arg_size(*arg2);
848         mc = ARM_ALWAYS;
849         mc |= ARM_VCVT_F_S32;
850         switch (fp_op_size) {
851                 case OP_SIZE_4:         break;
852                 case OP_SIZE_8:         mc |= ARM_V_D; break;
853                 default:                internal(file_line, "cgen_fp_from_int: invalid size %u", fp_op_size);
854         }
855         mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
856         mc |= (uint32_t)(arg1[0] & 1) << 22;
857         mc |= (uint32_t)(arg2[0] & 0x1e) >> 1;
858         mc |= (uint32_t)(arg2[0] & 1) << 5;
859         cgen_four(mc);
860         return true;
863 static bool attr_w cgen_fp_cvt(struct codegen_context *ctx, unsigned from_op_size, unsigned to_op_size)
865         uint32_t mc;
866         uint8_t *arg1 = ctx->code_position;
867         uint8_t *arg2 = arg1 + arg_size(*arg1);
868         ctx->code_position = arg2 + arg_size(*arg2);
869         mc = ARM_ALWAYS;
870         if (from_op_size == OP_SIZE_2 && to_op_size == OP_SIZE_4) {
871                 mc |= ARM_VCVT_F32_F16;
872         } else if (from_op_size == OP_SIZE_4 && to_op_size == OP_SIZE_2) {
873                 mc |= ARM_VCVT_F16_F32;
874         } else {
875                 internal(file_line, "cgen_fp_cvt: invalid types %u, %u", from_op_size, to_op_size);
876         }
877         mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
878         mc |= (uint32_t)(arg1[0] & 1) << 22;
879         mc |= (uint32_t)(arg2[0] & 0x1e) >> 1;
880         mc |= (uint32_t)(arg2[0] & 1) << 5;
881         cgen_four(mc);
882         return true;
885 static bool attr_w cgen_jmp_cond(struct codegen_context *ctx, unsigned aux, unsigned length)
887         int8_t cond;
888         uint32_t mc;
889         if (unlikely(length != JMP_SHORTEST))
890                 internal(file_line, "cgen_jmp_cond: invalid length %u", length);
891         cond = jmp_cond[aux];
892         if (unlikely(cond < 0))
893                 internal(file_line, "cgen_jmp_cond: invalid condition %u", aux);
894         g(add_relocation(ctx, JMP_SHORTEST, 0, NULL));
895         mc = (uint32_t)cond << 28;
896         mc |= ARM_B;
897         cgen_four(mc);
898         return true;
901 static bool attr_w cgen_jmp_indirect(struct codegen_context *ctx)
903         uint8_t pc = R_PC;
904         uint8_t *arg1 = ctx->code_position;
905         ctx->code_position = arg1 + arg_size(*arg1);
906         g(cgen_mov_args(ctx, ARM_ALWAYS, OP_SIZE_ADDRESS, false, &pc, arg1));
907         return true;
910 static bool attr_w resolve_relocation(struct codegen_context *ctx, struct relocation *reloc)
912         uint32_t mc;
913         int64_t offs = (int64_t)(ctx->label_to_pos[reloc->label_id] >> 2) - (int64_t)(reloc->position >> 2) - 2;
914         switch (reloc->length) {
915                 case JMP_SHORTEST:
916                         if (unlikely(offs < -0x00800000) || unlikely(offs >= 0x00800000))
917                                 return false;
918                         memcpy(&mc, ctx->mcode + reloc->position, 4);
919                         mc &= 0xff000000U;
920                         mc |= offs & 0x00ffffffU;
921                         memcpy(ctx->mcode + reloc->position, &mc, 4);
922                         return true;
923                 default:
924                         internal(file_line, "resolve_relocation: invalid relocation length %u", reloc->length);
925         }
926         return false;
929 static bool attr_w cgen_insn(struct codegen_context *ctx, uint32_t insn)
931         /*debug("insn: %08x (%s)", insn, da(ctx->fn,function)->function_name);*/
932         switch (insn_opcode(insn)) {
933                 case INSN_ENTRY:
934                         g(cgen_entry(ctx));
935                         return true;
936                 case INSN_LABEL:
937                         g(cgen_label(ctx));
938                         return true;
939                 case INSN_ARM_PUSH:
940                 case INSN_ARM_POP:
941                         g(cgen_arm_push_pop(ctx, insn_opcode(insn) == INSN_ARM_POP));
942                         return true;
943                 case INSN_CALL_INDIRECT:
944                         g(cgen_call_indirect(ctx));
945                         return true;
946                 case INSN_MOV:
947                         g(cgen_mov(ctx, insn_op_size(insn), false));
948                         return true;
949                 case INSN_MOVSX:
950                         g(cgen_mov(ctx, insn_op_size(insn), true));
951                         return true;
952                 case INSN_CMP:
953                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
954                                 goto invalid_insn;
955                         g(cgen_cmp(ctx, false));
956                         return true;
957                 case INSN_CMN:
958                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
959                                 goto invalid_insn;
960                         g(cgen_cmp(ctx, true));
961                         return true;
962                 case INSN_TEST:
963                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
964                                 goto invalid_insn;
965                         g(cgen_test(ctx));
966                         return true;
967                 case INSN_ALU:
968                 case INSN_ALU_FLAGS:
969                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
970                                 goto invalid_insn;
971                         g(cgen_alu(ctx, insn_writes_flags(insn), insn_aux(insn)));
972                         return true;
973                 case INSN_ALU1:
974                 case INSN_ALU1_FLAGS:
975                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
976                                 goto invalid_insn;
977                         g(cgen_alu1(ctx, insn_writes_flags(insn), insn_aux(insn)));
978                         return true;
979                 case INSN_ROT:
980                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
981                                 goto invalid_insn;
982                         g(cgen_rot(ctx, insn_writes_flags(insn), insn_aux(insn)));
983                         return true;
984                 case INSN_MUL_L:
985                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
986                                 goto invalid_insn;
987                         g(cgen_mul_l(ctx, insn_aux(insn)));
988                         return true;
989                 case INSN_MADD:
990                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
991                                 goto invalid_insn;
992                         g(cgen_madd(ctx, insn_aux(insn)));
993                         return true;
994                 case INSN_CMOV:
995                         g(cgen_cmov(ctx, insn_op_size(insn), insn_aux(insn)));
996                         return true;
997                 case INSN_STP:
998                 case INSN_LDP:
999                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
1000                                 goto invalid_insn;
1001                         g(cgen_ldp_stp(ctx, insn_opcode(insn) == INSN_LDP));
1002                         return true;
1003                 case INSN_MOV_MASK:
1004                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
1005                                 goto invalid_insn;
1006                         g(cgen_mov_mask(ctx, insn_aux(insn)));
1007                         return true;
1008                 case INSN_FP_CMP:
1009                         g(cgen_fp_cmp(ctx, insn_op_size(insn)));
1010                         return true;
1011                 case INSN_FP_TO_INT_FLAGS:
1012                         cgen_four(ARM_ALWAYS | ARM_VMRS_NZCV_FPSCR);
1013                         return true;
1014                 case INSN_FP_ALU:
1015                         g(cgen_fp_alu(ctx, insn_op_size(insn), insn_aux(insn)));
1016                         return true;
1017                 case INSN_FP_ALU1:
1018                         g(cgen_fp_alu1(ctx, insn_op_size(insn), insn_aux(insn)));
1019                         return true;
1020                 case INSN_FP_TO_INT32:
1021                         g(cgen_fp_to_int(ctx, insn_op_size(insn)));
1022                         return true;
1023                 case INSN_FP_FROM_INT32:
1024                         g(cgen_fp_from_int(ctx, insn_op_size(insn)));
1025                         return true;
1026                 case INSN_FP_CVT:
1027                         g(cgen_fp_cvt(ctx, insn_op_size(insn), insn_aux(insn)));
1028                         return true;
1029                 case INSN_JMP:
1030                         g(add_relocation(ctx, JMP_SHORTEST, 0, NULL));
1031                         cgen_four(ARM_ALWAYS | ARM_B);
1032                         return true;
1033                 case INSN_JMP_COND:
1034                         g(cgen_jmp_cond(ctx, insn_aux(insn), insn_jump_size(insn)));
1035                         return true;
1036                 case INSN_JMP_INDIRECT:
1037                         g(cgen_jmp_indirect(ctx));
1038                         return true;
1039                 invalid_insn:
1040                 default:
1041                         internal(file_line, "cgen_insn: invalid insn %08x", insn);
1042                         return false;
1043         }