kit

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

abi_win64_x64.c (6524B)


      1 /* Win64 (Microsoft x86_64) ABI classifier.
      2  *
      3  * Selected when (target.arch == X86_64, target.os == WINDOWS).
      4  *
      5  * Win64 vs SysV-x64 deltas:
      6  *   - Arg slots: RCX/RDX/R8/R9 share index with XMM0..3 (codegen
      7  *     assigns by index; classifier emits per-slot INT or FP parts).
      8  *   - Aggregates: pass-by-value only for sizes in {1,2,4,8}; otherwise
      9  *     hidden-pointer (byval for args, sret for returns).
     10  *   - __int128: passed as two INTEGER eightbytes (mingw convention;
     11  *     differs from MSVC spec which says by reference).
     12  *   - long double: 64-bit double (no x87).
     13  *   - va_list: void* (single pointer; no struct).
     14  *   - varargs: still in regs; FP-args duplicated in matching GPR slot
     15  *     by the call-site codegen (not encoded here).
     16  *
     17  * Shadow-space (32 B above return addr) is a call-site reservation,
     18  * not an ABI classifier concern -- see arch/x64/call.c.
     19  */
     20 
     21 #include <string.h>
     22 
     23 #include "abi/abi_internal.h"
     24 #include "cg/type.h"
     25 #include "core/arena.h"
     26 #include "core/core.h"
     27 
     28 static void classify_void(ABIArgInfo* out) {
     29   memset(out, 0, sizeof *out);
     30   out->kind = ABI_ARG_IGNORE;
     31 }
     32 
     33 static void classify_scalar(TargetABI* a, KitCgTypeId t, ABIArgInfo* out,
     34                             int is_return) {
     35   ABITypeInfo ti = abi_internal_type_info(a, t);
     36   (void)is_return;
     37   /* __int128 / __uint128: mingw/GCC convention emits two INTEGER
     38    * eightbytes (rcx+rdx for args, rax+rdx for return) -- same shape
     39    * as SysV.  MSVC's official spec says "by reference" for 16-byte
     40    * aggregates, but mingw is kit's interop target on Windows and
     41    * mingw matches SysV here. */
     42   if (ti.scalar_kind == ABI_SC_INT && ti.size == 16) {
     43     ABIArgPart* parts = arena_array(a->c->tu, ABIArgPart, 2);
     44     memset(parts, 0, sizeof(ABIArgPart) * 2);
     45     for (u32 i = 0; i < 2; ++i) {
     46       parts[i].cls = ABI_CLASS_INT;
     47       parts[i].loc = ABI_LOC_REG;
     48       parts[i].size = 8;
     49       parts[i].align = 8;
     50       parts[i].src_offset = i * 8;
     51     }
     52     out->kind = ABI_ARG_DIRECT;
     53     out->flags = ABI_AF_NONE;
     54     out->parts = parts;
     55     out->nparts = 2;
     56     out->indirect_align = 0;
     57     return;
     58   }
     59   /* long double on Win64 is 64-bit double (both MSVC and mingw, unless
     60    * mingw's -mlong-double-80 is in effect -- not supported in v1).
     61    * The front end should already have lowered `long double` to a size-8
     62    * float for Windows targets.  Defensive path: if a size-16 FP slips
     63    * through, treat it as a size-8 double (one FP part) -- this stays
     64    * register-passed, unlike SysV which routes long double through
     65    * memory. */
     66   if (ti.scalar_kind == ABI_SC_FLOAT && ti.size == 16) {
     67     ABIArgPart* parts = arena_new(a->c->tu, ABIArgPart);
     68     memset(parts, 0, sizeof *parts);
     69     parts->cls = ABI_CLASS_FP;
     70     parts->loc = ABI_LOC_REG;
     71     parts->size = 8;
     72     parts->align = 8;
     73     parts->src_offset = 0;
     74     out->kind = ABI_ARG_DIRECT;
     75     out->flags = ABI_AF_NONE;
     76     out->parts = parts;
     77     out->nparts = 1;
     78     out->indirect_align = 0;
     79     return;
     80   }
     81 
     82   out->kind = ABI_ARG_DIRECT;
     83   out->flags = ABI_AF_NONE;
     84   out->indirect_align = 0;
     85 
     86   ABIArgPart* parts = arena_new(a->c->tu, ABIArgPart);
     87   memset(parts, 0, sizeof *parts);
     88   parts->cls = (ti.scalar_kind == ABI_SC_FLOAT) ? ABI_CLASS_FP : ABI_CLASS_INT;
     89   parts->loc = ABI_LOC_REG;
     90   parts->size = ti.size;
     91   parts->align = ti.align;
     92   parts->src_offset = 0;
     93 
     94   out->parts = parts;
     95   out->nparts = 1;
     96 }
     97 
     98 static void classify_aggregate(TargetABI* a, KitCgTypeId t, ABIArgInfo* out,
     99                                int is_return) {
    100   ABITypeInfo ti = abi_internal_type_info(a, t);
    101   if (ti.size == 0) {
    102     classify_void(out);
    103     return;
    104   }
    105   /* Win64: aggregates pass by value only when the size is exactly one
    106    * of {1, 2, 4, 8}.  A 3-byte struct is NOT a 3-byte INT part -- it
    107    * goes by hidden pointer.  A 16-byte struct is also hidden-pointer
    108    * (no two-register pair, unlike SysV's <=16B path). */
    109   if (ti.size == 1 || ti.size == 2 || ti.size == 4 || ti.size == 8) {
    110     ABIArgPart* parts = arena_new(a->c->tu, ABIArgPart);
    111     memset(parts, 0, sizeof *parts);
    112     parts->cls = ABI_CLASS_INT;
    113     parts->loc = ABI_LOC_REG;
    114     parts->size = ti.size;
    115     parts->align = ti.align ? ti.align : ti.size;
    116     parts->src_offset = 0;
    117     out->kind = ABI_ARG_DIRECT;
    118     out->flags = ABI_AF_NONE;
    119     out->parts = parts;
    120     out->nparts = 1;
    121     out->indirect_align = 0;
    122   } else {
    123     out->kind = ABI_ARG_INDIRECT;
    124     out->flags = is_return ? ABI_AF_SRET : ABI_AF_BYVAL;
    125     out->indirect_align = ti.align ? ti.align : 8;
    126     out->parts = NULL;
    127     out->nparts = 0;
    128   }
    129 }
    130 
    131 static void classify_one(TargetABI* a, KitCgTypeId t, ABIArgInfo* out,
    132                          int is_return) {
    133   const CgType* ty = cg_type_get(a->c, t);
    134   if (!ty || ty->kind == KIT_CG_TYPE_VOID) {
    135     classify_void(out);
    136     return;
    137   }
    138   switch (ty->kind) {
    139     case KIT_CG_TYPE_RECORD:
    140       classify_aggregate(a, t, out, is_return);
    141       return;
    142     case KIT_CG_TYPE_ALIAS:
    143       classify_one(a, ty->alias.base, out, is_return);
    144       return;
    145     default:
    146       classify_scalar(a, t, out, is_return);
    147       return;
    148   }
    149 }
    150 
    151 static ABIFuncInfo* win64_x64_compute_func_info(TargetABI* a, KitCgTypeId fn) {
    152   ABIFuncInfo* info = arena_new(a->c->tu, ABIFuncInfo);
    153   const CgType* fnty = cg_type_get(a->c, fn);
    154   memset(info, 0, sizeof *info);
    155 
    156   classify_one(a, cg_func_ret_type(fnty), &info->ret, /*is_return=*/1);
    157   info->has_sret = (info->ret.kind == ABI_ARG_INDIRECT) ? 1 : 0;
    158   /* Win64 passes the sret pointer in rcx (the first integer arg register),
    159    * consuming that slot. */
    160   info->sret_consumes_int_arg = info->has_sret;
    161   info->variadic = fnty->func.abi_variadic;
    162 
    163   info->nparams = (u16)fnty->func.nparams;
    164   if (fnty->func.nparams) {
    165     ABIArgInfo* arr = arena_array(a->c->tu, ABIArgInfo, fnty->func.nparams);
    166     memset(arr, 0, sizeof(ABIArgInfo) * fnty->func.nparams);
    167     for (u32 i = 0; i < fnty->func.nparams; ++i) {
    168       classify_one(a, fnty->func.params[i].type, &arr[i], /*is_return=*/0);
    169     }
    170     info->params = arr;
    171   } else {
    172     info->params = NULL;
    173   }
    174   return info;
    175 }
    176 
    177 const ABIVtable win64_x64_vtable = {
    178     .compute_func_info = win64_x64_compute_func_info,
    179     /* Windows commits stack one guard page at a time; the x64 backend gates its
    180      * __chkstk call on this (frame_size > one page). See x64_build_prologue. */
    181     .stack_probe_interval = 4096,
    182     .va_list_info = {8, 8, ABI_SC_PTR, 0, 0, 0},
    183     .va_list_layout = {.type = {8, 8, ABI_SC_PTR, 0, 0, 0},
    184                        .kind = ABI_VA_LIST_POINTER},
    185 };