Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.

Commit 3961b6e

Browse files
authored
Merge pull request #3040 from kinke/stdarg
[stable] Refactor core.stdc.stdarg & add support for ARM, PowerPC and MIPS (AArch64: Apple only) merged-on-behalf-of: unknown
2 parents 3f9bfc9 + a9f9de4 commit 3961b6e

File tree

5 files changed

+673
-415
lines changed

5 files changed

+673
-415
lines changed

mak/COPY

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ COPY=\
5555
\
5656
$(IMPDIR)\core\internal\util\array.d \
5757
\
58+
$(IMPDIR)\core\internal\vararg\sysv_x64.d \
59+
\
5860
$(IMPDIR)\core\stdc\assert_.d \
5961
$(IMPDIR)\core\stdc\complex.d \
6062
$(IMPDIR)\core\stdc\config.d \

mak/SRCS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ SRCS=\
5555
\
5656
src\core\internal\util\array.d \
5757
\
58+
src\core\internal\vararg\sysv_x64.d \
59+
\
5860
src\core\stdc\assert_.d \
5961
src\core\stdc\complex.d \
6062
src\core\stdc\config.d \

src/core/internal/vararg/sysv_x64.d

Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
/**
2+
* Varargs implementation for the x86_64 System V ABI (not used for Win64).
3+
* Used by core.stdc.stdarg.
4+
*
5+
* Reference: https://www.uclibc.org/docs/psABI-x86_64.pdf
6+
*
7+
* Copyright: Copyright Digital Mars 2009 - 2020.
8+
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
9+
* Authors: Walter Bright, Hauke Duden
10+
* Source: $(DRUNTIMESRC core/internal/vararg/sysv_x64.d)
11+
*/
12+
13+
module core.internal.vararg.sysv_x64;
14+
15+
version (X86_64)
16+
{
17+
version (Windows) { /* different ABI */ }
18+
else version = SysV_x64;
19+
}
20+
21+
version (SysV_x64):
22+
23+
import core.stdc.stdarg: alignUp;
24+
25+
@system:
26+
//@nogc: // Not yet, need to make TypeInfo's member functions @nogc first
27+
nothrow:
28+
29+
// Layout of this struct must match __gnuc_va_list for C ABI compatibility
30+
struct __va_list_tag
31+
{
32+
uint offset_regs = 6 * 8; // no regs
33+
uint offset_fpregs = 6 * 8 + 8 * 16; // no fp regs
34+
void* stack_args;
35+
void* reg_args;
36+
}
37+
alias __va_list = __va_list_tag;
38+
39+
/*
40+
* Making it an array of 1 causes va_list to be passed as a pointer in
41+
* function argument lists
42+
*/
43+
alias va_list = __va_list*;
44+
45+
///
46+
T va_arg(T)(va_list ap)
47+
{
48+
static if (is(T U == __argTypes))
49+
{
50+
static if (U.length == 0 || T.sizeof > 16 || (U[0].sizeof > 8 && !is(U[0] == __vector)))
51+
{ // Always passed in memory
52+
// The arg may have more strict alignment than the stack
53+
void* p = ap.stack_args.alignUp!(T.alignof);
54+
ap.stack_args = p + T.sizeof.alignUp;
55+
return *cast(T*) p;
56+
}
57+
else static if (U.length == 1)
58+
{ // Arg is passed in one register
59+
alias U[0] T1;
60+
static if (is(T1 == double) || is(T1 == float) || is(T1 == __vector))
61+
{ // Passed in XMM register
62+
if (ap.offset_fpregs < (6 * 8 + 16 * 8))
63+
{
64+
auto p = cast(T*) (ap.reg_args + ap.offset_fpregs);
65+
ap.offset_fpregs += 16;
66+
return *p;
67+
}
68+
else
69+
{
70+
auto p = cast(T*) ap.stack_args;
71+
ap.stack_args += T1.sizeof.alignUp;
72+
return *p;
73+
}
74+
}
75+
else
76+
{ // Passed in regular register
77+
if (ap.offset_regs < 6 * 8 && T.sizeof <= 8)
78+
{
79+
auto p = cast(T*) (ap.reg_args + ap.offset_regs);
80+
ap.offset_regs += 8;
81+
return *p;
82+
}
83+
else
84+
{
85+
void* p = ap.stack_args.alignUp!(T.alignof);
86+
ap.stack_args = p + T.sizeof.alignUp;
87+
return *cast(T*) p;
88+
}
89+
}
90+
}
91+
else static if (U.length == 2)
92+
{ // Arg is passed in two registers
93+
alias U[0] T1;
94+
alias U[1] T2;
95+
96+
T result = void;
97+
auto p1 = cast(T1*) &result;
98+
auto p2 = cast(T2*) ((cast(void*) &result) + 8);
99+
100+
// Both must be in registers, or both on stack, hence 4 cases
101+
102+
static if ((is(T1 == double) || is(T1 == float)) &&
103+
(is(T2 == double) || is(T2 == float)))
104+
{
105+
if (ap.offset_fpregs < (6 * 8 + 16 * 8) - 16)
106+
{
107+
*p1 = *cast(T1*) (ap.reg_args + ap.offset_fpregs);
108+
*p2 = *cast(T2*) (ap.reg_args + ap.offset_fpregs + 16);
109+
ap.offset_fpregs += 32;
110+
}
111+
else
112+
{
113+
*p1 = *cast(T1*) ap.stack_args;
114+
ap.stack_args += T1.sizeof.alignUp;
115+
*p2 = *cast(T2*) ap.stack_args;
116+
ap.stack_args += T2.sizeof.alignUp;
117+
}
118+
}
119+
else static if (is(T1 == double) || is(T1 == float))
120+
{
121+
void* a = void;
122+
if (ap.offset_fpregs < (6 * 8 + 16 * 8) &&
123+
ap.offset_regs < 6 * 8 && T2.sizeof <= 8)
124+
{
125+
*p1 = *cast(T1*) (ap.reg_args + ap.offset_fpregs);
126+
ap.offset_fpregs += 16;
127+
a = ap.reg_args + ap.offset_regs;
128+
ap.offset_regs += 8;
129+
}
130+
else
131+
{
132+
*p1 = *cast(T1*) ap.stack_args;
133+
ap.stack_args += T1.sizeof.alignUp;
134+
a = ap.stack_args;
135+
ap.stack_args += 8;
136+
}
137+
// Be careful not to go past the size of the actual argument
138+
const sz2 = T.sizeof - 8;
139+
(cast(void*) p2)[0..sz2] = a[0..sz2];
140+
}
141+
else static if (is(T2 == double) || is(T2 == float))
142+
{
143+
if (ap.offset_regs < 6 * 8 && T1.sizeof <= 8 &&
144+
ap.offset_fpregs < (6 * 8 + 16 * 8))
145+
{
146+
*p1 = *cast(T1*) (ap.reg_args + ap.offset_regs);
147+
ap.offset_regs += 8;
148+
*p2 = *cast(T2*) (ap.reg_args + ap.offset_fpregs);
149+
ap.offset_fpregs += 16;
150+
}
151+
else
152+
{
153+
*p1 = *cast(T1*) ap.stack_args;
154+
ap.stack_args += 8;
155+
*p2 = *cast(T2*) ap.stack_args;
156+
ap.stack_args += T2.sizeof.alignUp;
157+
}
158+
}
159+
else // both in regular registers
160+
{
161+
void* a = void;
162+
if (ap.offset_regs < 5 * 8 && T1.sizeof <= 8 && T2.sizeof <= 8)
163+
{
164+
*p1 = *cast(T1*) (ap.reg_args + ap.offset_regs);
165+
ap.offset_regs += 8;
166+
a = ap.reg_args + ap.offset_regs;
167+
ap.offset_regs += 8;
168+
}
169+
else
170+
{
171+
*p1 = *cast(T1*) ap.stack_args;
172+
ap.stack_args += 8;
173+
a = ap.stack_args;
174+
ap.stack_args += 8;
175+
}
176+
// Be careful not to go past the size of the actual argument
177+
const sz2 = T.sizeof - 8;
178+
(cast(void*) p2)[0..sz2] = a[0..sz2];
179+
}
180+
181+
return result;
182+
}
183+
else
184+
{
185+
static assert(false);
186+
}
187+
}
188+
else
189+
{
190+
static assert(false, "not a valid argument type for va_arg");
191+
}
192+
}
193+
194+
///
195+
void va_arg()(va_list ap, TypeInfo ti, void* parmn)
196+
{
197+
TypeInfo arg1, arg2;
198+
if (!ti.argTypes(arg1, arg2))
199+
{
200+
bool inXMMregister(TypeInfo arg) pure nothrow @safe
201+
{
202+
return (arg.flags & 2) != 0;
203+
}
204+
205+
TypeInfo_Vector v1 = arg1 ? cast(TypeInfo_Vector) arg1 : null;
206+
if (arg1 && (arg1.tsize <= 8 || v1))
207+
{ // Arg is passed in one register
208+
auto tsize = arg1.tsize;
209+
void* p;
210+
bool stack = false;
211+
auto offset_fpregs_save = ap.offset_fpregs;
212+
auto offset_regs_save = ap.offset_regs;
213+
L1:
214+
if (inXMMregister(arg1) || v1)
215+
{ // Passed in XMM register
216+
if (ap.offset_fpregs < (6 * 8 + 16 * 8) && !stack)
217+
{
218+
p = ap.reg_args + ap.offset_fpregs;
219+
ap.offset_fpregs += 16;
220+
}
221+
else
222+
{
223+
p = ap.stack_args;
224+
ap.stack_args += tsize.alignUp;
225+
stack = true;
226+
}
227+
}
228+
else
229+
{ // Passed in regular register
230+
if (ap.offset_regs < 6 * 8 && !stack)
231+
{
232+
p = ap.reg_args + ap.offset_regs;
233+
ap.offset_regs += 8;
234+
}
235+
else
236+
{
237+
p = ap.stack_args;
238+
ap.stack_args += 8;
239+
stack = true;
240+
}
241+
}
242+
parmn[0..tsize] = p[0..tsize];
243+
244+
if (arg2)
245+
{
246+
if (inXMMregister(arg2))
247+
{ // Passed in XMM register
248+
if (ap.offset_fpregs < (6 * 8 + 16 * 8) && !stack)
249+
{
250+
p = ap.reg_args + ap.offset_fpregs;
251+
ap.offset_fpregs += 16;
252+
}
253+
else
254+
{
255+
if (!stack)
256+
{ // arg1 is really on the stack, so rewind and redo
257+
ap.offset_fpregs = offset_fpregs_save;
258+
ap.offset_regs = offset_regs_save;
259+
stack = true;
260+
goto L1;
261+
}
262+
p = ap.stack_args;
263+
ap.stack_args += arg2.tsize.alignUp;
264+
}
265+
}
266+
else
267+
{ // Passed in regular register
268+
if (ap.offset_regs < 6 * 8 && !stack)
269+
{
270+
p = ap.reg_args + ap.offset_regs;
271+
ap.offset_regs += 8;
272+
}
273+
else
274+
{
275+
if (!stack)
276+
{ // arg1 is really on the stack, so rewind and redo
277+
ap.offset_fpregs = offset_fpregs_save;
278+
ap.offset_regs = offset_regs_save;
279+
stack = true;
280+
goto L1;
281+
}
282+
p = ap.stack_args;
283+
ap.stack_args += 8;
284+
}
285+
}
286+
auto sz = ti.tsize - 8;
287+
(parmn + 8)[0..sz] = p[0..sz];
288+
}
289+
}
290+
else
291+
{ // Always passed in memory
292+
// The arg may have more strict alignment than the stack
293+
auto talign = ti.talign;
294+
auto tsize = ti.tsize;
295+
auto p = cast(void*) ((cast(size_t) ap.stack_args + talign - 1) & ~(talign - 1));
296+
ap.stack_args = p + tsize.alignUp;
297+
parmn[0..tsize] = p[0..tsize];
298+
}
299+
}
300+
else
301+
{
302+
assert(false, "not a valid argument type for va_arg");
303+
}
304+
}

0 commit comments

Comments
 (0)