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 )