Affix
view release on metacpan or search on metacpan
infix/src/arch/riscv/abi_riscv64_common.h view on Meta::CPAN
#pragma once
/**
* Copyright (c) 2026 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_riscv64_common.h
* @brief Common register definitions and instruction encodings for the RISC-V RV64GC architecture.
* @ingroup internal_abi_riscv64
*
* @internal
* This header serves two primary purposes for the RISC-V backend:
*
* 1. **Register Enumerations:** It defines enums for the general-purpose registers (GPRs) and
* the floating-point registers (FPRs). These enums provide a clear, type-safe,
* and self-documenting way to refer to specific registers when emitting machine
* code or implementing the ABI logic. The comments on each register describe its
* role according to the standard RISC-V ELF psABI calling convention (lp64d).
*
* 2. **Instruction Encoding Constants:** It contains preprocessor definitions for the
* fixed bitfields of various RISC-V instructions. This abstracts away the
* "magic numbers" of machine code generation, making the emitter code in
* `abi_riscv64_emitters.c` more readable and easier to verify against the
* RISC-V specification (Volume I: Unprivileged ISA).
* @endinternal
*/
#include <stdint.h>
/**
* @internal
* @enum riscv_gpr
* @brief Enumerates the RISC-V 64-bit General-Purpose Registers (x0-x31).
*
* @details The enum values (0-31) correspond directly to the 5-bit register numbers
* used in the encoding of machine code instructions. The comments on each register
* describe its primary role according to the RISC-V psABI.
*/
typedef enum {
X_ZERO_REG = 0, ///< x0: Hardwired zero. Reads return 0, writes are ignored.
X_RA_REG, ///< x1: Return Address. Set by `jal`/`jalr`; the standard link register.
X_SP_REG, ///< x2: Stack Pointer. Must always be 16-byte aligned.
X_GP_REG, ///< x3: Global Pointer (reserved for the ABI).
X_TP_REG, ///< x4: Thread Pointer (reserved for the ABI).
X_T0_REG, ///< x5: Temporary / caller-saved.
X_T1_REG, ///< x6: Temporary / caller-saved.
X_T2_REG, ///< x7: Temporary / caller-saved.
X_S0_REG, ///< x8: Saved register / Frame Pointer (`fp`). Callee-saved.
X_S1_REG, ///< x9: Saved register. Callee-saved.
X_A0_REG = 10, ///< x10: Argument 1 / Return value / caller-saved.
X_A1_REG, ///< x11: Argument 2 / Return value / caller-saved.
X_A2_REG, ///< x12: Argument 3 / caller-saved... (volatile).
X_A3_REG, ///< x13: Argument 4.
X_A4_REG, ///< x14: Argument 5.
X_A5_REG, ///< x15: Argument 6.
X_A6_REG, ///< x16: Argument 7.
X_A7_REG, ///< x17: Argument 8.
X_S2_REG = 18, ///< x18: Saved register. Callee-saved.
X_S3_REG, ///< x19: Saved register. Callee-saved.
X_S4_REG, ///< x20: Saved register. Callee-saved.
X_S5_REG, ///< x21: Saved register. Callee-saved.
X_S6_REG, ///< x22: Saved register. Callee-saved.
X_S7_REG, ///< x23: Saved register. Callee-saved.
X_S8_REG, ///< x24: Saved register. Callee-saved.
X_S9_REG, ///< x25: Saved register. Callee-saved.
X_S10_REG, ///< x26: Saved register. Callee-saved.
X_S11_REG, ///< x27: Saved register. Callee-saved.
X_T3_REG = 28, ///< x28: Temporary / caller-saved.
X_T4_REG, ///< x29: Temporary / caller-saved.
X_T5_REG, ///< x30: Temporary / caller-saved.
X_T6_REG ///< x31: Temporary / caller-saved.
} riscv_gpr;
/**
* @internal
* @enum riscv_fpr
* @brief Enumerates the RISC-V 64-bit Floating-Point Registers (f0-f31).
*
* @details The enum values (0-31) correspond directly to the 5-bit register numbers
* used in the encoding of FP instructions. In the LP64D psABI the floating-point
* argument registers are `fa0`-`fa7`, which map to f10-f17 (NOT f0-f7).
*/
typedef enum {
F_FT0_REG = 0, ///< f0: Temporary / caller-saved.
F_FT1_REG, ///< f1: Temporary / caller-saved.
F_FT2_REG, ///< f2: Temporary / caller-saved.
F_FT3_REG, ///< f3: Temporary / caller-saved.
F_FT4_REG, ///< f4: Temporary / caller-saved.
F_FT5_REG, ///< f5: Temporary / caller-saved.
F_FT6_REG, ///< f6: Temporary / caller-saved.
F_FT7_REG, ///< f7: Temporary / caller-saved.
F_FS0_REG = 8, ///< f8: Saved register. Callee-saved.
F_FS1_REG, ///< f9: Saved register. Callee-saved.
F_FA0_REG = 10, ///< f10: FP Argument 1 / FP Return value / caller-saved.
F_FA1_REG, ///< f11: FP Argument 2 / caller-saved.
F_FA2_REG, ///< f12: FP Argument 3.
F_FA3_REG, ///< f13: FP Argument 4.
F_FA4_REG, ///< f14: FP Argument 5.
F_FA5_REG, ///< f15: FP Argument 6.
F_FA6_REG, ///< f16: FP Argument 7.
F_FA7_REG, ///< f17: FP Argument 8.
F_FS2_REG = 18, ///< f18: Saved register. Callee-saved.
F_FS3_REG, ///< f19: Saved register. Callee-saved.
F_FS4_REG, ///< f20: Saved register. Callee-saved.
F_FS5_REG, ///< f21: Saved register. Callee-saved.
F_FS6_REG, ///< f22: Saved register. Callee-saved.
F_FS7_REG, ///< f23: Saved register. Callee-saved.
F_FS8_REG, ///< f24: Saved register. Callee-saved.
F_FS9_REG, ///< f25: Saved register. Callee-saved.
F_FS10_REG, ///< f26: Saved register. Callee-saved.
F_FS11_REG, ///< f27: Saved register. Callee-saved.
F_FT8_REG = 28, ///< f28: Temporary / caller-saved.
F_FT9_REG, ///< f29: Temporary / caller-saved.
F_FT10_REG, ///< f30: Temporary / caller-saved.
F_FT11_REG ///< f31: Temporary / caller-saved.
} riscv_fpr;
/**
* @internal
* @defgroup riscv64_opcodes RISC-V Instruction Opcodes and Bitfields
* @brief Defines for the bit-level encoding of RISC-V instructions.
* @details These constants represent the fixed bit patterns for various instruction
* classes as specified in "The RISC-V Instruction Set Manual, Volume I".
* Using these defines instead of raw hex literals makes the emitter code
* more readable and easier to verify. The `U` suffix is critical to prevent
* signed integer overflow during bit-shifting operations at compile time.
* @{
*/
// Base opcodes (bits 6:0)
#define RV_OP_LOAD 0x03U // Loads (lb, lh, lw, ld, lbu, lhu, lwu)
#define RV_OP_OP_IMM 0x13U // Register-immediate ALU ops (addi, slli, ...)
#define RV_OP_OP_IMM_32 0x1BU // Register-immediate 32-bit ALU ops (addiw)
#define RV_OP_AUIPC 0x17U // Add upper immediate to pc
#define RV_OP_STORE 0x23U // Stores (sb, sh, sw, sd)
#define RV_OP_OP 0x33U // Register-register ALU ops (add, sub, sll, ...)
#define RV_OP_LUI 0x37U // Load upper immediate
#define RV_OP_BRANCH 0x63U // Conditional branches
#define RV_OP_JALR 0x67U // Jump and link register
#define RV_OP_JAL 0x6FU // Jump and link
#define RV_OP_FP 0x53U // Floating-point register ops
#define RV_OP_SYSTEM 0x73U // System instructions
#define RV_OP_FLOAD 0x07U // Floating-point loads (flw, fld)
#define RV_OP_FSTORE 0x27U // Floating-point stores (fsw, fsd)
// funct3 fields (bits 14:12)
#define RV_F3_ADD_SUB 0x0U // ADDI / ADD / SUB / SLL
#define RV_F3_SLL 0x1U // SLLI / SLL
#define RV_F3_SLT 0x2U
#define RV_F3_SLTU 0x3U
#define RV_F3_XOR 0x4U
#define RV_F3_SR 0x5U // SRLI / SRAI / SRL / SRA
#define RV_F3_OR 0x6U
#define RV_F3_AND 0x7U
// funct3 fields for loads/stores
#define RV_F3_LD 0x3U // LD / SD
#define RV_F3_LW 0x2U // LW / SW
#define RV_F3_LH 0x1U // LH / SH
#define RV_F3_LB 0x0U // LB / SB
#define RV_F3_LBU 0x4U // LBU (no unsigned store exists)
#define RV_F3_LHU 0x5U // LHU
#define RV_F3_LWU 0x6U // LWU
#define RV_F3_FLD 0x3U // FLD / FSD
#define RV_F3_FLW 0x2U // FLW / FSW
#define RV_F3_FLH 0x1U // FLH / FSH (Zfh)
// funct3 fields for branches
#define RV_F3_BEQ 0x0U
#define RV_F3_BNE 0x1U
#define RV_F3_BLT 0x4U
#define RV_F3_BGE 0x5U
#define RV_F3_BLTU 0x6U
#define RV_F3_BGEU 0x7U
// funct7 fields (bits 31:25) for R-type integer ops
#define RV_F7_ADD 0x00U
#define RV_F7_SUB 0x20U
#define RV_F7_SLL 0x00U
#define RV_F7_SRL 0x00U
#define RV_F7_SRA 0x20U
// Shift immediate funct6 (bits 31:26)
#define RV_F6_SLLI 0x00U
#define RV_F6_SRLI 0x00U
#define RV_F6_SRAI 0x10U
// Floating-point move instructions (funct7 selects the operation).
// The RV64 W-level moves transfer between GPRs and single-precision FP registers.
#define RV_F7_FMV_X_S 0x70U // FMV.X.W: FP(single) -> GPR
#define RV_F7_FMV_S_X 0x78U // FMV.W.X: GPR -> FP(single)
#define RV_F7_FMV_X_D 0x71U // FMV.X.D: FP(double) -> GPR
( run in 1.490 second using v1.01-cache-2.11-cpan-9789f410c06 )