https://github.com/perry-ca created https://github.com/llvm/llvm-project/pull/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. >From 70f8ccec5ba4358e7fc6a29f02fb86e593f71be1 Mon Sep 17 00:00:00 2001 From: Sean Perry <[email protected]> Date: Fri, 5 Jun 2026 17:45:13 -0400 Subject: [PATCH 1/4] Implement zos va_list --- clang/include/clang/AST/ASTContext.h | 14 ++++++++ clang/include/clang/AST/DeclID.h | 3 ++ clang/include/clang/AST/Expr.h | 21 ++++++++---- clang/include/clang/Basic/Builtins.td | 19 ++++++++++ clang/include/clang/Basic/TargetInfo.h | 7 ++++ clang/lib/AST/ASTContext.cpp | 16 +++++++++ clang/lib/AST/ASTImporter.cpp | 6 ++-- clang/lib/Basic/TargetInfo.cpp | 1 + clang/lib/Basic/Targets/SystemZ.h | 4 +++ clang/lib/CodeGen/ABIInfo.cpp | 5 +++ clang/lib/CodeGen/ABIInfo.h | 6 ++++ clang/lib/CodeGen/CGBuiltin.cpp | 30 ++++++++++++++++ clang/lib/CodeGen/CGCall.cpp | 8 +++-- clang/lib/CodeGen/CodeGenFunction.cpp | 4 +++ clang/lib/CodeGen/CodeGenFunction.h | 5 +++ clang/lib/CodeGen/Targets/SystemZ.cpp | 42 +++++++++++++++++++++++ clang/lib/Sema/Sema.cpp | 8 +++++ clang/lib/Sema/SemaChecking.cpp | 1 + clang/lib/Sema/SemaExpr.cpp | 28 ++++++++++++--- clang/lib/Serialization/ASTReader.cpp | 6 ++++ clang/lib/Serialization/ASTReaderStmt.cpp | 2 +- clang/lib/Serialization/ASTWriter.cpp | 2 ++ 22 files changed, 221 insertions(+), 17 deletions(-) diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 54c046f5fab4a..0f196c6de1976 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -442,6 +442,9 @@ class ASTContext : public RefCountedBase<ASTContext> { /// 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; @@ -2523,6 +2526,17 @@ class ASTContext : public RefCountedBase<ASTContext> { 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 8a173e3d96349..b38875afeee84 100644 --- a/clang/include/clang/AST/DeclID.h +++ b/clang/include/clang/AST/DeclID.h @@ -80,6 +80,9 @@ enum PredefinedDeclIDs { /// 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 b91bf4a5375fb..dd96f7f1bf02e 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -4958,28 +4958,37 @@ class GNUNullExpr : public Expr { /// 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() { return TInfo.getInt(); } + void setVarargABI(int Kind) { TInfo.setInt(static_cast<VarArgKind>(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 8d2a824ef5610..8f62f1e995f32 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -5809,6 +5809,25 @@ def MSVaCopy : Builtin { 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 cc226403877e2..4f30408b2f0ef 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -272,6 +272,9 @@ class TargetInfo : public TransferrableTargetInfo, LLVM_PREFERRED_TYPE(bool) unsigned HasBuiltinMSVaList : 1; + LLVM_PREFERRED_TYPE(bool) + unsigned HasBuiltinZOSVaList : 1; + LLVM_PREFERRED_TYPE(bool) unsigned HasAArch64ACLETypes : 1; @@ -1072,6 +1075,10 @@ class TargetInfo : public TransferrableTargetInfo, /// 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 d7e2a0f9c4803..e3c21b48e90bd 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -9995,6 +9995,15 @@ static TypedefDecl *CreateMSVaListDecl(const ASTContext *Context) { return CreateCharPtrNamedVaListDecl(Context, "__builtin_ms_va_list"); } +static TypedefDecl *CreateZOSVaListDecl(const ASTContext *Context) { + // typedef char *__builtin_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"); } @@ -10422,6 +10431,13 @@ TypedefDecl *ASTContext::getBuiltinMSVaListDecl() const { 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 f8c641789bd10..221b2bb401fa0 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -7625,9 +7625,9 @@ ExpectedStmt ASTNodeImporter::VisitVAArgExpr(VAArgExpr *E) { 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 854d23cadaea2..4c48a97b45951 100644 --- a/clang/lib/Basic/TargetInfo.cpp +++ b/clang/lib/Basic/TargetInfo.cpp @@ -164,6 +164,7 @@ TargetInfo::TargetInfo(const llvm::Triple &T) : Triple(T) { 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 00f7d7a055b24..74ad7d722a43f 100644 --- a/clang/lib/Basic/Targets/SystemZ.h +++ b/clang/lib/Basic/Targets/SystemZ.h @@ -83,6 +83,8 @@ class LLVM_LIBRARY_VISIBILITY SystemZTargetInfo : public TargetInfo { 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; @@ -166,6 +168,8 @@ class LLVM_LIBRARY_VISIBILITY SystemZTargetInfo : public TargetInfo { } 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 16005890a0708..eede3b54d7b4a 100644 --- a/clang/lib/CodeGen/ABIInfo.cpp +++ b/clang/lib/CodeGen/ABIInfo.cpp @@ -44,6 +44,11 @@ RValue ABIInfo::EmitMSVAArg(CodeGenFunction &CGF, Address VAListAddr, 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 576cf8f742446..4563154d50960 100644 --- a/clang/lib/CodeGen/ABIInfo.h +++ b/clang/lib/CodeGen/ABIInfo.h @@ -91,6 +91,12 @@ class ABIInfo { 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 0cb5f95049789..4083c1f34e413 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -6976,6 +6976,36 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, 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)); + // Value *SizeVal = llvm::ConstantInt::get(Int64Ty, 2 * + // getPointerSize().getQuantity()); + 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 40cc275d40273..d2d6179b0616d 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -6584,13 +6584,17 @@ CGCallee CGCallee::prepareConcreteCallee(CodeGenFunction &CGF) const { 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 b920266b59808..80c5268495fe6 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -2700,6 +2700,10 @@ Address CodeGenFunction::EmitMSVAListRef(const Expr *E) { 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 6bb9f285ebcfd..ddf00c1860fb0 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3013,6 +3013,11 @@ class CodeGenFunction : public CodeGenTypeCache { /// 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 5c7485ed0944f..0ea918411509e 100644 --- a/clang/lib/CodeGen/Targets/SystemZ.cpp +++ b/clang/lib/CodeGen/Targets/SystemZ.cpp @@ -586,6 +586,9 @@ class ZOSXPLinkABIInfo : public ABIInfo { 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 @@ RValue ZOSXPLinkABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, /*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 arg + 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 78fbc9e31842d..ad24957feaae0 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -375,6 +375,8 @@ void Sema::Initialize() { // 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 @@ void Sema::Initialize() { 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 8a8c9cc9d2c23..ef4306f1d74c6 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -2946,6 +2946,7 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, 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 ad6e7183cb3a4..d34e68ebd4906 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -17203,7 +17203,7 @@ ExprResult Sema::BuildVAArgExpr(SourceLocation BuiltinLoc, 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) { @@ -17228,13 +17228,31 @@ ExprResult Sema::BuildVAArgExpr(SourceLocation BuiltinLoc, 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 @@ -17263,7 +17281,7 @@ ExprResult Sema::BuildVAArgExpr(SourceLocation BuiltinLoc, } } - if (!IsMS && !E->isTypeDependent() && + if ((VAKind != VAArgExpr::VA_MS) && !E->isTypeDependent() && !Context.hasSameType(VaListType, E->getType())) return ExprError( Diag(E->getBeginLoc(), @@ -17358,7 +17376,7 @@ ExprResult Sema::BuildVAArgExpr(SourceLocation BuiltinLoc, } 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 74a7b51368c28..8805ef6fa592d 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -8524,6 +8524,12 @@ Decl *ASTReader::getPredefinedDecl(PredefinedDeclIDs ID) { 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 02ccf8d4d41c2..c75b9f3598711 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -1372,7 +1372,7 @@ void ASTStmtReader::VisitVAArgExpr(VAArgExpr *E) { E->setWrittenTypeInfo(readTypeSourceInfo()); E->setBuiltinLoc(readSourceLocation()); E->setRParenLoc(readSourceLocation()); - E->setIsMicrosoftABI(Record.readInt()); + E->setVarargABI(Record.readInt()); } void ASTStmtReader::VisitSourceLocExpr(SourceLocExpr *E) { diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 074b0fccdb65d..d0162960efd25 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -5742,6 +5742,8 @@ void ASTWriter::PrepareWritingSpecialDecls(Sema &SemaRef) { 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, >From 78ca9ed196386056c2935e96e8b59ed21f83bda8 Mon Sep 17 00:00:00 2001 From: Sean Perry <[email protected]> Date: Fri, 5 Jun 2026 20:29:48 -0400 Subject: [PATCH 2/4] update the abi tests to include va_lists --- clang/test/CodeGen/SystemZ/zos-abi.c | 154 ++++++++++++++ clang/test/CodeGen/zos-abi.c | 307 +++++++++++++++++++++++++++ 2 files changed, 461 insertions(+) create mode 100644 clang/test/CodeGen/zos-abi.c diff --git a/clang/test/CodeGen/SystemZ/zos-abi.c b/clang/test/CodeGen/SystemZ/zos-abi.c index ac1d7b31328b6..60e058cf3167a 100644 --- a/clang/test/CodeGen/SystemZ/zos-abi.c +++ b/clang/test/CodeGen/SystemZ/zos-abi.c @@ -486,3 +486,157 @@ union tu_long pass_tu_long(union tu_long arg) { return arg; } 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]] diff --git a/clang/test/CodeGen/zos-abi.c b/clang/test/CodeGen/zos-abi.c new file mode 100644 index 0000000000000..1d505115f7f77 --- /dev/null +++ b/clang/test/CodeGen/zos-abi.c @@ -0,0 +1,307 @@ +// RUN: %clang_cc1 -triple s390x-ibm-zos \ +// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple s390x-ibm-zos -target-feature +vector \ +// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck --check-prefixes=CHECK,CHECKI128 %s +// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu z13 \ +// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck --check-prefixes=CHECK,CHECKI128 %s +// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch11 \ +// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck --check-prefixes=CHECK,CHECKI128 %s +// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu z14 \ +// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck --check-prefixes=CHECK,CHECKI128 %s +// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch12 \ +// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck --check-prefixes=CHECK,CHECKI128 %s +// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu z15 \ +// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck --check-prefixes=CHECK,CHECKI128 %s +// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch13 \ +// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck --check-prefixes=CHECK,CHECKI128 %s + +// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch11 \ +// RUN: -DTEST_VEC -fzvector -emit-llvm -no-enable-noundef-analysis \ +// RUN: -o - %s | FileCheck --check-prefixes=CHECK,CHECKVEC,CHECKI128 %s + +// Scalar types + +signed char pass_schar(signed char arg) { return arg; } +// CHECK-LABEL: define signext i8 @pass_schar(i8 signext %{{.*}}) + +unsigned char pass_uchar(unsigned char arg) { return arg; } +// CHECK-LABEL: define zeroext i8 @pass_uchar(i8 zeroext %{{.*}}) + +short pass_short(short arg) { return arg; } +// CHECK-LABEL: define signext i16 @pass_short(i16 signext %{{.*}}) + +int pass_int(int arg) { return arg; } +// CHECK-LABEL: define signext i32 @pass_int(i32 signext %{{.*}}) + +long pass_long(long arg) { return arg; } +// CHECK-LABEL: define signext i64 @pass_long(i64 signext %{{.*}}) + +long long pass_longlong(long long arg) { return arg; } +// CHECK-LABEL: define i64 @pass_longlong(i64 %{{.*}}) + +#ifdef __VX__ +__int128 pass_int128(__int128 arg) { return arg; } +// CHECKI128-LABEL: define i128 @pass_int128(i128 %{{.*}}) +#endif + +float pass_float(float arg) { return arg; } +// CHECK-LABEL: define float @pass_float(float %{{.*}}) + +double pass_double(double arg) { return arg; } +// CHECK-LABEL: define double @pass_double(double %{{.*}}) + +long double pass_longdouble(long double arg) { return arg; } +// CHECK-LABEL: define fp128 @pass_longdouble(fp128 %{{.*}}) + +enum Color { Red, Blue }; +enum Color pass_enum(enum Color arg) { return arg; } +// CHECK-LABEL: define zeroext i32 @pass_enum(i32 zeroext %{{.*}}) + +#ifdef TEST_VEC +vector unsigned int pass_vector(vector unsigned int arg) { return arg; }; +// CHECKVEC-LABEL: define <4 x i32> @pass_vector(<4 x i32> %{{.*}}) + +struct SingleVec { vector unsigned int v; }; +struct SingleVec pass_SingleVec_agg(struct SingleVec arg) { return arg; }; +// CHECKVEC-LABEL: define inreg [2 x i64] @pass_SingleVec_agg([2 x i64] %{{.*}}) +#endif + +// Complex types + +_Complex float pass_complex_float(_Complex float arg) { return arg; } +// CHECK-LABEL: define { float, float } @pass_complex_float({ float, float } %{{.*}}) + +_Complex double pass_complex_double(_Complex double arg) { return arg; } +// CHECK-LABEL: define { double, double } @pass_complex_double({ double, double } %{{.*}}) + +_Complex long double pass_complex_longdouble(_Complex long double arg) { return arg; } +// CHECK-LABEL: define { fp128, fp128 } @pass_complex_longdouble({ fp128, fp128 } %{{.*}}) + +// Verify that the following are complex-like types +struct complexlike_float { float re, im; }; +struct complexlike_float pass_complexlike_float(struct complexlike_float arg) { return arg; } +// CHECK-LABEL: define %struct.complexlike_float @pass_complexlike_float({ float, float } %{{.*}}) + +struct complexlike_double { double re, im; }; +struct complexlike_double pass_complexlike_double(struct complexlike_double arg) { return arg; } +// CHECK-LABEL: define %struct.complexlike_double @pass_complexlike_double({ double, double } %{{.*}}) + +struct complexlike_longdouble { long double re, im; }; +struct complexlike_longdouble pass_complexlike_longdouble(struct complexlike_longdouble arg) { return arg; } +// CHECK-LABEL: define %struct.complexlike_longdouble @pass_complexlike_longdouble({ fp128, fp128 } %{{.*}}) + +// Aggregate types + +struct agg_1byte { char a[1]; }; +struct agg_1byte pass_agg_1byte(struct agg_1byte arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_agg_1byte(i64 %{{.*}}) + +struct agg_2byte { char a[2]; }; +struct agg_2byte pass_agg_2byte(struct agg_2byte arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_agg_2byte(i64 %{{.*}}) + +struct agg_3byte { char a[3]; }; +struct agg_3byte pass_agg_3byte(struct agg_3byte arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_agg_3byte(i64 %{{.*}}) + +struct agg_4byte { char a[4]; }; +struct agg_4byte pass_agg_4byte(struct agg_4byte arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_agg_4byte(i64 %{{.*}}) + +struct agg_5byte { char a[5]; }; +struct agg_5byte pass_agg_5byte(struct agg_5byte arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_agg_5byte(i64 %{{.*}}) + +struct agg_6byte { char a[6]; }; +struct agg_6byte pass_agg_6byte(struct agg_6byte arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_agg_6byte(i64 %{{.*}}) + +struct agg_7byte { char a[7]; }; +struct agg_7byte pass_agg_7byte(struct agg_7byte arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_agg_7byte(i64 %{{.*}}) + +struct agg_8byte { char a[8]; }; +struct agg_8byte pass_agg_8byte(struct agg_8byte arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_agg_8byte(i64 %{{.*}}) + +struct agg_9byte { char a[9]; }; +struct agg_9byte pass_agg_9byte(struct agg_9byte arg) { return arg; } +// CHECK-LABEL: define inreg [2 x i64] @pass_agg_9byte([2 x i64] %{{.*}}) + +struct agg_16byte { char a[16]; }; +struct agg_16byte pass_agg_16byte(struct agg_16byte arg) { return arg; } +// CHECK-LABEL: define inreg [2 x i64] @pass_agg_16byte([2 x i64] %{{.*}}) + +struct agg_24byte { char a[24]; }; +struct agg_24byte pass_agg_24byte(struct agg_24byte arg) { return arg; } +// CHECK-LABEL: define inreg [3 x i64] @pass_agg_24byte([3 x i64] %{{.*}}) + +struct agg_25byte { char a[25]; }; +struct agg_25byte pass_agg_25byte(struct agg_25byte arg) { return arg; } +// CHECK-LABEL: define void @pass_agg_25byte(ptr dead_on_unwind noalias writable sret{{.*}} align 1 %{{.*}}, [4 x i64] %{{.*}}) + +// Check that a float-like aggregate type is really passed as aggregate +struct agg_float { float a; }; +struct agg_float pass_agg_float(struct agg_float arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_agg_float(i64 %{{.*}}) + +// Verify that the following are *not* float-like aggregate types + +struct agg_nofloat2 { float a; int b; }; +struct agg_nofloat2 pass_agg_nofloat2(struct agg_nofloat2 arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_agg_nofloat2(i64 %{{.*}}) + +struct agg_nofloat3 { float a; int : 0; }; +struct agg_nofloat3 pass_agg_nofloat3(struct agg_nofloat3 arg) { return arg; } +// CHECK-LABEL: define inreg [1 x i64] @pass_agg_nofloat3(i64 %{{.*}}) + +// 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]] >From 7520032f34f7b91f5a59c6ad245a7c6bba7d85f1 Mon Sep 17 00:00:00 2001 From: Sean Perry <[email protected]> Date: Mon, 8 Jun 2026 13:41:46 -0400 Subject: [PATCH 3/4] remove file --- clang/test/CodeGen/zos-abi.c | 307 ----------------------------------- 1 file changed, 307 deletions(-) delete mode 100644 clang/test/CodeGen/zos-abi.c diff --git a/clang/test/CodeGen/zos-abi.c b/clang/test/CodeGen/zos-abi.c deleted file mode 100644 index 1d505115f7f77..0000000000000 --- a/clang/test/CodeGen/zos-abi.c +++ /dev/null @@ -1,307 +0,0 @@ -// RUN: %clang_cc1 -triple s390x-ibm-zos \ -// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s -// RUN: %clang_cc1 -triple s390x-ibm-zos -target-feature +vector \ -// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck --check-prefixes=CHECK,CHECKI128 %s -// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu z13 \ -// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck --check-prefixes=CHECK,CHECKI128 %s -// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch11 \ -// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck --check-prefixes=CHECK,CHECKI128 %s -// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu z14 \ -// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck --check-prefixes=CHECK,CHECKI128 %s -// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch12 \ -// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck --check-prefixes=CHECK,CHECKI128 %s -// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu z15 \ -// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck --check-prefixes=CHECK,CHECKI128 %s -// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch13 \ -// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck --check-prefixes=CHECK,CHECKI128 %s - -// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch11 \ -// RUN: -DTEST_VEC -fzvector -emit-llvm -no-enable-noundef-analysis \ -// RUN: -o - %s | FileCheck --check-prefixes=CHECK,CHECKVEC,CHECKI128 %s - -// Scalar types - -signed char pass_schar(signed char arg) { return arg; } -// CHECK-LABEL: define signext i8 @pass_schar(i8 signext %{{.*}}) - -unsigned char pass_uchar(unsigned char arg) { return arg; } -// CHECK-LABEL: define zeroext i8 @pass_uchar(i8 zeroext %{{.*}}) - -short pass_short(short arg) { return arg; } -// CHECK-LABEL: define signext i16 @pass_short(i16 signext %{{.*}}) - -int pass_int(int arg) { return arg; } -// CHECK-LABEL: define signext i32 @pass_int(i32 signext %{{.*}}) - -long pass_long(long arg) { return arg; } -// CHECK-LABEL: define signext i64 @pass_long(i64 signext %{{.*}}) - -long long pass_longlong(long long arg) { return arg; } -// CHECK-LABEL: define i64 @pass_longlong(i64 %{{.*}}) - -#ifdef __VX__ -__int128 pass_int128(__int128 arg) { return arg; } -// CHECKI128-LABEL: define i128 @pass_int128(i128 %{{.*}}) -#endif - -float pass_float(float arg) { return arg; } -// CHECK-LABEL: define float @pass_float(float %{{.*}}) - -double pass_double(double arg) { return arg; } -// CHECK-LABEL: define double @pass_double(double %{{.*}}) - -long double pass_longdouble(long double arg) { return arg; } -// CHECK-LABEL: define fp128 @pass_longdouble(fp128 %{{.*}}) - -enum Color { Red, Blue }; -enum Color pass_enum(enum Color arg) { return arg; } -// CHECK-LABEL: define zeroext i32 @pass_enum(i32 zeroext %{{.*}}) - -#ifdef TEST_VEC -vector unsigned int pass_vector(vector unsigned int arg) { return arg; }; -// CHECKVEC-LABEL: define <4 x i32> @pass_vector(<4 x i32> %{{.*}}) - -struct SingleVec { vector unsigned int v; }; -struct SingleVec pass_SingleVec_agg(struct SingleVec arg) { return arg; }; -// CHECKVEC-LABEL: define inreg [2 x i64] @pass_SingleVec_agg([2 x i64] %{{.*}}) -#endif - -// Complex types - -_Complex float pass_complex_float(_Complex float arg) { return arg; } -// CHECK-LABEL: define { float, float } @pass_complex_float({ float, float } %{{.*}}) - -_Complex double pass_complex_double(_Complex double arg) { return arg; } -// CHECK-LABEL: define { double, double } @pass_complex_double({ double, double } %{{.*}}) - -_Complex long double pass_complex_longdouble(_Complex long double arg) { return arg; } -// CHECK-LABEL: define { fp128, fp128 } @pass_complex_longdouble({ fp128, fp128 } %{{.*}}) - -// Verify that the following are complex-like types -struct complexlike_float { float re, im; }; -struct complexlike_float pass_complexlike_float(struct complexlike_float arg) { return arg; } -// CHECK-LABEL: define %struct.complexlike_float @pass_complexlike_float({ float, float } %{{.*}}) - -struct complexlike_double { double re, im; }; -struct complexlike_double pass_complexlike_double(struct complexlike_double arg) { return arg; } -// CHECK-LABEL: define %struct.complexlike_double @pass_complexlike_double({ double, double } %{{.*}}) - -struct complexlike_longdouble { long double re, im; }; -struct complexlike_longdouble pass_complexlike_longdouble(struct complexlike_longdouble arg) { return arg; } -// CHECK-LABEL: define %struct.complexlike_longdouble @pass_complexlike_longdouble({ fp128, fp128 } %{{.*}}) - -// Aggregate types - -struct agg_1byte { char a[1]; }; -struct agg_1byte pass_agg_1byte(struct agg_1byte arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_agg_1byte(i64 %{{.*}}) - -struct agg_2byte { char a[2]; }; -struct agg_2byte pass_agg_2byte(struct agg_2byte arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_agg_2byte(i64 %{{.*}}) - -struct agg_3byte { char a[3]; }; -struct agg_3byte pass_agg_3byte(struct agg_3byte arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_agg_3byte(i64 %{{.*}}) - -struct agg_4byte { char a[4]; }; -struct agg_4byte pass_agg_4byte(struct agg_4byte arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_agg_4byte(i64 %{{.*}}) - -struct agg_5byte { char a[5]; }; -struct agg_5byte pass_agg_5byte(struct agg_5byte arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_agg_5byte(i64 %{{.*}}) - -struct agg_6byte { char a[6]; }; -struct agg_6byte pass_agg_6byte(struct agg_6byte arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_agg_6byte(i64 %{{.*}}) - -struct agg_7byte { char a[7]; }; -struct agg_7byte pass_agg_7byte(struct agg_7byte arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_agg_7byte(i64 %{{.*}}) - -struct agg_8byte { char a[8]; }; -struct agg_8byte pass_agg_8byte(struct agg_8byte arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_agg_8byte(i64 %{{.*}}) - -struct agg_9byte { char a[9]; }; -struct agg_9byte pass_agg_9byte(struct agg_9byte arg) { return arg; } -// CHECK-LABEL: define inreg [2 x i64] @pass_agg_9byte([2 x i64] %{{.*}}) - -struct agg_16byte { char a[16]; }; -struct agg_16byte pass_agg_16byte(struct agg_16byte arg) { return arg; } -// CHECK-LABEL: define inreg [2 x i64] @pass_agg_16byte([2 x i64] %{{.*}}) - -struct agg_24byte { char a[24]; }; -struct agg_24byte pass_agg_24byte(struct agg_24byte arg) { return arg; } -// CHECK-LABEL: define inreg [3 x i64] @pass_agg_24byte([3 x i64] %{{.*}}) - -struct agg_25byte { char a[25]; }; -struct agg_25byte pass_agg_25byte(struct agg_25byte arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_25byte(ptr dead_on_unwind noalias writable sret{{.*}} align 1 %{{.*}}, [4 x i64] %{{.*}}) - -// Check that a float-like aggregate type is really passed as aggregate -struct agg_float { float a; }; -struct agg_float pass_agg_float(struct agg_float arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_agg_float(i64 %{{.*}}) - -// Verify that the following are *not* float-like aggregate types - -struct agg_nofloat2 { float a; int b; }; -struct agg_nofloat2 pass_agg_nofloat2(struct agg_nofloat2 arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_agg_nofloat2(i64 %{{.*}}) - -struct agg_nofloat3 { float a; int : 0; }; -struct agg_nofloat3 pass_agg_nofloat3(struct agg_nofloat3 arg) { return arg; } -// CHECK-LABEL: define inreg [1 x i64] @pass_agg_nofloat3(i64 %{{.*}}) - -// 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]] >From 17409e0ae86d44105b0bb2983e507e40d7c1270a Mon Sep 17 00:00:00 2001 From: Sean Perry <[email protected]> Date: Mon, 8 Jun 2026 13:49:21 -0400 Subject: [PATCH 4/4] fix name in comment --- clang/lib/AST/ASTContext.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index e3c21b48e90bd..06af5261ed894 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -9996,7 +9996,7 @@ static TypedefDecl *CreateMSVaListDecl(const ASTContext *Context) { } static TypedefDecl *CreateZOSVaListDecl(const ASTContext *Context) { - // typedef char *__builtin_va_list[2]; + // 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( _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
