[SystemZ][Clang] Add support for the z/OS va_list type (#202397)
On z/OS, the user has a choice between two different va_list formats.
1. (default) uses the __builtin_zos_va_list typedef for va_list, being
added in this change
2. (-D_VARARG_EXT_) uses __builtin_va_list typedef for va_list.
The __builtin_zos_va_list type is `char * [2]`.
This PR adds this type along with the __builtin_zos_va_start(),
__builtin_zos_va_end() & __builtin_zos_va_copy() functions to go with
it.
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index c04b380..7ed6509 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -439,6 +439,9 @@
/// The typedef for the predefined \c __builtin_ms_va_list type.
mutable TypedefDecl *BuiltinMSVaListDecl = nullptr;
+ /// The typedef for the predefined \c __builtin_zos_va_list type.
+ mutable TypedefDecl *BuiltinZOSVaListDecl = nullptr;
+
/// The typedef for the predefined \c id type.
mutable TypedefDecl *ObjCIdDecl = nullptr;
@@ -2531,6 +2534,17 @@
return MSTypeInfoTagDecl;
}
+ /// Retrieve the C type declaration corresponding to the predefined
+ /// \c __builtin_zos_va_list type.
+ TypedefDecl *getBuiltinZOSVaListDecl() const;
+
+ /// Retrieve the type of the \c __builtin_zos_va_list type.
+ QualType getBuiltinZOSVaListType() const {
+ return getTypedefType(ElaboratedTypeKeyword::None,
+ /*Qualifier=*/std::nullopt,
+ getBuiltinZOSVaListDecl());
+ }
+
/// Return whether a declaration to a builtin is allowed to be
/// overloaded/redeclared.
bool canBuiltinBeRedeclared(const FunctionDecl *) const;
diff --git a/clang/include/clang/AST/DeclID.h b/clang/include/clang/AST/DeclID.h
index dab8ddc..8827ba9 100644
--- a/clang/include/clang/AST/DeclID.h
+++ b/clang/include/clang/AST/DeclID.h
@@ -80,6 +80,9 @@
/// The predeclared 'type_info' struct.
PREDEF_DECL_BUILTIN_MS_TYPE_INFO_TAG_ID,
+ /// The internal '__builtin_zos_va_list' typedef.
+ PREDEF_DECL_BUILTIN_ZOS_VA_LIST_ID,
+
#define BuiltinTemplate(BTName) PREDEF_DECL##BTName##_ID,
#include "clang/Basic/BuiltinTemplates.inc"
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index b016126..53107e2 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -4961,28 +4961,37 @@
/// Represents a call to the builtin function \c __builtin_va_arg.
class VAArgExpr : public Expr {
+public:
+ enum VarArgKind { VA_Std = 0, VA_MS, VA_ZOS };
+
+private:
Stmt *Val;
- llvm::PointerIntPair<TypeSourceInfo *, 1, bool> TInfo;
+ llvm::PointerIntPair<TypeSourceInfo *, 2, VarArgKind> TInfo;
SourceLocation BuiltinLoc, RParenLoc;
public:
VAArgExpr(SourceLocation BLoc, Expr *e, TypeSourceInfo *TInfo,
- SourceLocation RPLoc, QualType t, bool IsMS)
+ SourceLocation RPLoc, QualType t, VarArgKind VaKind)
: Expr(VAArgExprClass, t, VK_PRValue, OK_Ordinary), Val(e),
- TInfo(TInfo, IsMS), BuiltinLoc(BLoc), RParenLoc(RPLoc) {
+ TInfo(TInfo, VaKind), BuiltinLoc(BLoc), RParenLoc(RPLoc) {
setDependence(computeDependence(this));
}
/// Create an empty __builtin_va_arg expression.
explicit VAArgExpr(EmptyShell Empty)
- : Expr(VAArgExprClass, Empty), Val(nullptr), TInfo(nullptr, false) {}
+ : Expr(VAArgExprClass, Empty), Val(nullptr), TInfo(nullptr, VA_Std) {}
const Expr *getSubExpr() const { return cast<Expr>(Val); }
Expr *getSubExpr() { return cast<Expr>(Val); }
void setSubExpr(Expr *E) { Val = E; }
+ VarArgKind getVarargABI() const { return TInfo.getInt(); }
+ void setVarargABI(VarArgKind Kind) { TInfo.setInt(Kind); }
+
/// Returns whether this is really a Win64 ABI va_arg expression.
- bool isMicrosoftABI() const { return TInfo.getInt(); }
- void setIsMicrosoftABI(bool IsMS) { TInfo.setInt(IsMS); }
+ bool isMicrosoftABI() const { return TInfo.getInt() == VA_MS; }
+
+ /// Returns whether this is really a z/OS ABI va_arg expression.
+ bool isZOSABI() const { return TInfo.getInt() == VA_ZOS; }
TypeSourceInfo *getWrittenTypeInfo() const { return TInfo.getPointer(); }
void setWrittenTypeInfo(TypeSourceInfo *TI) { TInfo.setPointer(TI); }
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 3f8096e..344a712 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5918,6 +5918,25 @@
let Prototype = "void(char*&, char*&)";
}
+// z/OS-compatible va_list functions.
+def ZOSVaStart : Builtin {
+ let Spellings = ["__builtin_zos_va_start"];
+ let Attributes = [NoThrow, CustomTypeChecking];
+ let Prototype = "void(char**, ...)";
+}
+
+def ZOSVaEnd : Builtin {
+ let Spellings = ["__builtin_zos_va_end"];
+ let Attributes = [NoThrow];
+ let Prototype = "void(char**)";
+}
+
+def ZOSVaCopy : Builtin {
+ let Spellings = ["__builtin_zos_va_copy"];
+ let Attributes = [NoThrow];
+ let Prototype = "void(char**, char**)";
+}
+
// Arithmetic Fence: to prevent FP reordering and reassociation optimizations
// FIXME: Should this just be a Builtin?
def ArithmeticFence : LangBuiltin<"ALL_LANGUAGES"> {
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index 23b2b5e..3aba4d2 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -273,6 +273,9 @@
unsigned HasBuiltinMSVaList : 1;
LLVM_PREFERRED_TYPE(bool)
+ unsigned HasBuiltinZOSVaList : 1;
+
+ LLVM_PREFERRED_TYPE(bool)
unsigned HasAArch64ACLETypes : 1;
LLVM_PREFERRED_TYPE(bool)
@@ -1062,6 +1065,10 @@
/// available on this target.
bool hasBuiltinMSVaList() const { return HasBuiltinMSVaList; }
+ /// Returns whether or not type \c __builtin_zos_va_list type is
+ /// available on this target.
+ bool hasBuiltinZOSVaList() const { return HasBuiltinZOSVaList; }
+
/// Returns whether or not the AArch64 ACLE built-in types are
/// available on this target.
bool hasAArch64ACLETypes() const { return HasAArch64ACLETypes; }
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 94ea9ae..2228811 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -10014,6 +10014,15 @@
return CreateCharPtrNamedVaListDecl(Context, "__builtin_ms_va_list");
}
+static TypedefDecl *CreateZOSVaListDecl(const ASTContext *Context) {
+ // typedef char *__builtin_zos_va_list[2];
+ llvm::APInt Size(Context->getTypeSize(Context->getSizeType()), 2);
+ QualType T = Context->getPointerType(Context->CharTy);
+ QualType ArrayType = Context->getConstantArrayType(
+ T, Size, nullptr, ArraySizeModifier::Normal, 0);
+ return Context->buildImplicitTypedef(ArrayType, "__builtin_zos_va_list");
+}
+
static TypedefDecl *CreateCharPtrBuiltinVaListDecl(const ASTContext *Context) {
return CreateCharPtrNamedVaListDecl(Context, "__builtin_va_list");
}
@@ -10441,6 +10450,13 @@
return BuiltinMSVaListDecl;
}
+TypedefDecl *ASTContext::getBuiltinZOSVaListDecl() const {
+ if (!BuiltinZOSVaListDecl)
+ BuiltinZOSVaListDecl = CreateZOSVaListDecl(this);
+
+ return BuiltinZOSVaListDecl;
+}
+
bool ASTContext::canBuiltinBeRedeclared(const FunctionDecl *FD) const {
// Allow redecl custom type checking builtin for HLSL.
if (LangOpts.HLSL && FD->getBuiltinID() != Builtin::NotBuiltin &&
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 567d2d0..64e9b7a 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -7650,9 +7650,9 @@
if (Err)
return std::move(Err);
- return new (Importer.getToContext()) VAArgExpr(
- ToBuiltinLoc, ToSubExpr, ToWrittenTypeInfo, ToRParenLoc, ToType,
- E->isMicrosoftABI());
+ return new (Importer.getToContext())
+ VAArgExpr(ToBuiltinLoc, ToSubExpr, ToWrittenTypeInfo, ToRParenLoc, ToType,
+ E->getVarargABI());
}
ExpectedStmt ASTNodeImporter::VisitChooseExpr(ChooseExpr *E) {
diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp
index df7b238..46b5bde 100644
--- a/clang/lib/Basic/TargetInfo.cpp
+++ b/clang/lib/Basic/TargetInfo.cpp
@@ -164,6 +164,7 @@
SSERegParmMax = 0;
HasAlignMac68kSupport = false;
HasBuiltinMSVaList = false;
+ HasBuiltinZOSVaList = false;
HasAArch64ACLETypes = false;
HasRISCVVTypes = false;
AllowAMDGPUUnsafeFPAtomics = false;
diff --git a/clang/lib/Basic/Targets/SystemZ.h b/clang/lib/Basic/Targets/SystemZ.h
index d56306c..c06b142 100644
--- a/clang/lib/Basic/Targets/SystemZ.h
+++ b/clang/lib/Basic/Targets/SystemZ.h
@@ -83,6 +83,8 @@
AddrSpaceMap = &ZOSAddressMap;
}
TLSSupported = false;
+ HasBuiltinZOSVaList = true;
+
// All vector types are default aligned on an 8-byte boundary, even if the
// vector facility is not available. That is different from Linux.
MaxVectorAlign = 64;
@@ -164,6 +166,8 @@
}
BuiltinVaListKind getBuiltinVaListKind() const override {
+ if (getTriple().isOSzOS())
+ return TargetInfo::CharPtrBuiltinVaList;
return TargetInfo::SystemZBuiltinVaList;
}
diff --git a/clang/lib/CodeGen/ABIInfo.cpp b/clang/lib/CodeGen/ABIInfo.cpp
index 1600589..eede3b54 100644
--- a/clang/lib/CodeGen/ABIInfo.cpp
+++ b/clang/lib/CodeGen/ABIInfo.cpp
@@ -44,6 +44,11 @@
return RValue::getIgnored();
}
+RValue ABIInfo::EmitZOSVAArg(CodeGenFunction &CGF, Address VAListAddr,
+ QualType Ty, AggValueSlot Slot) const {
+ return RValue::getIgnored();
+}
+
bool ABIInfo::isHomogeneousAggregateBaseType(QualType Ty) const {
return false;
}
diff --git a/clang/lib/CodeGen/ABIInfo.h b/clang/lib/CodeGen/ABIInfo.h
index 576cf8f..4563154 100644
--- a/clang/lib/CodeGen/ABIInfo.h
+++ b/clang/lib/CodeGen/ABIInfo.h
@@ -91,6 +91,12 @@
CodeGen::Address VAListAddr, QualType Ty,
AggValueSlot Slot) const;
+ /// Emit the target dependent code to load a value of
+ /// \arg Ty from the \c __builtin_zos_va_list pointed to by \arg VAListAddr.
+ virtual RValue EmitZOSVAArg(CodeGen::CodeGenFunction &CGF,
+ CodeGen::Address VAListAddr, QualType Ty,
+ AggValueSlot Slot) const;
+
virtual bool isHomogeneousAggregateBaseType(QualType Ty) const;
virtual bool isHomogeneousAggregateSmallEnough(const Type *Base,
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 8691cc1..a41ac70 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -6983,6 +6983,34 @@
return RValue::get(Builder.CreateStore(ArgPtr, DestAddr));
}
+ case Builtin::BI__builtin_zos_va_start:
+ case Builtin::BI__builtin_zos_va_end: {
+ // The va_list is an array with 2 elements, called curr and next.
+ // Element curr is set to 0. For builtin_zos_va_start, next is initialized
+ // with a call to @llvm.va_start. Otherwise, next is passed to @llvm.va_end.
+ Address VAList = EmitZOSVAListRef(E->getArg(0));
+ llvm::Type *VAListTy = ConvertType(getContext().getBuiltinZOSVaListType());
+ VAList = VAList.withElementType(VAListTy);
+ Address Curr = Builder.CreateConstArrayGEP(VAList, 0, "curr");
+ Value *Zero = llvm::Constant::getNullValue(VoidPtrTy);
+ Builder.CreateStore(Zero, Curr);
+ Address Next = Builder.CreateConstArrayGEP(VAList, 1, "next");
+ return RValue::get(
+ EmitVAStartEnd(Next.emitRawPointer(*this),
+ BuiltinID == Builtin::BI__builtin_zos_va_start));
+ }
+ case Builtin::BI__builtin_zos_va_copy: {
+ // Lower this manually because later can't reliably determine the type.
+ Address Dest = EmitZOSVAListRef(E->getArg(0));
+ Address Src = EmitZOSVAListRef(E->getArg(1));
+ llvm::Type *VAListTy = ConvertType(getContext().getBuiltinZOSVaListType());
+ uint64_t SizeBytes =
+ CGM.getDataLayout().getTypeAllocSize(VAListTy).getFixedValue();
+ Value *SizeVal = llvm::ConstantInt::get(Int64Ty, SizeBytes);
+ Builder.CreateMemCpy(Dest, Src, SizeVal, false);
+ return RValue::get(Dest.emitRawPointer(*this));
+ }
+
case Builtin::BI__builtin_get_device_side_mangled_name: {
auto Name = CGM.getCUDARuntime().getDeviceSideName(
cast<DeclRefExpr>(E->getArg(0)->IgnoreImpCasts())->getDecl());
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index afec6f8..08cb986 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -6703,13 +6703,17 @@
RValue CodeGenFunction::EmitVAArg(VAArgExpr *VE, Address &VAListAddr,
AggValueSlot Slot) {
- VAListAddr = VE->isMicrosoftABI() ? EmitMSVAListRef(VE->getSubExpr())
- : EmitVAListRef(VE->getSubExpr());
+ VAListAddr = VE->isMicrosoftABI()
+ ? EmitMSVAListRef(VE->getSubExpr())
+ : (VE->isZOSABI() ? EmitZOSVAListRef(VE->getSubExpr())
+ : EmitVAListRef(VE->getSubExpr()));
QualType Ty = VE->getType();
if (Ty->isVariablyModifiedType())
EmitVariablyModifiedType(Ty);
if (VE->isMicrosoftABI())
return CGM.getABIInfo().EmitMSVAArg(*this, VAListAddr, Ty, Slot);
+ if (VE->isZOSABI())
+ return CGM.getABIInfo().EmitZOSVAArg(*this, VAListAddr, Ty, Slot);
return CGM.getABIInfo().EmitVAArg(*this, VAListAddr, Ty, Slot);
}
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index b920266..80c5268 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -2700,6 +2700,10 @@
return EmitLValue(E).getAddress();
}
+Address CodeGenFunction::EmitZOSVAListRef(const Expr *E) {
+ return EmitPointerWithAlignment(E);
+}
+
void CodeGenFunction::EmitDeclRefExprDbgValue(const DeclRefExpr *E,
const APValue &Init) {
assert(Init.hasValue() && "Invalid DeclRefExpr initializer!");
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 6d0718c..190c177 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3011,6 +3011,11 @@
/// pointer to a char.
Address EmitMSVAListRef(const Expr *E);
+ /// Emit a "reference" to a __builtin_zos_va_list; this is always the
+ /// address of the expression, because a __builtin_zos_va_list is an
+ /// array of pointer to a char.
+ Address EmitZOSVAListRef(const Expr *E);
+
/// EmitAnyExprToTemp - Similarly to EmitAnyExpr(), however, the result will
/// always be accessible even if no aggregate location is provided.
RValue EmitAnyExprToTemp(const Expr *E);
diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp b/clang/lib/CodeGen/Targets/SystemZ.cpp
index 37f6b30..a27fee6 100644
--- a/clang/lib/CodeGen/Targets/SystemZ.cpp
+++ b/clang/lib/CodeGen/Targets/SystemZ.cpp
@@ -586,6 +586,9 @@
RValue EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty,
AggValueSlot Slot) const override;
+
+ RValue EmitZOSVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty,
+ AggValueSlot Slot) const override;
};
class ZOSXPLinkTargetCodeGenInfo : public TargetCodeGenInfo {
@@ -907,6 +910,45 @@
/*allowHigherAlign*/ false, Slot);
}
+RValue ZOSXPLinkABIInfo::EmitZOSVAArg(CodeGenFunction &CGF, Address VAListAddr,
+ QualType ArgTy, AggValueSlot Slot) const {
+ // Assume that va_list type is correct; should be pointer
+ // to LLVM type: [2 x i8*].
+ llvm::Type *VAListTy =
+ CGF.ConvertType(getContext().getBuiltinZOSVaListType());
+ VAListAddr = VAListAddr.withElementType(VAListTy);
+ Address Curr = CGF.Builder.CreateConstArrayGEP(VAListAddr, 0, "va_list.curr");
+ Address Next = CGF.Builder.CreateConstArrayGEP(VAListAddr, 1, "va_list.next");
+
+ // Get information about the argument type.
+ auto ArgTyInfo = CGF.getContext().getTypeInfoInChars(ArgTy);
+ CharUnits ArgTySize = ArgTyInfo.Width;
+
+ llvm::Type *Ty = CGF.ConvertTypeForMem(ArgTy);
+
+ // Slot size is the same as the size of a pointer.
+ CharUnits SlotSize = CGF.getPointerSize();
+
+ // Align next and copy to curr.
+ CharUnits PtrAlign = CGF.getPointerAlign();
+ llvm::Value *OldNext = CGF.Builder.CreateLoad(Next, "arg.next");
+ Address Addr = Address(emitRoundPointerUpToAlignment(CGF, OldNext, PtrAlign),
+ CGF.Int8Ty, PtrAlign);
+ CGF.Builder.CreateStore(Addr.emitRawPointer(CGF), Curr);
+
+ // Advance next and store.
+ Address NextPtr = CGF.Builder.CreateConstInBoundsByteGEP(
+ Addr, ArgTySize.isZero() ? SlotSize : ArgTySize, "arg.next.next");
+ CGF.Builder.CreateStore(NextPtr.emitRawPointer(CGF), Next);
+
+ // Fetch next argument.
+ if (ArgTySize < SlotSize && !isAggregateTypeForABI(ArgTy))
+ Addr = CGF.Builder.CreateConstInBoundsByteGEP(Addr, SlotSize - ArgTySize);
+
+ return CGF.EmitLoadOfAnyValue(
+ CGF.MakeAddrLValue(Addr.withElementType(Ty), ArgTy), Slot);
+}
+
std::unique_ptr<TargetCodeGenInfo>
CodeGen::createSystemZTargetCodeGenInfo(CodeGenModule &CGM, bool HasVector,
bool SoftFloatABI) {
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 78fbc9e..ad24957 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -375,6 +375,8 @@
// name mangling. And the name mangling uses BuiltinVaListDecl.
if (Context.getTargetInfo().hasBuiltinMSVaList())
(void)Context.getBuiltinMSVaListDecl();
+ if (Context.getTargetInfo().hasBuiltinZOSVaList())
+ (void)Context.getBuiltinZOSVaListDecl();
(void)Context.getBuiltinVaListDecl();
if (SemaConsumer *SC = dyn_cast<SemaConsumer>(&Consumer))
@@ -586,6 +588,12 @@
PushOnScopeChains(Context.getBuiltinMSVaListDecl(), TUScope);
}
+ if (Context.getTargetInfo().hasBuiltinZOSVaList()) {
+ DeclarationName ZOSVaList = &Context.Idents.get("__builtin_zos_va_list");
+ if (IdResolver.begin(ZOSVaList) == IdResolver.end())
+ PushOnScopeChains(Context.getBuiltinZOSVaListDecl(), TUScope);
+ }
+
DeclarationName BuiltinVaList = &Context.Idents.get("__builtin_va_list");
if (IdResolver.begin(BuiltinVaList) == IdResolver.end())
PushOnScopeChains(Context.getBuiltinVaListDecl(), TUScope);
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 8537772..2a8395e 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -3033,6 +3033,7 @@
return ExprError();
break;
case Builtin::BI__builtin_ms_va_start:
+ case Builtin::BI__builtin_zos_va_start:
case Builtin::BI__builtin_stdarg_start:
case Builtin::BI__builtin_va_start:
case Builtin::BI__builtin_c23_va_start:
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index e5b1926..4e9c0ec 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -17285,7 +17285,7 @@
Expr *E, TypeSourceInfo *TInfo,
SourceLocation RPLoc) {
Expr *OrigExpr = E;
- bool IsMS = false;
+ VAArgExpr::VarArgKind VAKind = VAArgExpr::VA_Std;
// CUDA device global function does not support varargs.
if (getLangOpts().CUDA && getLangOpts().CUDAIsDevice) {
@@ -17310,13 +17310,31 @@
if (Context.hasSameType(MSVaListType, E->getType())) {
if (CheckForModifiableLvalue(E, BuiltinLoc, *this))
return ExprError();
- IsMS = true;
+ VAKind = VAArgExpr::VA_MS;
}
}
// Get the va_list type
QualType VaListType = Context.getBuiltinVaListType();
- if (!IsMS) {
+
+ // It might be a __builtin_zos_va_list!
+ if (!E->isTypeDependent() && Context.getTargetInfo().hasBuiltinZOSVaList()) {
+ // E->getType() can be:
+ // - va_list: equal to array (char*)[2] (inside function)
+ // - char **: decayed array (va_list passed as parameter)
+ // We need to check for both cases.
+ QualType ZOSVaListType = Context.getBuiltinZOSVaListType();
+ assert(ZOSVaListType->isArrayType() &&
+ "__builtin_zos_va_list must be an array type");
+ QualType DecayedType = Context.getArrayDecayedType(ZOSVaListType);
+ if (Context.hasSameType(ZOSVaListType, E->getType()) ||
+ Context.hasSameType(DecayedType, E->getType())) {
+ VAKind = VAArgExpr::VA_ZOS;
+ VaListType = ZOSVaListType;
+ }
+ }
+
+ if (VAKind != VAArgExpr::VA_MS) {
if (VaListType->isArrayType()) {
// Deal with implicit array decay; for example, on x86-64,
// va_list is an array, but it's supposed to decay to
@@ -17345,7 +17363,7 @@
}
}
- if (!IsMS && !E->isTypeDependent() &&
+ if ((VAKind != VAArgExpr::VA_MS) && !E->isTypeDependent() &&
!Context.hasSameType(VaListType, E->getType()))
return ExprError(
Diag(E->getBeginLoc(),
@@ -17440,7 +17458,7 @@
}
QualType T = TInfo->getType().getNonLValueExprType(Context);
- return new (Context) VAArgExpr(BuiltinLoc, E, TInfo, RPLoc, T, IsMS);
+ return new (Context) VAArgExpr(BuiltinLoc, E, TInfo, RPLoc, T, VAKind);
}
ExprResult Sema::ActOnGNUNullExpr(SourceLocation TokenLoc) {
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index de1287b..080cb7d 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -8527,6 +8527,12 @@
NewLoaded = Context.getBuiltinMSVaListDecl();
break;
+ case PREDEF_DECL_BUILTIN_ZOS_VA_LIST_ID:
+ if (Context.BuiltinZOSVaListDecl)
+ return Context.BuiltinZOSVaListDecl;
+ NewLoaded = Context.getBuiltinZOSVaListDecl();
+ break;
+
case PREDEF_DECL_BUILTIN_MS_GUID_ID:
// ASTContext::getMSGuidTagDecl won't create MSGuidTagDecl conditionally.
return Context.getMSGuidTagDecl();
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 02ccf8d..09e31a9 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1372,7 +1372,7 @@
E->setWrittenTypeInfo(readTypeSourceInfo());
E->setBuiltinLoc(readSourceLocation());
E->setRParenLoc(readSourceLocation());
- E->setIsMicrosoftABI(Record.readInt());
+ E->setVarargABI(static_cast<clang::VAArgExpr::VarArgKind>(Record.readInt()));
}
void ASTStmtReader::VisitSourceLocExpr(SourceLocExpr *E) {
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index ecf935e..b6b5768 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -5720,6 +5720,8 @@
RegisterPredefDecl(Context.VaListTagDecl, PREDEF_DECL_VA_LIST_TAG);
RegisterPredefDecl(Context.BuiltinMSVaListDecl,
PREDEF_DECL_BUILTIN_MS_VA_LIST_ID);
+ RegisterPredefDecl(Context.BuiltinZOSVaListDecl,
+ PREDEF_DECL_BUILTIN_ZOS_VA_LIST_ID);
RegisterPredefDecl(Context.MSGuidTagDecl,
PREDEF_DECL_BUILTIN_MS_GUID_ID);
RegisterPredefDecl(Context.MSTypeInfoTagDecl,
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 4e9eadd..c193e9d 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1324,7 +1324,7 @@
Record.AddTypeSourceInfo(E->getWrittenTypeInfo());
Record.AddSourceLocation(E->getBuiltinLoc());
Record.AddSourceLocation(E->getRParenLoc());
- Record.push_back(E->isMicrosoftABI());
+ Record.push_back(E->getVarargABI());
Code = serialization::EXPR_VA_ARG;
}
diff --git a/clang/test/CodeGen/SystemZ/zos-abi.c b/clang/test/CodeGen/SystemZ/zos-abi.c
index c5535cb..0a31b47 100644
--- a/clang/test/CodeGen/SystemZ/zos-abi.c
+++ b/clang/test/CodeGen/SystemZ/zos-abi.c
@@ -489,3 +489,157 @@
union tu_ptr { void *a; } __attribute__((transparent_union));
union tu_ptr pass_tu_ptr(union tu_ptr arg) { return arg; }
// CHECK-LABEL: define{{.*}} ptr @pass_tu_ptr(ptr %{{.*}})
+
+
+// =================================================================
+// Accessing variable argument lists
+// =================================================================
+
+// z/OS has two different implementations for variable argument handling.
+// Functions ending with _e test the extended variant of vararg functions
+// (__builtin_va_start, __builtin_va_arg, __builtin_va_end). The type of
+// va_list is __builtin_va_list.
+// Functions ending with _s test the standard variant of vararg functions
+// (__builtin_zos_va_start, __builtin_va_arg, __builtin_zos_va_end). The type of
+// va_list is __builtin_va_list.
+
+int dofmt_e(const char *fmt, ...) {
+ __builtin_va_list va;
+
+ __builtin_va_start(va, fmt);
+ int v = __builtin_va_arg(va, int);
+ __builtin_va_end(va);
+
+ return v;
+}
+// CHECK-LABEL: define signext i32 @dofmt_e(ptr %{{.*}}, ...)
+// CHECK: [[FMT_ADDR:%[._a-z0-9]+]] = alloca ptr, align 8
+// CHECK: [[VA:%[._a-z0-9]+]] = alloca ptr, align 8
+// CHECK: [[V:%[._a-z0-9]+]] = alloca i32, align 4
+// CHECK: store ptr %{{.*}}, ptr [[FMT_ADDR]], align 8
+// CHECK: call void @llvm.va_start.p0(ptr [[VA]])
+// CHECK: [[ARGP_CURR:%[._a-z0-9]+]] = load ptr, ptr [[VA]], align 8
+// CHECK: [[ARGP_NEXT:%[._a-z0-9]+]] = getelementptr inbounds i8, ptr [[ARGP_CURR]], i64 8
+// CHECK: store ptr [[ARGP_NEXT]], ptr [[VA]], align 8
+// CHECK: [[V_ADDR:%[._a-z0-9]+]] = getelementptr inbounds i8, ptr [[ARGP_CURR]], i64 4
+// CHECK: [[VAL:%[._a-z0-9]+]] = load i32, ptr [[V_ADDR]], align 4
+// CHECK: store i32 [[VAL]], ptr [[V]], align 4
+// CHECK: call void @llvm.va_end.p0(ptr [[VA]])
+// CHECK: [[VAL2:%[._a-z0-9]+]] = load i32, ptr [[V]], align 4
+// CHECK: ret i32 [[VAL2]]
+
+int dofmt_s(const char *fmt, ...) {
+ __builtin_zos_va_list va;
+
+ __builtin_zos_va_start(va, fmt);
+ int v = __builtin_va_arg(va, int);
+ __builtin_zos_va_end(va);
+
+ return v;
+}
+// CHECK-LABEL: define signext i32 @dofmt_s(ptr %{{.*}}, ...)
+// CHECK: [[FMT_ADDR:%[._a-z0-9]+]] = alloca ptr, align 8
+// CHECK: [[VA:%[._a-z0-9]+]] = alloca [2 x ptr], align 8
+// CHECK: [[V:%[._a-z0-9]+]] = alloca i32, align 4
+// CHECK: store ptr %{{.*}}, ptr [[FMT_ADDR]], align 8
+// CHECK: [[DECAY1:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[VA]], i64 0, i64 0
+// CHECK: [[VALIST_CURR1:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[DECAY1]], i64 0, i64 0
+// CHECK: store ptr null, ptr [[VALIST_CURR1]], align 8
+// CHECK: [[VALIST_NEXT1:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[DECAY1]], i64 0, i64 1
+// CHECK: call void @llvm.va_start.p0(ptr [[VALIST_NEXT1]])
+// CHECK: [[DECAY2:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[VA]], i64 0, i64 0
+// CHECK: [[VALIST_CURR2:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[DECAY2]], i64 0, i64 0
+// CHECK: [[VALIST_NEXT2:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[DECAY2]], i64 0, i64 1
+// CHECK: [[ARGP_NEXT:%[._a-z0-9]+]] = load ptr, ptr [[VALIST_NEXT2]], align 8
+// CHECK: %0 = getelementptr inbounds i8, ptr [[ARGP_NEXT]], i32 7
+// CHECK: [[ARGP_NEXT_ALIGNED:%[._a-z0-9]+]] = call ptr @llvm.ptrmask.p0.i64(ptr %0, i64 -8)
+// CHECK: store ptr [[ARGP_NEXT_ALIGNED]], ptr [[VALIST_CURR2]], align 8
+// CHECK: [[ARGP_NEXT_NEXT:%[._a-z0-9]+]] = getelementptr inbounds i8, ptr [[ARGP_NEXT_ALIGNED]], i64 4
+// CHECK: store ptr [[ARGP_NEXT_NEXT]], ptr [[VALIST_NEXT2]], align 8
+// CHECK: [[V_ADDR:%[._a-z0-9]+]] = getelementptr inbounds i8, ptr [[ARGP_NEXT_ALIGNED]], i64 4
+// CHECK: [[VAL:%[._a-z0-9]+]] = load i32, ptr [[V_ADDR]], align 4
+// CHECK: store i32 [[VAL]], ptr [[V]], align 4
+// CHECK: [[DECAY3:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[VA]], i64 0, i64 0
+// CHECK: [[VALIST_CURR3:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[DECAY3]], i64 0, i64 0
+// CHECK: store ptr null, ptr [[VALIST_CURR3]], align 8
+// CHECK: [[VALIST_NEXT3:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[DECAY3]], i64 0, i64 1
+// CHECK: call void @llvm.va_end.p0(ptr [[VALIST_NEXT3]])
+// CHECK: [[VAL2:%[._a-z0-9]+]] = load i32, ptr [[V]], align 4
+// CHECK: ret i32 [[VAL2]]
+
+int va_int_e(__builtin_va_list l) { return __builtin_va_arg(l, int); }
+// CHECK-LABEL: define signext i32 @va_int_e(ptr %{{.*}})
+// CHECK: [[L_ADDR:%[._a-z0-9]+]] = alloca ptr, align 8
+// CHECK: store ptr %{{.*}}, ptr [[L_ADDR]], align 8
+// CHECK: [[ARGP_CURR:%[._a-z0-9]+]] = load ptr, ptr [[L_ADDR]], align 8
+// CHECK: [[ARGP_NEXT:%[._a-z0-9]+]] = getelementptr inbounds i8, ptr [[ARGP_CURR]], i64 8
+// CHECK: store ptr [[ARGP_NEXT]], ptr [[L_ADDR]], align 8
+// CHECK: [[V_ADDR:%[._a-z0-9]+]] = getelementptr inbounds i8, ptr [[ARGP_CURR]], i64 4
+// CHECK: [[VAL:%[._a-z0-9]+]] = load i32, ptr [[V_ADDR]], align 4
+// CHECK: ret i32 [[VAL]]
+
+int va_int_s(__builtin_zos_va_list l) { return __builtin_va_arg(l, int); }
+// CHECK-LABEL: define signext i32 @va_int_s(ptr %{{.*}})
+// CHECK: [[L_ADDR:%[._a-z0-9]+]] = alloca ptr, align 8
+// CHECK: store ptr %{{.*}}, ptr [[L_ADDR]], align 8
+// CHECK: [[VALIST:%[._a-z0-9]+]] = load ptr, ptr [[L_ADDR]], align 8
+// CHECK: [[VALIST_CURR:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[VALIST]], i64 0, i64 0
+// CHECK: [[VALIST_NEXT:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[VALIST]], i64 0, i64 1
+// CHECK: [[ARGP_NEXT:%[._a-z0-9]+]] = load ptr, ptr [[VALIST_NEXT]], align 8
+// CHECK: %1 = getelementptr inbounds i8, ptr %arg.next, i32 7
+// CHECK: [[ARGP_NEXT_ALIGNED]] = call ptr @llvm.ptrmask.p0.i64(ptr %1, i64 -8)
+// CHECK: store ptr [[ARGP_NEXT_ALIGNED]], ptr [[VALIST_CURR]], align 8
+// CHECK: [[ARGP_NEXT_NEXT:%[._a-z0-9]+]] = getelementptr inbounds i8, ptr [[ARGP_NEXT_ALIGNED]], i64 4
+// CHECK: store ptr [[ARGP_NEXT_NEXT]], ptr [[VALIST_NEXT]], align 8
+// CHECK: [[V_ADDR:%[._a-z0-9]+]] = getelementptr inbounds i8, ptr [[ARGP_NEXT_ALIGNED]], i64 4
+// CHECK: [[VAL:%[._a-z0-9]+]] = load i32, ptr [[V_ADDR]], align 4
+// CHECK: ret i32 [[VAL]]
+
+long va_long_e(__builtin_va_list l) { return __builtin_va_arg(l, long); }
+// CHECK-LABEL: define signext i64 @va_long_e(ptr %{{.*}})
+// CHECK: [[L_ADDR:%[._a-z0-9]+]] = alloca ptr, align 8
+// CHECK: store ptr %{{.*}}, ptr [[L_ADDR]], align 8
+// CHECK: [[ARGP_CURR:%[._a-z0-9]+]] = load ptr, ptr [[L_ADDR]], align 8
+// CHECK: [[ARGP_NEXT:%[._a-z0-9]+]] = getelementptr inbounds i8, ptr [[ARGP_CURR]], i64 8
+// CHECK: store ptr [[ARGP_NEXT]], ptr [[L_ADDR]], align 8
+// CHECK: [[VAL:%[._a-z0-9]+]] = load i64, ptr [[ARGP_CURR]], align 8
+// CHECK: ret i64 [[VAL]]
+
+long va_long_s(__builtin_zos_va_list l) { return __builtin_va_arg(l, long); }
+// CHECK-LABEL: define signext i64 @va_long_s(ptr %{{.*}})
+// CHECK: [[L_ADDR:%[._a-z0-9]+]] = alloca ptr, align 8
+// CHECK: store ptr %{{.*}}, ptr [[L_ADDR]], align 8
+// CHECK: [[VALIST:%[._a-z0-9]+]] = load ptr, ptr [[L_ADDR]], align 8
+// CHECK: [[VALIST_CURR:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[VALIST]], i64 0, i64 0
+// CHECK: [[VALIST_NEXT:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[VALIST]], i64 0, i64 1
+// CHECK: [[ARGP_NEXT:%[._a-z0-9]+]] = load ptr, ptr [[VALIST_NEXT]], align 8
+// CHECK: %1 = getelementptr inbounds i8, ptr %arg.next, i32 7
+// CHECK: %arg.next.aligned = call ptr @llvm.ptrmask.p0.i64(ptr %1, i64 -8)
+// CHECK: store ptr [[ARGP_NEXT_ALIGNED]], ptr [[VALIST_CURR]], align 8
+// CHECK: [[ARGP_NEXT_NEXT:%[._a-z0-9]+]] = getelementptr inbounds i8, ptr [[ARGP_NEXT_ALIGNED]], i64 8
+// CHECK: store ptr [[ARGP_NEXT_NEXT]], ptr [[VALIST_NEXT]], align 8
+// CHECK: [[VAL:%[._a-z0-9]+]] = load i64, ptr [[ARGP_NEXT_ALIGNED]], align 8
+// CHECK: ret i64 [[VAL]]
+
+struct agg_threedouble {
+ double a, b, c;
+};
+struct agg_threedouble va_3double_s(__builtin_zos_va_list l) {
+ return __builtin_va_arg(l, struct agg_threedouble);
+}
+// CHECK-LABEL: define inreg [3 x i64] @va_3double_s(ptr %{{.*}})
+// CHECK: [[RETVAL:%[._a-z0-9]+]] = alloca %struct.agg_threedouble, align 8
+// CHECK: [[L_ADDR:%[._a-z0-9]+]] = alloca ptr, align 8
+// CHECK: store ptr %{{.*}}, ptr [[L_ADDR]], align 8
+// CHECK: [[VALIST:%[._a-z0-9]+]] = load ptr, ptr [[L_ADDR]], align 8
+// CHECK: [[VALIST_CURR:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[VALIST]], i64 0, i64 0
+// CHECK: [[VALIST_NEXT:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[VALIST]], i64 0, i64 1
+// CHECK: [[ARGP_NEXT:%[._a-z0-9]+]] = load ptr, ptr [[VALIST_NEXT]], align 8
+// CHECK: %1 = getelementptr inbounds i8, ptr %arg.next, i32 7
+// CHECK: [[ARGP_NEXT_ALIGNED]] = call ptr @llvm.ptrmask.p0.i64(ptr %1, i64 -8)
+// CHECK: store ptr [[ARGP_NEXT_ALIGNED]], ptr [[VALIST_CURR]], align 8
+// CHECK: [[ARGP_NEXT_NEXT:%[._a-z0-9]+]] = getelementptr inbounds i8, ptr [[ARGP_NEXT_ALIGNED]], i64 24
+// CHECK: store ptr [[ARGP_NEXT_NEXT]], ptr [[VALIST_NEXT]], align 8
+// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL]], ptr align 8 [[ARGP_NEXT_ALIGNED]], i64 24, i1 false)
+// CHECK: [[RETVAL2:%[._a-z0-9]+]] = load [3 x i64], ptr [[RETVAL]], align 8
+// CHECK: ret [3 x i64] [[RETVAL2]]