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