Skip to content

Commit 07c9c8f

Browse files
committed
gen/ir: add support for s390x special va_arg type
1 parent 4c14626 commit 07c9c8f

File tree

7 files changed

+237
-1
lines changed

7 files changed

+237
-1
lines changed

gen/abi/systemz.cpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
// https://github.com/IBM/s390x-abi
1515
//===----------------------------------------------------------------------===//
1616

17+
#include "dmd/identifier.h"
18+
#include "dmd/nspace.h"
1719
#include "gen/abi/abi.h"
1820
#include "gen/abi/generic.h"
1921
#include "gen/dvalue.h"
@@ -26,6 +28,20 @@ struct SystemZTargetABI : TargetABI {
2628

2729
explicit SystemZTargetABI() {}
2830

31+
bool isSystemZVaList(Type *t) {
32+
// look for a __va_list struct in a `std` C++ namespace
33+
if (auto ts = t->isTypeStruct()) {
34+
auto sd = ts->sym;
35+
if (strcmp(sd->ident->toChars(), "__va_list") == 0) {
36+
if (auto ns = sd->parent->isNspace()) {
37+
return strcmp(ns->toChars(), "std") == 0;
38+
}
39+
}
40+
}
41+
42+
return false;
43+
}
44+
2945
bool returnInArg(TypeFunction *tf, bool) override {
3046
if (tf->isref()) {
3147
return false;
@@ -57,12 +73,75 @@ struct SystemZTargetABI : TargetABI {
5773
return;
5874
}
5975
Type *ty = arg.type->toBasetype();
76+
// compiler magic: pass va_list args implicitly by reference
77+
if (isSystemZVaList(ty)) {
78+
arg.byref = true;
79+
arg.ltype = arg.ltype->getPointerTo();
80+
return;
81+
}
6082
// integer types less than 64-bits should be extended to 64 bits
6183
if (ty->isintegral()) {
6284
arg.attrs.addAttribute(ty->isunsigned() ? LLAttribute::ZExt
6385
: LLAttribute::SExt);
6486
}
6587
}
88+
89+
Type *vaListType() override {
90+
// We need to pass the actual va_list type for correct mangling. Simply
91+
// using TypeIdentifier here is a bit wonky but works, as long as the name
92+
// is actually available in the scope (this is what DMD does, so if a
93+
// better solution is found there, this should be adapted).
94+
return dmd::pointerTo(
95+
TypeIdentifier::create(Loc(), Identifier::idPool("__va_list_tag")));
96+
}
97+
98+
/**
99+
* The SystemZ ABI (like AMD64) uses a special native va_list type -
100+
* a 32-bytes struct passed by reference.
101+
* In druntime, the struct is aliased as object.__va_list_tag; the actually
102+
* used core.stdc.stdarg.va_list type is a __va_list_tag* pointer though to
103+
* achieve byref semantics. This requires a little bit of compiler magic in
104+
* the following implementations.
105+
*/
106+
107+
LLType *getValistType() {
108+
LLType *longType = LLType::getInt64Ty(gIR->context());
109+
LLType *pointerType = getOpaquePtrType();
110+
111+
std::vector<LLType *> parts; // struct __va_list_tag {
112+
parts.push_back(longType); // long __gpr;
113+
parts.push_back(longType); // long __fpr;
114+
parts.push_back(pointerType); // void *__overflow_arg_area;
115+
parts.push_back(pointerType); // void *__reg_save_area; }
116+
117+
return LLStructType::get(gIR->context(), parts);
118+
}
119+
120+
LLValue *prepareVaStart(DLValue *ap) override {
121+
// Since the user only created a __va_list_tag* pointer (ap) on the stack
122+
// before invoking va_start, we first need to allocate the actual
123+
// __va_list_tag struct and set `ap` to its address.
124+
LLValue *valistmem = DtoRawAlloca(getValistType(), 0, "__va_list_mem");
125+
DtoStore(valistmem, DtoLVal(ap));
126+
// Pass an opaque pointer to the actual struct to LLVM's va_start intrinsic.
127+
return valistmem;
128+
}
129+
130+
void vaCopy(DLValue *dest, DValue *src) override {
131+
// Analog to va_start, we first need to allocate a new __va_list_tag struct
132+
// on the stack and set `dest` to its address.
133+
LLValue *valistmem = DtoRawAlloca(getValistType(), 0, "__va_list_mem");
134+
DtoStore(valistmem, DtoLVal(dest));
135+
// Then fill the new struct with a bitcopy of the source struct.
136+
// `src` is a __va_list_tag* pointer to the source struct.
137+
DtoMemCpy(getValistType(), valistmem, DtoRVal(src));
138+
}
139+
140+
LLValue *prepareVaArg(DLValue *ap) override {
141+
// Pass an opaque pointer to the actual __va_list_tag struct to LLVM's
142+
// va_arg intrinsic.
143+
return DtoRVal(ap);
144+
}
66145
};
67146

68147
// The public getter for abi.cpp

ir/irstruct.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ LLConstant *IrStruct::getTypeInfoInit() {
107107
const bool withArgTypes =
108108
(arch == llvm::Triple::x86_64 && !triple.isOSWindows()) ||
109109
(!triple.isOSDarwin() && // Apple uses a simpler scheme
110-
(arch == llvm::Triple::aarch64 || arch == llvm::Triple::aarch64_be));
110+
(arch == llvm::Triple::aarch64 || arch == llvm::Triple::aarch64_be)) ||
111+
(arch == llvm::Triple::systemz);
111112
const unsigned expectedFields = 11 + (withArgTypes ? 2 : 0);
112113
const unsigned actualFields =
113114
structTypeInfoDecl->fields.length -

runtime/druntime/src/__importc_builtins.di

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ version (LDC)
5151
else version (AArch64)
5252
public import core.internal.vararg.aarch64 : __va_list;
5353
}
54+
else version (SystemZ)
55+
{
56+
public import core.internal.vararg.s390x : __va_list;
57+
}
5458
}
5559
else version (Posix)
5660
{
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
module core.internal.vararg.s390x;
2+
3+
version (SystemZ) : import core.stdc.stdarg : alignUp;
4+
5+
nothrow:
6+
7+
// Layout of this struct must match __gnuc_va_list for C ABI compatibility
8+
struct __va_list_tag
9+
{
10+
long __gpr = 0; // no regs
11+
long __fpr = 0; // no fp regs
12+
void* __overflow_arg_area;
13+
void* __reg_save_area;
14+
}
15+
16+
alias __va_list = __va_list_tag;
17+
18+
/**
19+
* Making it an array of 1 causes va_list to be passed as a pointer in
20+
* function argument lists
21+
*/
22+
alias va_list = __va_list*;
23+
24+
/// Compile-time `va_arg` extraction for s390x
25+
T va_arg(T)(va_list ap)
26+
{
27+
static if (is(T U == __argTypes))
28+
{
29+
static if (U.length == 0 || U[0].sizeof > 8 || is(T1 == __vector))
30+
{
31+
// Always passed in memory (varying vectors are passed in parameter area)
32+
auto p = *cast(T*) ap.__overflow_arg_area;
33+
ap.__overflow_arg_area = p + T.alignof.alignUp;
34+
return p;
35+
}
36+
else static if (U.length == 1)
37+
{
38+
// Arg is passed in one register
39+
alias T1 = U[0];
40+
static if (is(T1 == double) || is(T1 == float))
41+
{
42+
// Maybe passed in $fr registers
43+
if (ap.__fpr <= 4)
44+
{
45+
// Passed in $fr registers (FPR region starts at +0x80)
46+
auto p = cast(T*) ap.__reg_save_area + 128 + ap.__fpr * 8;
47+
ap.__fpr++;
48+
return p;
49+
}
50+
else
51+
{
52+
// overflow arguments
53+
auto p = cast(T*) ap.__overflow_arg_area;
54+
// no matter the actual size of the fp variable
55+
// parameter slot is always 8-byte-wide (f32 is extended to f64)
56+
ap.__overflow_arg_area += 8;
57+
return p;
58+
}
59+
}
60+
else
61+
{
62+
// Maybe passed in $r (GPR) registers
63+
if (ap.__gpr <= 5)
64+
{
65+
// Passed in $gpr registers (GPR region starts at +0x10)
66+
auto p = cast(T*) ap.__reg_save_area + 16 + ap.__gpr * 8;
67+
ap.__gpr++;
68+
return p;
69+
}
70+
else
71+
{
72+
// overflow arguments
73+
auto p = cast(T*) ap.__overflow_arg_area;
74+
// no matter the actual size of the gpr variable
75+
// parameter slot is always 8-byte-wide (after ABI adjustments)
76+
ap.__overflow_arg_area += 8;
77+
return p;
78+
}
79+
}
80+
}
81+
else
82+
{
83+
static assert(false);
84+
}
85+
}
86+
else
87+
{
88+
static assert(false, "not a valid argument type for va_arg");
89+
}
90+
}
91+
92+
/// Runtime `va_arg` extraction for s390x
93+
void va_arg()(va_list ap, TypeInfo ti, void* parmn)
94+
{
95+
TypeInfo arg1, arg2;
96+
if (!ti.argTypes(arg1, arg2))
97+
{
98+
TypeInfo_Vector v1 = arg1 ? cast(TypeInfo_Vector) arg1 : null;
99+
if (arg1 && (arg1.tsize <= 8 && !v1))
100+
{
101+
auto tsize = arg1.tsize;
102+
// Maybe passed in $r (GPR) registers
103+
if (ap.__gpr <= 5)
104+
{
105+
// Passed in $gpr registers (GPR region starts at +0x10)
106+
auto p = cast(T*) ap.__reg_save_area + 16 + ap.__gpr * 8;
107+
ap.__gpr++;
108+
parmn[0..tsize] = p[0..tsize];
109+
}
110+
else
111+
{
112+
// overflow arguments
113+
auto p = cast(T*) ap.__overflow_arg_area;
114+
// no matter the actual size of the gpr variable
115+
// parameter slot is always 8-byte-wide (after ABI adjustments)
116+
ap.__overflow_arg_area += 8;
117+
parmn[0..tsize] = p[0..tsize];
118+
}
119+
}
120+
assert(!arg2);
121+
}
122+
else
123+
{
124+
assert(false, "not a valid argument type for va_arg");
125+
}
126+
}

runtime/druntime/src/core/stdc/stdarg.d

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ else version (ARM_Any)
7272
static import core.internal.vararg.aarch64;
7373
}
7474
}
75+
else version (SystemZ)
76+
{
77+
static import core.internal.vararg.s390x;
78+
}
7579

