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 };