Affix
view release on metacpan or search on metacpan
infix/src/arch/aarch64/abi_arm64_emitters.c view on Meta::CPAN
}
}
/**
* @internal
* @brief Emits a `STRH` (Store Register Halfword) instruction.
* @details Stores the low 16 bits of a register
* Assembly: `STRH <Wt>, [<Xn|SP>, #imm]`
*
* Opcode: 01_111_00_1_00_... (base 0x79000000)
*
*/
INFIX_INTERNAL void emit_arm64_strh_imm(code_buffer * buf, arm64_gpr src, arm64_gpr base, int32_t offset) {
if (buf->error)
return;
if (offset >= 0 && offset % 2 == 0 && (offset / 2) <= 0xFFF) {
uint32_t instr = (0b01U << 30) | A64_OP_LOAD_STORE_IMM_UNSIGNED; // STRH opcode
instr |= ((uint32_t)(offset / 2) & 0xFFF) << 10;
instr |= (uint32_t)(base & 0x1F) << 5;
instr |= (uint32_t)(src & 0x1F);
emit_int32(buf, instr);
}
else {
// Fallback
if (offset >= 0)
emit_arm64_add_imm(buf, true, false, X16_REG, base, (uint32_t)offset);
else
emit_arm64_sub_imm(buf, true, false, X16_REG, base, (uint32_t)(-offset));
emit_arm64_strh_imm(buf, src, X16_REG, 0);
}
}
/**
* @internal
* @brief Emits an `STP` (Store Pair) instruction with pre-indexing.
* @details Assembly: `STP <Xt1>, <Xt2>, [Xn|SP, #imm]!`
* This instruction stores two registers and updates the base register.
*
* Opcode (64-bit): 1010100110...
*
* @param offset A signed, scaled immediate offset.
*/
INFIX_INTERNAL void emit_arm64_stp_pre_index(
code_buffer * buf, bool is64, arm64_gpr src1, arm64_gpr src2, arm64_gpr base, int32_t offset) {
if (buf->error)
return;
int scale = is64 ? 8 : 4;
if (offset % scale != 0 || (offset / scale) < -64 || (offset / scale) > 63) {
buf->error = true;
return;
}
// Instruction format: opc:101001:L=0:imm7:Rt2:Rn:Rt
// For STP: opc=?, L=0
uint32_t instr =
(is64 ? A64_SF_64BIT : A64_SF_32BIT) | A64_OPC_STP | A64_OP_LOAD_STORE_PAIR_BASE | A64_ADDR_PRE_INDEX;
instr |= ((uint32_t)(offset / scale) & 0x7F) << 15;
instr |= (uint32_t)(src2 & 0x1F) << 10;
instr |= (uint32_t)(base & 0x1F) << 5;
instr |= (uint32_t)(src1 & 0x1F);
emit_int32(buf, instr);
}
/*
* Implementation for emit_arm64_ldp_post_index (Load Pair).
* Encodes `LDP <Xt1>, <Xt2>, [Xn|SP], #imm`.
* Opcode (64-bit): 1010100011...
*/
INFIX_INTERNAL void emit_arm64_ldp_post_index(
code_buffer * buf, bool is64, arm64_gpr dest1, arm64_gpr dest2, arm64_gpr base, int32_t offset) {
uint32_t instr = 0xA8C00000; // Base for LDP post-indexed
if (is64)
instr |= (1u << 31);
int scale = is64 ? 8 : 4;
assert(offset % scale == 0 && (offset / scale) >= -64 && (offset / scale) <= 63);
instr |= ((uint32_t)(offset / scale) & 0x7F) << 15;
instr |= (uint32_t)(dest2 & 0x1F) << 10;
instr |= (uint32_t)(base & 0x1F) << 5;
instr |= (uint32_t)(dest1 & 0x1F);
emit_int32(buf, instr);
}
// Memory <-> VPR (SIMD/FP) Emitters
/*
* Implementation for emit_arm64_ldr_vpr.
* Encodes `LDR <Ht|St|Dt>, [<Xn|SP>, #imm]`.
* Opcode (64-bit, D reg): 11_111_10_1_01_... (base 0xBD400000)
* Opcode (32-bit, S reg): 10_111_10_1_01_... (base 0x7D400000)
* Opcode (16-bit, H reg): 01_111_10_1_01_... (base 0x3D400000)
*/
INFIX_INTERNAL void emit_arm64_ldr_vpr(code_buffer * buf, size_t size, arm64_vpr dest, arm64_gpr base, int32_t offset) {
if (buf->error)
return;
const int scale = (int)size;
if (offset >= 0 && offset % scale == 0 && (offset / scale) <= 0xFFF) {
uint32_t instr = 0x3d400000;
uint32_t size_bits = (size == 8) ? 0b11 : (size == 4) ? 0b10 : 0b01;
instr |= (size_bits << 30);
instr |= ((uint32_t)(offset / scale) & 0xFFF) << 10;
instr |= (uint32_t)(base & 0x1F) << 5;
instr |= (uint32_t)(dest & 0x1F);
emit_int32(buf, instr);
}
else {
// Fallback
if (offset >= 0)
emit_arm64_add_imm(buf, true, false, X16_REG, base, (uint32_t)offset);
else
emit_arm64_sub_imm(buf, true, false, X16_REG, base, (uint32_t)(-offset));
emit_arm64_ldr_vpr(buf, size, dest, X16_REG, 0);
}
}
/*
* Implementation for emit_arm64_str_vpr.
* Encodes `STR <Ht|St|Dt>, [<Xn|SP>, #imm]`.
* Opcode (64-bit, D reg): 11_111_10_1_00_... (base 0xBD000000)
* Opcode (32-bit, S reg): 10_111_10_1_00_... (base 0x7D000000)
* Opcode (16-bit, H reg): 01_111_10_1_00_... (base 0x3D000000)
*/
INFIX_INTERNAL void emit_arm64_str_vpr(code_buffer * buf, size_t size, arm64_vpr src, arm64_gpr base, int32_t offset) {
if (buf->error)
return;
const int scale = (int)size;
if (offset >= 0 && offset % scale == 0 && (offset / scale) <= 0xFFF) {
uint32_t instr = 0x3d000000;
uint32_t size_bits = (size == 8) ? 0b11 : (size == 4) ? 0b10 : 0b01;
instr |= (size_bits << 30);
instr |= ((uint32_t)(offset / scale) & 0xFFF) << 10;
instr |= (uint32_t)(base & 0x1F) << 5;
instr |= (uint32_t)(src & 0x1F);
emit_int32(buf, instr);
}
( run in 3.269 seconds using v1.01-cache-2.11-cpan-5735350b133 )