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 static bool attr_w gen_mov(struct codegen_context *ctx, unsigned size, unsigned dest, unsigned src)
21 if (dest == src && (size == OP_SIZE_NATIVE || reg_is_fp(dest)))
24 gen_insn(INSN_MOV, size, 0, 0);
31 static bool attr_w gen_sanitize_returned_pointer(struct codegen_context attr_unused *ctx, unsigned attr_unused reg)
33 #if defined(ARCH_X86_X32)
34 g(gen_mov(ctx, OP_SIZE_ADDRESS, reg, reg));
39 static bool alu_is_commutative(unsigned alu)
41 return alu == ALU_ADD || alu == ALU_OR || alu == ALU_AND || alu == ALU_XOR || alu == ALU_MUL || alu == ALU_UMULH || alu == ALU_SMULH;
44 static bool attr_w gen_3address_alu(struct codegen_context *ctx, unsigned size, unsigned alu, unsigned dest, unsigned src1, unsigned src2, unsigned writes_flags)
46 if (unlikely(dest == src2) && alu_is_commutative(alu)) {
51 if (!ARCH_IS_3ADDRESS(alu, writes_flags) && unlikely(dest == src2) && unlikely(dest != src1)) {
52 internal(file_line, "gen_3address_alu: invalid registers: %u, %u, %x, %x, %x", size, alu, dest, src1, src2);
54 if (!ARCH_IS_3ADDRESS(alu, writes_flags) && dest != src1) {
55 g(gen_mov(ctx, OP_SIZE_NATIVE, dest, src1));
57 gen_insn(INSN_ALU + ARCH_PARTIAL_ALU(size), size, alu, ALU_WRITES_FLAGS(size, alu, false, false, 0) | writes_flags);
64 gen_insn(INSN_ALU + ARCH_PARTIAL_ALU(size), size, alu, ALU_WRITES_FLAGS(size, alu, false, false, 0) | writes_flags);
71 static bool attr_w gen_3address_alu_imm(struct codegen_context *ctx, unsigned size, unsigned alu, unsigned dest, unsigned src, int64_t imm, unsigned writes_flags)
73 unsigned purpose = alu_purpose(alu);
74 if (!ARCH_IS_3ADDRESS_IMM(alu, writes_flags) && dest != src) {
75 g(gen_mov(ctx, OP_SIZE_NATIVE, dest, src));
77 g(gen_imm(ctx, imm, purpose, i_size(OP_SIZE_ADDRESS)));
78 gen_insn(INSN_ALU + ARCH_PARTIAL_ALU(size), size, alu, ALU_WRITES_FLAGS(size, alu, false, is_imm(), ctx->const_imm) | writes_flags);
85 g(gen_imm(ctx, imm, purpose, i_size(OP_SIZE_ADDRESS)));
86 gen_insn(INSN_ALU + ARCH_PARTIAL_ALU(size), size, alu, ALU_WRITES_FLAGS(size, alu, false, is_imm(), ctx->const_imm) | writes_flags);
94 static bool attr_w attr_unused gen_3address_rot(struct codegen_context *ctx, unsigned size, unsigned alu, unsigned dest, unsigned src1, unsigned src2)
97 if (dest == src1 && src2 == R_CX) {
98 gen_insn(INSN_ROT + ARCH_PARTIAL_ALU(size), size, alu, 1);
106 if (!ARCH_IS_3ADDRESS_ROT(alu, size) && dest != src1) {
107 if (unlikely(dest == src2))
108 internal(file_line, "gen_3address_rot: invalid registers: %u, %u, %x, %x, %x", size, alu, dest, src1, src2);
110 g(gen_mov(ctx, OP_SIZE_NATIVE, dest, src1));
112 gen_insn(INSN_ROT + ARCH_PARTIAL_ALU(size), size, alu, ROT_WRITES_FLAGS(alu, size, false));
119 gen_insn(INSN_ROT + ARCH_PARTIAL_ALU(size), size, alu, ROT_WRITES_FLAGS(alu, size, false));
127 static bool attr_w gen_3address_rot_imm(struct codegen_context *ctx, unsigned size, unsigned alu, unsigned dest, unsigned src, int64_t imm, unsigned writes_flags)
129 if (!ARCH_IS_3ADDRESS_ROT_IMM(alu) && dest != src) {
130 g(gen_mov(ctx, OP_SIZE_NATIVE, dest, src));
132 gen_insn(INSN_ROT + ARCH_PARTIAL_ALU(size), size, alu, ROT_WRITES_FLAGS(alu, size, true) | writes_flags);
140 gen_insn(INSN_ROT + ARCH_PARTIAL_ALU(size), size, alu, ROT_WRITES_FLAGS(alu, size, true) | writes_flags);
149 static bool attr_w gen_2address_alu1(struct codegen_context *ctx, unsigned size, unsigned alu, unsigned dest, unsigned src, unsigned writes_flags)
151 if (!ARCH_IS_2ADDRESS(alu) && dest != src) {
152 g(gen_mov(ctx, OP_SIZE_NATIVE, dest, src));
154 gen_insn(INSN_ALU1 + ARCH_PARTIAL_ALU(size), size, alu, ALU1_WRITES_FLAGS(alu) | writes_flags);
160 gen_insn(INSN_ALU1 + ARCH_PARTIAL_ALU(size), size, alu, ALU1_WRITES_FLAGS(alu) | writes_flags);
167 static bool attr_w gen_3address_fp_alu(struct codegen_context *ctx, unsigned size, unsigned alu, unsigned dest, unsigned src1, unsigned src2)
169 if (!ARCH_IS_3ADDRESS_FP && unlikely(dest == src2) && unlikely(dest != src1)) {
170 internal(file_line, "gen_3address_fp_alu: invalid registers: %u, %u, %x, %x, %x", size, alu, dest, src1, src2);
172 if (!ARCH_IS_3ADDRESS_FP && dest != src1) {
173 g(gen_mov(ctx, size, dest, src1));
175 gen_insn(INSN_FP_ALU, size, alu, 0);
182 gen_insn(INSN_FP_ALU, size, alu, 0);
191 static bool attr_w attr_unused gen_load_two(struct codegen_context *ctx, unsigned dest, unsigned src, int64_t offset)
195 g(gen_address(ctx, src, offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_8));
196 gen_insn(INSN_MOV_U, OP_SIZE_NATIVE, 0, 0);
198 gen_address_offset();
200 g(gen_3address_alu(ctx, OP_SIZE_NATIVE, ALU_EXTWL, dest, dest, src, 0));
202 g(gen_imm(ctx, offset, IMM_PURPOSE_ADD, i_size(OP_SIZE_ADDRESS)));
203 gen_insn(INSN_ALU, i_size(OP_SIZE_ADDRESS), ALU_ADD, ALU_WRITES_FLAGS(i_size(OP_SIZE_ADDRESS), ALU_ADD, false, is_imm(), ctx->const_imm));
204 gen_one(R_OFFSET_IMM);
208 gen_insn(INSN_MOV_U, OP_SIZE_NATIVE, 0, 0);
210 gen_one(ARG_ADDRESS_1);
211 gen_one(R_OFFSET_IMM);
214 g(gen_3address_alu(ctx, OP_SIZE_NATIVE, ALU_EXTWL, dest, dest, R_OFFSET_IMM, 0));
216 #if defined(ARCH_S390)
217 } else if (!cpu_test_feature(CPU_FEATURE_extended_imm)) {
218 g(gen_address(ctx, src, offset, IMM_PURPOSE_LDR_SX_OFFSET, OP_SIZE_2));
219 gen_insn(INSN_MOVSX, OP_SIZE_2, 0, 0);
221 gen_address_offset();
223 g(gen_3address_alu_imm(ctx, OP_SIZE_NATIVE, ALU_AND, dest, dest, 0xffff, 0));
226 g(gen_address(ctx, src, offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_2));
227 gen_insn(INSN_MOV, OP_SIZE_2, 0, 0);
229 gen_address_offset();
234 static bool attr_w gen_load_code_32(struct codegen_context *ctx, unsigned dest, unsigned src, int64_t offset)
236 #if ARG_MODE_N == 3 && defined(ARCH_ALPHA) && !(defined(C_BIG_ENDIAN) ^ CODE_ENDIAN)
237 if (!ARCH_HAS_BWX && UNALIGNED_TRAP) {
239 g(gen_3address_alu_imm(ctx, OP_SIZE_NATIVE, ALU_ADD, R_OFFSET_IMM, src, offset, 0));
243 g(gen_address(ctx, src, offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_8));
244 gen_insn(INSN_MOV_U, OP_SIZE_NATIVE, 0, 0);
246 gen_address_offset();
248 g(gen_3address_alu(ctx, OP_SIZE_NATIVE, ALU_EXTLL, dest, dest, src, 0));
250 g(gen_address(ctx, src, offset + 3, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_8));
251 gen_insn(INSN_MOV_U, OP_SIZE_NATIVE, 0, 0);
252 gen_one(R_CONST_IMM);
253 gen_address_offset();
255 g(gen_3address_alu(ctx, OP_SIZE_NATIVE, ALU_EXTLH, R_CONST_IMM, R_CONST_IMM, src, 0));
257 g(gen_3address_alu(ctx, OP_SIZE_NATIVE, ALU_OR, dest, dest, R_CONST_IMM, 0));
262 #if ARG_MODE_N == 3 && defined(ARCH_MIPS) && !(defined(C_BIG_ENDIAN) ^ CODE_ENDIAN)
263 if (!MIPS_R6 && UNALIGNED_TRAP) {
264 g(gen_address(ctx, src, offset, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_4));
265 gen_insn(INSN_MOV_LR, OP_SIZE_4, !CODE_ENDIAN, 0);
268 gen_address_offset();
270 g(gen_address(ctx, src, offset + 3, IMM_PURPOSE_LDR_OFFSET, OP_SIZE_4));
271 gen_insn(INSN_MOV_LR, OP_SIZE_4, CODE_ENDIAN, 0);
274 gen_address_offset();
280 #if !(defined(C_BIG_ENDIAN) ^ CODE_ENDIAN)
284 g(gen_load_two(ctx, dest, src, offset));
285 g(gen_load_two(ctx, R_CONST_IMM, src, offset + 2));
287 g(gen_3address_rot_imm(ctx, OP_SIZE_NATIVE, ROT_SHL, dest, dest, 16, false));
289 g(gen_3address_rot_imm(ctx, OP_SIZE_NATIVE, ROT_SHL, R_CONST_IMM, R_CONST_IMM, 16, false));
291 g(gen_3address_alu(ctx, OP_SIZE_NATIVE, ALU_OR, dest, dest, R_CONST_IMM, 0));
295 g(gen_address(ctx, src, offset, IMM_PURPOSE_LDR_OFFSET, ARG_MODE_N - 1));
296 gen_insn(INSN_MOV, ARG_MODE_N - 1, 0, 0);
298 gen_address_offset();
302 static bool attr_w attr_unused gen_cmp_dest_reg(struct codegen_context *ctx, unsigned attr_unused size, unsigned reg1, unsigned reg2, unsigned reg_dest, int64_t imm, unsigned cond)
304 unsigned neg_result = false;
306 #if defined(ARCH_ALPHA)
307 if (cond == COND_NE) {
308 if (reg2 == (unsigned)-1)
309 g(gen_imm(ctx, imm, IMM_PURPOSE_CMP, i_size_cmp(size)));
310 gen_insn(INSN_CMP_DEST_REG, i_size_cmp(size), COND_E, 0);
313 if (reg2 == (unsigned)-1)
321 #if defined(ARCH_LOONGARCH64) || defined(ARCH_MIPS) || defined(ARCH_RISCV64)
322 if (cond == COND_E || cond == COND_NE) {
324 if (reg2 == (unsigned)-1 && !imm) {
328 if (reg2 == (unsigned)-1)
329 g(gen_imm(ctx, imm, IMM_PURPOSE_XOR, i_size(size)));
330 gen_insn(INSN_ALU, i_size(size), ALU_XOR, ALU_WRITES_FLAGS(i_size(size), ALU_XOR, false, reg2 == (unsigned)-1 && is_imm(), ctx->const_imm));
333 if (reg2 == (unsigned)-1)
339 if (cond == COND_E) {
340 g(gen_imm(ctx, 1, IMM_PURPOSE_CMP, i_size_cmp(size)));
341 gen_insn(INSN_CMP_DEST_REG, i_size_cmp(size), COND_B, 0);
346 gen_insn(INSN_CMP_DEST_REG, i_size_cmp(size), COND_B, 0);
354 if (cond == COND_GE || cond == COND_LE || cond == COND_AE || cond == COND_BE) {
359 #if defined(ARCH_IA64)
360 if (reg2 == (unsigned)-1)
361 g(gen_imm(ctx, imm, IMM_PURPOSE_CMP, i_size_cmp(size)));
362 gen_insn(INSN_CMP_DEST_REG, i_size_cmp(size), cond, 0);
363 gen_one(R_CMP_RESULT);
365 if (reg2 == (unsigned)-1)
370 g(gen_mov(ctx, OP_SIZE_NATIVE, reg_dest, R_CMP_RESULT));
374 if (reg2 == (unsigned)-1)
375 g(gen_imm(ctx, imm, IMM_PURPOSE_CMP, i_size_cmp(size)));
376 gen_insn(INSN_CMP_DEST_REG, i_size_cmp(size), cond, 0);
379 if (reg2 == (unsigned)-1)
387 g(gen_3address_alu_imm(ctx, i_size(size), ALU_XOR, reg_dest, reg_dest, 1, 0));
392 static bool attr_w gen_cmp_test_jmp(struct codegen_context *ctx, unsigned insn, unsigned op_size, unsigned reg1, unsigned reg2, unsigned cond, uint32_t label)
394 bool arch_use_flags = ARCH_HAS_FLAGS;
395 #if defined(ARCH_ARM64)
396 if (insn == INSN_TEST && reg1 == reg2 && (cond == COND_E || cond == COND_NE))
397 arch_use_flags = false;
399 #if defined(ARCH_SPARC)
400 if (insn == INSN_TEST && reg1 == reg2)
401 arch_use_flags = false;
403 if (arch_use_flags) {
404 if (COND_IS_LOGICAL(cond)) {
405 gen_insn(insn, op_size, 0, 2);
409 gen_insn(INSN_JMP_COND_LOGICAL, op_size, cond, 0);
415 gen_insn(insn, op_size, 0, 1);
419 #if defined(ARCH_POWER) || defined(ARCH_S390)
420 if (insn == INSN_TEST) {
427 gen_insn(INSN_JMP_COND, op_size, cond, 0);
430 if (insn == INSN_CMP) {
431 #if defined(ARCH_LOONGARCH64) || defined(ARCH_PARISC) || defined(ARCH_RISCV64) || (defined(ARCH_MIPS) && MIPS_R6)
432 gen_insn(INSN_JMP_2REGS, op_size, cond, 0);
439 unsigned jmp_cond = COND_NE;
440 #if defined(ARCH_MIPS)
441 if (cond == COND_E || cond == COND_NE) {
442 gen_insn(INSN_JMP_2REGS, op_size, cond, 0);
448 if (cond == COND_AE || cond == COND_BE || cond == COND_LE || cond == COND_GE) {
453 #if defined(ARCH_ALPHA)
454 if (cond == COND_NE) {
455 g(gen_3address_alu(ctx, op_size, ALU_XOR, R_CMP_RESULT, reg1, reg2, 0));
459 gen_insn(INSN_CMP_DEST_REG, op_size, cond, 0);
460 gen_one(R_CMP_RESULT);
465 gen_insn(INSN_JMP_REG, OP_SIZE_NATIVE, jmp_cond, 0);
466 gen_one(R_CMP_RESULT);
469 internal(file_line, "gen_cmp_test_jmp: R_CMP_RESULT not defined");
472 } else if (insn == INSN_TEST) {
474 internal(file_line, "gen_cmp_test_jmp: INSN_TEST with two distinct registers is unsupported");
476 #if defined(ARCH_IA64)
481 g(gen_imm(ctx, 0, IMM_PURPOSE_CMP, OP_SIZE_NATIVE));
482 gen_insn(INSN_CMP_DEST_REG, OP_SIZE_NATIVE, cond, 0);
483 gen_one(R_CMP_RESULT);
490 gen_insn(INSN_JMP_REG, OP_SIZE_NATIVE, cond, 0);
498 static bool attr_w gen_cmp_test_imm_jmp(struct codegen_context *ctx, unsigned insn, unsigned attr_unused op_size, unsigned reg1, int64_t value, unsigned cond, uint32_t label)
500 if (insn == INSN_TEST && (cond == COND_E || cond == COND_NE) && is_power_of_2((uint64_t)value)) {
501 #ifdef HAVE_BUILTIN_CTZ
502 unsigned attr_unused bit = __builtin_ctzll(value);
504 unsigned attr_unused bit = 0;
509 #if defined(ARCH_ALPHA) || defined(ARCH_PARISC)
510 if (value == 1 && (cond == COND_E || cond == COND_NE)) {
511 gen_insn(INSN_JMP_REG, OP_SIZE_NATIVE, cond == COND_E ? COND_BLBC : COND_BLBS, 0);
517 #if defined(ARCH_ARM64) || defined(ARCH_PARISC)
518 gen_insn(INSN_JMP_REG_BIT, OP_SIZE_NATIVE, bit | ((cond == COND_NE) << 6), 0);
524 #if defined(ARCH_POWER)
525 g(gen_3address_rot_imm(ctx, OP_SIZE_NATIVE, ROT_SHL, R_CONST_IMM, reg1, (8U << OP_SIZE_NATIVE) - 1 - bit, true));
527 gen_insn(INSN_JMP_COND, OP_SIZE_NATIVE, cond == COND_E ? COND_GE : COND_L, 0);
532 #if defined(ARCH_IA64)
533 gen_insn(INSN_TEST_DEST_REG, OP_SIZE_NATIVE, bit | ((cond == COND_NE) << 6), 0);
534 gen_one(R_CMP_RESULT);
537 gen_insn(INSN_JMP_REG, OP_SIZE_NATIVE, COND_NE, 0);
538 gen_one(R_CMP_RESULT);
543 #if defined(R_CMP_RESULT)
544 if (!is_direct_const(1ULL << bit, IMM_PURPOSE_AND, OP_SIZE_NATIVE) && ARCH_HAS_BTX(BTX_BTEXT, OP_SIZE_NATIVE, true)) {
545 gen_insn(INSN_BTX, OP_SIZE_NATIVE, BTX_BTEXT, 0);
546 gen_one(R_CMP_RESULT);
551 gen_insn(INSN_JMP_REG, OP_SIZE_NATIVE, cond, 0);
552 gen_one(R_CMP_RESULT);
560 if (unlikely(insn == INSN_CMP) && COND_IS_LOGICAL(cond)) {
561 g(gen_imm(ctx, value, IMM_PURPOSE_CMP_LOGICAL, op_size));
562 gen_insn(insn, op_size, 0, 2);
566 gen_insn(INSN_JMP_COND_LOGICAL, op_size, cond, 0);
571 g(gen_imm(ctx, value, insn == INSN_CMP ? IMM_PURPOSE_CMP : IMM_PURPOSE_TEST, op_size));
572 gen_insn(insn, op_size, 0, 1);
576 gen_insn(INSN_JMP_COND, op_size, cond, 0);
579 if (insn == INSN_CMP) {
580 #if defined(ARCH_LOONGARCH64) || defined(ARCH_PARISC) || defined(ARCH_RISCV64)
581 g(gen_imm(ctx, value, IMM_PURPOSE_JMP_2REGS, op_size));
582 #if defined(ARCH_PARISC)
583 gen_insn(INSN_JMP_2REGS, op_size, cond, 0);
585 gen_insn(INSN_JMP_2REGS, i_size_cmp(op_size), cond, 0);
592 unsigned final_cond = COND_NE;
593 #if defined(ARCH_ALPHA)
594 if (cond == COND_AE || cond == COND_A || cond == COND_GE || cond == COND_G) {
598 } else if (cond == COND_NE) {
599 g(gen_3address_alu_imm(ctx, OP_SIZE_NATIVE, ALU_XOR, R_CMP_RESULT, reg1, value, 0));
603 #if defined(ARCH_MIPS)
604 if (cond == COND_E || cond == COND_NE) {
605 g(gen_load_constant(ctx, R_CONST_IMM, value));
606 gen_insn(INSN_JMP_2REGS, OP_SIZE_NATIVE, cond, 0);
608 gen_one(R_CONST_IMM);
612 if (cond == COND_AE || cond == COND_BE || cond == COND_LE || cond == COND_GE) {
616 if (cond == COND_A || cond == COND_G) {
617 g(gen_load_constant(ctx, R_CONST_IMM, value));
618 gen_insn(INSN_CMP_DEST_REG, OP_SIZE_NATIVE, cond, 0);
619 gen_one(R_CMP_RESULT);
621 gen_one(R_CONST_IMM);
625 g(gen_imm(ctx, value, IMM_PURPOSE_CMP, OP_SIZE_NATIVE));
626 gen_insn(INSN_CMP_DEST_REG, OP_SIZE_NATIVE, cond, 0);
627 gen_one(R_CMP_RESULT);
632 gen_insn(INSN_JMP_REG, OP_SIZE_NATIVE, final_cond, 0);
633 gen_one(R_CMP_RESULT);
636 } else if (insn == INSN_TEST) {
637 #if defined(ARCH_IA64)
638 internal(file_line, "gen_cmp_test_imm_jmp: value %"PRIxMAX" not supported", (uintmax_t)value);
640 g(gen_3address_alu_imm(ctx, OP_SIZE_NATIVE, ALU_AND, R_CMP_RESULT, reg1, value, 0));
642 gen_insn(INSN_JMP_REG, OP_SIZE_NATIVE, cond, 0);
643 gen_one(R_CMP_RESULT);
646 internal(file_line, "gen_cmp_test_imm_jmp: invalid insn");
652 static bool attr_w gen_jmp_on_zero(struct codegen_context *ctx, unsigned attr_unused op_size, unsigned reg, unsigned cond, uint32_t label)
654 bool jmp_reg = false;
655 #if defined(ARCH_ALPHA) || defined(ARCH_ARM64) || defined(ARCH_LOONGARCH64) || defined(ARCH_RISCV64)
658 #if defined(ARCH_SPARC)
662 gen_insn(INSN_JMP_REG, i_size(op_size), cond, 0);
668 g(gen_cmp_test_jmp(ctx, INSN_TEST, i_size(op_size), reg, reg, cond, label));
673 static bool attr_w gen_jmp_if_negative(struct codegen_context *ctx, unsigned reg, uint32_t label)
675 #if defined(ARCH_ARM64) || defined(ARCH_PARISC)
676 gen_insn(INSN_JMP_REG_BIT, OP_SIZE_NATIVE, (INT_DEFAULT_BITS - 1) | ((uint32_t)1 << 6), 0);
680 g(gen_jmp_on_zero(ctx, OP_SIZE_INT, reg, COND_S, label));
685 #if defined(ARCH_X86)
686 static bool attr_w gen_cmov(struct codegen_context *ctx, unsigned op_size, unsigned cond, unsigned reg, uint32_t *label)
688 if (unlikely(op_size < OP_SIZE_4))
689 internal(file_line, "gen_cmov: unsupported operand size");
690 if (likely(cpu_test_feature(CPU_FEATURE_cmov))) {
691 gen_insn(INSN_CMOV, op_size, cond, 0);
696 *label = alloc_label(ctx);
697 if (unlikely(!*label))
699 gen_insn(INSN_JMP_COND, op_size, cond ^ 1, 0);
701 gen_insn(INSN_MOV, op_size, 0, 0);
715 static bool attr_w gen_extend(struct codegen_context *ctx, unsigned op_size, enum extend ex, unsigned dest, unsigned src)
717 unsigned attr_unused shift;
719 ex = ARCH_PREFERS_SX(op_size) ? sign_x : zero_x;
720 ajla_assert_lo(ex == zero_x || ex == sign_x, (file_line, "gen_extend: invalid mode %u", (unsigned)ex));
721 if (unlikely(op_size == OP_SIZE_NATIVE)) {
722 g(gen_mov(ctx, op_size, dest, src));
725 if (OP_SIZE_NATIVE == OP_SIZE_4) {
726 shift = op_size == OP_SIZE_1 ? 24 : 16;
727 } else if (OP_SIZE_NATIVE == OP_SIZE_8) {
728 shift = op_size == OP_SIZE_1 ? 56 : op_size == OP_SIZE_2 ? 48 : 32;
730 internal(file_line, "gen_extend: invalid OP_SIZE_NATIVE");
732 #if defined(ARCH_ARM) || defined(ARCH_IA64) || defined(ARCH_LOONGARCH64) || defined(ARCH_PARISC) || defined(ARCH_X86)
733 #if defined(ARCH_ARM32)
734 if (unlikely(!cpu_test_feature(CPU_FEATURE_armv6)))
737 gen_insn(ex == sign_x ? INSN_MOVSX : INSN_MOV, op_size, 0, 0);
742 #if defined(ARCH_POWER)
743 if (ex == zero_x || op_size == OP_SIZE_2 || cpu_test_feature(CPU_FEATURE_ppc)) {
744 gen_insn(ex == sign_x ? INSN_MOVSX : INSN_MOV, op_size, 0, 0);
750 #if defined(ARCH_ALPHA)
752 g(gen_3address_alu_imm(ctx, OP_SIZE_NATIVE, ALU_ZAPNOT, dest, src, op_size == OP_SIZE_1 ? 0x1 : op_size == OP_SIZE_2 ? 0x3 : 0xf, 0));
754 } else if (op_size == OP_SIZE_4 || ARCH_HAS_BWX) {
755 gen_insn(INSN_MOVSX, op_size, 0, 0);
761 #if defined(ARCH_MIPS)
762 if (ex == sign_x && shift == 32) {
763 g(gen_3address_rot_imm(ctx, OP_SIZE_4, ROT_SHL, dest, src, 0, 0));
766 if (ex == sign_x && MIPS_HAS_ROT) {
767 gen_insn(INSN_MOVSX, op_size, 0, 0);
773 #if defined(ARCH_S390)
774 if (((op_size == OP_SIZE_1 || op_size == OP_SIZE_2) && cpu_test_feature(CPU_FEATURE_extended_imm)) || op_size == OP_SIZE_4) {
775 gen_insn(ex == zero_x ? INSN_MOV : INSN_MOVSX, op_size, 0, 0);
781 #if defined(ARCH_SPARC)
783 g(gen_3address_rot_imm(ctx, OP_SIZE_4, ex == sign_x ? ROT_SAR : ROT_SHR, dest, src, 0, 0));
787 #if defined(ARCH_RISCV64)
788 if (ex == sign_x && (op_size == OP_SIZE_4 || likely(cpu_test_feature(CPU_FEATURE_zbb)))) {
789 gen_insn(INSN_MOVSX, op_size, 0, 0);
794 if (ex == zero_x && ((op_size == OP_SIZE_1) ||
795 (op_size == OP_SIZE_2 && likely(cpu_test_feature(CPU_FEATURE_zbb))) ||
796 (op_size == OP_SIZE_4 && likely(cpu_test_feature(CPU_FEATURE_zba))))) {
797 g(gen_mov(ctx, op_size, dest, src));
803 if (ex == zero_x && op_size <= OP_SIZE_4) {
804 int64_t cnst = (0x1ULL << (8U << op_size)) - 1;
805 if (is_direct_const(cnst, IMM_PURPOSE_AND, OP_SIZE_NATIVE)) {
806 g(gen_imm(ctx, 0xff, IMM_PURPOSE_AND, OP_SIZE_NATIVE));
807 gen_insn(INSN_ALU, OP_SIZE_NATIVE, ALU_AND, ALU_WRITES_FLAGS(OP_SIZE_NATIVE, ALU_AND, false, is_imm(), ctx->const_imm));
814 g(gen_3address_rot_imm(ctx, OP_SIZE_NATIVE, ROT_SHL, dest, src, shift, false));
815 g(gen_3address_rot_imm(ctx, OP_SIZE_NATIVE, ex == sign_x ? ROT_SAR : ROT_SHR, dest, dest, shift, false));
819 static bool attr_w gen_cmp_extended(struct codegen_context *ctx, unsigned cmp_op_size, unsigned sub_op_size, unsigned reg, unsigned attr_unused tmp_reg, uint32_t label_ovf)
821 if (unlikely(sub_op_size >= cmp_op_size))
823 #if defined(ARCH_ARM64)
824 gen_insn(INSN_CMP, cmp_op_size, 0, 1);
826 gen_one(ARG_EXTENDED_REGISTER);
827 gen_one(sub_op_size == OP_SIZE_1 ? ARG_EXTEND_SXTB : sub_op_size == OP_SIZE_2 ? ARG_EXTEND_SXTH : ARG_EXTEND_SXTW);
830 gen_insn(INSN_JMP_COND, cmp_op_size, COND_NE, 0);
833 g(gen_extend(ctx, sub_op_size, sign_x, tmp_reg, reg));
835 g(gen_cmp_test_jmp(ctx, INSN_CMP, cmp_op_size, reg, tmp_reg, COND_NE, label_ovf));
840 static bool attr_w gen_lea3(struct codegen_context *ctx, unsigned dest, unsigned base, unsigned shifted, unsigned shift, int64_t offset)
842 #if defined(ARCH_X86)
843 gen_insn(INSN_LEA3, i_size(OP_SIZE_ADDRESS), shift, 0);
848 gen_eight(likely(imm_is_32bit(offset)) ? offset : 0);
850 if (unlikely(!imm_is_32bit(offset)))
851 g(gen_3address_alu_imm(ctx, i_size(OP_SIZE_ADDRESS), ALU_ADD, dest, dest, offset, 0));
855 if (ARCH_HAS_SHIFTED_ADD(shift)) {
856 gen_insn(INSN_ALU, i_size(OP_SIZE_ADDRESS), ALU_ADD, ALU_WRITES_FLAGS(i_size(OP_SIZE_ADDRESS), ALU_ADD, false, false, 0));
859 gen_one(ARG_SHIFTED_REGISTER);
860 gen_one(ARG_SHIFT_LSL | shift);
864 g(gen_imm(ctx, offset, IMM_PURPOSE_ADD, i_size(OP_SIZE_ADDRESS)));
865 gen_insn(INSN_ALU, i_size(OP_SIZE_ADDRESS), ALU_ADD, ALU_WRITES_FLAGS(i_size(OP_SIZE_ADDRESS), ALU_ADD, false, is_imm(), ctx->const_imm));
874 g(gen_3address_rot_imm(ctx, OP_SIZE_NATIVE, ROT_SHL, dest, shifted, shift, false));
876 g(gen_3address_alu(ctx, OP_SIZE_NATIVE, ALU_ADD, dest, dest, base, 0));
879 g(gen_3address_alu_imm(ctx, i_size(OP_SIZE_ADDRESS), ALU_ADD, dest, dest, offset, 0));
883 #if !defined(POINTER_COMPRESSION)
884 #define gen_pointer_compression(base) do { } while (0)
885 #define gen_address_offset_compressed() gen_address_offset()
886 #elif defined(ARCH_X86)
887 #define gen_pointer_compression(base) do { } while (0)
888 #define gen_address_offset_compressed() \
890 if (likely(!ctx->offset_reg)) { \
891 gen_one(ARG_ADDRESS_1 + POINTER_COMPRESSION); \
892 gen_one(ctx->base_reg); \
893 gen_eight(ctx->offset_imm); \
895 gen_one(ARG_ADDRESS_2 + POINTER_COMPRESSION); \
896 gen_one(R_OFFSET_IMM); \
897 gen_one(ctx->base_reg); \
902 #define gen_pointer_compression(base) \
904 if (ARCH_PREFERS_SX(OP_SIZE_4)) { \
905 g(gen_extend(ctx, OP_SIZE_4, zero_x, base, base)); \
907 g(gen_3address_rot_imm(ctx, OP_SIZE_ADDRESS, ROT_SHL, base, base, POINTER_COMPRESSION, 0));\
909 #define gen_address_offset_compressed() gen_address_offset()