codegen: add a 'size' argument to ALU_WRITES_FLAGS
[ajla.git] / c2-arm.inc
blobb86b81dff5e6a0c6d1a33af5427bf6c07f585095
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 *arg1 = ctx->code_position;
537         uint8_t *arg2 = arg1 + arg_size(*arg1);
538         ctx->code_position = arg2 + arg_size(*arg2);
539         switch (alu) {
540                 case ALU1_NOT:
541                         return cgen_alu_args(ctx, writes_flags, ARM_MVN, arg1, &z, arg2);
542                 case ALU1_NEG:
543                         return cgen_alu_args(ctx, writes_flags, ARM_RSB, arg1, arg2, z_imm);
544                 case ALU1_NGC:
545                         return cgen_alu_args(ctx, writes_flags, ARM_RSC, arg1, arg2, z_imm);
546                 case ALU1_BSWAP:
547                         mc = ARM_ALWAYS | ARM_REV;
548                         break;
549                 case ALU1_BSWAP16:
550                         mc = ARM_ALWAYS | ARM_REV16;
551                         break;
552                 case ALU1_BREV:
553                         mc = ARM_ALWAYS | ARM_RBIT;
554                         break;
555                 case ALU1_LZCNT:
556                         mc = ARM_ALWAYS | ARM_CLZ;
557                         break;
558                 default:
559                         internal(file_line, "cgen_alu1: invalid alu %u", alu);
560                         return false;
561         }
562         mc |= (uint32_t)arg1[0] << 12;
563         mc |= arg2[0];
564         cgen_four(mc);
565         return true;
568 static bool attr_w cgen_rot(struct codegen_context *ctx, unsigned writes_flags, unsigned rot)
570         int8_t arm_rot;
571         uint32_t mc;
572         uint8_t *arg1 = ctx->code_position;
573         uint8_t *arg2 = arg1 + arg_size(*arg1);
574         uint8_t *arg3 = arg2 + arg_size(*arg2);
575         ctx->code_position = arg3 + arg_size(*arg3);
576         mc = ARM_ALWAYS | ARM_MOV;
577         if (writes_flags)
578                 mc |= ARM_WRITE_FLAGS;
579         arm_rot = rot_codes[rot];
580         if (unlikely(arm_rot < 0))
581                 internal(file_line, "cgen_rot: invalid rotation %u", rot);
582         if (arg3[0] == ARG_IMM) {
583                 uint8_t imm = arg3[1];
584                 if (!imm)
585                         arm_rot = ARM_ROT_LSL;
586                 mc |= (uint32_t)imm << 7;
587         } else {
588                 mc |= ARM_ALU_REG_SHIFTED;
589                 mc |= (uint32_t)arg3[0] << 8;
590         }
591         mc |= arm_rot;
592         mc |= (uint32_t)arg1[0] << 12;
593         mc |= arg2[0];
594         cgen_four(mc);
595         return true;
598 static bool attr_w cgen_mul_l(struct codegen_context *ctx, bool sgn)
600         uint32_t mc;
601         uint8_t *arg1 = ctx->code_position;
602         uint8_t *arg2 = arg1 + arg_size(*arg1);
603         uint8_t *arg3 = arg2 + arg_size(*arg2);
604         uint8_t *arg4 = arg3 + arg_size(*arg3);
605         ctx->code_position = arg4 + arg_size(*arg4);
606         if (unlikely(arg1[0] >= 16) || unlikely(arg2[0] >= 16) || unlikely(arg3[0] >= 16) || unlikely(arg4[0] >= 16))
607                 internal(file_line, "cgen_madd: invalid arguments");
608         mc = ARM_ALWAYS;
609         mc |= sgn ? ARM_SMULL : ARM_UMULL;
610         mc |= (uint32_t)arg1[0] << 12;
611         mc |= (uint32_t)arg2[0] << 16;
612         mc |= arg3[0];
613         mc |= (uint32_t)arg4[0] << 8;
614         cgen_four(mc);
615         return true;
618 static bool attr_w cgen_madd(struct codegen_context *ctx, bool sub)
620         uint32_t mc;
621         uint8_t *arg1 = ctx->code_position;
622         uint8_t *arg2 = arg1 + arg_size(*arg1);
623         uint8_t *arg3 = arg2 + arg_size(*arg2);
624         uint8_t *arg4 = arg3 + arg_size(*arg3);
625         ctx->code_position = arg4 + arg_size(*arg4);
626         if (unlikely(arg1[0] >= 16) || unlikely(arg2[0] >= 16) || unlikely(arg3[0] >= 16) || unlikely(arg4[0] >= 16))
627                 internal(file_line, "cgen_madd: invalid arguments");
628         mc = ARM_ALWAYS;
629         mc |= sub ? ARM_MLS : ARM_MLA;
630         mc |= (uint32_t)arg1[0] << 16;
631         mc |= arg2[0];
632         mc |= (uint32_t)arg3[0] << 8;
633         mc |= (uint32_t)arg4[0] << 12;
634         cgen_four(mc);
635         return true;
638 static bool attr_w cgen_cmov(struct codegen_context *ctx, unsigned size, unsigned aux)
640         int8_t cond;
641         uint8_t *arg1 = ctx->code_position;
642         uint8_t *arg2 = arg1 + arg_size(*arg1);
643         uint8_t *arg3 = arg2 + arg_size(*arg2);
644         ctx->code_position = arg3 + arg_size(*arg3);
645         if (unlikely(arg1[0] != arg2[0]))
646                 internal(file_line, "cgen_cmov: invalid arguments");
647         cond = jmp_cond[aux];
648         if (unlikely(cond < 0))
649                 internal(file_line, "cgen_cmov: invalid condition %u", aux);
650         g(cgen_mov_args(ctx, (uint32_t)cond << 28, size, false, arg1, arg3));
651         return true;
654 static bool attr_w cgen_ldp_stp(struct codegen_context *ctx, bool ldr)
656         uint8_t *arg1, *arg2, *arg3;
657         int64_t imm;
658         uint32_t mc = ARM_ALWAYS;
659         if (!ldr) {
660                 arg1 = ctx->code_position;
661                 arg2 = arg1 + arg_size(*arg1);
662                 arg3 = arg2 + arg_size(*arg2);
663                 ctx->code_position = arg3 + arg_size(*arg3);
664         } else {
665                 arg2 = ctx->code_position;
666                 arg3 = arg2 + arg_size(*arg2);
667                 arg1 = arg3 + arg_size(*arg3);
668                 ctx->code_position = arg1 + arg_size(*arg1);
669         }
670         if (unlikely(arg2[0] >= 16) || unlikely(arg3[0] >= 16))
671                 goto invalid;
672         if (unlikely((arg2[0] & 1) != 0) || unlikely(arg3[0] != arg2[0] + 1))
673                 goto invalid;
674         if (arg1[0] == ARG_ADDRESS_1 || arg1[0] == ARG_ADDRESS_1_PRE_I || arg1[0] == ARG_ADDRESS_1_POST_I) {
675                 mc |= !ldr ? ARM_STRD_IMM : ARM_LDRD_IMM;
676                 if (arg1[0] == ARG_ADDRESS_1) {
677                         mc |= ARM_LDR_STR_P;
678                 } else if (arg1[0] == ARG_ADDRESS_1_PRE_I) {
679                         mc |= ARM_LDR_STR_P;
680                         mc |= ARM_LDR_STR_W;
681                 }
682                 imm = get_imm(&arg1[2]);
683                 if (!(imm >= -255 && imm <= 255))
684                         goto invalid;
685                 if (imm < 0) {
686                         imm = -imm;
687                 } else {
688                         mc |= ARM_LDR_STR_U;
689                 }
690                 mc |= imm & 0xf;
691                 mc |= (imm & 0xf0) << 4;
692         } else if (arg1[0] == ARG_ADDRESS_2) {
693                 imm = get_imm(&arg1[3]);
694                 if (unlikely(imm != 0))
695                         goto invalid;
696                 mc |= !ldr ? ARM_STRD_REG : ARM_LDRD_REG;
697                 mc |= ARM_LDR_STR_P;
698                 mc |= ARM_LDR_STR_U;
699                 mc |= arg1[2];
700         } else {
701                 goto invalid;
702         }
703         mc |= (uint32_t)arg1[1] << 16;
704         mc |= (uint32_t)arg2[0] << 12;
705         cgen_four(mc);
706         return true;
708 invalid:
709         internal(file_line, "cgen_ldp_stp: invalid arguments %02x, %02x, %02x", arg1[0], arg2[0], arg3[0]);
710         return false;
713 static bool attr_w cgen_mov_mask(struct codegen_context *ctx, unsigned aux)
715         uint32_t mc;
716         uint64_t imm;
717         uint8_t *arg1 = ctx->code_position;
718         uint8_t *arg2 = arg1 + arg_size(*arg1);
719         uint8_t *arg3 = arg2 + arg_size(*arg2);
720         ctx->code_position = arg3 + arg_size(*arg3);
721         if (unlikely(arg1[0] >= 16) || unlikely(arg2[0] >= 16) || unlikely(arg3[0] != ARG_IMM) || unlikely(aux != MOV_MASK_16_32))
722                 internal(file_line, "cgen_mov_mask: bad arguments");
723         imm = get_imm(&arg3[1]);
724         if (unlikely(imm >= 0x10000))
725                 internal(file_line, "cgen_mov_mask: bad number");
726         mc = ARM_ALWAYS | ARM_MOVT;
727         mc |= (uint32_t)arg1[0] << 12;
728         mc |= imm & 0xfff;
729         mc |= (imm & 0xf000) << 4;
730         cgen_four(mc);
731         return true;
734 static bool attr_w cgen_fp_cmp(struct codegen_context *ctx, unsigned op_size)
736         uint32_t mc;
737         uint8_t *arg1 = ctx->code_position;
738         uint8_t *arg2 = arg1 + arg_size(*arg1);
739         ctx->code_position = arg2 + arg_size(*arg2);
740         mc = ARM_ALWAYS;
741         mc |= ARM_VCMP;
742         switch (op_size) {
743                 case OP_SIZE_4:         break;
744                 case OP_SIZE_8:         mc |= ARM_V_D; break;
745                 default:                internal(file_line, "cgen_fp_cmp: invalid size %u", op_size);
746         }
747         mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
748         mc |= (uint32_t)(arg1[0] & 1) << 22;
749         mc |= (uint32_t)(arg2[0] & 0x1e) >> 1;
750         mc |= (uint32_t)(arg2[0] & 1) << 5;
751         cgen_four(mc);
752         return true;
755 static bool attr_w cgen_fp_alu(struct codegen_context *ctx, unsigned op_size, unsigned aux)
757         uint32_t mc;
758         uint8_t *arg1 = ctx->code_position;
759         uint8_t *arg2 = arg1 + arg_size(*arg1);
760         uint8_t *arg3 = arg2 + arg_size(*arg2);
761         ctx->code_position = arg3 + arg_size(*arg3);
762         mc = ARM_ALWAYS;
763         switch (aux) {
764                 case FP_ALU_ADD:        mc |= ARM_VADD; break;
765                 case FP_ALU_SUB:        mc |= ARM_VSUB; break;
766                 case FP_ALU_MUL:        mc |= ARM_VMUL; break;
767                 case FP_ALU_DIV:        mc |= ARM_VDIV; break;
768                 default:                internal(file_line, "cgen_fp_alu: invalid alu %u", aux);
769         }
770         switch (op_size) {
771                 case OP_SIZE_4:         break;
772                 case OP_SIZE_8:         mc |= ARM_V_D; break;
773                 default:                internal(file_line, "cgen_fp_alu: invalid size %u", op_size);
774         }
775         mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
776         mc |= (uint32_t)(arg1[0] & 1) << 22;
777         mc |= (uint32_t)(arg2[0] & 0x1e) << 15;
778         mc |= (uint32_t)(arg2[0] & 1) << 7;
779         mc |= (uint32_t)(arg3[0] & 0x1e) >> 1;
780         mc |= (uint32_t)(arg3[0] & 1) << 5;
781         cgen_four(mc);
782         return true;
785 static bool attr_w cgen_fp_alu1(struct codegen_context *ctx, unsigned op_size, unsigned aux)
787         uint32_t mc;
788         uint8_t *arg1 = ctx->code_position;
789         uint8_t *arg2 = arg1 + arg_size(*arg1);
790         ctx->code_position = arg2 + arg_size(*arg2);
791         switch (aux) {
792                 case FP_ALU1_NEG:       mc = ARM_ALWAYS | ARM_VNEG; break;
793                 case FP_ALU1_SQRT:      mc = ARM_ALWAYS | ARM_VSQRT; break;
794                 case FP_ALU1_VCNT8:     mc = ARM_VCNT_8; goto do_regs;
795                 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;
796                                         if (!mc)
797                                                 goto invalid_size;
798                                         goto do_regs;
799                 default:                internal(file_line, "cgen_fp_alu1: invalid alu %u", aux);
800         }
801         switch (op_size) {
802                 case OP_SIZE_4:         break;
803                 case OP_SIZE_8:         mc |= ARM_V_D; break;
804 invalid_size:
805                 default:                internal(file_line, "cgen_fp_alu1: invalid size %u", op_size);
806         }
807 do_regs:
808         mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
809         mc |= (uint32_t)(arg1[0] & 1) << 22;
810         mc |= (uint32_t)(arg2[0] & 0x1e) >> 1;
811         mc |= (uint32_t)(arg2[0] & 1) << 5;
812         cgen_four(mc);
813         return true;
816 static bool attr_w cgen_fp_to_int(struct codegen_context *ctx, unsigned fp_op_size)
818         uint32_t mc;
819         uint8_t *arg1 = ctx->code_position;
820         uint8_t *arg2 = arg1 + arg_size(*arg1);
821         ctx->code_position = arg2 + arg_size(*arg2);
822         mc = ARM_ALWAYS;
823         mc |= ARM_VCVT_S32_F;
824         switch (fp_op_size) {
825                 case OP_SIZE_4:         break;
826                 case OP_SIZE_8:         mc |= ARM_V_D; break;
827                 default:                internal(file_line, "cgen_fp_to_int: invalid size %u", fp_op_size);
828         }
829         mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
830         mc |= (uint32_t)(arg1[0] & 1) << 22;
831         mc |= (uint32_t)(arg2[0] & 0x1e) >> 1;
832         mc |= (uint32_t)(arg2[0] & 1) << 5;
833         cgen_four(mc);
834         return true;
837 static bool attr_w cgen_fp_from_int(struct codegen_context *ctx, unsigned fp_op_size)
839         uint32_t mc;
840         uint8_t *arg1 = ctx->code_position;
841         uint8_t *arg2 = arg1 + arg_size(*arg1);
842         ctx->code_position = arg2 + arg_size(*arg2);
843         mc = ARM_ALWAYS;
844         mc |= ARM_VCVT_F_S32;
845         switch (fp_op_size) {
846                 case OP_SIZE_4:         break;
847                 case OP_SIZE_8:         mc |= ARM_V_D; break;
848                 default:                internal(file_line, "cgen_fp_from_int: invalid size %u", fp_op_size);
849         }
850         mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
851         mc |= (uint32_t)(arg1[0] & 1) << 22;
852         mc |= (uint32_t)(arg2[0] & 0x1e) >> 1;
853         mc |= (uint32_t)(arg2[0] & 1) << 5;
854         cgen_four(mc);
855         return true;
858 static bool attr_w cgen_fp_cvt(struct codegen_context *ctx, unsigned from_op_size, unsigned to_op_size)
860         uint32_t mc;
861         uint8_t *arg1 = ctx->code_position;
862         uint8_t *arg2 = arg1 + arg_size(*arg1);
863         ctx->code_position = arg2 + arg_size(*arg2);
864         mc = ARM_ALWAYS;
865         if (from_op_size == OP_SIZE_2 && to_op_size == OP_SIZE_4) {
866                 mc |= ARM_VCVT_F32_F16;
867         } else if (from_op_size == OP_SIZE_4 && to_op_size == OP_SIZE_2) {
868                 mc |= ARM_VCVT_F16_F32;
869         } else {
870                 internal(file_line, "cgen_fp_cvt: invalid types %u, %u", from_op_size, to_op_size);
871         }
872         mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
873         mc |= (uint32_t)(arg1[0] & 1) << 22;
874         mc |= (uint32_t)(arg2[0] & 0x1e) >> 1;
875         mc |= (uint32_t)(arg2[0] & 1) << 5;
876         cgen_four(mc);
877         return true;
880 static bool attr_w cgen_jmp_cond(struct codegen_context *ctx, unsigned aux, unsigned length)
882         int8_t cond;
883         uint32_t mc;
884         if (unlikely(length != JMP_SHORTEST))
885                 internal(file_line, "cgen_jmp_cond: invalid length %u", length);
886         cond = jmp_cond[aux];
887         if (unlikely(cond < 0))
888                 internal(file_line, "cgen_jmp_cond: invalid condition %u", aux);
889         g(add_relocation(ctx, JMP_SHORTEST, 0, NULL));
890         mc = (uint32_t)cond << 28;
891         mc |= ARM_B;
892         cgen_four(mc);
893         return true;
896 static bool attr_w cgen_jmp_indirect(struct codegen_context *ctx)
898         uint8_t pc = R_PC;
899         uint8_t *arg1 = ctx->code_position;
900         ctx->code_position = arg1 + arg_size(*arg1);
901         g(cgen_mov_args(ctx, ARM_ALWAYS, OP_SIZE_ADDRESS, false, &pc, arg1));
902         return true;
905 static bool attr_w resolve_relocation(struct codegen_context *ctx, struct relocation *reloc)
907         uint32_t mc;
908         int64_t offs = (int64_t)(ctx->label_to_pos[reloc->label_id] >> 2) - (int64_t)(reloc->position >> 2) - 2;
909         switch (reloc->length) {
910                 case JMP_SHORTEST:
911                         if (unlikely(offs < -0x00800000) || unlikely(offs >= 0x00800000))
912                                 return false;
913                         memcpy(&mc, ctx->mcode + reloc->position, 4);
914                         mc &= 0xff000000U;
915                         mc |= offs & 0x00ffffffU;
916                         memcpy(ctx->mcode + reloc->position, &mc, 4);
917                         return true;
918                 default:
919                         internal(file_line, "resolve_relocation: invalid relocation length %u", reloc->length);
920         }
921         return false;
924 static bool attr_w cgen_insn(struct codegen_context *ctx, uint32_t insn)
926         /*debug("insn: %08x (%s)", insn, da(ctx->fn,function)->function_name);*/
927         switch (insn_opcode(insn)) {
928                 case INSN_ENTRY:
929                         g(cgen_entry(ctx));
930                         return true;
931                 case INSN_LABEL:
932                         g(cgen_label(ctx));
933                         return true;
934                 case INSN_ARM_PUSH:
935                 case INSN_ARM_POP:
936                         g(cgen_arm_push_pop(ctx, insn_opcode(insn) == INSN_ARM_POP));
937                         return true;
938                 case INSN_CALL_INDIRECT:
939                         g(cgen_call_indirect(ctx));
940                         return true;
941                 case INSN_MOV:
942                         g(cgen_mov(ctx, insn_op_size(insn), false));
943                         return true;
944                 case INSN_MOVSX:
945                         g(cgen_mov(ctx, insn_op_size(insn), true));
946                         return true;
947                 case INSN_CMP:
948                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
949                                 goto invalid_insn;
950                         g(cgen_cmp(ctx, false));
951                         return true;
952                 case INSN_CMN:
953                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
954                                 goto invalid_insn;
955                         g(cgen_cmp(ctx, true));
956                         return true;
957                 case INSN_TEST:
958                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
959                                 goto invalid_insn;
960                         g(cgen_test(ctx));
961                         return true;
962                 case INSN_ALU:
963                 case INSN_ALU_FLAGS:
964                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
965                                 goto invalid_insn;
966                         g(cgen_alu(ctx, insn_writes_flags(insn), insn_aux(insn)));
967                         return true;
968                 case INSN_ALU1:
969                 case INSN_ALU1_FLAGS:
970                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
971                                 goto invalid_insn;
972                         g(cgen_alu1(ctx, insn_writes_flags(insn), insn_aux(insn)));
973                         return true;
974                 case INSN_ROT:
975                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
976                                 goto invalid_insn;
977                         g(cgen_rot(ctx, insn_writes_flags(insn), insn_aux(insn)));
978                         return true;
979                 case INSN_MUL_L:
980                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
981                                 goto invalid_insn;
982                         g(cgen_mul_l(ctx, insn_aux(insn)));
983                         return true;
984                 case INSN_MADD:
985                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
986                                 goto invalid_insn;
987                         g(cgen_madd(ctx, insn_aux(insn)));
988                         return true;
989                 case INSN_CMOV:
990                         g(cgen_cmov(ctx, insn_op_size(insn), insn_aux(insn)));
991                         return true;
992                 case INSN_STP:
993                 case INSN_LDP:
994                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
995                                 goto invalid_insn;
996                         g(cgen_ldp_stp(ctx, insn_opcode(insn) == INSN_LDP));
997                         return true;
998                 case INSN_MOV_MASK:
999                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
1000                                 goto invalid_insn;
1001                         g(cgen_mov_mask(ctx, insn_aux(insn)));
1002                         return true;
1003                 case INSN_FP_CMP:
1004                         g(cgen_fp_cmp(ctx, insn_op_size(insn)));
1005                         return true;
1006                 case INSN_FP_TO_INT_FLAGS:
1007                         cgen_four(ARM_ALWAYS | ARM_VMRS_NZCV_FPSCR);
1008                         return true;
1009                 case INSN_FP_ALU:
1010                         g(cgen_fp_alu(ctx, insn_op_size(insn), insn_aux(insn)));
1011                         return true;
1012                 case INSN_FP_ALU1:
1013                         g(cgen_fp_alu1(ctx, insn_op_size(insn), insn_aux(insn)));
1014                         return true;
1015                 case INSN_FP_TO_INT32:
1016                         g(cgen_fp_to_int(ctx, insn_op_size(insn)));
1017                         return true;
1018                 case INSN_FP_FROM_INT32:
1019                         g(cgen_fp_from_int(ctx, insn_op_size(insn)));
1020                         return true;
1021                 case INSN_FP_CVT:
1022                         g(cgen_fp_cvt(ctx, insn_op_size(insn), insn_aux(insn)));
1023                         return true;
1024                 case INSN_JMP:
1025                         g(add_relocation(ctx, JMP_SHORTEST, 0, NULL));
1026                         cgen_four(ARM_ALWAYS | ARM_B);
1027                         return true;
1028                 case INSN_JMP_COND:
1029                         g(cgen_jmp_cond(ctx, insn_aux(insn), insn_jump_size(insn)));
1030                         return true;
1031                 case INSN_JMP_INDIRECT:
1032                         g(cgen_jmp_indirect(ctx));
1033                         return true;
1034                 invalid_insn:
1035                 default:
1036                         internal(file_line, "cgen_insn: invalid insn %08x", insn);
1037                         return false;
1038         }