abi_sysv_x64.c (9384B)
1 /* SysV AMD64 ABI classifier. 2 * 3 * Implements the INTEGER/SSE/MEMORY subset used by kit's scalar and record 4 * types. x87 long double still routes through memory because the backend does 5 * not have x87 codegen yet. */ 6 7 #include <string.h> 8 9 #include "abi/abi_internal.h" 10 #include "cg/type.h" 11 #include "core/arena.h" 12 #include "core/core.h" 13 14 static void classify_scalar(TargetABI* a, KitCgTypeId t, ABIArgInfo* out, 15 int is_return) { 16 ABITypeInfo ti = abi_cg_type_info(a, t); 17 /* __int128 / __uint128: SysV psABI classifies as two INTEGER eightbytes 18 * (rdi+rsi etc. for args; rax+rdx for return). */ 19 if (ti.scalar_kind == ABI_SC_INT && ti.size == 16) { 20 abi_classify_int128_pair(a, out); 21 return; 22 } 23 /* long double: 80-bit x87 (padded to 16B with 16B alignment). SysV class 24 * is X87/X87UP which always routes through memory. kit has no x87 25 * backend, so route through a stack image — sret for return, byval for 26 * args — consistent with the rest of the in-memory aggregate path. */ 27 if (ti.scalar_kind == ABI_SC_FLOAT && ti.size == 16) { 28 out->kind = ABI_ARG_INDIRECT; 29 out->flags = is_return ? ABI_AF_SRET : ABI_AF_BYVAL; 30 out->indirect_align = ti.align ? ti.align : 16; 31 out->parts = NULL; 32 out->nparts = 0; 33 return; 34 } 35 36 abi_classify_scalar_reg_part(a, out, ti, ti.scalar_kind == ABI_SC_FLOAT); 37 } 38 39 typedef enum SysVClass { 40 SYSV_NO_CLASS, 41 SYSV_INTEGER, 42 SYSV_SSE, 43 SYSV_MEMORY, 44 } SysVClass; 45 46 static SysVClass merge_class(SysVClass a, SysVClass b) { 47 if (a == b) return a; 48 if (a == SYSV_NO_CLASS) return b; 49 if (b == SYSV_NO_CLASS) return a; 50 if (a == SYSV_MEMORY || b == SYSV_MEMORY) return SYSV_MEMORY; 51 if (a == SYSV_INTEGER || b == SYSV_INTEGER) return SYSV_INTEGER; 52 return SYSV_SSE; 53 } 54 55 static int mark_eightbytes(SysVClass cls[2], u32 offset, u32 size, 56 SysVClass k) { 57 if (!size) return 1; 58 if (offset >= 16u || offset + size > 16u) return 0; 59 u32 first = offset / 8u; 60 u32 last = (offset + size - 1u) / 8u; 61 for (u32 i = first; i <= last; ++i) { 62 cls[i] = merge_class(cls[i], k); 63 if (cls[i] == SYSV_MEMORY) return 0; 64 } 65 return 1; 66 } 67 68 static int classify_range(TargetABI* a, KitCgTypeId t, u32 base, 69 SysVClass cls[2]) { 70 const CgType* ty = cg_type_get(a->c, t); 71 ABITypeInfo ti; 72 if (!ty) return 0; 73 if (ty->kind == KIT_CG_TYPE_ENUM) { 74 return classify_range(a, ty->enum_.base, base, cls); 75 } 76 ti = abi_cg_type_info(a, t); 77 switch (ty->kind) { 78 case KIT_CG_TYPE_BOOL: 79 case KIT_CG_TYPE_INT: 80 case KIT_CG_TYPE_PTR: 81 return mark_eightbytes(cls, base, ti.size, SYSV_INTEGER); 82 case KIT_CG_TYPE_FLOAT: 83 if (ti.size == 4u || ti.size == 8u) 84 return mark_eightbytes(cls, base, ti.size, SYSV_SSE); 85 return 0; 86 case KIT_CG_TYPE_ARRAY: { 87 ABITypeInfo ei = abi_cg_type_info(a, ty->array.elem); 88 for (u64 i = 0; i < ty->array.count; ++i) { 89 if (i > UINT32_MAX || ei.size > UINT32_MAX || 90 base > UINT32_MAX - (u32)(i * ei.size)) 91 return 0; 92 if (!classify_range(a, ty->array.elem, base + (u32)(i * ei.size), cls)) 93 return 0; 94 } 95 return 1; 96 } 97 case KIT_CG_TYPE_RECORD: { 98 const ABIRecordLayout* L = abi_cg_record_layout(a, t); 99 if (!L || L->size > 16u) return 0; 100 for (u32 i = 0; i < ty->record.nfields; ++i) { 101 const CgTypeField* f = &ty->record.fields[i]; 102 const ABIFieldLayout* fl = &L->fields[i]; 103 ABITypeInfo fi = abi_cg_type_info(a, f->type); 104 if ((f->flags & KIT_CG_FIELD_BITFIELD) != 0) { 105 if (fl->bit_width == 0) continue; 106 if (!mark_eightbytes(cls, base + fl->offset, fl->storage_size, 107 SYSV_INTEGER)) 108 return 0; 109 continue; 110 } 111 if (fi.size && fi.align && ((base + fl->offset) % fi.align) != 0) 112 return 0; 113 if (!classify_range(a, f->type, base + fl->offset, cls)) return 0; 114 } 115 return 1; 116 } 117 case KIT_CG_TYPE_VOID: 118 return 1; 119 default: 120 return mark_eightbytes(cls, base, ti.size, SYSV_INTEGER); 121 } 122 } 123 124 static void classify_aggregate(TargetABI* a, KitCgTypeId t, ABIArgInfo* out, 125 int is_return) { 126 ABITypeInfo ti = abi_cg_type_info(a, t); 127 if (ti.size == 0) { 128 abi_classify_void(out); 129 return; 130 } 131 if (ti.size <= 16) { 132 SysVClass cls[2] = {SYSV_NO_CLASS, SYSV_NO_CLASS}; 133 if (!classify_range(a, t, 0, cls)) { 134 out->kind = ABI_ARG_INDIRECT; 135 out->flags = is_return ? ABI_AF_SRET : ABI_AF_BYVAL; 136 out->indirect_align = ti.align ? ti.align : 8; 137 out->parts = NULL; 138 out->nparts = 0; 139 return; 140 } 141 u32 nparts = (ti.size + 7) / 8; 142 ABIArgPart* parts = arena_array(a->c->tu, ABIArgPart, nparts); 143 memset(parts, 0, sizeof(ABIArgPart) * nparts); 144 u32 off = 0; 145 for (u32 i = 0; i < nparts; ++i) { 146 u32 chunk = (ti.size - off > 8) ? 8 : (ti.size - off); 147 parts[i].cls = (cls[i] == SYSV_SSE) ? ABI_CLASS_FP : ABI_CLASS_INT; 148 parts[i].loc = ABI_LOC_REG; 149 parts[i].size = chunk; 150 parts[i].align = 8; 151 parts[i].src_offset = off; 152 off += chunk; 153 } 154 out->kind = ABI_ARG_DIRECT; 155 out->flags = nparts > 1 ? ABI_AF_SPLIT : ABI_AF_NONE; 156 out->parts = parts; 157 out->nparts = nparts; 158 out->indirect_align = 0; 159 } else { 160 out->kind = ABI_ARG_INDIRECT; 161 out->flags = is_return ? ABI_AF_SRET : ABI_AF_BYVAL; 162 out->indirect_align = ti.align ? ti.align : 8; 163 out->parts = NULL; 164 out->nparts = 0; 165 } 166 } 167 168 static void classify_one(TargetABI* a, KitCgTypeId t, ABIArgInfo* out, 169 int is_return) { 170 const CgType* ty = cg_type_get(a->c, t); 171 if (!ty || ty->kind == KIT_CG_TYPE_VOID) { 172 abi_classify_void(out); 173 return; 174 } 175 switch (ty->kind) { 176 case KIT_CG_TYPE_RECORD: 177 classify_aggregate(a, t, out, is_return); 178 return; 179 default: 180 classify_scalar(a, t, out, is_return); 181 return; 182 } 183 } 184 185 /* SysV x86_64 register-pool sizes for the variadic reg-save area. 186 * GP: rdi, rsi, rdx, rcx, r8, r9 — 6 slots * 8 bytes = 48 187 * FP: xmm0..xmm7 — 8 slots * 16 bytes = 128 188 * Total reg-save area is 48 + 128 = 176 bytes; the fp_offset field starts 189 * at 48 (right after the GP block) and ranges up to 176. */ 190 #define SYSV_X64_GP_REG_COUNT 6u 191 #define SYSV_X64_FP_REG_COUNT 8u 192 #define SYSV_X64_GP_SLOT_SIZE 8u 193 #define SYSV_X64_FP_SLOT_SIZE 16u 194 #define SYSV_X64_GP_MAX_OFFSET (SYSV_X64_GP_REG_COUNT * SYSV_X64_GP_SLOT_SIZE) 195 #define SYSV_X64_FP_BASE_OFFSET SYSV_X64_GP_MAX_OFFSET 196 #define SYSV_X64_FP_MAX_OFFSET \ 197 (SYSV_X64_FP_BASE_OFFSET + SYSV_X64_FP_REG_COUNT * SYSV_X64_FP_SLOT_SIZE) 198 199 static ABIFuncInfo* sysv_x64_compute_func_info(TargetABI* a, KitCgTypeId fn) { 200 /* SysV-x64 passes the sret pointer in rdi (the first integer arg register), 201 * consuming that slot, so sret_consumes_int_arg follows has_sret. */ 202 ABIFuncInfo* info = 203 abi_compute_func_info_generic(a, fn, classify_one, 204 /*sret_consumes_int_arg=*/1); 205 206 /* Variadic register-save-area offsets at function entry. Counts the 207 * GP/FP register slots consumed by the fixed (named) parameters; va_start 208 * uses these as the initial gp_offset / fp_offset in the __va_list_tag 209 * struct so the va_arg fetch path skips over the already-consumed slots 210 * in the reg_save_area before falling through to overflow_arg_area. 211 * 212 * overflow_arg_area is computed at the call site from rbp + 16 + stack 213 * args at va_start time (see x_va_start_ in src/arch/x64/ops.c), so the 214 * vararg_overflow_offset metadata is left at 0 here. */ 215 if (info->variadic) { 216 u32 gp_used = info->has_sret ? 1u : 0u; 217 u32 fp_used = 0u; 218 for (u32 i = 0; i < info->nparams; ++i) { 219 const ABIArgInfo* ai = &info->params[i]; 220 if (ai->kind == ABI_ARG_INDIRECT) { 221 if (gp_used < SYSV_X64_GP_REG_COUNT) ++gp_used; 222 continue; 223 } 224 if (ai->kind != ABI_ARG_DIRECT) continue; 225 for (u32 p = 0; p < ai->nparts; ++p) { 226 if (ai->parts[p].cls == ABI_CLASS_FP) { 227 if (fp_used < SYSV_X64_FP_REG_COUNT) ++fp_used; 228 } else if (ai->parts[p].cls == ABI_CLASS_INT) { 229 if (gp_used < SYSV_X64_GP_REG_COUNT) ++gp_used; 230 } 231 } 232 } 233 if (gp_used > SYSV_X64_GP_REG_COUNT) gp_used = SYSV_X64_GP_REG_COUNT; 234 if (fp_used > SYSV_X64_FP_REG_COUNT) fp_used = SYSV_X64_FP_REG_COUNT; 235 info->vararg_gp_offset = gp_used * SYSV_X64_GP_SLOT_SIZE; 236 info->vararg_fp_offset = 237 SYSV_X64_FP_BASE_OFFSET + fp_used * SYSV_X64_FP_SLOT_SIZE; 238 info->vararg_overflow_offset = 0; 239 (void)SYSV_X64_GP_MAX_OFFSET; 240 (void)SYSV_X64_FP_MAX_OFFSET; 241 } 242 return info; 243 } 244 245 const ABIVtable sysv_x64_vtable = { 246 .compute_func_info = sysv_x64_compute_func_info, 247 .va_list_info = {24, 8, ABI_SC_VOID, 0, 0, 0}, 248 .va_list_layout = {.kind = ABI_VA_LIST_SYSV_X64, 249 .stack_offset = 8, 250 .gr_top_offset = 16, 251 .gr_offs_offset = 0, 252 .vr_offs_offset = 4, 253 .gp_reg_count = SYSV_X64_GP_REG_COUNT, 254 .fp_reg_count = SYSV_X64_FP_REG_COUNT, 255 .gp_slot_size = SYSV_X64_GP_SLOT_SIZE, 256 .fp_slot_size = SYSV_X64_FP_SLOT_SIZE}, 257 };