Skip to content

Commit c0cd3a2

Browse files
committed
dmd: more s390x va_arg implementations
1 parent 4c8495a commit c0cd3a2

File tree

5 files changed

+133
-5
lines changed

5 files changed

+133
-5
lines changed

dmd/argtypes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,7 @@ namespace dmd
2121
TypeTuple *toArgTypes_sysv_x64(Type *t);
2222
// in argtypes_aarch64.d
2323
TypeTuple *toArgTypes_aarch64(Type *t);
24+
// in argtypes_s390x.d
25+
TypeTuple *toArgTypes_s390x(Type *t);
2426
bool isHFVA(Type *t, int maxNumElements = 4, Type **rewriteType = nullptr);
2527
}

dmd/argtypes_s390x.d

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* Break down a D type into basic (register) types for the IBM Z ELF ABI.
3+
*
4+
* Copyright: Copyright (C) 2024-2025 by The D Language Foundation, All Rights Reserved
5+
* Authors: Martin Kinkelin
6+
* License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7+
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/argtypes_s390x.d, _argtypes_s390x.d)
8+
* Documentation: https://dlang.org/phobos/dmd_argtypes_s390x.html
9+
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/argtypes_s390x.d
10+
*/
11+
12+
module dmd.argtypes_s390x;
13+
14+
import dmd.astenums;
15+
import dmd.mtype;
16+
import dmd.typesem;
17+
18+
/****************************************************
19+
* This breaks a type down into 'simpler' types that can be passed to a function
20+
* in registers, and returned in registers.
21+
* This is the implementation for the IBM Z ELF ABI,
22+
* based on https://github.com/IBM/s390x-abi/releases/download/v1.6/lzsabi_s390x.pdf.
23+
* Params:
24+
* t = type to break down
25+
* Returns:
26+
* tuple of types, each element can be passed in a register.
27+
* A tuple of zero length means the type cannot be passed/returned in registers.
28+
* null indicates a `void`.
29+
*/
30+
TypeTuple toArgTypes_s390x(Type t)
31+
{
32+
if (t == Type.terror)
33+
return new TypeTuple(t);
34+
35+
const size = cast(size_t) t.size();
36+
if (size == 0)
37+
return null;
38+
39+
// TODO
40+
// Implement the rest of the va args passing
41+
//...
42+
Type tb = t.toBasetype();
43+
const isAggregate = tb.ty == Tstruct || tb.ty == Tsarray || tb.ty == Tarray || tb.ty == Tdelegate || tb.iscomplex();
44+
if (!isAggregate)
45+
return new TypeTuple(t);
46+
// unwrap single-float struct per ABI requirements
47+
if (auto tstruct = t.isTypeStruct())
48+
{
49+
if (tstruct.sym.fields.length == 1)
50+
{
51+
Type fieldType = tstruct.sym.fields[0].type.toBasetype();
52+
if (fieldType.isfloating())
53+
{
54+
return new TypeTuple(fieldType);
55+
}
56+
}
57+
}
58+
59+
// pass remaining aggregates in 1 or 2 GP registers
60+
static Type getGPType(size_t size)
61+
{
62+
switch (size)
63+
{
64+
case 1: return Type.tint8;
65+
case 2: return Type.tint16;
66+
case 4: return Type.tint32;
67+
case 8: return Type.tint64;
68+
default:
69+
import dmd.typesem : sarrayOf;
70+
return Type.tint64.sarrayOf((size + 7) / 8);
71+
}
72+
}
73+
return new TypeTuple(getGPType(size));
74+
}

dmd/cxxfrontend.d

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -698,4 +698,13 @@ version (IN_LLVM)
698698
import dmd.argtypes_x86;
699699
return dmd.argtypes_x86.toArgTypes_x86(t);
700700
}
701+
702+
/***********************************************************
703+
* argtypes_s390x.d
704+
*/
705+
TypeTuple toArgTypes_s390x(Type t)
706+
{
707+
import dmd.argtypes_s390x;
708+
return dmd.argtypes_s390x.toArgTypes_s390x(t);
709+
}
701710
}

gen/target.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,8 @@ TypeTuple *Target::toArgTypes(Type *t) {
258258
return toArgTypes_sysv_x64(t);
259259
if (arch == llvm::Triple::aarch64 || arch == llvm::Triple::aarch64_be)
260260
return toArgTypes_aarch64(t);
261+
if (arch == llvm::Triple::systemz)
262+
return toArgTypes_s390x(t);
261263
return nullptr;
262264
}
263265

