kit

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

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