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