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_SXTB 0x06af0070U
75 #define ARM_SXTH 0x06bf0070U
76 #define ARM_REV 0x06bf0f30U
77 #define ARM_REV16 0x06bf0fb0U
78 #define ARM_UXTB 0x06ef0070U
79 #define ARM_UXTH 0x06ff0070U
80 #define ARM_RBIT 0x06ff0f30U
81 #define ARM_SDIV 0x0710f010U
82 #define ARM_UDIV 0x0730f010U
83 #define ARM_POP 0x08bd0000U
84 #define ARM_PUSH 0x092d0000U
85 #define ARM_B 0x0a000000U
87 #define ARM_V_D 0x00000100U
88 #define ARM_VSTR 0x0d000a00U
89 #define ARM_VLDR 0x0d100a00U
90 #define ARM_VMOV_S32_R 0x0e000a10U
91 #define ARM_VMOV_R_S32 0x0e100a10U
92 #define ARM_VMUL 0x0e200a00U
93 #define ARM_VADD 0x0e300a00U
94 #define ARM_VSUB 0x0e300a40U
95 #define ARM_VDIV 0x0e800a00U
96 #define ARM_VMOV 0x0eb00a40U
97 #define ARM_VNEG 0x0eb10a40U
98 #define ARM_VSQRT 0x0eb10ac0U
99 #define ARM_VCVT_F32_F16 0x0eb20a40U
100 #define ARM_VCVT_F16_F32 0x0eb30a40U
101 #define ARM_VCMP 0x0eb40a40U
102 #define ARM_VCVT_F_S32 0x0eb80ac0U
103 #define ARM_VCVT_S32_F 0x0ebd0ac0U
104 #define ARM_VMRS_NZCV_FPSCR 0x0ef1fa10U
106 #define ARM_V_BYTE 0x00000000U
107 #define ARM_V_HALF 0x00000400U
108 #define ARM_VST1 0xf480000fU
109 #define ARM_VLD1 0xf4a0000fU
110 #define ARM_VCNT_8_Q 0x00000040U
111 #define ARM_VCNT_8 0xf3b00500U
112 #define ARM_VPADDL_U8 0xf3b00280U
113 #define ARM_VPADDL_U16 0xf3b40280U
114 #define ARM_VPADDL_U32 0xf3b80280U
116 static const uint32_t alu_codes[7] = {
126 static const int8_t jmp_cond[48] = {
127 0x6, 0x7, 0x3, 0x2, 0x0, 0x1, 0x9, 0x8,
128 0x4, 0x5, -1, -1, 0xb, 0xa, 0xd, 0xc,
129 -1, -1, -1, -1, -1, -1, -1, -1,
130 -1, -1, -1, -1, -1, -1, -1, -1,
131 -1, -1, 0x3, 0x2, 0x0, 0x1, 0x9, 0x8,
132 -1, -1, 0x6, 0x7, -1, -1, -1, -1,
135 static const int8_t rot_codes[8] = {
146 static bool attr_w cgen_arm_push_pop(struct codegen_context *ctx, bool pop)
148 bool need_bx = false;
149 uint32_t mc = ARM_ALWAYS;
150 mc |= pop ? ARM_POP : ARM_PUSH;
151 #if defined(__thumb__) || defined(__thumb2__)
153 if (unlikely(!cpu_test_feature(CPU_FEATURE_armv5)))
166 mc |= 1U << (pop && !need_bx ? R_PC : R_LR);
169 mc = ARM_ALWAYS | ARM_BX;
176 static bool attr_w cgen_call_indirect(struct codegen_context *ctx)
180 uint8_t reg = cget_one(ctx);
182 #if defined(__thumb__) || defined(__thumb2__)
185 if (likely(cpu_test_feature(CPU_FEATURE_armv6)))
190 mc = ARM_ALWAYS | ARM_BLX_REG;
194 mc = ARM_ALWAYS | ARM_MOV;
195 mc |= (uint32_t)R_LR << 12;
199 mc = ARM_ALWAYS | ARM_MOV;
200 mc |= (uint32_t)R_PC << 12;
208 static bool attr_w cgen_ldr_str(struct codegen_context *ctx, bool ld, uint32_t cond, unsigned size, bool sx, uint8_t reg, uint8_t *address)
213 mc |= ARM_LDR_STR_LD;
214 if (size == OP_SIZE_NATIVE)
216 if (address[0] >= ARG_ADDRESS_2 && address[0] <= ARG_ADDRESS_2_8) {
217 imm = get_imm(&address[3]);
218 if (unlikely(imm != 0))
220 if (sx || size == OP_SIZE_2) {
221 if (unlikely(address[0] != ARG_ADDRESS_2))
224 mc |= size == OP_SIZE_2 ? ARM_LDRSH_REG : ARM_LDRSB_REG;
228 mc |= size == OP_SIZE_1 ? ARM_STRB_REG : ARM_STR_REG;
232 mc |= (uint32_t)address[1] << 16;
234 mc |= (uint32_t)reg << 12;
235 mc |= (uint32_t)(address[0] - ARG_ADDRESS_2) << 7;
239 if (address[0] == ARG_ADDRESS_1 || address[0] == ARG_ADDRESS_1_PRE_I || address[0] == ARG_ADDRESS_1_POST_I) {
240 imm = get_imm(&address[2]);
241 if (!(imm >= -4095 && imm <= 4095))
248 if (sx || size == OP_SIZE_2) {
249 if (unlikely(imm >= 256))
252 mc |= size == OP_SIZE_2 ? ARM_LDRSH_IMM : ARM_LDRSB_IMM;
256 mc |= (imm & 0xf0) << 4;
258 mc |= size == OP_SIZE_1 ? ARM_STRB_IMM : ARM_STR_IMM;
261 if (address[0] == ARG_ADDRESS_1) {
263 } else if (address[0] == ARG_ADDRESS_1_PRE_I) {
267 mc |= (uint32_t)address[1] << 16;
268 mc |= (uint32_t)reg << 12;
275 internal(file_line, "cgen_ldr_str: invalid address: %02x, %02x, %"PRIxMAX"", reg, address[0], (uintmax_t)imm);
279 static bool attr_w cgen_vldr_vstr(struct codegen_context *ctx, bool ld, uint32_t cond, unsigned size, uint8_t reg, uint8_t *address)
284 if (size < OP_SIZE_4) {
285 mc = ld ? ARM_VLD1 : ARM_VST1;
286 mc |= size == OP_SIZE_1 ? ARM_V_BYTE : ARM_V_HALF;
287 mc |= (uint32_t)address[1] << 16;
288 mc |= (uint32_t)(reg & 0x1e) << 11;
289 mc |= (uint32_t)(reg & 1) << 22;
295 mc |= ld ? ARM_VLDR : ARM_VSTR;
297 if (unlikely(address[0] != ARG_ADDRESS_1))
300 imm = get_imm(&address[2]);
301 if (!(imm >= -1023 && imm <= 1023))
310 mc |= (uint32_t)address[1] << 16;
311 mc |= (uint32_t)(reg & 0x1e) << 11;
312 mc |= (uint32_t)(reg & 1) << 22;
313 mc |= (imm >> 2) & 0xff;
314 if (size == OP_SIZE_8)
320 internal(file_line, "cgen_vldr_vstr: invalid address: %02x, %02x, %"PRIxMAX"", reg, address[0], (uintmax_t)imm);
324 static bool attr_w cgen_mov_args(struct codegen_context *ctx, uint32_t cond, unsigned size, bool sx, uint8_t *arg1, uint8_t *arg2)
332 case OP_SIZE_1: mc = cond | ARM_SXTB; break;
333 case OP_SIZE_2: mc = cond | ARM_SXTH; break;
334 default: internal(file_line, "cgen_mov_args: invalid sign extend size");
338 case OP_SIZE_1: mc = cond | ARM_UXTB; break;
339 case OP_SIZE_2: mc = cond | ARM_UXTH; break;
340 case OP_SIZE_4: mc = cond | ARM_MOV; break;
341 default: internal(file_line, "cgen_mov_args: invalid zero extend size");
345 mc |= (uint32_t)arg1[0] << 12;
349 if (reg_is_fp(arg2[0])) {
351 internal(file_line, "cgen_mov_args: unsupported sign extension");
352 if (unlikely(size != OP_SIZE_NATIVE))
353 internal(file_line, "cgen_mov_args: unsupported size %u", size);
354 mc = cond | ARM_VMOV_R_S32;
355 mc |= (uint32_t)(arg2[0] & 0x1e) << 15;
356 mc |= (uint32_t)(arg2[0] & 1) << 7;
357 mc |= (uint32_t)arg1[0] << 12;
361 if (arg2[0] == ARG_IMM) {
363 imm = get_imm(&arg2[1]);
364 imm12 = gen_imm12(imm);
366 mc = cond | ARM_MOV | ARM_ALU_IMM;
367 mc |= (uint32_t)arg1[0] << 12;
372 imm12 = gen_imm12(~imm);
374 mc = cond | ARM_MVN | ARM_ALU_IMM;
375 mc |= (uint32_t)arg1[0] << 12;
380 if ((uint32_t)imm >= 0x10000)
382 mc = cond | ARM_MOV_IMM16;
383 mc |= (uint32_t)arg1[0] << 12;
385 mc |= (imm & 0xf000) << 4;
389 return cgen_ldr_str(ctx, true, cond, size, sx, arg1[0], arg2);
391 if (reg_is_fp(arg1[0])) {
394 internal(file_line, "cgen_mov_args: unsupported sign extension");
395 if (unlikely(size != OP_SIZE_NATIVE))
396 internal(file_line, "cgen_mov_args: unsupported size %u", size);
397 mc = cond | ARM_VMOV_S32_R;
398 mc |= (uint32_t)(arg1[0] & 0x1e) << 15;
399 mc |= (uint32_t)(arg1[0] & 1) << 7;
400 mc |= (uint32_t)arg2[0] << 12;
404 if (reg_is_fp(arg2[0])) {
405 mc = cond | ARM_VMOV;
407 case OP_SIZE_4: break;
408 case OP_SIZE_8: mc |= ARM_V_D; break;
409 default: internal(file_line, "cgen_mov_args: invalid size %u", size);
411 mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
412 mc |= (uint32_t)(arg1[0] & 1) << 22;
413 mc |= (uint32_t)(arg2[0] & 0x1e) >> 1;
414 mc |= (uint32_t)(arg2[0] & 1) << 5;
418 return cgen_vldr_vstr(ctx, true, cond, size, arg1[0] & 31, arg2);
421 return cgen_ldr_str(ctx, false, cond, size, false, arg2[0], arg1);
423 if (reg_is_fp(arg2[0])) {
424 return cgen_vldr_vstr(ctx, false, cond, size, arg2[0] & 31, arg1);
427 internal(file_line, "cgen_mov_args: invalid arguments %02x, %02x", arg1[0], arg2[0]);
431 static bool attr_w cgen_mov(struct codegen_context *ctx, unsigned size, bool sx)
433 uint8_t *arg1 = ctx->code_position;
434 uint8_t *arg2 = arg1 + arg_size(*arg1);
435 ctx->code_position = arg2 + arg_size(*arg2);
436 g(cgen_mov_args(ctx, ARM_ALWAYS, size, sx, arg1, arg2));
440 static bool attr_w cgen_alu_args(struct codegen_context *ctx, unsigned writes_flags, uint32_t mc, uint8_t *arg1, uint8_t *arg2, uint8_t *arg3)
444 if (unlikely(arg1[0] >= 16))
446 if (unlikely(arg2[0] >= 16))
450 mc |= ARM_WRITE_FLAGS;
451 mc |= (uint32_t)arg1[0] << 12;
452 mc |= (uint32_t)arg2[0] << 16;
453 if (arg3[0] == ARG_IMM) {
454 imm = get_imm(&arg3[1]);
455 imm12 = gen_imm12(imm);
456 if (unlikely(imm12 < 0))
463 if (likely(arg3[0] < 16)) {
468 if (arg3[0] == ARG_SHIFTED_REGISTER) {
469 unsigned mode = arg3[1] >> 6;
470 unsigned amount = arg3[1] & ARG_SHIFT_AMOUNT;
474 mc |= (uint32_t)mode << 5;
475 mc |= ((uint32_t)amount & 0x1f) << 7;
481 internal(file_line, "cgen_alu_args: invalid arguments %02x, %02x, %02x, %08x, %u", arg1[0], arg2[0], arg3[0], (unsigned)mc, writes_flags);
485 static bool attr_w cgen_cmp(struct codegen_context *ctx, bool cmn)
488 uint8_t *arg1 = ctx->code_position;
489 uint8_t *arg2 = arg1 + arg_size(*arg1);
490 ctx->code_position = arg2 + arg_size(*arg2);
491 return cgen_alu_args(ctx, true, cmn ? ARM_CMN : ARM_CMP, &z, arg1, arg2);
494 static bool attr_w cgen_test(struct codegen_context *ctx)
497 uint8_t *arg1 = ctx->code_position;
498 uint8_t *arg2 = arg1 + arg_size(*arg1);
499 ctx->code_position = arg2 + arg_size(*arg2);
500 return cgen_alu_args(ctx, true, ARM_TST, &z, arg1, arg2);
503 static bool attr_w cgen_alu(struct codegen_context *ctx, unsigned writes_flags, unsigned alu)
505 uint8_t *arg1 = ctx->code_position;
506 uint8_t *arg2 = arg1 + arg_size(*arg1);
507 uint8_t *arg3 = arg2 + arg_size(*arg2);
508 ctx->code_position = arg3 + arg_size(*arg3);
509 if (unlikely(alu >= 7)) {
510 uint32_t mc = ARM_ALWAYS;
511 if (alu == ALU_ANDN) {
512 return cgen_alu_args(ctx, writes_flags, ARM_BIC, arg1, arg2, arg3);
513 } else if (alu == ALU_MUL) {
515 } else if (alu == ALU_UDIV) {
517 } else if (alu == ALU_SDIV) {
520 internal(file_line, "cgen_alu: invalid alu %u", alu);
522 mc |= (uint32_t)arg1[0] << 16;
524 mc |= (uint32_t)arg3[0] << 8;
528 return cgen_alu_args(ctx, writes_flags, alu_codes[alu], arg1, arg2, arg3);
531 static bool attr_w cgen_alu1(struct codegen_context *ctx, unsigned writes_flags, unsigned alu)
535 uint8_t z_imm[9] = { ARG_IMM, 0, 0, 0, 0, 0, 0, 0, 0 };
536 uint8_t one_imm[9] = { ARG_IMM, 1, 0, 0, 0, 0, 0, 0, 0 };
537 uint8_t *arg1 = ctx->code_position;
538 uint8_t *arg2 = arg1 + arg_size(*arg1);
539 ctx->code_position = arg2 + arg_size(*arg2);
542 return cgen_alu_args(ctx, writes_flags, ARM_MVN, arg1, &z, arg2);
544 return cgen_alu_args(ctx, writes_flags, ARM_RSB, arg1, arg2, z_imm);
546 return cgen_alu_args(ctx, writes_flags, ARM_RSC, arg1, arg2, z_imm);
548 return cgen_alu_args(ctx, writes_flags, ARM_ADD, arg1, arg2, one_imm);
550 return cgen_alu_args(ctx, writes_flags, ARM_SUB, arg1, arg2, one_imm);
552 mc = ARM_ALWAYS | ARM_REV;
555 mc = ARM_ALWAYS | ARM_REV16;
558 mc = ARM_ALWAYS | ARM_RBIT;
561 mc = ARM_ALWAYS | ARM_CLZ;
564 internal(file_line, "cgen_alu1: invalid alu %u", alu);
567 mc |= (uint32_t)arg1[0] << 12;
573 static bool attr_w cgen_rot(struct codegen_context *ctx, unsigned writes_flags, unsigned rot)
577 uint8_t *arg1 = ctx->code_position;
578 uint8_t *arg2 = arg1 + arg_size(*arg1);
579 uint8_t *arg3 = arg2 + arg_size(*arg2);
580 ctx->code_position = arg3 + arg_size(*arg3);
581 mc = ARM_ALWAYS | ARM_MOV;
583 mc |= ARM_WRITE_FLAGS;
584 arm_rot = rot_codes[rot];
585 if (unlikely(arm_rot < 0))
586 internal(file_line, "cgen_rot: invalid rotation %u", rot);
587 if (arg3[0] == ARG_IMM) {
588 uint8_t imm = arg3[1];
590 arm_rot = ARM_ROT_LSL;
591 mc |= (uint32_t)imm << 7;
593 mc |= ARM_ALU_REG_SHIFTED;
594 mc |= (uint32_t)arg3[0] << 8;
597 mc |= (uint32_t)arg1[0] << 12;
603 static bool attr_w cgen_mul_l(struct codegen_context *ctx, bool sgn)
606 uint8_t *arg1 = ctx->code_position;
607 uint8_t *arg2 = arg1 + arg_size(*arg1);
608 uint8_t *arg3 = arg2 + arg_size(*arg2);
609 uint8_t *arg4 = arg3 + arg_size(*arg3);
610 ctx->code_position = arg4 + arg_size(*arg4);
611 if (unlikely(arg1[0] >= 16) || unlikely(arg2[0] >= 16) || unlikely(arg3[0] >= 16) || unlikely(arg4[0] >= 16))
612 internal(file_line, "cgen_madd: invalid arguments");
614 mc |= sgn ? ARM_SMULL : ARM_UMULL;
615 mc |= (uint32_t)arg1[0] << 12;
616 mc |= (uint32_t)arg2[0] << 16;
618 mc |= (uint32_t)arg4[0] << 8;
623 static bool attr_w cgen_madd(struct codegen_context *ctx, bool sub)
626 uint8_t *arg1 = ctx->code_position;
627 uint8_t *arg2 = arg1 + arg_size(*arg1);
628 uint8_t *arg3 = arg2 + arg_size(*arg2);
629 uint8_t *arg4 = arg3 + arg_size(*arg3);
630 ctx->code_position = arg4 + arg_size(*arg4);
631 if (unlikely(arg1[0] >= 16) || unlikely(arg2[0] >= 16) || unlikely(arg3[0] >= 16) || unlikely(arg4[0] >= 16))
632 internal(file_line, "cgen_madd: invalid arguments");
634 mc |= sub ? ARM_MLS : ARM_MLA;
635 mc |= (uint32_t)arg1[0] << 16;
637 mc |= (uint32_t)arg3[0] << 8;
638 mc |= (uint32_t)arg4[0] << 12;
643 static bool attr_w cgen_cmov(struct codegen_context *ctx, unsigned size, unsigned aux)
646 uint8_t *arg1 = ctx->code_position;
647 uint8_t *arg2 = arg1 + arg_size(*arg1);
648 uint8_t *arg3 = arg2 + arg_size(*arg2);
649 ctx->code_position = arg3 + arg_size(*arg3);
650 if (unlikely(arg1[0] != arg2[0]))
651 internal(file_line, "cgen_cmov: invalid arguments");
652 cond = jmp_cond[aux];
653 if (unlikely(cond < 0))
654 internal(file_line, "cgen_cmov: invalid condition %u", aux);
655 g(cgen_mov_args(ctx, (uint32_t)cond << 28, size, false, arg1, arg3));
659 static bool attr_w cgen_ldp_stp(struct codegen_context *ctx, bool ldr)
661 uint8_t *arg1, *arg2, *arg3;
663 uint32_t mc = ARM_ALWAYS;
665 arg1 = ctx->code_position;
666 arg2 = arg1 + arg_size(*arg1);
667 arg3 = arg2 + arg_size(*arg2);
668 ctx->code_position = arg3 + arg_size(*arg3);
670 arg2 = ctx->code_position;
671 arg3 = arg2 + arg_size(*arg2);
672 arg1 = arg3 + arg_size(*arg3);
673 ctx->code_position = arg1 + arg_size(*arg1);
675 if (unlikely(arg2[0] >= 16) || unlikely(arg3[0] >= 16))
677 if (unlikely((arg2[0] & 1) != 0) || unlikely(arg3[0] != arg2[0] + 1))
679 if (arg1[0] == ARG_ADDRESS_1 || arg1[0] == ARG_ADDRESS_1_PRE_I || arg1[0] == ARG_ADDRESS_1_POST_I) {
680 mc |= !ldr ? ARM_STRD_IMM : ARM_LDRD_IMM;
681 if (arg1[0] == ARG_ADDRESS_1) {
683 } else if (arg1[0] == ARG_ADDRESS_1_PRE_I) {
687 imm = get_imm(&arg1[2]);
688 if (!(imm >= -255 && imm <= 255))
696 mc |= (imm & 0xf0) << 4;
697 } else if (arg1[0] == ARG_ADDRESS_2) {
698 imm = get_imm(&arg1[3]);
699 if (unlikely(imm != 0))
701 mc |= !ldr ? ARM_STRD_REG : ARM_LDRD_REG;
708 mc |= (uint32_t)arg1[1] << 16;
709 mc |= (uint32_t)arg2[0] << 12;
714 internal(file_line, "cgen_ldp_stp: invalid arguments %02x, %02x, %02x", arg1[0], arg2[0], arg3[0]);
718 static bool attr_w cgen_mov_mask(struct codegen_context *ctx, unsigned aux)
722 uint8_t *arg1 = ctx->code_position;
723 uint8_t *arg2 = arg1 + arg_size(*arg1);
724 uint8_t *arg3 = arg2 + arg_size(*arg2);
725 ctx->code_position = arg3 + arg_size(*arg3);
726 if (unlikely(arg1[0] >= 16) || unlikely(arg2[0] >= 16) || unlikely(arg3[0] != ARG_IMM) || unlikely(aux != MOV_MASK_16_32))
727 internal(file_line, "cgen_mov_mask: bad arguments");
728 imm = get_imm(&arg3[1]);
729 if (unlikely(imm >= 0x10000))
730 internal(file_line, "cgen_mov_mask: bad number");
731 mc = ARM_ALWAYS | ARM_MOVT;
732 mc |= (uint32_t)arg1[0] << 12;
734 mc |= (imm & 0xf000) << 4;
739 static bool attr_w cgen_fp_cmp(struct codegen_context *ctx, unsigned op_size)
742 uint8_t *arg1 = ctx->code_position;
743 uint8_t *arg2 = arg1 + arg_size(*arg1);
744 ctx->code_position = arg2 + arg_size(*arg2);
748 case OP_SIZE_4: break;
749 case OP_SIZE_8: mc |= ARM_V_D; break;
750 default: internal(file_line, "cgen_fp_cmp: invalid size %u", op_size);
752 mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
753 mc |= (uint32_t)(arg1[0] & 1) << 22;
754 mc |= (uint32_t)(arg2[0] & 0x1e) >> 1;
755 mc |= (uint32_t)(arg2[0] & 1) << 5;
760 static bool attr_w cgen_fp_alu(struct codegen_context *ctx, unsigned op_size, unsigned aux)
763 uint8_t *arg1 = ctx->code_position;
764 uint8_t *arg2 = arg1 + arg_size(*arg1);
765 uint8_t *arg3 = arg2 + arg_size(*arg2);
766 ctx->code_position = arg3 + arg_size(*arg3);
769 case FP_ALU_ADD: mc |= ARM_VADD; break;
770 case FP_ALU_SUB: mc |= ARM_VSUB; break;
771 case FP_ALU_MUL: mc |= ARM_VMUL; break;
772 case FP_ALU_DIV: mc |= ARM_VDIV; break;
773 default: internal(file_line, "cgen_fp_alu: invalid alu %u", aux);
776 case OP_SIZE_4: break;
777 case OP_SIZE_8: mc |= ARM_V_D; break;
778 default: internal(file_line, "cgen_fp_alu: invalid size %u", op_size);
780 mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
781 mc |= (uint32_t)(arg1[0] & 1) << 22;
782 mc |= (uint32_t)(arg2[0] & 0x1e) << 15;
783 mc |= (uint32_t)(arg2[0] & 1) << 7;
784 mc |= (uint32_t)(arg3[0] & 0x1e) >> 1;
785 mc |= (uint32_t)(arg3[0] & 1) << 5;
790 static bool attr_w cgen_fp_alu1(struct codegen_context *ctx, unsigned op_size, unsigned aux)
793 uint8_t *arg1 = ctx->code_position;
794 uint8_t *arg2 = arg1 + arg_size(*arg1);
795 ctx->code_position = arg2 + arg_size(*arg2);
797 case FP_ALU1_NEG: mc = ARM_ALWAYS | ARM_VNEG; break;
798 case FP_ALU1_SQRT: mc = ARM_ALWAYS | ARM_VSQRT; break;
799 case FP_ALU1_VCNT8: mc = ARM_VCNT_8; goto do_regs;
800 case FP_ALU1_VPADDL: mc = op_size == OP_SIZE_1 ? ARM_VPADDL_U8 : op_size == OP_SIZE_2 ? ARM_VPADDL_U16 : op_size == OP_SIZE_4 ? ARM_VPADDL_U32 : 0;
804 default: internal(file_line, "cgen_fp_alu1: invalid alu %u", aux);
807 case OP_SIZE_4: break;
808 case OP_SIZE_8: mc |= ARM_V_D; break;
810 default: internal(file_line, "cgen_fp_alu1: invalid size %u", op_size);
813 mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
814 mc |= (uint32_t)(arg1[0] & 1) << 22;
815 mc |= (uint32_t)(arg2[0] & 0x1e) >> 1;
816 mc |= (uint32_t)(arg2[0] & 1) << 5;
821 static bool attr_w cgen_fp_to_int(struct codegen_context *ctx, unsigned fp_op_size)
824 uint8_t *arg1 = ctx->code_position;
825 uint8_t *arg2 = arg1 + arg_size(*arg1);
826 ctx->code_position = arg2 + arg_size(*arg2);
828 mc |= ARM_VCVT_S32_F;
829 switch (fp_op_size) {
830 case OP_SIZE_4: break;
831 case OP_SIZE_8: mc |= ARM_V_D; break;
832 default: internal(file_line, "cgen_fp_to_int: invalid size %u", fp_op_size);
834 mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
835 mc |= (uint32_t)(arg1[0] & 1) << 22;
836 mc |= (uint32_t)(arg2[0] & 0x1e) >> 1;
837 mc |= (uint32_t)(arg2[0] & 1) << 5;
842 static bool attr_w cgen_fp_from_int(struct codegen_context *ctx, unsigned fp_op_size)
845 uint8_t *arg1 = ctx->code_position;
846 uint8_t *arg2 = arg1 + arg_size(*arg1);
847 ctx->code_position = arg2 + arg_size(*arg2);
849 mc |= ARM_VCVT_F_S32;
850 switch (fp_op_size) {
851 case OP_SIZE_4: break;
852 case OP_SIZE_8: mc |= ARM_V_D; break;
853 default: internal(file_line, "cgen_fp_from_int: invalid size %u", fp_op_size);
855 mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
856 mc |= (uint32_t)(arg1[0] & 1) << 22;
857 mc |= (uint32_t)(arg2[0] & 0x1e) >> 1;
858 mc |= (uint32_t)(arg2[0] & 1) << 5;
863 static bool attr_w cgen_fp_cvt(struct codegen_context *ctx, unsigned from_op_size, unsigned to_op_size)
866 uint8_t *arg1 = ctx->code_position;
867 uint8_t *arg2 = arg1 + arg_size(*arg1);
868 ctx->code_position = arg2 + arg_size(*arg2);
870 if (from_op_size == OP_SIZE_2 && to_op_size == OP_SIZE_4) {
871 mc |= ARM_VCVT_F32_F16;
872 } else if (from_op_size == OP_SIZE_4 && to_op_size == OP_SIZE_2) {
873 mc |= ARM_VCVT_F16_F32;
875 internal(file_line, "cgen_fp_cvt: invalid types %u, %u", from_op_size, to_op_size);
877 mc |= (uint32_t)(arg1[0] & 0x1e) << 11;
878 mc |= (uint32_t)(arg1[0] & 1) << 22;
879 mc |= (uint32_t)(arg2[0] & 0x1e) >> 1;
880 mc |= (uint32_t)(arg2[0] & 1) << 5;
885 static bool attr_w cgen_jmp_cond(struct codegen_context *ctx, unsigned aux, unsigned length)
889 if (unlikely(length != JMP_SHORTEST))
890 internal(file_line, "cgen_jmp_cond: invalid length %u", length);
891 cond = jmp_cond[aux];
892 if (unlikely(cond < 0))
893 internal(file_line, "cgen_jmp_cond: invalid condition %u", aux);
894 g(add_relocation(ctx, JMP_SHORTEST, 0, NULL));
895 mc = (uint32_t)cond << 28;
901 static bool attr_w cgen_jmp_indirect(struct codegen_context *ctx)
904 uint8_t *arg1 = ctx->code_position;
905 ctx->code_position = arg1 + arg_size(*arg1);
906 g(cgen_mov_args(ctx, ARM_ALWAYS, OP_SIZE_ADDRESS, false, &pc, arg1));
910 static bool attr_w resolve_relocation(struct codegen_context *ctx, struct relocation *reloc)
913 int64_t offs = (int64_t)(ctx->label_to_pos[reloc->label_id] >> 2) - (int64_t)(reloc->position >> 2) - 2;
914 switch (reloc->length) {
916 if (unlikely(offs < -0x00800000) || unlikely(offs >= 0x00800000))
918 memcpy(&mc, ctx->mcode + reloc->position, 4);
920 mc |= offs & 0x00ffffffU;
921 memcpy(ctx->mcode + reloc->position, &mc, 4);
924 internal(file_line, "resolve_relocation: invalid relocation length %u", reloc->length);
929 static bool attr_w cgen_insn(struct codegen_context *ctx, uint32_t insn)
931 /*debug("insn: %08x (%s)", insn, da(ctx->fn,function)->function_name);*/
932 switch (insn_opcode(insn)) {
941 g(cgen_arm_push_pop(ctx, insn_opcode(insn) == INSN_ARM_POP));
943 case INSN_CALL_INDIRECT:
944 g(cgen_call_indirect(ctx));
947 g(cgen_mov(ctx, insn_op_size(insn), false));
950 g(cgen_mov(ctx, insn_op_size(insn), true));
953 if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
955 g(cgen_cmp(ctx, false));
958 if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
960 g(cgen_cmp(ctx, true));
963 if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
969 if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
971 g(cgen_alu(ctx, insn_writes_flags(insn), insn_aux(insn)));
974 case INSN_ALU1_FLAGS:
975 if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
977 g(cgen_alu1(ctx, insn_writes_flags(insn), insn_aux(insn)));
980 if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
982 g(cgen_rot(ctx, insn_writes_flags(insn), insn_aux(insn)));
985 if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
987 g(cgen_mul_l(ctx, insn_aux(insn)));
990 if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
992 g(cgen_madd(ctx, insn_aux(insn)));
995 g(cgen_cmov(ctx, insn_op_size(insn), insn_aux(insn)));
999 if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
1001 g(cgen_ldp_stp(ctx, insn_opcode(insn) == INSN_LDP));
1004 if (unlikely(insn_op_size(insn) != OP_SIZE_NATIVE))
1006 g(cgen_mov_mask(ctx, insn_aux(insn)));
1009 g(cgen_fp_cmp(ctx, insn_op_size(insn)));
1011 case INSN_FP_TO_INT_FLAGS:
1012 cgen_four(ARM_ALWAYS | ARM_VMRS_NZCV_FPSCR);
1015 g(cgen_fp_alu(ctx, insn_op_size(insn), insn_aux(insn)));
1018 g(cgen_fp_alu1(ctx, insn_op_size(insn), insn_aux(insn)));
1020 case INSN_FP_TO_INT32:
1021 g(cgen_fp_to_int(ctx, insn_op_size(insn)));
1023 case INSN_FP_FROM_INT32:
1024 g(cgen_fp_from_int(ctx, insn_op_size(insn)));
1027 g(cgen_fp_cvt(ctx, insn_op_size(insn), insn_aux(insn)));
1030 g(add_relocation(ctx, JMP_SHORTEST, 0, NULL));
1031 cgen_four(ARM_ALWAYS | ARM_B);
1034 g(cgen_jmp_cond(ctx, insn_aux(insn), insn_jump_size(insn)));
1036 case INSN_JMP_INDIRECT:
1037 g(cgen_jmp_indirect(ctx));
1041 internal(file_line, "cgen_insn: invalid insn %08x", insn);