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
0 commit comments