2 * Copyright (C) 2024 Mikulas Patocka
4 * This file is part of Ajla.
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
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.
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/>.
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] = {
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] = {
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__)
149 if (unlikely(!cpu_test_feature(CPU_FEATURE_armv5)))
162 mc |= 1U << (pop && !need_bx ? R_PC : R_LR);
165 mc = ARM_ALWAYS | ARM_BX;
172 static bool attr_w cgen_call_indirect(struct codegen_context *ctx)
176 uint8_t reg = cget_one(ctx);
178 #if defined(__thumb__) || defined(__thumb2__)
181 if (likely(cpu_test_feature(CPU_FEATURE_armv6)))
186 mc = ARM_ALWAYS | ARM_BLX_REG;
190 mc = ARM_ALWAYS | ARM_MOV;
191 mc |= (uint32_t)R_LR << 12;
195 mc = ARM_ALWAYS | ARM_MOV;
196 mc |= (uint32_t)R_PC << 12;
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)
209 mc |= ARM_LDR_STR_LD;
210 if (size == OP_SIZE_NATIVE)
212 if (address[0] >= ARG_ADDRESS_2 && address[0] <= ARG_ADDRESS_2_8) {
213 imm = get_imm(&address[3]);
214 if (unlikely(imm != 0))
216 if (sx || size == OP_SIZE_2) {
217 if (unlikely(address[0] != ARG_ADDRESS_2))
220 mc |= size == OP_SIZE_2 ? ARM_LDRSH_REG : ARM_LDRSB_REG;
224 mc |= size == OP_SIZE_1 ? ARM_STRB_REG : ARM_STR_REG;
228 mc |= (uint32_t)address[1] << 16;
230 mc |= (uint32_t)reg << 12;
231 mc |= (uint32_t)(address[0] - ARG_ADDRESS_2) << 7;
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))
244 if (sx || size == OP_SIZE_2) {
245 if (unlikely(imm >= 256))
248 mc |= size == OP_SIZE_2 ? ARM_LDRSH_IMM : ARM_LDRSB_IMM;
252 mc |= (imm & 0xf0) << 4;
254 mc |= size == OP_SIZE_1 ? ARM_STRB_IMM : ARM_STR_IMM;
257 if (address[0] == ARG_ADDRESS_1) {
259 } else if (address[0] == ARG_ADDRESS_1_PRE_I) {
263 mc |= (uint32_t)address[1] << 16;
264 mc |= (uint32_t)reg << 12;
271 internal(file_line, "cgen_ldr_str: invalid address: %02x, %02x, %"PRIxMAX"", reg, address[0], (uintmax_t)imm);
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)
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;
291 mc |= ld ? ARM_VLDR : ARM_VSTR;
293 if (unlikely(address[0] != ARG_ADDRESS_1))
296 imm = get_imm(&address[2]);
297 if (!(imm >= -1023 && imm <= 1023))
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)
316 internal(file_line, "cgen_vldr_vstr: invalid address: %02x, %02x, %"PRIxMAX"", reg, address[0], (uintmax_t)imm);
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)
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);
332 mc |= (uint32_t)arg1[0] << 12;
336 if (reg_is_fp(arg2[0])) {
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;
348 if (arg2[0] == ARG_IMM) {
350 imm = get_imm(&arg2[1]);
351 imm12 = gen_imm12(imm);
353 mc = cond | ARM_MOV | ARM_ALU_IMM;
354 mc |= (uint32_t)arg1[0] << 12;
359 imm12 = gen_imm12(~imm);
361 mc = cond | ARM_MVN | ARM_ALU_IMM;
362 mc |= (uint32_t)arg1[0] << 12;
367 if ((uint32_t)imm >= 0x10000)
369 mc = cond | ARM_MOV_IMM16;
370 mc |= (uint32_t)arg1[0] << 12;
372 mc |= (imm & 0xf000) << 4;
376 return cgen_ldr_str(ctx, true, cond, size, sx, arg1[0], arg2);
378 if (reg_is_fp(arg1[0])) {
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;
391 if (reg_is_fp(arg2[0])) {
392 mc = cond | ARM_VMOV;
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);
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;
405 return cgen_vldr_vstr(ctx, true, cond, size, arg1[0] & 31, arg2);
408 return cgen_ldr_str(ctx, false, cond, size, false, arg2[0], arg1);
410 if (reg_is_fp(arg2[0])) {
411 return cgen_vldr_vstr(ctx, false, cond, size, arg2[0] & 31, arg1);
414 internal(file_line, "cgen_mov_args: invalid arguments %02x, %02x", arg1[0], arg2[0]);
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));
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)
431 if (unlikely(arg1[0] >= 16))
433 if (unlikely(arg2[0] >= 16))
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))
450 if (likely(arg3[0] < 16)) {
455 if (arg3[0] == ARG_SHIFTED_REGISTER) {
456 unsigned mode = arg3[1] >> 6;
457 unsigned amount = arg3[1] & ARG_SHIFT_AMOUNT;
461 mc |= (uint32_t)mode << 5;
462 mc |= ((uint32_t)amount & 0x1f) << 7;
468 internal(file_line, "cgen_alu_args: invalid arguments %02x, %02x, %02x, %08x, %u", arg1[0], arg2[0], arg3[0], (unsigned)mc, writes_flags);
472 static bool attr_w cgen_cmp(struct codegen_context *ctx, bool cmn)
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)
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) {
502 } else if (alu == ALU_UDIV) {
504 } else if (alu == ALU_SDIV) {
507 internal(file_line, "cgen_alu: invalid alu %u", alu);
509 mc |= (uint32_t)arg1[0] << 16;
511 mc |= (uint32_t)arg3[0] << 8;
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)
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);
529 return cgen_alu_args(ctx, writes_flags, ARM_MVN, arg1, &z, arg2);
531 return cgen_alu_args(ctx, writes_flags, ARM_RSB, arg1, arg2, z_imm);
533 return cgen_alu_args(ctx, writes_flags, ARM_RSC, arg1, arg2, z_imm);
535 return cgen_alu_args(ctx, writes_flags, ARM_ADD, arg1, arg2, one_imm);
537 return cgen_alu_args(ctx, writes_flags, ARM_SUB, arg1, arg2, one_imm);
539 mc = ARM_ALWAYS | ARM_REV;
542 mc = ARM_ALWAYS | ARM_REV16;
545 mc = ARM_ALWAYS | ARM_RBIT;
548 mc = ARM_ALWAYS | ARM_CLZ;
551 internal(file_line, "cgen_alu1: invalid alu %u", alu);
554 mc |= (uint32_t)arg1[0] << 12;
560 static bool attr_w cgen_rot(struct codegen_context *ctx, unsigned writes_flags, unsigned rot)
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;
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];
577 arm_rot = ARM_ROT_LSL;
578 mc |= (uint32_t)imm << 7;
580 mc |= ARM_ALU_REG_SHIFTED;
581 mc |= (uint32_t)arg3[0] << 8;
584 mc |= (uint32_t)arg1[0] << 12;
590 static bool attr_w cgen_mul_l(struct codegen_context *ctx, bool sgn)
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");
601 mc |= sgn ? ARM_SMULL : ARM_UMULL;
602 mc |= (uint32_t)arg1[0] << 12;
603 mc |= (uint32_t)arg2[0] << 16;
605 mc |= (uint32_t)arg4[0] << 8;
610 static bool attr_w cgen_madd(struct codegen_context *ctx, bool sub)
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");
621 mc |= sub ? ARM_MLS : ARM_MLA;
622 mc |= (uint32_t)arg1[0] << 16;
624 mc |= (uint32_t)arg3[0] << 8;
625 mc |= (uint32_t)arg4[0] << 12;
630 static bool attr_w cgen_cmov(struct codegen_context *ctx, unsigned size, unsigned aux)
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));
646 static bool attr_w cgen_ldp_stp(struct codegen_context *ctx, bool ldr)
648 uint8_t *arg1, *arg2, *arg3;
650 uint32_t mc = ARM_ALWAYS;
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);
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);
662 if (unlikely(arg2[0] >= 16) || unlikely(arg3[0] >= 16))
664 if (unlikely((arg2[0] & 1) != 0) || unlikely(arg3[0] != arg2[0] + 1))
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) {
670 } else if (arg1[0] == ARG_ADDRESS_1_PRE_I) {
674 imm = get_imm(&arg1[2]);
675 if (!(imm >= -255 && imm <= 255))
683 mc |= (imm & 0xf0) << 4;
684 } else if (arg1[0] == ARG_ADDRESS_2) {
685 imm = get_imm(&arg1[3]);
686 if (unlikely(imm != 0))
688 mc |= !ldr ? ARM_STRD_REG : ARM_LDRD_REG;
695 mc |= (uint32_t)arg1[1] << 16;
696 mc |= (uint32_t)arg2[0] << 12;
701 internal(file_line, "cgen_ldp_stp: invalid arguments %02x, %02x, %02x", arg1[0], arg2[0], arg3[0]);
705 static bool attr_w cgen_mov_mask(struct codegen_context *ctx, unsigned aux)
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;
721 mc |= (imm & 0xf000) << 4;
726 static bool attr_w cgen_fp_cmp(struct codegen_context *ctx, unsigned op_size)
729 uint8_t *arg1 = ctx->code_position;
730 uint8_t *arg2 = arg1 + arg_size(*arg1);
731 ctx->code_position = arg2 + arg_size(*arg2);
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);
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;
747 static bool attr_w cgen_fp_alu(struct codegen_context *ctx, unsigned op_size, unsigned aux)
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);
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);
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);
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;
777 static bool attr_w cgen_fp_alu1(struct codegen_context *ctx, unsigned op_size, unsigned aux)
780 uint8_t *arg1 = ctx->code_position;
781 uint8_t *arg2 = arg1 + arg_size(*arg1);
782 ctx->code_position = arg2 + arg_size(*arg2);
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;
791 default: internal(file_line, "cgen_fp_alu1: invalid alu %u", aux);
794 case OP_SIZE_4: break;
795 case OP_SIZE_8: mc |= ARM_V_D; break;
797 default: internal(file_line, "cgen_fp_alu1: invalid size %u", op_size);
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;
808 static bool attr_w cgen_fp_to_int(struct codegen_context *ctx, unsigned fp_op_size)
811 uint8_t *arg1 = ctx->code_position;
812 uint8_t *arg2 = arg1 + arg_size(*arg1);
813 ctx->code_position = arg2 + arg_size(*arg2);
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);
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;
829 static bool attr_w cgen_fp_from_int(struct codegen_context *ctx, unsigned fp_op_size)
832 uint8_t *arg1 = ctx->code_position;
833 uint8_t *arg2 = arg1 + arg_size(*arg1);
834 ctx->code_position = arg2 + arg_size(*arg2);
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);
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;
850 static bool attr_w cgen_fp_cvt(struct codegen_context *ctx, unsigned from_op_size, unsigned to_op_size)
853 uint8_t *arg1 = ctx->code_position;
854 uint8_t *arg2 = arg1 + arg_size(*arg1);
855 ctx->code_position = arg2 + arg_size(*arg2);
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;
862 internal(file_line, "cgen_fp_cvt: invalid types %u, %u", from_op_size, to_op_size);
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;
872 static bool attr_w cgen_jmp_cond(struct codegen_context *ctx, unsigned aux, unsigned length)
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;
888 static bool attr_w cgen_jmp_indirect(struct codegen_context *ctx)
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));
897 static bool attr_w resolve_relocation(struct codegen_context *ctx, struct relocation *reloc)
900 int64_t offs = (int64_t)(ctx->label_to_pos[reloc->label_id] >> 2) - (int64_t)(reloc->position >> 2) - 2;
901 switch (reloc->length) {
903 if (unlikely(offs < -0x00800000) || unlikely(offs >= 0x00800000))
905 memcpy(&mc, ctx->mcode + reloc->position, 4);
907 mc |= offs & 0x00ffffffU;
908 memcpy(ctx->mcode + reloc->position, &mc, 4);
911 internal(file_line, "resolve_relocation: invalid relocation length %u", reloc->length);
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)) {
928 g(cgen_arm_push_pop(ctx, insn_opcode(insn) == INSN_ARM_POP));
930 case INSN_CALL_INDIRECT:
931 g(cgen_call_indirect(ctx));
934 g(cgen_mov(ctx, insn_op_size(insn), false));
937 g(cgen_mov(ctx, insn_op_size(insn), true));
940 if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
942 g(cgen_cmp(ctx, false));
945 if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
947 g(cgen_cmp(ctx, true));
950 if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
956 if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
958 g(cgen_alu(ctx, insn_writes_flags(insn), insn_aux(insn)));
961 case INSN_ALU1_FLAGS:
962 if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
964 g(cgen_alu1(ctx, insn_writes_flags(insn), insn_aux(insn)));
967 if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
969 g(cgen_rot(ctx, insn_writes_flags(insn), insn_aux(insn)));
972 if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
974 g(cgen_mul_l(ctx, insn_aux(insn)));
977 if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
979 g(cgen_madd(ctx, insn_aux(insn)));
982 g(cgen_cmov(ctx, insn_op_size(insn), insn_aux(insn)));
986 if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
988 g(cgen_ldp_stp(ctx, insn_opcode(insn) == INSN_LDP));
991 if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
993 g(cgen_mov_mask(ctx, insn_aux(insn)));
996 g(cgen_fp_cmp(ctx, insn_op_size(insn)));
998 case INSN_FP_TO_INT_FLAGS:
999 cgen_four(ARM_ALWAYS | ARM_VMRS_NZCV_FPSCR);
1002 g(cgen_fp_alu(ctx, insn_op_size(insn), insn_aux(insn)));
1005 g(cgen_fp_alu1(ctx, insn_op_size(insn), insn_aux(insn)));
1007 case INSN_FP_TO_INT32:
1008 g(cgen_fp_to_int(ctx, insn_op_size(insn)));
1010 case INSN_FP_FROM_INT32:
1011 g(cgen_fp_from_int(ctx, insn_op_size(insn)));
1014 g(cgen_fp_cvt(ctx, insn_op_size(insn), insn_aux(insn)));
1017 g(add_relocation(ctx, JMP_SHORTEST, 0, NULL));
1018 cgen_four(ARM_ALWAYS | ARM_B);
1021 g(cgen_jmp_cond(ctx, insn_aux(insn), insn_jump_size(insn)));
1023 case INSN_JMP_INDIRECT:
1024 g(cgen_jmp_indirect(ctx));
1028 internal(file_line, "cgen_insn: invalid insn %08x", insn);