Affix

 view release on metacpan or  search on metacpan

infix/src/arch/aarch64/abi_arm64_emitters.c  view on Meta::CPAN

    }
}
/*
 * Implementation for emit_arm64_ldr_q_imm.
 * Encodes `LDR <Qt>, [Xn, #imm]` for a 128-bit load into a full V-register.
 * Opcode: 00_111_10_1_01... (base 0x3DC00000)
 */
INFIX_INTERNAL void emit_arm64_ldr_q_imm(code_buffer * buf, arm64_vpr dest, arm64_gpr base, int32_t offset) {
    if (buf->error)
        return;
    // Validate immediate offset for 128-bit (16-byte) access
    if (offset >= 0 && offset % 16 == 0 && (offset / 16) <= 0xFFF) {
        uint32_t instr = 0x3DC00000;
        instr |= ((uint32_t)(offset / 16) & 0xFFF) << 10;
        instr |= (uint32_t)(base & 0x1F) << 5;
        instr |= (uint32_t)(dest & 0x1F);
        emit_int32(buf, instr);
    }
    else {
        // Fallback: Calculate address into X16 and load with 0 offset
        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_q_imm(buf, dest, X16_REG, 0);
    }
}
/*
 * Implementation for emit_arm64_str_q_imm.
 * Encodes `STR <Qt>, [Xn, #imm]` for a 128-bit store from a full V-register.
 * Opcode: 00_111_10_1_00... (base 0x3D800000)
 */
INFIX_INTERNAL void emit_arm64_str_q_imm(code_buffer * buf, arm64_vpr src, arm64_gpr base, int32_t offset) {
    if (buf->error)
        return;
    // Validate immediate offset for 128-bit (16-byte) access
    if (offset >= 0 && offset % 16 == 0 && (offset / 16) <= 0xFFF) {
        uint32_t instr = 0x3D800000;
        instr |= ((uint32_t)(offset / 16) & 0xFFF) << 10;
        instr |= (uint32_t)(base & 0x1F) << 5;
        instr |= (uint32_t)(src & 0x1F);
        emit_int32(buf, instr);
    }
    else {
        // Fallback: Calculate address into X16 and store with 0 offset
        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_str_q_imm(buf, src, X16_REG, 0);
    }
}
// Arithmetic Emitters
/*
 * @internal
 * Generic helper for emitting ARM64 `ADD` or `SUB` with an immediate.
 * It handles large immediates by falling back to a multi-instruction sequence that
 * uses a scratch register (X15), since single instructions have a limited immediate range.
 */
INFIX_INTERNAL void emit_arm64_arith_imm(
    code_buffer * buf, bool is_sub, bool is64, bool set_flags, arm64_gpr dest, arm64_gpr base, uint32_t imm) {
    uint32_t instr = is_sub ? 0x51000000 : 0x11000000;
    if (is64)
        instr |= (1u << 31);
    if (set_flags)
        instr |= (1u << 29);
    if (imm <= 0xFFF)  // Check for un-shifted 12-bit immediate.
        instr |= (imm & 0xFFF) << 10;
    else if ((imm & 0xFFF) == 0 && (imm >> 12) <= 0xFFF && (imm >> 12) > 0) {  // Check for shifted 12-bit immediate.
        instr |= (1u << 22);                                                   // 'sh' bit selects LSL #12 shift.
        instr |= ((imm >> 12) & 0xFFF) << 10;
    }
    else {
        // Immediate is too large. Load it into a scratch register (X15) and do a register-based operation.
        arm64_gpr scratch_reg = X15_REG;
        emit_arm64_load_u64_immediate(buf, scratch_reg, imm);
        uint32_t reg_instr = is_sub ? 0x4B000000 : 0x0B000000;
        if (is64)
            reg_instr |= (1u << 31);
        if (set_flags)
            reg_instr |= (1u << 29);
        reg_instr |= (uint32_t)(scratch_reg & 0x1F) << 16;
        reg_instr |= (uint32_t)(base & 0x1F) << 5;
        reg_instr |= (uint32_t)(dest & 0x1F);
        emit_int32(buf, reg_instr);
        return;
    }
    instr |= (uint32_t)(base & 0x1F) << 5;
    instr |= (uint32_t)(dest & 0x1F);
    emit_int32(buf, instr);
}
/*
 * Implementation for emit_arm64_add_imm.
 * Opcode (64-bit): 10_0_10001_... (0x91...)
 * Opcode (32-bit): 00_0_10001_... (0x11...)
 */
