Affix

 view release on metacpan or  search on metacpan

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

/**
 * Copyright (c) 2025 Sanko Robinson
 *
 * This source code is dual-licensed under the Artistic License 2.0 or the MIT License.
 * You may choose to use this code under the terms of either license.
 *
 * SPDX-License-Identifier: (Artistic-2.0 OR MIT)
 *
 * The documentation blocks within this file are licensed under the
 * Creative Commons Attribution 4.0 International License (CC BY 4.0).
 *
 * SPDX-License-Identifier: CC-BY-4.0
 */
/**
 * @file abi_arm64_emitters.c
 * @brief Implements internal helper functions for emitting AArch64 machine code.
 * @ingroup internal_abi_aarch64
 *
 * @internal
 * This file provides the concrete implementations for the low-level AArch64
 * instruction emitters. Each function constructs a single, valid 32-bit AArch64
 * instruction word from its component parts (registers, immediates, etc.) and
 * appends it to a `code_buffer`.
 *
 * This module encapsulates the bitwise logic for encoding ARM64 instructions,
 * keeping the main `abi_arm64.c` file focused on the higher-level logic of
 * applying the AAPCS64 ABI rules.
 * @endinternal
 */
#include "arch/aarch64/abi_arm64_emitters.h"
#include "common/utility.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
// GPR <-> Immediate Value Emitters
/*
 * @internal
 * @brief Emits a single AArch64 `MOVZ` or `MOVK` instruction.
 * @details This is a fundamental building block for loading large 64-bit constants.
 *          - `MOVZ` (Move Wide with Zero): Zeros the register and writes a 16-bit immediate.
 *          - `MOVK` (Move Wide with Keep): Writes a 16-bit immediate, preserving other bits.
 *
 *          Opcode format (MOVZ, 64-bit): 1 1 0 100101 hw imm16 Rd  (base 0xD2800000)
 *          Opcode format (MOVK, 64-bit): 1 1 1 100101 hw imm16 Rd  (base 0xF2800000)
 *
 * @param buf The code buffer to append the instruction to.
 * @param is_movz If true, emits `MOVZ`; otherwise, emits `MOVK`.
 * @param dest_reg The destination GPR (X0-X30).
 * @param imm The 16-bit immediate value to load.
 * @param shift_count The left shift to apply (0 for LSL #0, 1 for LSL #16, etc.).
 */
INFIX_INTERNAL void emit_arm64_mov_imm_chunk(
    code_buffer * buf, bool is_movz, uint64_t dest_reg, uint16_t imm, uint8_t shift_count) {
    if (buf->error)
        return;
    // Base encoding for MOVZ Xd, #imm, LSL #shift
    uint32_t instr = A64_SF_64BIT | A64_OP_MOVE_WIDE_IMM | A64_OPC_MOVZ;
    if (!is_movz)
        // Change opcode from MOVZ to MOVK by setting the 'opc' field to '11'.
        instr = (instr & ~A64_OPC_MOVZ) | A64_OPC_MOVK;
    // 'hw' field encodes the shift: 00=LSL 0, 01=LSL 16, 10=LSL 32, 11=LSL 48.
    instr |= ((uint32_t)shift_count & 0x3) << 21;
    // 'imm16' field holds the 16-bit immediate.
    instr |= ((uint32_t)imm & 0xFFFF) << 5;
    // 'Rd' field holds the destination register.
    instr |= (dest_reg & 0x1F);
    emit_int32(buf, instr);
}
/**
 * @internal
 * @brief Emits a sequence of instructions to load an arbitrary 64-bit immediate into a GPR.
 * @details As AArch64 instructions are fixed-size, loading a full 64-bit value requires
 *          multiple instructions. This function implements the standard pattern of one
 *          `MOVZ` followed by up to three `MOVK` instructions. It intelligently omits
 *          `MOVK` for any 16-bit chunk that is zero.
 * @param buf The code buffer.
 * @param dest The destination GPR.
 * @param value The 64-bit immediate value to load.
 */
INFIX_INTERNAL void emit_arm64_load_u64_immediate(code_buffer * buf, arm64_gpr dest, uint64_t value) {
    // Load the lowest 16 bits with MOVZ (zeros the rest of the register).
    emit_arm64_mov_imm_chunk(buf, true, dest, (value >> 0) & 0xFFFF, 0);
    // For each subsequent 16-bit chunk, use MOVK (Move Wide with Keep) only if
    // the chunk is not zero to avoid emitting redundant instructions.
    if ((value >> 16) & 0xFFFF)
        emit_arm64_mov_imm_chunk(buf, false, dest, (value >> 16) & 0xFFFF, 1);



( run in 0.688 second using v1.01-cache-2.11-cpan-5b529ec07f3 )