7680

7781
T alignUp(size_t alignment = size_t.sizeof, T)(T base) pure
@@ -137,6 +141,11 @@ else version (RISCV_Any)
137141
// https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc
138142
alias va_list = void*;
139143
}
144+
else version (SystemZ)
145+
{
146+
alias va_list = core.internal.vararg.s390x.va_list;
147+
public import core.internal.vararg.s390x : __va_list, __va_list_tag;
148+
}
140149
else
141150
{
142151
alias va_list = char*; // incl. unknown platforms
@@ -285,6 +294,10 @@ T va_arg(T)(ref va_list ap)
285294
ap += T.sizeof.alignUp;
286295
return *p;
287296
}
297+
else version (SystemZ)
298+
{
299+
return core.internal.vararg.s390x.va_arg!T(ap);
300+
}
288301
else
289302
static assert(0, "Unsupported platform");
290303
}

runtime/druntime/src/core/vararg.d

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@ void va_arg()(ref va_list ap, TypeInfo ti, void* parmn)
118118
ap += tsize.alignUp;
119119
parmn[0..tsize] = p[0..tsize];
120120
}
121+
else version (SystemZ)
122+
{
123+
static import core.internal.vararg.s390x;
124+
core.internal.vararg.s390x.va_arg(ap, ti, parmn);
125+
}
121126
else version (PPC_Any)
122127
{
123128
if (ti.talign >= 8)

runtime/druntime/src/object.d

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ version (LDC) // note: there's a copy for importC in __importc_builtins.di
103103
else version (AArch64)
104104
public import core.internal.vararg.aarch64 : __va_list;
105105
}
106+
else version (SystemZ)
107+
{
108+
public import core.internal.vararg.s390x : __va_list;
109+
}
106110
}
107111

108112
version (D_ObjectiveC)
@@ -132,6 +136,10 @@ else version (AArch64)
132136
else version (WatchOS) {}
133137
else version = WithArgTypes;
134138
}
139+
else version (SystemZ)
140+
{
141+
version = WithArgTypes;
142+
}
135143

136144
/**
137145
* All D class objects inherit from Object.

0 commit comments

Comments
 (0)