kit

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

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