diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -7418,18 +7418,28 @@ }; class SystemZTargetCodeGenInfo : public TargetCodeGenInfo { + ASTContext &Ctx; + + const SystemZABIInfo &getABIInfo() const { + return static_cast(TargetCodeGenInfo::getABIInfo()); + } + // These are used for speeding up the search for a visible vector ABI. mutable bool HasVisibleVecABIFlag = false; mutable std::set SeenTypes; - // Returns true (the first time) if Ty is or found to make use of a vector - // type (e.g. as a function argument). - bool isVectorTypeBased(const Type *Ty) const; + // Returns true (the first time) if Ty is, or is found to include, a vector + // type that exposes the vector ABI. This is any vector >=16 bytes which + // with vector support are aligned to only 8 bytes. When IsParam is true, + // the type belongs to a value as passed between functions. If it is a + // vector <=16 bytes it will be passed in a vector register (if supported). + bool isVectorTypeBased(const Type *Ty, bool IsParam) const; public: SystemZTargetCodeGenInfo(CodeGenTypes &CGT, bool HasVector, bool SoftFloatABI) : TargetCodeGenInfo( - std::make_unique(CGT, HasVector, SoftFloatABI)) { + std::make_unique(CGT, HasVector, SoftFloatABI)), + Ctx(CGT.getContext()) { SwiftInfo = std::make_unique(CGT, /*SwiftErrorInRegister=*/false); } @@ -7439,9 +7449,9 @@ // indicating a visible vector ABI is added. Eventually this will result in // a GNU attribute indicating the vector ABI of the module. Ty is the type // of a variable or function parameter that is globally visible. - void handleExternallyVisibleObjABI(const Type *Ty, - CodeGen::CodeGenModule &M) const { - if (!HasVisibleVecABIFlag && isVectorTypeBased(Ty)) { + void handleExternallyVisibleObjABI(const Type *Ty, CodeGen::CodeGenModule &M, + bool IsParam) const { + if (!HasVisibleVecABIFlag && isVectorTypeBased(Ty, IsParam)) { M.getModule().addModuleFlag(llvm::Module::Warning, "s390x-visible-vector-ABI", 1); HasVisibleVecABIFlag = true; @@ -7457,11 +7467,13 @@ // variable or function. if (const auto *VD = dyn_cast(D)) { if (VD->isExternallyVisible()) - handleExternallyVisibleObjABI(VD->getType().getTypePtr(), M); + handleExternallyVisibleObjABI(VD->getType().getTypePtr(), M, + /*IsParam*/false); } else if (const FunctionDecl *FD = dyn_cast(D)) { if (FD->isExternallyVisible()) - handleExternallyVisibleObjABI(FD->getType().getTypePtr(), M); + handleExternallyVisibleObjABI(FD->getType().getTypePtr(), M, + /*IsParam*/false); } } @@ -7571,17 +7583,18 @@ // If this is a C++ record, check the bases first. if (const CXXRecordDecl *CXXRD = dyn_cast(RD)) - for (const auto &I : CXXRD->bases()) { - QualType Base = I.getType(); + if (CXXRD->hasDefinition()) + for (const auto &I : CXXRD->bases()) { + QualType Base = I.getType(); - // Empty bases don't affect things either way. - if (isEmptyRecord(getContext(), Base, true)) - continue; + // Empty bases don't affect things either way. + if (isEmptyRecord(getContext(), Base, true)) + continue; - if (!Found.isNull()) - return Ty; - Found = GetSingleElementType(Base); - } + if (!Found.isNull()) + return Ty; + Found = GetSingleElementType(Base); + } // Check the fields. for (const auto *FD : RD->fields()) { @@ -7635,7 +7648,8 @@ bool IsVector = false; CharUnits UnpaddedSize; CharUnits DirectAlign; - SZCGI.handleExternallyVisibleObjABI(Ty.getTypePtr(), CGT.getCGM()); + SZCGI.handleExternallyVisibleObjABI(Ty.getTypePtr(), CGT.getCGM(), + /*IsParam*/true); if (IsIndirect) { DirectTy = llvm::PointerType::getUnqual(DirectTy); UnpaddedSize = DirectAlign = CharUnits::fromQuantity(8); @@ -7843,35 +7857,57 @@ // Check if a vararg vector argument is passed, in which case the // vector ABI becomes visible as the va_list could be passed on to // other functions. - SZCGI.handleExternallyVisibleObjABI(I.type.getTypePtr(), CGT.getCGM()); + SZCGI.handleExternallyVisibleObjABI(I.type.getTypePtr(), CGT.getCGM(), + /*IsParam*/true); } } -bool SystemZTargetCodeGenInfo::isVectorTypeBased(const Type *Ty) const { - while (Ty->isPointerType() || Ty->isArrayType()) - Ty = Ty->getPointeeOrArrayElementType(); +bool SystemZTargetCodeGenInfo::isVectorTypeBased(const Type *Ty, + bool IsParam) const { if (!SeenTypes.insert(Ty).second) return false; - if (Ty->isVectorType()) - return true; + + if (IsParam) { + // A narrow (<16 bytes) vector will as a parameter also expose the ABI as + // it will be passed in a vector register. A wide (>16 bytes) vector will + // be passed via "hidden" pointer where any extra alignment is not + // required (per GCC). + const Type *SingleEltTy = + getABIInfo().GetSingleElementType(QualType(Ty, 0)).getTypePtr(); + bool SingleVecEltStruct = SingleEltTy != Ty && SingleEltTy->isVectorType() && + Ctx.getTypeSize(SingleEltTy) == Ctx.getTypeSize(Ty); + if (Ty->isVectorType() || SingleVecEltStruct) + return Ctx.getTypeSize(Ty) / 8 <= 16; + } + + // Assume pointers are dereferenced. + while (Ty->isPointerType() || Ty->isArrayType()) + Ty = Ty->getPointeeOrArrayElementType(); + + // Vectors >= 16 bytes expose the ABI through alignment requirements. + if (Ty->isVectorType() && Ctx.getTypeSize(Ty) / 8 >= 16) + return true; + if (const auto *RecordTy = Ty->getAs()) { const RecordDecl *RD = RecordTy->getDecl(); if (const CXXRecordDecl *CXXRD = dyn_cast(RD)) if (CXXRD->hasDefinition()) for (const auto &I : CXXRD->bases()) - if (isVectorTypeBased(I.getType().getTypePtr())) + if (isVectorTypeBased(I.getType().getTypePtr(), /*IsParam*/false)) return true; for (const auto *FD : RD->fields()) - if (isVectorTypeBased(FD->getType().getTypePtr())) + if (isVectorTypeBased(FD->getType().getTypePtr(), /*IsParam*/false)) return true; } + if (const auto *FT = Ty->getAs()) - if (isVectorTypeBased(FT->getReturnType().getTypePtr())) + if (isVectorTypeBased(FT->getReturnType().getTypePtr(), /*IsParam*/true)) return true; if (const FunctionProtoType *Proto = Ty->getAs()) for (auto ParamType : Proto->getParamTypes()) - if (isVectorTypeBased(ParamType.getTypePtr())) + if (isVectorTypeBased(ParamType.getTypePtr(), /*IsParam*/true)) return true; + return false; } diff --git a/clang/test/CodeGen/SystemZ/vec-abi-gnuattr-03b.c b/clang/test/CodeGen/SystemZ/vec-abi-gnuattr-03b.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/SystemZ/vec-abi-gnuattr-03b.c @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -triple s390x-ibm-linux -emit-llvm -fzvector -o - %s 2>&1 \ +// RUN: | FileCheck %s +// +// Test that the "s390x-visible-vector-ABI" module flag is emitted. + +// Call to external function with with narrow vector argument. + +typedef __attribute__((vector_size(8))) int v2i32; + +void bar(v2i32 arg); + +void foo() { + v2i32 Var = {0, 0}; + bar(Var); +} + +//CHECK: !llvm.module.flags = !{!0, !1} +//CHECK: !0 = !{i32 2, !"s390x-visible-vector-ABI", i32 1} + diff --git a/clang/test/CodeGen/SystemZ/vec-abi-gnuattr-08b.c b/clang/test/CodeGen/SystemZ/vec-abi-gnuattr-08b.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/SystemZ/vec-abi-gnuattr-08b.c @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -triple s390x-ibm-linux -emit-llvm -fzvector -o - %s 2>&1 \ +// RUN: | FileCheck %s +// +// Test the emission of the "s390x-visible-vector-ABI" module flag. + +// Passing a single element struct containing a narrow (8 byte) vector element. + +typedef __attribute__((vector_size(8))) int v2i32; + +struct S { + v2i32 B; +}; + +void bar(struct S Arg); + +void foo() { + struct S Var = {{0, 0}}; + bar(Var); +} + +//CHECK: !llvm.module.flags = !{!0, !1} +//CHECK: !0 = !{i32 2, !"s390x-visible-vector-ABI", i32 1} diff --git a/clang/test/CodeGen/SystemZ/vec-abi-gnuattr-09b.c b/clang/test/CodeGen/SystemZ/vec-abi-gnuattr-09b.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/SystemZ/vec-abi-gnuattr-09b.c @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -triple s390x-ibm-linux -emit-llvm -fzvector -o - %s 2>&1 \ +// RUN: | FileCheck %s +// +// Test the emission of the "s390x-visible-vector-ABI" module flag. + +// Call to vararg function with a narrow (8 bytes) vector argument. + +typedef __attribute__((vector_size(8))) int v2i32; + +void bar(int N, ...); + +void foo() { + v2i32 Var = {0, 0}; + bar(0, Var); +} + +//CHECK: !llvm.module.flags = !{!0, !1} +//CHECK: !0 = !{i32 2, !"s390x-visible-vector-ABI", i32 1} diff --git a/clang/test/CodeGen/SystemZ/vec-abi-gnuattr-17b.c b/clang/test/CodeGen/SystemZ/vec-abi-gnuattr-17b.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/SystemZ/vec-abi-gnuattr-17b.c @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -triple s390x-ibm-linux -emit-llvm -fzvector -o - %s 2>&1 \ +// RUN: | FileCheck %s +// +// Test that the "s390x-visible-vector-ABI" module flag is emitted. + +// Globally visible function pointer with narrow vector argument. + +typedef __attribute__((vector_size(8))) int v2i32; + +void (*bar)(v2i32 Arg); + +//CHECK: !llvm.module.flags = !{!0, !1} +//CHECK: !0 = !{i32 2, !"s390x-visible-vector-ABI", i32 1} diff --git a/clang/test/CodeGen/SystemZ/vec-abi-gnuattr-24.c b/clang/test/CodeGen/SystemZ/vec-abi-gnuattr-24.c --- a/clang/test/CodeGen/SystemZ/vec-abi-gnuattr-24.c +++ b/clang/test/CodeGen/SystemZ/vec-abi-gnuattr-24.c @@ -34,4 +34,20 @@ return foo(V)[0] + GlobVal + GlobExtVar; } +// Globally visible vector variable less than 16 bytes in size. +typedef __attribute__((vector_size(8))) int v2i32; +v2i32 NarrowVecVar; + +// Global function taking narrow vector array and pointer. +void bar(v2i32 VArr[4], v2i32 *Dst) { *Dst = VArr[3]; } + +// Wide vector parameters via "hidden" pointers. +typedef __attribute__((vector_size(32))) int v8i32; +v8i32 bar2(v8i32 Arg) { return Arg; } + +// Same but with a single element struct. +struct SingleElStruct { v8i32 B; }; +struct SingleElStruct bar3(struct SingleElStruct Arg) { return Arg; } + + //CHECK-NOT: !{i32 2, !"s390x-visible-vector-ABI", i32 1}