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