runtime/druntime/src/core/internal/vararg/s390x.d

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ T va_arg(T)(va_list ap)
2626
{
2727
static if (is(T U == __argTypes))
2828
{
29-
static if (U.length == 0 || U[0].sizeof > 8 || is(T1 == __vector))
29+
static if (U.length == 0 || U[0].sizeof > 8 || is(U[0] == __vector))
3030
{
3131
// Always passed in memory (varying vectors are passed in parameter area)
3232
auto p = *cast(T*) ap.__overflow_arg_area;
@@ -45,7 +45,7 @@ T va_arg(T)(va_list ap)
4545
// Passed in $fr registers (FPR region starts at +0x80)
4646
auto p = cast(T*) ap.__reg_save_area + 128 + ap.__fpr * 8;
4747
ap.__fpr++;
48-
return p;
48+
return *p;
4949
}
5050
else
5151
{
@@ -54,7 +54,7 @@ T va_arg(T)(va_list ap)
5454
// no matter the actual size of the fp variable
5555
// parameter slot is always 8-byte-wide (f32 is extended to f64)
5656
ap.__overflow_arg_area += 8;
57-
return p;
57+
return *p;
5858
}
5959
}
6060
else
@@ -65,7 +65,7 @@ T va_arg(T)(va_list ap)
6565
// Passed in $gpr registers (GPR region starts at +0x10)
6666
auto p = cast(T*) ap.__reg_save_area + 16 + ap.__gpr * 8;
6767
ap.__gpr++;
68-
return p;
68+
return *p;
6969
}
7070
else
7171
{
@@ -74,7 +74,7 @@ T va_arg(T)(va_list ap)
7474
// no matter the actual size of the gpr variable
7575
// parameter slot is always 8-byte-wide (after ABI adjustments)
7676
ap.__overflow_arg_area += 8;
77-
return p;
77+
return *p;
7878
}
7979
}
8080
}
@@ -93,6 +93,23 @@ T va_arg(T)(va_list ap)
9393
void va_arg()(va_list ap, TypeInfo ti, void* parmn)
9494
{
9595
TypeInfo arg1, arg2;
96+
if (TypeInfo_Struct ti_struct = cast(TypeInfo_Struct) ti)
97+
{
98+
// handle single-float element struct
99+
const rtFields = ti_struct.offTi();
100+
if (rtFields && rtFields.length == 1)
101+
{
102+
TypeInfo field1TypeInfo = rtFields[0].ti;
103+
if (field1TypeInfo is typeid(float) || field1TypeInfo is typeid(double))
104+
{
105+
auto tsize = field1TypeInfo.tsize;
106+
auto toffset = rtFields[0].offset;
107+
parmn[0..tsize] = p[toffset..tsize];
108+
return;
109+
}
110+
}
111+
}
112+
96113
if (!ti.argTypes(arg1, arg2))
97114
{
98115
TypeInfo_Vector v1 = arg1 ? cast(TypeInfo_Vector) arg1 : null;
@@ -117,6 +134,30 @@ void va_arg()(va_list ap, TypeInfo ti, void* parmn)
117134
parmn[0..tsize] = p[0..tsize];
118135
}
119136
}
137+
else if (arg1 && (arg1 is typeid(float) || arg1 is typeid(double)))
138+
{
139+
// Maybe passed in $fr registers
140+
if (ap.__fpr <= 4)
141+
{
142+
// Passed in $fr registers (FPR region starts at +0x80)
143+
auto p = cast(T*) ap.__reg_save_area + 128 + ap.__fpr * 8;
144+
ap.__fpr++;
145+
parmn[0..tsize] = p[0..tsize];
146+
}
147+
else
148+
{
149+
// overflow arguments
150+
auto p = cast(T*) ap.__overflow_arg_area;
151+
// no matter the actual size of the fp variable
152+
// parameter slot is always 8-byte-wide (f32 is extended to f64)
153+
ap.__overflow_arg_area += 8;
154+
parmn[0..tsize] = p[0..tsize];
155+
}
156+
}
157+
else
158+
{
159+
assert(false, "unhandled va_arg type!");
160+
}
120161
assert(!arg2);
121162
}
122163
else

0 commit comments

Comments
 (0)