1 /** 2 * Varargs implementation for the AArch64 Procedure Call Standard (not followed by Apple). 3 * Used by core.stdc.stdarg and core.vararg. 4 * 5 * Reference: https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst#appendix-variable-argument-lists 6 * 7 * Copyright: Copyright Digital Mars 2020 - 2020. 8 * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 9 * Authors: Martin Kinkelin 10 * Source: $(DRUNTIMESRC core/internal/vararg/aarch64.d) 11 */ 12 13 module core.internal.vararg.aarch64; 14 15 version (AArch64): 16 17 // Darwin uses a simpler varargs implementation 18 version (OSX) {} 19 else version (iOS) {} 20 else version (TVOS) {} 21 else version (WatchOS) {} 22 else: 23 24 import core.stdc.stdarg : alignUp; 25 26 //@nogc: // Not yet, need to make TypeInfo's member functions @nogc first 27 nothrow: 28 29 extern (C++, std) struct __va_list 30 { 31 void* __stack; 32 void* __gr_top; 33 void* __vr_top; 34 int __gr_offs; 35 int __vr_offs; 36 } 37 38 /// 39 alias va_list = __va_list; 40 41 /// 42 T va_arg(T)(ref va_list ap) 43 { 44 static if (is(T ArgTypes == __argTypes)) 45 { 46 T onStack() 47 { 48 void* arg = ap.__stack; 49 static if (T.alignof > 8) 50 arg = arg.alignUp!16; 51 ap.__stack = alignUp(arg + T.sizeof); 52 version (BigEndian) 53 static if (T.sizeof < 8) 54 arg += 8 - T.sizeof; 55 return *cast(T*) arg; 56 } 57 58 static if (ArgTypes.length == 0) 59 { 60 // indirectly by value; get pointer and copy 61 T* ptr = va_arg!(T*)(ap); 62 return *ptr; 63 } 64 65 static assert(ArgTypes.length == 1); 66 67 static if (is(ArgTypes[0] E : E[N], int N)) 68 alias FundamentalType = E; // static array element type 69 else 70 alias FundamentalType = ArgTypes[0]; 71 72 static if (__traits(isFloating, FundamentalType) || is(FundamentalType == __vector)) 73 { 74 import core.stdc.string : memcpy; 75 76 // SIMD register(s) 77 int offs = ap.__vr_offs; 78 if (offs >= 0) 79 return onStack(); // reg save area empty 80 enum int usedRegSize = FundamentalType.sizeof; 81 static assert(T.sizeof % usedRegSize == 0); 82 enum int nreg = T.sizeof / usedRegSize; 83 ap.__vr_offs = offs + (nreg * 16); 84 if (ap.__vr_offs > 0) 85 return onStack(); // overflowed reg save area 86 version (BigEndian) 87 static if (usedRegSize < 16) 88 offs += 16 - usedRegSize; 89 90 T result = void; 91 static foreach (i; 0 .. nreg) 92 memcpy((cast(void*) &result) + i * usedRegSize, ap.__vr_top + (offs + i * 16), usedRegSize); 93 return result; 94 } 95 else 96 { 97 // GP register(s) 98 int offs = ap.__gr_offs; 99 if (offs >= 0) 100 return onStack(); // reg save area empty 101 static if (T.alignof > 8) 102 offs = offs.alignUp!16; 103 enum int nreg = (T.sizeof + 7) / 8; 104 ap.__gr_offs = offs + (nreg * 8); 105 if (ap.__gr_offs > 0) 106 return onStack(); // overflowed reg save area 107 version (BigEndian) 108 static if (T.sizeof < 8) 109 offs += 8 - T.sizeof; 110 return *cast(T*) (ap.__gr_top + offs); 111 } 112 } 113 else 114 { 115 static assert(false, "not a valid argument type for va_arg"); 116 } 117 } 118 119 /// 120 void va_arg()(ref va_list ap, TypeInfo ti, void* parmn) 121 { 122 import core.stdc.string : memcpy; 123 124 const size = ti.tsize; 125 const alignment = ti.talign; 126 127 if (auto ti_struct = cast(TypeInfo_Struct) ti) 128 { 129 TypeInfo arg1, arg2; 130 ti.argTypes(arg1, arg2); 131 132 if (!arg1) 133 { 134 // indirectly by value; get pointer and move 135 void* ptr = va_arg!(void*)(ap); 136 memcpy(parmn, ptr, size); 137 return; 138 } 139 140 assert(!arg2); 141 ti = arg1; 142 } 143 144 void onStack() 145 { 146 void* arg = ap.__stack; 147 if (alignment > 8) 148 arg = arg.alignUp!16; 149 ap.__stack = alignUp(arg + size); 150 version (BigEndian) 151 if (size < 8) 152 arg += 8 - size; 153 memcpy(parmn, arg, size); 154 } 155 156 // HFVA structs have already been lowered to static arrays; 157 // lower `ti` further to the fundamental type, including HFVA 158 // static arrays. 159 // TODO: complex numbers 160 if (auto ti_sarray = cast(TypeInfo_StaticArray) ti) 161 ti = ti_sarray.value; 162 163 if (ti.flags() & 2) 164 { 165 // SIMD register(s) 166 int offs = ap.__vr_offs; 167 if (offs >= 0) 168 return onStack(); // reg save area empty 169 const usedRegSize = cast(int) ti.tsize; 170 assert(size % usedRegSize == 0); 171 const nreg = cast(int) (size / usedRegSize); 172 ap.__vr_offs = offs + (nreg * 16); 173 if (ap.__vr_offs > 0) 174 return onStack(); // overflowed reg save area 175 version (BigEndian) 176 if (usedRegSize < 16) 177 offs += 16 - usedRegSize; 178 foreach (i; 0 .. nreg) 179 memcpy(parmn + i * usedRegSize, ap.__vr_top + (offs + i * 16), usedRegSize); 180 181 return; 182 } 183 184 // GP register(s) 185 int offs = ap.__gr_offs; 186 if (offs >= 0) 187 return onStack(); // reg save area empty 188 if (alignment > 8) 189 offs = offs.alignUp!16; 190 const nreg = cast(int) ((size + 7) / 8); 191 ap.__gr_offs = offs + (nreg * 8); 192 if (ap.__gr_offs > 0) 193 return onStack(); // overflowed reg save area 194 version (BigEndian) 195 if (size < 8) 196 offs += 8 - size; 197 memcpy(parmn, ap.__gr_top + offs, size); 198 }