kit

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

abi_win64_x64.c (4910B)


      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_scalar(TargetABI* a, KitCgTypeId t, ABIArgInfo* out,
     29                             int is_return) {
     30   ABITypeInfo ti = abi_cg_type_info(a, t);
     31   (void)is_return;
     32   /* __int128 / __uint128: mingw/GCC convention emits two INTEGER
     33    * eightbytes (rcx+rdx for args, rax+rdx for return) -- same shape
     34    * as SysV.  MSVC's official spec says "by reference" for 16-byte
     35    * aggregates, but mingw is kit's interop target on Windows and
     36    * mingw matches SysV here. */
     37   if (ti.scalar_kind == ABI_SC_INT && ti.size == 16) {
     38     abi_classify_int128_pair(a, out);
     39     return;
     40   }
     41   /* long double on Win64 is 64-bit double (both MSVC and mingw, unless
     42    * mingw's -mlong-double-80 is in effect -- not supported in v1).
     43    * The front end should already have lowered `long double` to a size-8
     44    * float for Windows targets.  Defensive path: if a size-16 FP slips
     45    * through, treat it as a size-8 double (one FP part) -- this stays
     46    * register-passed, unlike SysV which routes long double through
     47    * memory. */
     48   if (ti.scalar_kind == ABI_SC_FLOAT && ti.size == 16) {
     49     ABIArgPart* parts = arena_new(a->c->tu, ABIArgPart);
     50     memset(parts, 0, sizeof *parts);
     51     parts->cls = ABI_CLASS_FP;
     52     parts->loc = ABI_LOC_REG;
     53     parts->size = 8;
     54     parts->align = 8;
     55     parts->src_offset = 0;
     56     out->kind = ABI_ARG_DIRECT;
     57     out->flags = ABI_AF_NONE;
     58     out->parts = parts;
     59     out->nparts = 1;
     60     out->indirect_align = 0;
     61     return;
     62   }
     63 
     64   abi_classify_scalar_reg_part(a, out, ti, ti.scalar_kind == ABI_SC_FLOAT);
     65 }
     66 
     67 static void classify_aggregate(TargetABI* a, KitCgTypeId t, ABIArgInfo* out,
     68                                int is_return) {
     69   ABITypeInfo ti = abi_cg_type_info(a, t);
     70   if (ti.size == 0) {
     71     abi_classify_void(out);
     72     return;
     73   }
     74   /* Win64: aggregates pass by value only when the size is exactly one
     75    * of {1, 2, 4, 8}.  A 3-byte struct is NOT a 3-byte INT part -- it
     76    * goes by hidden pointer.  A 16-byte struct is also hidden-pointer
     77    * (no two-register pair, unlike SysV's <=16B path). */
     78   if (ti.size == 1 || ti.size == 2 || ti.size == 4 || ti.size == 8) {
     79     ABIArgPart* parts = arena_new(a->c->tu, ABIArgPart);
     80     memset(parts, 0, sizeof *parts);
     81     parts->cls = ABI_CLASS_INT;
     82     parts->loc = ABI_LOC_REG;
     83     parts->size = ti.size;
     84     parts->align = ti.align ? ti.align : ti.size;
     85     parts->src_offset = 0;
     86     out->kind = ABI_ARG_DIRECT;
     87     out->flags = ABI_AF_NONE;
     88     out->parts = parts;
     89     out->nparts = 1;
     90     out->indirect_align = 0;
     91   } else {
     92     out->kind = ABI_ARG_INDIRECT;
     93     out->flags = is_return ? ABI_AF_SRET : ABI_AF_BYVAL;
     94     out->indirect_align = ti.align ? ti.align : 8;
     95     out->parts = NULL;
     96     out->nparts = 0;
     97   }
     98 }
     99 
    100 static void classify_one(TargetABI* a, KitCgTypeId t, ABIArgInfo* out,
    101                          int is_return) {
    102   const CgType* ty = cg_type_get(a->c, t);
    103   if (!ty || ty->kind == KIT_CG_TYPE_VOID) {
    104     abi_classify_void(out);
    105     return;
    106   }
    107   switch (ty->kind) {
    108     case KIT_CG_TYPE_RECORD:
    109       classify_aggregate(a, t, out, is_return);
    110       return;
    111     default:
    112       classify_scalar(a, t, out, is_return);
    113       return;
    114   }
    115 }
    116 
    117 static ABIFuncInfo* win64_x64_compute_func_info(TargetABI* a, KitCgTypeId fn) {
    118   /* Win64 passes the sret pointer in rcx (the first integer arg register),
    119    * consuming that slot, so sret_consumes_int_arg follows has_sret. */
    120   return abi_compute_func_info_generic(a, fn, classify_one,
    121                                        /*sret_consumes_int_arg=*/1);
    122 }
    123 
    124 const ABIVtable win64_x64_vtable = {
    125     .compute_func_info = win64_x64_compute_func_info,
    126     /* Windows commits stack one guard page at a time; the x64 backend gates its
    127      * __chkstk call on this (frame_size > one page). See x64_build_prologue. */
    128     .stack_probe_interval = 4096,
    129     .va_list_info = {8, 8, ABI_SC_PTR, 0, 0, 0},
    130     .va_list_layout = {.kind = ABI_VA_LIST_POINTER},
    131 };