codegen: use gen_frame_target for generic bit operations
[ajla.git] / c2-arm.inc
blob7db30ac6d708f566e65f779224fb819f642835f8
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_REV                 0x06bf0f30U
75 #define ARM_REV16               0x06bf0fb0U
76 #define ARM_RBIT                0x06ff0f30U
77 #define ARM_SDIV                0x0710f010U
78 #define ARM_UDIV                0x0730f010U
79 #define ARM_POP                 0x08bd0000U
80 #define ARM_PUSH                0x092d0000U
81 #define ARM_B                   0x0a000000U
83 #define  ARM_V_D                        0x00000100U
84 #define ARM_VSTR                0x0d000a00U
85 #define ARM_VLDR                0x0d100a00U
86 #define ARM_VMOV_S32_R          0x0e000a10U
87 #define ARM_VMOV_R_S32          0x0e100a10U
88 #define ARM_VMUL                0x0e200a00U
89 #define ARM_VADD                0x0e300a00U
90 #define ARM_VSUB                0x0e300a40U
91 #define ARM_VDIV                0x0e800a00U
92 #define ARM_VMOV                0x0eb00a40U
93 #define ARM_VNEG                0x0eb10a40U
94 #define ARM_VSQRT               0x0eb10ac0U
95 #define ARM_VCVT_F32_F16        0x0eb20a40U
96 #define ARM_VCVT_F16_F32        0x0eb30a40U
97 #define ARM_VCMP                0x0eb40a40U
98 #define ARM_VCVT_F_S32          0x0eb80ac0U
99 #define ARM_VCVT_S32_F          0x0ebd0ac0U
100 #define ARM_VMRS_NZCV_FPSCR     0x0ef1fa10U
102 #define  ARM_V_BYTE                     0x00000000U
103 #define  ARM_V_HALF                     0x00000400U
104 #define ARM_VST1                0xf480000fU
105 #define ARM_VLD1                0xf4a0000fU
106 #define  ARM_VCNT_8_Q                   0x00000040U
107 #define ARM_VCNT_8              0xf3b00500U
108 #define ARM_VPADDL_U8           0xf3b00280U
109 #define ARM_VPADDL_U16          0xf3b40280U
110 #define ARM_VPADDL_U32          0xf3b80280U
112 static const uint32_t alu_codes[7] = {
113         ARM_ADD,
114         ARM_ORR,
115         ARM_ADC,
116         ARM_SBC,
117         ARM_AND,
118         ARM_SUB,
119         ARM_EOR,
122 static const int8_t jmp_cond[48] = {
123         0x6, 0x7, 0x3, 0x2, 0x0, 0x1, 0x9, 0x8,
124         0x4, 0x5,  -1,  -1, 0xb, 0xa, 0xd, 0xc,
125          -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
126          -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
127          -1,  -1, 0x3, 0x2, 0x0, 0x1, 0x9, 0x8,
128          -1,  -1, 0x6, 0x7,  -1,  -1,  -1,  -1,
131 static const int8_t rot_codes[8] = {
132         -1,
133         ARM_ROT_ROR,
134         -1,
135         -1,
136         ARM_ROT_LSL,
137         ARM_ROT_LSR,
138         -1,
139         ARM_ROT_ASR,
142 static bool attr_w cgen_arm_push_pop(struct codegen_context *ctx, bool pop)
144         bool need_bx = false;
145         uint32_t mc = ARM_ALWAYS;
146         mc |= pop ? ARM_POP : ARM_PUSH;
147 #if defined(__thumb__) || defined(__thumb2__)
148         if (pop) {
149                 if (unlikely(!cpu_test_feature(CPU_FEATURE_armv5)))
150                         need_bx = true;
151         }
152 #endif
153         mc |= 1U << R_2;
154         mc |= 1U << R_4;
155         mc |= 1U << R_5;
156         mc |= 1U << R_6;
157         mc |= 1U << R_7;
158         mc |= 1U << R_8;
159         mc |= 1U << R_9;
160         mc |= 1U << R_10;
161         mc |= 1U << R_FP;
162         mc |= 1U << (pop && !need_bx ? R_PC : R_LR);
163         cgen_four(mc);
164         if (need_bx) {
165                 mc = ARM_ALWAYS | ARM_BX;
166                 mc |= R_LR;
167                 cgen_four(mc);
168         }
169         return true;
172 static bool attr_w cgen_call_indirect(struct codegen_context *ctx)
174         uint32_t mc;
175         bool blx = false;
176         uint8_t reg = cget_one(ctx);
178 #if defined(__thumb__) || defined(__thumb2__)
179         blx = true;
180 #else
181         if (likely(cpu_test_feature(CPU_FEATURE_armv6)))
182                 blx = true;
183 #endif
185         if (blx) {
186                 mc = ARM_ALWAYS | ARM_BLX_REG;
187                 mc |= reg;
188                 cgen_four(mc);
189         } else {
190                 mc = ARM_ALWAYS | ARM_MOV;
191                 mc |= (uint32_t)R_LR << 12;
192                 mc |= R_PC;
193                 cgen_four(mc);
195                 mc = ARM_ALWAYS | ARM_MOV;
196                 mc |= (uint32_t)R_PC << 12;
197                 mc |= reg;
198                 cgen_four(mc);
199         }
201         return true;
204 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)
206         int64_t imm;
207         uint32_t mc = cond;
208         if (ld)
209                 mc |= ARM_LDR_STR_LD;
210         if (size == OP_SIZE_NATIVE)
211                 sx = false;
212         if (address[0] >= ARG_ADDRESS_2 && address[0] <= ARG_ADDRESS_2_8) {
213                 imm = get_imm(&address[3]);
214                 if (unlikely(imm != 0))
215                         goto invalid;
216                 if (sx || size == OP_SIZE_2) {
217                         if (unlikely(address[0] != ARG_ADDRESS_2))
218                                 goto invalid;
219                         if (sx)
220                                 mc |= size == OP_SIZE_2 ? ARM_LDRSH_REG : ARM_LDRSB_REG;
221                         else
222                                 mc |= ARM_STRH_REG;
223                 } else {
224                         mc |= size == OP_SIZE_1 ? ARM_STRB_REG : ARM_STR_REG;
225                 }
226                 mc |= ARM_LDR_STR_U;
227                 mc |= ARM_LDR_STR_P;
228                 mc |= (uint32_t)address[1] << 16;
229                 mc |= address[2];
230                 mc |= (uint32_t)reg << 12;
231                 mc |= (uint32_t)(address[0] - ARG_ADDRESS_2) << 7;
232                 cgen_four(mc);
233                 return true;
234         }
235         if (address[0] == ARG_ADDRESS_1 || address[0] == ARG_ADDRESS_1_PRE_I || address[0] == ARG_ADDRESS_1_POST_I) {
236                 imm = get_imm(&address[2]);
237                 if (!(imm >= -4095 && imm <= 4095))
238                         goto invalid;
239                 if (imm < 0) {
240                         imm = -imm;
241                 } else {
242                         mc |= ARM_LDR_STR_U;
243                 }
244                 if (sx || size == OP_SIZE_2) {
245                         if (unlikely(imm >= 256))
246                                 goto invalid;
247                         if (sx)
248                                 mc |= size == OP_SIZE_2 ? ARM_LDRSH_IMM : ARM_LDRSB_IMM;
249                         else
250                                 mc |= ARM_STRH_IMM;
251                         mc |= imm & 0xf;
252                         mc |= (imm & 0xf0) << 4;
253                 } else {
254                         mc |= size == OP_SIZE_1 ? ARM_STRB_IMM : ARM_STR_IMM;
255                         mc |= imm;
256                 }
257                 if (address[0] == ARG_ADDRESS_1) {
258                         mc |= ARM_LDR_STR_P;
259                 } else if (address[0] == ARG_ADDRESS_1_PRE_I) {
260                         mc |= ARM_LDR_STR_P;
261                         mc |= ARM_LDR_STR_W;
262                 }
263                 mc |= (uint32_t)address[1] << 16;
264                 mc |= (uint32_t)reg << 12;
265                 cgen_four(mc);
266                 return true;
267         }
269         imm = 0;
270 invalid:
271         internal(file_line, "cgen_ldr_str: invalid address: %02x, %02x, %"PRIxMAX"", reg, address[0], (uintmax_t)imm);
272         return false;
275 static bool attr_w cgen_vldr_vstr(struct codegen_context *ctx, bool ld, uint32_t cond, unsigned size, uint8_t reg, uint8_t *address)
277         int64_t imm = 0;
278         uint32_t mc;
280         if (size < OP_SIZE_4) {
281                 mc = ld ? ARM_VLD1 : ARM_VST1;
282                 mc |= size == OP_SIZE_1 ? ARM_V_BYTE : ARM_V_HALF;
283                 mc |= (uint32_t)address[1] << 16;
284                 mc |= (uint32_t)(reg & 0x1e) << 11;
285                 mc |= (uint32_t)(reg & 1) << 22;
286                 cgen_four(mc);
287                 return true;
288         }
290         mc = cond;
291         mc |= ld ? ARM_VLDR : ARM_VSTR;
293         if (unlikely(address[0] != ARG_ADDRESS_1))
294                 goto invalid;
296         imm = get_imm(&address[2]);
297         if (!(imm >= -1023 && imm <= 1023))
298                 goto invalid;
300         if (imm < 0) {
301                 imm = -imm;
302         } else {
303                 mc |= ARM_LDR_STR_U;
304         }
306         mc |= (uint32_t)address[1] << 16;
307         mc |= (uint32_t)(reg & 0x1e) << 11;
308         mc |= (uint32_t)(reg & 1) << 22;
309         mc |= (imm >> 2) & 0xff;
310         if (size == OP_SIZE_8)
311                 mc |= ARM_V_D;
312         cgen_four(mc);
313         return true;
315 invalid:
316         internal(file_line, "cgen_vldr_vstr: invalid address: %02x, %02x, %"PRIxMAX"", reg, address[0], (uintmax_t)imm);
317         return false;
320 static bool attr_w cgen_mov_args(struct codegen_context *ctx, uint32_t cond, unsigned size, bool sx, uint8_t *arg1, uint8_t *arg2)
322         int64_t imm;
323         uint32_t mc;
324         if (arg1[0] < 16) {
325                 if (arg2[0] < 16) {
326                         if (unlikely(sx))
327                                 internal(file_line, "cgen_mov_args: unsupported sign extension");
328                         if (unlikely(size != OP_SIZE_NATIVE))
329                                 internal(file_line, "cgen_mov_args: unsupported size %u", size);
330                         mc = cond | ARM_MOV;
331                         mc |= arg2[0];
332                         mc |= (uint32_t)arg1[0] << 12;
333                         cgen_four(mc);
334                         return true;
335                 }
336                 if (reg_is_fp(arg2[0])) {
337                         if (unlikely(sx))
338                                 internal(file_line, "cgen_mov_args: unsupported sign extension");
339                         if (unlikely(size != OP_SIZE_NATIVE))
340                                 internal(file_line, "cgen_mov_args: unsupported size %u", size);
341                         mc = cond | ARM_VMOV_R_S32;
342                         mc |= (uint32_t)(arg2[0] & 0x1e) << 15;
343                         mc |= (uint32_t)(arg2[0] & 1) << 7;
344                         mc |= (uint32_t)arg1[0] << 12;
345                         cgen_four(mc);
346                         return true;
347                 }
348                 if (arg2[0] == ARG_IMM) {
349                         int imm12;
350                         imm = get_imm(&arg2[1]);
351                         imm12 = gen_imm12(imm);
352                         if (imm12 >= 0) {
353                                 mc = cond | ARM_MOV | ARM_ALU_IMM;
354                                 mc |= (uint32_t)arg1[0] << 12;
355                                 mc |= imm12;
356                                 cgen_four(mc);
357                                 return true;
358                         }
359                         imm12 = gen_imm12(~imm);
360                         if (imm12 >= 0) {
361                                 mc = cond | ARM_MVN | ARM_ALU_IMM;
362                                 mc |= (uint32_t)arg1[0] << 12;
363                                 mc |= imm12;
364                                 cgen_four(mc);
365                                 return true;
366                         }
367                         if ((uint32_t)imm >= 0x10000)
368                                 goto invalid;
369                         mc = cond | ARM_MOV_IMM16;
370                         mc |= (uint32_t)arg1[0] << 12;
371                         mc |= imm & 0xfff;
372                         mc |= (imm & 0xf000) << 4;
373                         cgen_four(mc);
374                         return true;
375                 }
376                 return cgen_ldr_str(ctx, true, cond, size, sx, arg1[0], arg2);
377         }
378         if (reg_is_fp(arg1[0])) {
379                 if (arg2[0] < 16) {
380                         if (unlikely(sx))
381                                 internal(file_line, "cgen_mov_args: unsupported sign extension");
382                         if (unlikely(size != OP_SIZE_NATIVE))
383                                 internal(file_line, "cgen_mov_args: unsupported size %u", size);
384                         mc = cond | ARM_VMOV_S32_R;
385                         mc |= (uint32_t)(arg1[0] & 0x1e) << 15;
386                         mc |= (uint32_t)(arg1[0] & 1) << 7;
387                         mc |= (uint32_t)arg2[0] << 12;
388                         cgen_four(mc);
389                         return true;
390                 }
391                 if (reg_is_fp(arg2[0])) {
392                         mc = cond | ARM_VMOV;
393                         switch (size) {
394                                 case OP_SIZE_4:         break;
395                                 case OP_SIZE_8:         mc |= ARM_V_D; break;
396                                 default:                internal(file_line, "cgen_mov_args: invalid size %u", size);
397                         }
398                         mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
399                         mc |= (uint32_t)(arg1[0] & 1) << 22;
400                         mc |= (uint32_t)(arg2[0] & 0x1e) >> 1;
401                         mc |= (uint32_t)(arg2[0] & 1) << 5;
402                         cgen_four(mc);
403                         return true;
404                 }
405                 return cgen_vldr_vstr(ctx, true, cond, size, arg1[0] & 31, arg2);
406         }
407         if (arg2[0] < 16) {
408                 return cgen_ldr_str(ctx, false, cond, size, false, arg2[0], arg1);
409         }
410         if (reg_is_fp(arg2[0])) {
411                 return cgen_vldr_vstr(ctx, false, cond, size, arg2[0] & 31, arg1);
412         }
413 invalid:
414         internal(file_line, "cgen_mov_args: invalid arguments %02x, %02x", arg1[0], arg2[0]);
415         return false;
418 static bool attr_w cgen_mov(struct codegen_context *ctx, unsigned size, bool sx)
420         uint8_t *arg1 = ctx->code_position;
421         uint8_t *arg2 = arg1 + arg_size(*arg1);
422         ctx->code_position = arg2 + arg_size(*arg2);
423         g(cgen_mov_args(ctx, ARM_ALWAYS, size, sx, arg1, arg2));
424         return true;
427 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)
429         int64_t imm;
430         int imm12;
431         if (unlikely(arg1[0] >= 16))
432                 goto invalid;
433         if (unlikely(arg2[0] >= 16))
434                 goto invalid;
435         mc |= ARM_ALWAYS;
436         if (writes_flags)
437                 mc |= ARM_WRITE_FLAGS;
438         mc |= (uint32_t)arg1[0] << 12;
439         mc |= (uint32_t)arg2[0] << 16;
440         if (arg3[0] == ARG_IMM) {
441                 imm = get_imm(&arg3[1]);
442                 imm12 = gen_imm12(imm);
443                 if (unlikely(imm12 < 0))
444                         goto invalid;
445                 mc |= ARM_ALU_IMM;
446                 mc |= imm12;
447                 cgen_four(mc);
448                 return true;
449         }
450         if (likely(arg3[0] < 16)) {
451                 mc |= arg3[0];
452                 cgen_four(mc);
453                 return true;
454         }
455         if (arg3[0] == ARG_SHIFTED_REGISTER) {
456                 unsigned mode = arg3[1] >> 6;
457                 unsigned amount = arg3[1] & ARG_SHIFT_AMOUNT;
458                 if (!amount)
459                         mode = 0;
460                 mc |= arg3[2];
461                 mc |= (uint32_t)mode << 5;
462                 mc |= ((uint32_t)amount & 0x1f) << 7;
463                 cgen_four(mc);
464                 return true;
465         }
467 invalid:
468         internal(file_line, "cgen_alu_args: invalid arguments %02x, %02x, %02x, %08x, %u", arg1[0], arg2[0], arg3[0], (unsigned)mc, writes_flags);
469         return false;
472 static bool attr_w cgen_cmp(struct codegen_context *ctx, bool cmn)
474         uint8_t z = 0;
475         uint8_t *arg1 = ctx->code_position;
476         uint8_t *arg2 = arg1 + arg_size(*arg1);
477         ctx->code_position = arg2 + arg_size(*arg2);
478         return cgen_alu_args(ctx, true, cmn ? ARM_CMN : ARM_CMP, &z, arg1, arg2);
481 static bool attr_w cgen_test(struct codegen_context *ctx)
483         uint8_t z = 0;
484         uint8_t *arg1 = ctx->code_position;
485         uint8_t *arg2 = arg1 + arg_size(*arg1);
486         ctx->code_position = arg2 + arg_size(*arg2);
487         return cgen_alu_args(ctx, true, ARM_TST, &z, arg1, arg2);
490 static bool attr_w cgen_alu(struct codegen_context *ctx, unsigned writes_flags, unsigned alu)
492         uint8_t *arg1 = ctx->code_position;
493         uint8_t *arg2 = arg1 + arg_size(*arg1);
494         uint8_t *arg3 = arg2 + arg_size(*arg2);
495         ctx->code_position = arg3 + arg_size(*arg3);
496         if (unlikely(alu >= 7)) {
497                 uint32_t mc = ARM_ALWAYS;
498                 if (alu == ALU_ANDN) {
499                         return cgen_alu_args(ctx, writes_flags, ARM_BIC, arg1, arg2, arg3);
500                 } else if (alu == ALU_MUL) {
501                         mc |= ARM_MUL;
502                 } else if (alu == ALU_UDIV) {
503                         mc |= ARM_UDIV;
504                 } else if (alu == ALU_SDIV) {
505                         mc |= ARM_SDIV;
506                 } else {
507                         internal(file_line, "cgen_alu: invalid alu %u", alu);
508                 }
509                 mc |= (uint32_t)arg1[0] << 16;
510                 mc |= arg2[0];
511                 mc |= (uint32_t)arg3[0] << 8;
512                 cgen_four(mc);
513                 return true;
514         }
515         return cgen_alu_args(ctx, writes_flags, alu_codes[alu], arg1, arg2, arg3);
518 static bool attr_w cgen_alu1(struct codegen_context *ctx, unsigned writes_flags, unsigned alu)
520         uint32_t mc;
521         uint8_t z = 0;
522         uint8_t z_imm[9] = { ARG_IMM, 0, 0, 0, 0, 0, 0, 0, 0 };
523         uint8_t one_imm[9] = { ARG_IMM, 1, 0, 0, 0, 0, 0, 0, 0 };
524         uint8_t *arg1 = ctx->code_position;
525         uint8_t *arg2 = arg1 + arg_size(*arg1);
526         ctx->code_position = arg2 + arg_size(*arg2);
527         switch (alu) {
528                 case ALU1_NOT:
529                         return cgen_alu_args(ctx, writes_flags, ARM_MVN, arg1, &z, arg2);
530                 case ALU1_NEG:
531                         return cgen_alu_args(ctx, writes_flags, ARM_RSB, arg1, arg2, z_imm);
532                 case ALU1_NGC:
533                         return cgen_alu_args(ctx, writes_flags, ARM_RSC, arg1, arg2, z_imm);
534                 case ALU1_INC:
535                         return cgen_alu_args(ctx, writes_flags, ARM_ADD, arg1, arg2, one_imm);
536                 case ALU1_DEC:
537                         return cgen_alu_args(ctx, writes_flags, ARM_SUB, arg1, arg2, one_imm);
538                 case ALU1_BSWAP:
539                         mc = ARM_ALWAYS | ARM_REV;
540                         break;
541                 case ALU1_BSWAP16:
542                         mc = ARM_ALWAYS | ARM_REV16;
543                         break;
544                 case ALU1_BREV:
545                         mc = ARM_ALWAYS | ARM_RBIT;
546                         break;
547                 case ALU1_LZCNT:
548                         mc = ARM_ALWAYS | ARM_CLZ;
549                         break;
550                 default:
551                         internal(file_line, "cgen_alu1: invalid alu %u", alu);
552                         return false;
553         }
554         mc |= (uint32_t)arg1[0] << 12;
555         mc |= arg2[0];
556         cgen_four(mc);
557         return true;
560 static bool attr_w cgen_rot(struct codegen_context *ctx, unsigned writes_flags, unsigned rot)
562         int8_t arm_rot;
563         uint32_t mc;
564         uint8_t *arg1 = ctx->code_position;
565         uint8_t *arg2 = arg1 + arg_size(*arg1);
566         uint8_t *arg3 = arg2 + arg_size(*arg2);
567         ctx->code_position = arg3 + arg_size(*arg3);
568         mc = ARM_ALWAYS | ARM_MOV;
569         if (writes_flags)
570                 mc |= ARM_WRITE_FLAGS;
571         arm_rot = rot_codes[rot];
572         if (unlikely(arm_rot < 0))
573                 internal(file_line, "cgen_rot: invalid rotation %u", rot);
574         if (arg3[0] == ARG_IMM) {
575                 uint8_t imm = arg3[1];
576                 if (!imm)
577                         arm_rot = ARM_ROT_LSL;
578                 mc |= (uint32_t)imm << 7;
579         } else {
580                 mc |= ARM_ALU_REG_SHIFTED;
581                 mc |= (uint32_t)arg3[0] << 8;
582         }
583         mc |= arm_rot;
584         mc |= (uint32_t)arg1[0] << 12;
585         mc |= arg2[0];
586         cgen_four(mc);
587         return true;
590 static bool attr_w cgen_mul_l(struct codegen_context *ctx, bool sgn)
592         uint32_t mc;
593         uint8_t *arg1 = ctx->code_position;
594         uint8_t *arg2 = arg1 + arg_size(*arg1);
595         uint8_t *arg3 = arg2 + arg_size(*arg2);
596         uint8_t *arg4 = arg3 + arg_size(*arg3);
597         ctx->code_position = arg4 + arg_size(*arg4);
598         if (unlikely(arg1[0] >= 16) || unlikely(arg2[0] >= 16) || unlikely(arg3[0] >= 16) || unlikely(arg4[0] >= 16))
599                 internal(file_line, "cgen_madd: invalid arguments");
600         mc = ARM_ALWAYS;
601         mc |= sgn ? ARM_SMULL : ARM_UMULL;
602         mc |= (uint32_t)arg1[0] << 12;
603         mc |= (uint32_t)arg2[0] << 16;
604         mc |= arg3[0];
605         mc |= (uint32_t)arg4[0] << 8;
606         cgen_four(mc);
607         return true;
610 static bool attr_w cgen_madd(struct codegen_context *ctx, bool sub)
612         uint32_t mc;
613         uint8_t *arg1 = ctx->code_position;
614         uint8_t *arg2 = arg1 + arg_size(*arg1);
615         uint8_t *arg3 = arg2 + arg_size(*arg2);
616         uint8_t *arg4 = arg3 + arg_size(*arg3);
617         ctx->code_position = arg4 + arg_size(*arg4);
618         if (unlikely(arg1[0] >= 16) || unlikely(arg2[0] >= 16) || unlikely(arg3[0] >= 16) || unlikely(arg4[0] >= 16))
619                 internal(file_line, "cgen_madd: invalid arguments");
620         mc = ARM_ALWAYS;
621         mc |= sub ? ARM_MLS : ARM_MLA;
622         mc |= (uint32_t)arg1[0] << 16;
623         mc |= arg2[0];
624         mc |= (uint32_t)arg3[0] << 8;
625         mc |= (uint32_t)arg4[0] << 12;
626         cgen_four(mc);
627         return true;
630 static bool attr_w cgen_cmov(struct codegen_context *ctx, unsigned size, unsigned aux)
632         int8_t cond;
633         uint8_t *arg1 = ctx->code_position;
634         uint8_t *arg2 = arg1 + arg_size(*arg1);
635         uint8_t *arg3 = arg2 + arg_size(*arg2);
636         ctx->code_position = arg3 + arg_size(*arg3);
637         if (unlikely(arg1[0] != arg2[0]))
638                 internal(file_line, "cgen_cmov: invalid arguments");
639         cond = jmp_cond[aux];
640         if (unlikely(cond < 0))
641                 internal(file_line, "cgen_cmov: invalid condition %u", aux);
642         g(cgen_mov_args(ctx, (uint32_t)cond << 28, size, false, arg1, arg3));
643         return true;
646 static bool attr_w cgen_ldp_stp(struct codegen_context *ctx, bool ldr)
648         uint8_t *arg1, *arg2, *arg3;
649         int64_t imm;
650         uint32_t mc = ARM_ALWAYS;
651         if (!ldr) {
652                 arg1 = ctx->code_position;
653                 arg2 = arg1 + arg_size(*arg1);
654                 arg3 = arg2 + arg_size(*arg2);
655                 ctx->code_position = arg3 + arg_size(*arg3);
656         } else {
657                 arg2 = ctx->code_position;
658                 arg3 = arg2 + arg_size(*arg2);
659                 arg1 = arg3 + arg_size(*arg3);
660                 ctx->code_position = arg1 + arg_size(*arg1);
661         }
662         if (unlikely(arg2[0] >= 16) || unlikely(arg3[0] >= 16))
663                 goto invalid;
664         if (unlikely((arg2[0] & 1) != 0) || unlikely(arg3[0] != arg2[0] + 1))
665                 goto invalid;
666         if (arg1[0] == ARG_ADDRESS_1 || arg1[0] == ARG_ADDRESS_1_PRE_I || arg1[0] == ARG_ADDRESS_1_POST_I) {
667                 mc |= !ldr ? ARM_STRD_IMM : ARM_LDRD_IMM;
668                 if (arg1[0] == ARG_ADDRESS_1) {
669                         mc |= ARM_LDR_STR_P;
670                 } else if (arg1[0] == ARG_ADDRESS_1_PRE_I) {
671                         mc |= ARM_LDR_STR_P;
672                         mc |= ARM_LDR_STR_W;
673                 }
674                 imm = get_imm(&arg1[2]);
675                 if (!(imm >= -255 && imm <= 255))
676                         goto invalid;
677                 if (imm < 0) {
678                         imm = -imm;
679                 } else {
680                         mc |= ARM_LDR_STR_U;
681                 }
682                 mc |= imm & 0xf;
683                 mc |= (imm & 0xf0) << 4;
684         } else if (arg1[0] == ARG_ADDRESS_2) {
685                 imm = get_imm(&arg1[3]);
686                 if (unlikely(imm != 0))
687                         goto invalid;
688                 mc |= !ldr ? ARM_STRD_REG : ARM_LDRD_REG;
689                 mc |= ARM_LDR_STR_P;
690                 mc |= ARM_LDR_STR_U;
691                 mc |= arg1[2];
692         } else {
693                 goto invalid;
694         }
695         mc |= (uint32_t)arg1[1] << 16;
696         mc |= (uint32_t)arg2[0] << 12;
697         cgen_four(mc);
698         return true;
700 invalid:
701         internal(file_line, "cgen_ldp_stp: invalid arguments %02x, %02x, %02x", arg1[0], arg2[0], arg3[0]);
702         return false;
705 static bool attr_w cgen_mov_mask(struct codegen_context *ctx, unsigned aux)
707         uint32_t mc;
708         uint64_t imm;
709         uint8_t *arg1 = ctx->code_position;
710         uint8_t *arg2 = arg1 + arg_size(*arg1);
711         uint8_t *arg3 = arg2 + arg_size(*arg2);
712         ctx->code_position = arg3 + arg_size(*arg3);
713         if (unlikely(arg1[0] >= 16) || unlikely(arg2[0] >= 16) || unlikely(arg3[0] != ARG_IMM) || unlikely(aux != MOV_MASK_16_32))
714                 internal(file_line, "cgen_mov_mask: bad arguments");
715         imm = get_imm(&arg3[1]);
716         if (unlikely(imm >= 0x10000))
717                 internal(file_line, "cgen_mov_mask: bad number");
718         mc = ARM_ALWAYS | ARM_MOVT;
719         mc |= (uint32_t)arg1[0] << 12;
720         mc |= imm & 0xfff;
721         mc |= (imm & 0xf000) << 4;
722         cgen_four(mc);
723         return true;
726 static bool attr_w cgen_fp_cmp(struct codegen_context *ctx, unsigned op_size)
728         uint32_t mc;
729         uint8_t *arg1 = ctx->code_position;
730         uint8_t *arg2 = arg1 + arg_size(*arg1);
731         ctx->code_position = arg2 + arg_size(*arg2);
732         mc = ARM_ALWAYS;
733         mc |= ARM_VCMP;
734         switch (op_size) {
735                 case OP_SIZE_4:         break;
736                 case OP_SIZE_8:         mc |= ARM_V_D; break;
737                 default:                internal(file_line, "cgen_fp_cmp: invalid size %u", op_size);
738         }
739         mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
740         mc |= (uint32_t)(arg1[0] & 1) << 22;
741         mc |= (uint32_t)(arg2[0] & 0x1e) >> 1;
742         mc |= (uint32_t)(arg2[0] & 1) << 5;
743         cgen_four(mc);
744         return true;
747 static bool attr_w cgen_fp_alu(struct codegen_context *ctx, unsigned op_size, unsigned aux)
749         uint32_t mc;
750         uint8_t *arg1 = ctx->code_position;
751         uint8_t *arg2 = arg1 + arg_size(*arg1);
752         uint8_t *arg3 = arg2 + arg_size(*arg2);
753         ctx->code_position = arg3 + arg_size(*arg3);
754         mc = ARM_ALWAYS;
755         switch (aux) {
756                 case FP_ALU_ADD:        mc |= ARM_VADD; break;
757                 case FP_ALU_SUB:        mc |= ARM_VSUB; break;
758                 case FP_ALU_MUL:        mc |= ARM_VMUL; break;
759                 case FP_ALU_DIV:        mc |= ARM_VDIV; break;
760                 default:                internal(file_line, "cgen_fp_alu: invalid alu %u", aux);
761         }
762         switch (op_size) {
763                 case OP_SIZE_4:         break;
764                 case OP_SIZE_8:         mc |= ARM_V_D; break;
765                 default:                internal(file_line, "cgen_fp_alu: invalid size %u", op_size);
766         }
767         mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
768         mc |= (uint32_t)(arg1[0] & 1) << 22;
769         mc |= (uint32_t)(arg2[0] & 0x1e) << 15;
770         mc |= (uint32_t)(arg2[0] & 1) << 7;
771         mc |= (uint32_t)(arg3[0] & 0x1e) >> 1;
772         mc |= (uint32_t)(arg3[0] & 1) << 5;
773         cgen_four(mc);
774         return true;
777 static bool attr_w cgen_fp_alu1(struct codegen_context *ctx, unsigned op_size, unsigned aux)
779         uint32_t mc;
780         uint8_t *arg1 = ctx->code_position;
781         uint8_t *arg2 = arg1 + arg_size(*arg1);
782         ctx->code_position = arg2 + arg_size(*arg2);
783         switch (aux) {
784                 case FP_ALU1_NEG:       mc = ARM_ALWAYS | ARM_VNEG; break;
785                 case FP_ALU1_SQRT:      mc = ARM_ALWAYS | ARM_VSQRT; break;
786                 case FP_ALU1_VCNT8:     mc = ARM_VCNT_8; goto do_regs;
787                 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;
788                                         if (!mc)
789                                                 goto invalid_size;
790                                         goto do_regs;
791                 default:                internal(file_line, "cgen_fp_alu1: invalid alu %u", aux);
792         }
793         switch (op_size) {
794                 case OP_SIZE_4:         break;
795                 case OP_SIZE_8:         mc |= ARM_V_D; break;
796 invalid_size:
797                 default:                internal(file_line, "cgen_fp_alu1: invalid size %u", op_size);
798         }
799 do_regs:
800         mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
801         mc |= (uint32_t)(arg1[0] & 1) << 22;
802         mc |= (uint32_t)(arg2[0] & 0x1e) >> 1;
803         mc |= (uint32_t)(arg2[0] & 1) << 5;
804         cgen_four(mc);
805         return true;
808 static bool attr_w cgen_fp_to_int(struct codegen_context *ctx, unsigned fp_op_size)
810         uint32_t mc;
811         uint8_t *arg1 = ctx->code_position;
812         uint8_t *arg2 = arg1 + arg_size(*arg1);
813         ctx->code_position = arg2 + arg_size(*arg2);
814         mc = ARM_ALWAYS;
815         mc |= ARM_VCVT_S32_F;
816         switch (fp_op_size) {
817                 case OP_SIZE_4:         break;
818                 case OP_SIZE_8:         mc |= ARM_V_D; break;
819                 default:                internal(file_line, "cgen_fp_to_int: invalid size %u", fp_op_size);
820         }
821         mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
822         mc |= (uint32_t)(arg1[0] & 1) << 22;
823         mc |= (uint32_t)(arg2[0] & 0x1e) >> 1;
824         mc |= (uint32_t)(arg2[0] & 1) << 5;
825         cgen_four(mc);
826         return true;
829 static bool attr_w cgen_fp_from_int(struct codegen_context *ctx, unsigned fp_op_size)
831         uint32_t mc;
832         uint8_t *arg1 = ctx->code_position;
833         uint8_t *arg2 = arg1 + arg_size(*arg1);
834         ctx->code_position = arg2 + arg_size(*arg2);
835         mc = ARM_ALWAYS;
836         mc |= ARM_VCVT_F_S32;
837         switch (fp_op_size) {
838                 case OP_SIZE_4:         break;
839                 case OP_SIZE_8:         mc |= ARM_V_D; break;
840                 default:                internal(file_line, "cgen_fp_from_int: invalid size %u", fp_op_size);
841         }
842         mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
843         mc |= (uint32_t)(arg1[0] & 1) << 22;
844         mc |= (uint32_t)(arg2[0] & 0x1e) >> 1;
845         mc |= (uint32_t)(arg2[0] & 1) << 5;
846         cgen_four(mc);
847         return true;
850 static bool attr_w cgen_fp_cvt(struct codegen_context *ctx, unsigned from_op_size, unsigned to_op_size)
852         uint32_t mc;
853         uint8_t *arg1 = ctx->code_position;
854         uint8_t *arg2 = arg1 + arg_size(*arg1);
855         ctx->code_position = arg2 + arg_size(*arg2);
856         mc = ARM_ALWAYS;
857         if (from_op_size == OP_SIZE_2 && to_op_size == OP_SIZE_4) {
858                 mc |= ARM_VCVT_F32_F16;
859         } else if (from_op_size == OP_SIZE_4 && to_op_size == OP_SIZE_2) {
860                 mc |= ARM_VCVT_F16_F32;
861         } else {
862                 internal(file_line, "cgen_fp_cvt: invalid types %u, %u", from_op_size, to_op_size);
863         }
864         mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
865         mc |= (uint32_t)(arg1[0] & 1) << 22;
866         mc |= (uint32_t)(arg2[0] & 0x1e) >> 1;
867         mc |= (uint32_t)(arg2[0] & 1) << 5;
868         cgen_four(mc);
869         return true;
872 static bool attr_w cgen_jmp_cond(struct codegen_context *ctx, unsigned aux, unsigned length)
874         int8_t cond;
875         uint32_t mc;
876         if (unlikely(length != JMP_SHORTEST))
877                 internal(file_line, "cgen_jmp_cond: invalid length %u", length);
878         cond = jmp_cond[aux];
879         if (unlikely(cond < 0))
880                 internal(file_line, "cgen_jmp_cond: invalid condition %u", aux);
881         g(add_relocation(ctx, JMP_SHORTEST, 0, NULL));
882         mc = (uint32_t)cond << 28;
883         mc |= ARM_B;
884         cgen_four(mc);
885         return true;
888 static bool attr_w cgen_jmp_indirect(struct codegen_context *ctx)
890         uint8_t pc = R_PC;
891         uint8_t *arg1 = ctx->code_position;
892         ctx->code_position = arg1 + arg_size(*arg1);
893         g(cgen_mov_args(ctx, ARM_ALWAYS, OP_SIZE_ADDRESS, false, &pc, arg1));
894         return true;
897 static bool attr_w resolve_relocation(struct codegen_context *ctx, struct relocation *reloc)
899         uint32_t mc;
900         int64_t offs = (int64_t)(ctx->label_to_pos[reloc->label_id] >> 2) - (int64_t)(reloc->position >> 2) - 2;
901         switch (reloc->length) {
902                 case JMP_SHORTEST:
903                         if (unlikely(offs < -0x00800000) || unlikely(offs >= 0x00800000))
904                                 return false;
905                         memcpy(&mc, ctx->mcode + reloc->position, 4);
906                         mc &= 0xff000000U;
907                         mc |= offs & 0x00ffffffU;
908                         memcpy(ctx->mcode + reloc->position, &mc, 4);
909                         return true;
910                 default:
911                         internal(file_line, "resolve_relocation: invalid relocation length %u", reloc->length);
912         }
913         return false;
916 static bool attr_w cgen_insn(struct codegen_context *ctx, uint32_t insn)
918         /*debug("insn: %08x (%s)", insn, da(ctx->fn,function)->function_name);*/
919         switch (insn_opcode(insn)) {
920                 case INSN_ENTRY:
921                         g(cgen_entry(ctx));
922                         return true;
923                 case INSN_LABEL:
924                         g(cgen_label(ctx));
925                         return true;
926                 case INSN_ARM_PUSH:
927                 case INSN_ARM_POP:
928                         g(cgen_arm_push_pop(ctx, insn_opcode(insn) == INSN_ARM_POP));
929                         return true;
930                 case INSN_CALL_INDIRECT:
931                         g(cgen_call_indirect(ctx));
932                         return true;
933                 case INSN_MOV:
934                         g(cgen_mov(ctx, insn_op_size(insn), false));
935                         return true;
936                 case INSN_MOVSX:
937                         g(cgen_mov(ctx, insn_op_size(insn), true));
938                         return true;
939                 case INSN_CMP:
940                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
941                                 goto invalid_insn;
942                         g(cgen_cmp(ctx, false));
943                         return true;
944                 case INSN_CMN:
945                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
946                                 goto invalid_insn;
947                         g(cgen_cmp(ctx, true));
948                         return true;
949                 case INSN_TEST:
950                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
951                                 goto invalid_insn;
952                         g(cgen_test(ctx));
953                         return true;
954                 case INSN_ALU:
955                 case INSN_ALU_FLAGS:
956                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
957                                 goto invalid_insn;
958                         g(cgen_alu(ctx, insn_writes_flags(insn), insn_aux(insn)));
959                         return true;
960                 case INSN_ALU1:
961                 case INSN_ALU1_FLAGS:
962                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
963                                 goto invalid_insn;
964                         g(cgen_alu1(ctx, insn_writes_flags(insn), insn_aux(insn)));
965                         return true;
966                 case INSN_ROT:
967                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
968                                 goto invalid_insn;
969                         g(cgen_rot(ctx, insn_writes_flags(insn), insn_aux(insn)));
970                         return true;
971                 case INSN_MUL_L:
972                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
973                                 goto invalid_insn;
974                         g(cgen_mul_l(ctx, insn_aux(insn)));
975                         return true;
976                 case INSN_MADD:
977                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
978                                 goto invalid_insn;
979                         g(cgen_madd(ctx, insn_aux(insn)));
980                         return true;
981                 case INSN_CMOV:
982                         g(cgen_cmov(ctx, insn_op_size(insn), insn_aux(insn)));
983                         return true;
984                 case INSN_STP:
985                 case INSN_LDP:
986                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
987                                 goto invalid_insn;
988                         g(cgen_ldp_stp(ctx, insn_opcode(insn) == INSN_LDP));
989                         return true;
990                 case INSN_MOV_MASK:
991                         if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
992                                 goto invalid_insn;
993                         g(cgen_mov_mask(ctx, insn_aux(insn)));
994                         return true;
995                 case INSN_FP_CMP:
996                         g(cgen_fp_cmp(ctx, insn_op_size(insn)));
997                         return true;
998                 case INSN_FP_TO_INT_FLAGS:
999                         cgen_four(ARM_ALWAYS | ARM_VMRS_NZCV_FPSCR);
1000                         return true;
1001                 case INSN_FP_ALU:
1002                         g(cgen_fp_alu(ctx, insn_op_size(insn), insn_aux(insn)));
1003                         return true;
1004                 case INSN_FP_ALU1:
1005                         g(cgen_fp_alu1(ctx, insn_op_size(insn), insn_aux(insn)));
1006                         return true;
1007                 case INSN_FP_TO_INT32:
1008                         g(cgen_fp_to_int(ctx, insn_op_size(insn)));
1009                         return true;
1010                 case INSN_FP_FROM_INT32:
1011                         g(cgen_fp_from_int(ctx, insn_op_size(insn)));
1012                         return true;
1013                 case INSN_FP_CVT:
1014                         g(cgen_fp_cvt(ctx, insn_op_size(insn), insn_aux(insn)));
1015                         return true;
1016                 case INSN_JMP:
1017                         g(add_relocation(ctx, JMP_SHORTEST, 0, NULL));
1018                         cgen_four(ARM_ALWAYS | ARM_B);
1019                         return true;
1020                 case INSN_JMP_COND:
1021                         g(cgen_jmp_cond(ctx, insn_aux(insn), insn_jump_size(insn)));
1022                         return true;
1023                 case INSN_JMP_INDIRECT:
1024                         g(cgen_jmp_indirect(ctx));
1025                         return true;
1026                 invalid_insn:
1027                 default:
1028                         internal(file_line, "cgen_insn: invalid insn %08x", insn);
1029                         return false;
1030         }