INFIX_INTERNAL void emit_arm64_add_imm(
    code_buffer * buf, bool is64, bool set_flags, arm64_gpr dest, arm64_gpr base, uint32_t imm) {
    emit_arm64_arith_imm(buf, false, is64, set_flags, dest, base, imm);
}
/*
 * Implementation for emit_arm64_sub_imm.
 * Opcode (64-bit): 11_0_10001_... (0xD1...)
 * Opcode (32-bit): 01_0_10001_... (0x51...)
 */
INFIX_INTERNAL void emit_arm64_sub_imm(
    code_buffer * buf, bool is64, bool set_flags, arm64_gpr dest, arm64_gpr base, uint32_t imm) {
    emit_arm64_arith_imm(buf, true, is64, set_flags, dest, base, imm);
}
/**
 * @internal
 * @brief Emits `CMP <Xn|Wn>, <Xm|Wm>` instruction (alias for SUBS <Xd>, <Xn>, <Xm> with XZR destination).
 * @details Opcode (64-bit): 11101011...
 */
INFIX_INTERNAL void emit_arm64_cmp_reg_reg(code_buffer * buf, bool is64, arm64_gpr reg1, arm64_gpr reg2) {
    if (buf->error)
        return;
    // SUBS <Xd>, <Xn>, <Xm> { , <shift> #<amount> }
    // We use Rd = 31 (XZR), shift = 0.
    uint32_t instr = (is64 ? A64_SF_64BIT : A64_SF_32BIT) | 0x6B000000;
    instr |= (uint32_t)(reg2 & 0x1F) << 16;  // Rm
    instr |= (uint32_t)(reg1 & 0x1F) << 5;   // Rn
    instr |= 31U;                            // Rd = XZR (zero register)
    emit_int32(buf, instr);
}
// Control Flow Emitters
/*
 * Implementation for emit_arm64_blr_reg (Branch with Link to Register).
 * Opcode: 1101011000111111000000... (0xD63F0000)
 */
INFIX_INTERNAL void emit_arm64_blr_reg(code_buffer * buf, arm64_gpr reg) {
    uint32_t instr = 0xD63F0000;
    instr |= (uint32_t)(reg & 0x1F) << 5;
    emit_int32(buf, instr);
}
/*
 * Implementation for emit_arm64_ret.
 * Opcode: 1101011001011111000000... (0xD65F0000)
 * Defaults to `RET X30` if X30_LR_REG is passed.
 */
INFIX_INTERNAL void emit_arm64_ret(code_buffer * buf, arm64_gpr reg) {
    uint32_t instr = 0xD65F0000;
    instr |= (uint32_t)(reg & 0x1F) << 5;
    emit_int32(buf, instr);
}
/**
 * @internal
 * @brief Emits a `B.<cond>` (Branch Conditionally) instruction.
 * @details Assembly: `B.<cond> #imm`.
 *
 *          Opcode: 01010100...
 *
 * @param offset A signed byte offset from the current instruction, which must be a multiple of 4.
 */
INFIX_INTERNAL void emit_arm64_b_cond(code_buffer * buf, arm64_cond cond, int32_t offset) {
    if (buf->error)
        return;
    // Offset is encoded as a 19-bit immediate, scaled by 4 bytes.
    if (offset % 4 != 0 || (offset / 4) < -262144 || (offset / 4) > 262143) {
        buf->error = true;
        return;
    }
    uint32_t instr = 0x54000000 | ((uint32_t)cond & 0xF);
    instr |= ((uint32_t)(offset / 4) & 0x7FFFF) << 5;
    emit_int32(buf, instr);
}
/**
 * @internal



( run in 2.371 seconds using v1.01-cache-2.11-cpan-a49fcb8fa48 )