kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

abi_aapcs32.c (6291B)


      1 /* AAPCS32 / ARM EABI (AArch32, ELF, soft-float v1).
      2  *
      3  * Structurally the rv32 ILP32 classifier with the float-ABI pinned to soft:
      4  *   void              -> IGNORE
      5  *   integer/ptr <= 4  -> DIRECT, one INT part (r0..r3 for args; r0 for return)
      6  *   i64 / double (8B) -> DIRECT, two 4-byte INT parts (the r0:r1 / r2:r3 pair).
      7  *                        The 8-byte even-register / 8-byte-stack alignment that
      8  *                        AAPCS requires is surfaced to the native backend via
      9  *                        ABIArgPart.align; this layer just declares two lanes.
     10  *   aggregate return <= 4B -> DIRECT, one INT part in r0
     11  *   aggregate return > 4B  -> INDIRECT (sret pointer in r0)
     12  *   fixed-size aggregate argument -> DIRECT word parts, filled through
     13  *                                    r0..r3 and then the stack
     14  *
     15  * The sret pointer is passed in r0 (the first integer arg register), consuming
     16  * that slot, so sret_consumes_int_arg = 1 (like RISC-V, unlike AArch64's x8).
     17  *
     18  * Hard-float (FPv4-SP) + HFAs + __int128 are out of scope for v1. */
     19 #include <string.h>
     20 
     21 #include "abi/abi_internal.h"
     22 #include "cg/type.h"
     23 #include "core/arena.h"
     24 #include "core/core.h"
     25 
     26 #define ARM32_GPR_BYTES 4u /* r0..r3 are 32-bit */
     27 
     28 static void classify_scalar(TargetABI* a, KitCgTypeId t, ABIArgInfo* out) {
     29   ABITypeInfo ti = abi_cg_type_info(a, t);
     30   /* i64 / soft double -> even/odd GPR pair (r0:r1 or r2:r3). */
     31   if (ti.size == 2u * ARM32_GPR_BYTES &&
     32       (ti.scalar_kind == ABI_SC_INT || ti.scalar_kind == ABI_SC_FLOAT)) {
     33     ABIArgPart* parts = arena_array(a->c->tu, ABIArgPart, 2);
     34     memset(parts, 0, sizeof(ABIArgPart) * 2);
     35     /* Both lanes carry align=8: AAPCS requires an 8-byte scalar to land in an
     36      * even/odd core-register pair (r0:r1 or r2:r3) and to 8-byte-align its stack
     37      * slot. The native backend reads this part alignment (> the 4-byte slot) as
     38      * the round-NCRN-to-even / 8-align-stack signal — distinguishing an i64/
     39      * double pair from a 4-byte-aligned two-word aggregate (struct{int;int;}). */
     40     parts[0].cls = ABI_CLASS_INT;
     41     parts[0].loc = ABI_LOC_REG;
     42     parts[0].size = ARM32_GPR_BYTES;
     43     parts[0].align = 2u * ARM32_GPR_BYTES;
     44     parts[0].src_offset = 0;
     45     parts[1].cls = ABI_CLASS_INT;
     46     parts[1].loc = ABI_LOC_REG;
     47     parts[1].size = ARM32_GPR_BYTES;
     48     parts[1].align = 2u * ARM32_GPR_BYTES;
     49     parts[1].src_offset = ARM32_GPR_BYTES;
     50     out->kind = ABI_ARG_DIRECT;
     51     out->flags = ABI_AF_NONE;
     52     out->parts = parts;
     53     out->nparts = 2;
     54     out->indirect_align = 0;
     55     return;
     56   }
     57   /* int / pointer / float<=4 -> one INT part (soft-float: floats in core regs). */
     58   abi_classify_scalar_reg_part(a, out, ti, /*is_fp=*/0);
     59 }
     60 
     61 static u32 arm32_scalar_split_lane_size(TargetABI* a, KitCgTypeId t) {
     62   ABITypeInfo ti = abi_cg_type_info(a, t);
     63   if (ti.size == 2u * ARM32_GPR_BYTES &&
     64       (ti.scalar_kind == ABI_SC_INT || ti.scalar_kind == ABI_SC_FLOAT))
     65     return ARM32_GPR_BYTES; /* 4 -> drives the arch-neutral wide8 path */
     66   return 0;
     67 }
     68 
     69 static void classify_aggregate(TargetABI* a, KitCgTypeId t, ABIArgInfo* out,
     70                                int is_return) {
     71   ABITypeInfo ti = abi_cg_type_info(a, t);
     72   if (ti.size == 0) {
     73     abi_classify_void(out);
     74     return;
     75   }
     76   /* AAPCS32 treats results and parameters asymmetrically. Composite results
     77    * larger than one word use caller-provided result storage, while every
     78    * statically sized composite parameter remains a by-value memory image that
     79    * may occupy any number of consecutive core-register/stack words. */
     80   if (is_return && ti.size > ARM32_GPR_BYTES) {
     81     out->kind = ABI_ARG_INDIRECT;
     82     out->flags = ABI_AF_SRET;
     83     out->indirect_align = ti.align ? ti.align : ARM32_GPR_BYTES;
     84     out->parts = NULL;
     85     out->nparts = 0;
     86   } else {
     87     u32 nparts = (ti.size + ARM32_GPR_BYTES - 1u) / ARM32_GPR_BYTES;
     88     ABIArgPart* parts = arena_array(a->c->tu, ABIArgPart, nparts);
     89     u32 off = 0;
     90     u32 i;
     91     memset(parts, 0, sizeof(ABIArgPart) * nparts);
     92     for (i = 0; i < nparts; ++i) {
     93       u32 chunk =
     94           (ti.size - off > ARM32_GPR_BYTES) ? ARM32_GPR_BYTES : (ti.size - off);
     95       parts[i].cls = ABI_CLASS_INT;
     96       parts[i].loc = ABI_LOC_REG;
     97       parts[i].size = chunk;
     98       parts[i].align = ARM32_GPR_BYTES;
     99       parts[i].src_offset = off;
    100       off += chunk;
    101     }
    102     /* Surface the aggregate's natural alignment on the first part so the native
    103      * AAPCS walk applies the round-NCRN-to-even / 8-byte-stack rule to an
    104      * 8-byte-aligned record (e.g. struct{double;long;}) exactly as it does to an
    105      * i64/double scalar pair. The remaining parts keep 4-byte (word) alignment;
    106      * only parts[0].align drives the even/8-align decision. An aggregate that
    107      * does not fully fit straddles the reg/stack boundary (AAPCS C.5) — the
    108      * native walk realizes that without a no-straddle override. */
    109     parts[0].align = ti.align ? ti.align : ARM32_GPR_BYTES;
    110     out->kind = ABI_ARG_DIRECT;
    111     out->flags = ABI_AF_NONE;
    112     out->parts = parts;
    113     out->nparts = nparts;
    114     out->indirect_align = 0;
    115   }
    116 }
    117 
    118 static void classify_one(TargetABI* a, KitCgTypeId t, ABIArgInfo* out,
    119                          int is_return) {
    120   const CgType* ty = cg_type_get(a->c, t);
    121   if (!ty || ty->kind == KIT_CG_TYPE_VOID) {
    122     abi_classify_void(out);
    123     return;
    124   }
    125   switch (ty->kind) {
    126     case KIT_CG_TYPE_RECORD:
    127       classify_aggregate(a, t, out, is_return);
    128       return;
    129     default:
    130       classify_scalar(a, t, out);
    131       return;
    132   }
    133 }
    134 
    135 static ABIFuncInfo* arm32_compute_func_info(TargetABI* a, KitCgTypeId fn) {
    136   return abi_compute_func_info_generic(a, fn, classify_one,
    137                                        /*sret_consumes_int_arg=*/1);
    138 }
    139 
    140 const ABIVtable aapcs32_vtable = {
    141     .compute_func_info = arm32_compute_func_info,
    142     .scalar_split_lane_size = arm32_scalar_split_lane_size,
    143     /* AAPCS32 va_list is a plain 4-byte pointer; the variadic register-save
    144      * area is r0..r3 spilled contiguously. No FP save area (soft-float). */
    145     .va_list_info = {4, 4, ABI_SC_PTR, 0, 0, 0},
    146     .va_list_layout = {.kind = ABI_VA_LIST_POINTER,
    147                        .gp_reg_count = 4,
    148                        .fp_reg_count = 0,
    149                        .gp_slot_size = 4,
    150                        .fp_slot_size = 0},
    151 };