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 }