abi_aapcs64.c (5440B)
1 /* AAPCS64 (AArch64 SysV) ABI dispatch. 2 * 3 * v1 covers the cases the cg test harness exercises: 4 * void -> IGNORE 5 * integer ≤ 8B -> DIRECT, one INT part in a register 6 * integer 16B -> DIRECT, two INT parts (X0+X1) 7 * pointer -> DIRECT, one INT part in a register 8 * float/double -> DIRECT, one FP part in a register 9 * small struct -> DIRECT, INT parts (HFA/HVA refinement: TODO) 10 * large struct -> INDIRECT (sret for return; passed by reference) 11 * Variadics, HFA classification, and split GPR+stack tail still 12 * land with the parser. */ 13 14 #include <string.h> 15 16 #include "abi/abi_internal.h" 17 #include "cg/type.h" 18 #include "core/arena.h" 19 #include "core/core.h" 20 21 static void classify_scalar(TargetABI* a, KitCgTypeId t, ABIArgInfo* out) { 22 ABITypeInfo ti = abi_internal_type_info(a, t); 23 if (ti.scalar_kind == ABI_SC_INT && ti.size == 16) { 24 ABIArgPart* parts = arena_array(a->c->tu, ABIArgPart, 2); 25 memset(parts, 0, sizeof(ABIArgPart) * 2); 26 for (u32 i = 0; i < 2; ++i) { 27 parts[i].cls = ABI_CLASS_INT; 28 parts[i].loc = ABI_LOC_REG; 29 parts[i].size = 8; 30 parts[i].align = 8; 31 parts[i].src_offset = i * 8; 32 } 33 out->kind = ABI_ARG_DIRECT; 34 out->flags = ABI_AF_NONE; 35 out->parts = parts; 36 out->nparts = 2; 37 out->indirect_align = 0; 38 return; 39 } 40 out->kind = ABI_ARG_DIRECT; 41 out->flags = ABI_AF_NONE; 42 out->indirect_align = 0; 43 44 ABIArgPart* parts = arena_new(a->c->tu, ABIArgPart); 45 memset(parts, 0, sizeof *parts); 46 parts->cls = (ti.scalar_kind == ABI_SC_FLOAT) ? ABI_CLASS_FP : ABI_CLASS_INT; 47 parts->loc = ABI_LOC_REG; 48 parts->size = ti.size; 49 parts->align = ti.align; 50 parts->src_offset = 0; 51 52 out->parts = parts; 53 out->nparts = 1; 54 } 55 56 static void classify_void(ABIArgInfo* out) { 57 memset(out, 0, sizeof *out); 58 out->kind = ABI_ARG_IGNORE; 59 } 60 61 static void classify_aggregate(TargetABI* a, KitCgTypeId t, ABIArgInfo* out, 62 int is_return) { 63 ABITypeInfo ti = abi_internal_type_info(a, t); 64 if (ti.size == 0) { 65 classify_void(out); 66 return; 67 } 68 /* AAPCS64: aggregates ≤ 16 bytes pass in up to 2 GPRs (or HFA in FP regs; 69 * v1 ignores HFA). Larger aggregates pass by reference (caller copy for 70 * args, sret pointer for return). */ 71 if (ti.size <= 16) { 72 u32 nparts = (ti.size + 7) / 8; 73 ABIArgPart* parts = arena_array(a->c->tu, ABIArgPart, nparts); 74 memset(parts, 0, sizeof(ABIArgPart) * nparts); 75 u32 off = 0; 76 for (u32 i = 0; i < nparts; ++i) { 77 u32 chunk = (ti.size - off > 8) ? 8 : (ti.size - off); 78 parts[i].cls = ABI_CLASS_INT; 79 parts[i].loc = ABI_LOC_REG; 80 parts[i].size = chunk; 81 parts[i].align = 8; 82 parts[i].src_offset = off; 83 off += chunk; 84 } 85 out->kind = ABI_ARG_DIRECT; 86 out->flags = ABI_AF_NONE; 87 out->parts = parts; 88 out->nparts = (u16)nparts; 89 out->indirect_align = 0; 90 } else { 91 out->kind = ABI_ARG_INDIRECT; 92 out->flags = is_return ? ABI_AF_SRET : ABI_AF_BYVAL; 93 out->indirect_align = ti.align; 94 out->parts = NULL; 95 out->nparts = 0; 96 } 97 } 98 99 static void classify_one(TargetABI* a, KitCgTypeId t, ABIArgInfo* out, 100 int is_return) { 101 const CgType* ty = cg_type_get(a->c, t); 102 if (!ty || ty->kind == KIT_CG_TYPE_VOID) { 103 classify_void(out); 104 return; 105 } 106 switch (ty->kind) { 107 case KIT_CG_TYPE_RECORD: 108 classify_aggregate(a, t, out, is_return); 109 return; 110 case KIT_CG_TYPE_ALIAS: 111 classify_one(a, ty->alias.base, out, is_return); 112 return; 113 default: 114 classify_scalar(a, t, out); 115 return; 116 } 117 } 118 119 /* Non-static so apple_arm64_compute_func_info can delegate to it during 120 * the Phase 1 alias period — see abi_apple_arm64.c. */ 121 ABIFuncInfo* aapcs64_compute_func_info(TargetABI* a, KitCgTypeId fn) { 122 ABIFuncInfo* info = arena_new(a->c->tu, ABIFuncInfo); 123 const CgType* fnty = cg_type_get(a->c, fn); 124 memset(info, 0, sizeof *info); 125 126 classify_one(a, cg_func_ret_type(fnty), &info->ret, /*is_return=*/1); 127 info->has_sret = (info->ret.kind == ABI_ARG_INDIRECT) ? 1 : 0; 128 /* AArch64 returns the sret pointer in the dedicated x8 register, so it never 129 * consumes an x0..x7 argument slot. (memset above already cleared the field; 130 * set explicitly for documentation.) */ 131 info->sret_consumes_int_arg = 0; 132 info->variadic = fnty->func.abi_variadic; 133 134 info->nparams = (u16)fnty->func.nparams; 135 if (fnty->func.nparams) { 136 ABIArgInfo* arr = arena_array(a->c->tu, ABIArgInfo, fnty->func.nparams); 137 memset(arr, 0, sizeof(ABIArgInfo) * fnty->func.nparams); 138 for (u32 i = 0; i < fnty->func.nparams; ++i) { 139 classify_one(a, fnty->func.params[i].type, &arr[i], /*is_return=*/0); 140 } 141 info->params = arr; 142 } else { 143 info->params = NULL; 144 } 145 return info; 146 } 147 148 const ABIVtable aapcs64_vtable = { 149 .compute_func_info = aapcs64_compute_func_info, 150 .va_list_info = {32, 8, ABI_SC_VOID, 0, 0, 0}, 151 .va_list_layout = {.type = {32, 8, ABI_SC_VOID, 0, 0, 0}, 152 .kind = ABI_VA_LIST_AAPCS64, 153 .stack_offset = 0, 154 .gr_top_offset = 8, 155 .vr_top_offset = 16, 156 .gr_offs_offset = 24, 157 .vr_offs_offset = 28, 158 .gp_reg_count = 8, 159 .fp_reg_count = 8, 160 .gp_slot_size = 8, 161 .fp_slot_size = 16}, 162 };