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 )