Index: clang/lib/CodeGen/TargetInfo.cpp =================================================================== --- clang/lib/CodeGen/TargetInfo.cpp +++ clang/lib/CodeGen/TargetInfo.cpp @@ -7424,6 +7424,8 @@ bool IsVector = false; CharUnits UnpaddedSize; CharUnits DirectAlign; + if (!CGF.CurFn->hasLocalLinkage() && ArgTy->isVectorTy()) + CGF.CurFn->addFnAttr("visible-vector-ABI"); if (IsIndirect) { DirectTy = llvm::PointerType::getUnqual(DirectTy); UnpaddedSize = DirectAlign = CharUnits::fromQuantity(8); Index: clang/test/CodeGen/SystemZ/systemz-abi-vector2.c =================================================================== --- /dev/null +++ clang/test/CodeGen/SystemZ/systemz-abi-vector2.c @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -triple s390x-ibm-linux -target-cpu arch10 -emit-llvm \ +// RUN: -fzvector -o - %s 2>&1 | FileCheck %s --check-prefix=ARCH10-ATTR +// RUN: %clang_cc1 -triple s390x-ibm-linux -target-cpu arch13 -emit-llvm \ +// RUN: -fzvector -o - %s 2>&1 | FileCheck %s --check-prefix=ARCH13-ATTR + +// RUN: %clang_cc1 -triple s390x-ibm-linux -target-cpu arch10 -S \ +// RUN: -fzvector -o - %s 2>&1 | FileCheck %s --check-prefix=ARCH10-ASM +// RUN: %clang_cc1 -triple s390x-ibm-linux -target-cpu arch13 -S \ +// RUN: -fzvector -o - %s 2>&1 | FileCheck %s --check-prefix=ARCH13-ASM + +// Use of va_arg with a vector type exposes the vector ABI. + +#include + +int fun(va_list vl) { + return va_arg(vl, vector int)[0]; +} + +//ARCH10-ATTR: attributes #0 = {{{.*}} "visible-vector-ABI" {{.*}}} +//ARCH13-ATTR: attributes #0 = {{{.*}} "visible-vector-ABI" {{.*}}} + +//ARCH10-ASM: .gnu_attribute 8, 1 +//ARCH13-ASM: .gnu_attribute 8, 2 Index: llvm/lib/Target/SystemZ/AsmParser/SystemZAsmParser.cpp =================================================================== --- llvm/lib/Target/SystemZ/AsmParser/SystemZAsmParser.cpp +++ llvm/lib/Target/SystemZ/AsmParser/SystemZAsmParser.cpp @@ -420,6 +420,7 @@ bool parseAddressRegister(Register &Reg); bool ParseDirectiveInsn(SMLoc L); + bool ParseGNUAttribute(SMLoc L); OperandMatchResultTy parseAddress(OperandVector &Operands, MemoryKind MemKind, @@ -1210,6 +1211,8 @@ if (IDVal == ".insn") return ParseDirectiveInsn(DirectiveID.getLoc()); + if (IDVal.startswith(".gnu_attribute")) + return ParseGNUAttribute(DirectiveID.getLoc()); return true; } @@ -1322,6 +1325,25 @@ return false; } +bool SystemZAsmParser::ParseGNUAttribute(SMLoc L) { + int64_t Tag; + int64_t IntegerValue; + if (!Parser.parseGNUAttribute(L, Tag, IntegerValue)) + return false; + + // Tag_GNU_S390_ABI_Vector tag is '8' and can be 0, 1, or 2. + if (Tag != 8 || (IntegerValue < 0 || IntegerValue > 2)) { + Error(Parser.getTok().getLoc(), + "Unrecognized .gnu_attribute tag/value pair."); + return false; + } + + Parser.getStreamer().emitGNUAttribute(Tag, IntegerValue); + + return true; +} + + bool SystemZAsmParser::ParseRegister(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc, bool RestoreOnFailure) { Register Reg; Index: llvm/lib/Target/SystemZ/SystemZAsmPrinter.h =================================================================== --- llvm/lib/Target/SystemZ/SystemZAsmPrinter.h +++ llvm/lib/Target/SystemZ/SystemZAsmPrinter.h @@ -78,6 +78,7 @@ void LowerSTACKMAP(const MachineInstr &MI); void LowerPATCHPOINT(const MachineInstr &MI, SystemZMCInstLower &Lower); void emitEXRLTargetInstructions(); + void emitAttributes(Module &M); }; } // end namespace llvm Index: llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp =================================================================== --- llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp +++ llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "SystemZMachineFunctionInfo.h" #include "SystemZAsmPrinter.h" #include "MCTargetDesc/SystemZInstPrinter.h" #include "SystemZConstantPoolValue.h" @@ -735,6 +736,64 @@ EXRLTargets2Sym.clear(); } +// Returns true the first time Ty is passed and found to be or contain a +// vector type. +bool llvm::SystemZ::isVectorTypeBased(Type *Ty, std::set &Visited) { + if (!Visited.insert(Ty).second) // Avoid infinite recursion via pointers. + return false; + if (Ty->isVectorTy()) + return true; + if (auto *PTy = dyn_cast(Ty)) + return isVectorTypeBased(PTy->getElementType(), Visited); + if (auto *ArrayTy = dyn_cast(Ty)) + return isVectorTypeBased(ArrayTy->getElementType(), Visited); + if (auto *STy = dyn_cast(Ty)) { + for (StructType::element_iterator I = STy->element_begin(), + E = STy->element_end(); I != E; ++I) + if (isVectorTypeBased(*I, Visited)) + return true; + } + if (auto *FTy = dyn_cast(Ty)) { + if (isVectorTypeBased(FTy->getReturnType(), Visited)) + return true; + for (auto I = FTy->param_begin(), E = FTy->param_end(); I != E; ++I) + if (isVectorTypeBased(*I, Visited)) + return true; + } + return false; +} + +// The *alignment* of 128-bit vector types is different between the software +// / hardware vector ABIs, so it is needed to find out if the there is an +// externally visible use of a vector type in the module. The output file +// should then be annotated with a gnu attribute (vector ABI). +void SystemZAsmPrinter::emitAttributes(Module &M) { + std::set VisitedTypes; + bool HasVectorABI = false; + + for (const GlobalValue &GV : M.global_values()) { + if (const Function *F = dyn_cast(&GV)) + if (F->hasFnAttribute("visible-vector-ABI") || + (!F->hasLocalLinkage() && !F->isDeclaration() && + SystemZ::isVectorTypeBased(F->getFunctionType(), VisitedTypes))) { + HasVectorABI = true; + break; + } + if (isa(GV) && !GV.hasLocalLinkage() && + (!GV.isDeclaration() || !GV.use_empty()) && + SystemZ::isVectorTypeBased(GV.getValueType(), VisitedTypes)) { // XXX Add test + HasVectorABI = true; + break; + } + } + + if (HasVectorABI) { + bool HasVectorFeature = + TM.getMCSubtargetInfo()->getFeatureBits()[SystemZ::FeatureVector]; + OutStreamer->emitGNUAttribute(8, HasVectorFeature ? 2 : 1); + } +} + // Convert a SystemZ-specific constant pool modifier into the associated // MCSymbolRefExpr variant kind. static MCSymbolRefExpr::VariantKind @@ -791,6 +850,7 @@ void SystemZAsmPrinter::emitEndOfAsmFile(Module &M) { emitEXRLTargetInstructions(); + emitAttributes(M); emitStackMaps(SM); } Index: llvm/lib/Target/SystemZ/SystemZISelLowering.cpp =================================================================== --- llvm/lib/Target/SystemZ/SystemZISelLowering.cpp +++ llvm/lib/Target/SystemZ/SystemZISelLowering.cpp @@ -1584,6 +1584,36 @@ return true; } +static void +detectVisibleVectorABI(SystemZTargetLowering::CallLoweringInfo &CLI) { + MachineFunction &MF = CLI.DAG.getMachineFunction(); + Function &F = MF.getFunction(); + if (F.hasFnAttribute("visible-vector-ABI") || !CLI.CB) + return; + bool LocalCallee = false; + Value *Callee = CLI.CB->getCalledOperand(); + if (const Function *F = dyn_cast(Callee)) + LocalCallee = F->hasLocalLinkage(); + bool HasVectorABI = false; + std::set VisitedTypes; + if (!LocalCallee && + SystemZ::isVectorTypeBased(Callee->getType(), VisitedTypes)) + HasVectorABI = true; + else if (CLI.IsVarArg) { + // Also check local vararg callee as va_list may be passed on by it. + for (unsigned Idx = CLI.CB->getFunctionType()->getNumParams(); + Idx < CLI.CB->getNumArgOperands(); Idx++) { + Type *ArgTy = CLI.CB->getArgOperand(Idx)->getType(); + if (SystemZ::isVectorTypeBased(ArgTy, VisitedTypes)) { + HasVectorABI = true; + break; + } + } + } + if (HasVectorABI) + F.addFnAttr("visible-vector-ABI"); +} + SDValue SystemZTargetLowering::LowerCall(CallLoweringInfo &CLI, SmallVectorImpl &InVals) const { @@ -1607,6 +1637,8 @@ VerifyVectorTypes(Ins); } + detectVisibleVectorABI(CLI); + // Analyze the operands of the call, assigning locations to each operand. SmallVector ArgLocs; SystemZCCState ArgCCInfo(CallConv, IsVarArg, MF, ArgLocs, Ctx); Index: llvm/lib/Target/SystemZ/SystemZMachineFunctionInfo.h =================================================================== --- llvm/lib/Target/SystemZ/SystemZMachineFunctionInfo.h +++ llvm/lib/Target/SystemZ/SystemZMachineFunctionInfo.h @@ -22,6 +22,8 @@ unsigned GPROffset; GPRRegs() : LowGPR(0), HighGPR(0), GPROffset(0) {} }; + + bool isVectorTypeBased(Type *Ty, std::set &Visited); } class SystemZMachineFunctionInfo : public MachineFunctionInfo { @@ -94,7 +96,6 @@ unsigned getNumLocalDynamicTLSAccesses() const { return NumLocalDynamics; } void incNumLocalDynamicTLSAccesses() { ++NumLocalDynamics; } }; - } // end namespace llvm #endif Index: llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-00.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-00.ll @@ -0,0 +1,14 @@ +; RUN: llc -mtriple=s390x-linux-gnu -mcpu=arch13 < %s | FileCheck %s +; +; Test the emission of a .gnu_attribute describing the vector abi. + +; Call to external function with vector argument. +define internal void @fun() { +entry: + tail call void @foo(<4 x i32> zeroinitializer) + ret void +} + +declare void @foo(<4 x i32>) + +; CHECK: .gnu_attribute 8, 2 Index: llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-01.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-01.ll @@ -0,0 +1,16 @@ +; RUN: llc -mtriple=s390x-linux-gnu -mcpu=arch13 < %s | FileCheck %s +; +; Test the emission of a .gnu_attribute describing the vector abi. + +; Call to external function with vector return value. +define internal void @fun() { +entry: + %indirect-arg-temp = alloca <4 x i32>, align 16 + %C = call <4 x i32> @foo() + store <4 x i32> %C, <4 x i32>* %indirect-arg-temp + ret void +} + +declare <4 x i32> @foo() + +; CHECK: .gnu_attribute 8, 2 Index: llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-02.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-02.ll @@ -0,0 +1,14 @@ +; RUN: llc -mtriple=s390x-linux-gnu -mcpu=arch13 < %s | FileCheck %s +; +; Test the emission of a .gnu_attribute describing the vector abi. + +; Call to external function *without* vector arguments. +define internal void @fun() { +entry: + tail call void @foo(i64 0) + ret void +} + +declare void @foo(i64) + +; CHECK-NOT: .gnu_attribute 8 Index: llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-03.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-03.ll @@ -0,0 +1,17 @@ +; RUN: llc -mtriple=s390x-linux-gnu -mcpu=arch10 < %s | FileCheck %s +; +; Test the emission of a .gnu_attribute describing the vector abi. + +; Calling *local* function with vector argument. +define void @fun() { +entry: + %indirect-arg-temp = alloca <4 x i32>, align 16 + store <4 x i32> zeroinitializer, <4 x i32>* %indirect-arg-temp + call void @foo(<4 x i32>* nonnull %indirect-arg-temp) + ret void +} + +define internal void @foo(<4 x i32>*) { ret void } + +; CHECK-LABEL: fun: +; CHECK-NOT: .gnu_attribute 8 Index: llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-04.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-04.ll @@ -0,0 +1,18 @@ +; RUN: llc -mtriple=s390x-linux-gnu -mcpu=arch10 < %s | FileCheck %s +; +; Test the emission of a .gnu_attribute describing the vector abi. + +; Call via global function pointer in internal function. + +@foo = global void (<4 x i32>*)* null, align 8 + +define internal void @fun() { +entry: + %V = alloca <4 x i32>, align 16 + store <4 x i32> zeroinitializer, <4 x i32>* %V, align 16 + %FuncPtr = load void (<4 x i32>*)*, void (<4 x i32>*)** @foo, align 8 + call void %FuncPtr(<4 x i32>* nonnull %V) + ret void +} + +; CHECK: .gnu_attribute 8, 1 Index: llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-05.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-05.ll @@ -0,0 +1,21 @@ +; RUN: llc -mtriple=s390x-linux-gnu -mcpu=arch13 < %s | FileCheck %s +; +; Test the emission of a .gnu_attribute describing the vector abi. + +; Passing address of local function with vector arg to global function. + +define void @fun() { +entry: + tail call void @GlobFun(void (<4 x i32>*)* nonnull @InternalFun) + ret void +} + +declare void @GlobFun(void (<4 x i32>*)*) + +define internal void @InternalFun(<4 x i32>* nocapture %V) { +entry: + store <4 x i32> zeroinitializer, <4 x i32>* %V, align 8 + ret void +} + +; CHECK: .gnu_attribute 8, 2 Index: llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-06.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-06.ll @@ -0,0 +1,15 @@ +; RUN: llc -mtriple=s390x-linux-gnu -mcpu=arch13 < %s | FileCheck %s +; +; Test the emission of a .gnu_attribute describing the vector abi. + +; Passing address of an array of vectors. +define internal void @fun() { +entry: + %V = alloca [2 x <4 x i32>], align 16 + call void ([2 x <4 x i32>]*) @foo([2 x <4 x i32>]* nonnull %V) + ret void +} + +declare void @foo([2 x <4 x i32>]* %V) + +; CHECK: .gnu_attribute 8, 2 Index: llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-07.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-07.ll @@ -0,0 +1,16 @@ +; RUN: llc -mtriple=s390x-linux-gnu -mcpu=arch10 < %s | FileCheck %s +; +; Test the emission of a .gnu_attribute describing the vector abi. + +; Passing a struct containing a vector element. +%struct.S = type { <4 x i32>, i32, [12 x i8] } +define internal void @fun() { +entry: + %V = alloca %struct.S, align 16 + call void (%struct.S*) @foo(%struct.S* nonnull %V) + ret void +} + +declare void @foo(%struct.S*) + +; CHECK: .gnu_attribute 8, 1 Index: llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-08.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-08.ll @@ -0,0 +1,14 @@ +; RUN: llc -mtriple=s390x-linux-gnu -mcpu=arch13 < %s | FileCheck %s +; +; Test the emission of a .gnu_attribute describing the vector abi. + +; Call to vararg function with a vector argument. +define internal void @fun() { +entry: + call void (...) @foo(<2 x i64> zeroinitializer) + ret void +} + +declare void @foo(...) + +; CHECK: .gnu_attribute 8, 2 Index: llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-09.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-09.ll @@ -0,0 +1,14 @@ +; RUN: llc -mtriple=s390x-linux-gnu -mcpu=arch10 < %s | FileCheck %s +; +; Test the emission of a .gnu_attribute describing the vector abi. + +; Call to vararg function *without* any vector argument. +define internal void @fun() { +entry: + call void (...) @foo(i32 0) + ret void +} + +declare void @foo(...) + +; CHECK-NOT: .gnu_attribute 8 Index: llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-10.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-10.ll @@ -0,0 +1,16 @@ +; RUN: llc -mtriple=s390x-linux-gnu -mcpu=arch10 < %s | FileCheck %s +; +; Test the emission of a .gnu_attribute describing the vector abi. + +; Call to varargs function via global pointer to pointer to function. + +@fptrp = common global void (i32, ...)** null, align 8 + +define void @fun() { + %1 = load void (i32, ...)**, void (i32, ...)*** @fptrp, align 8 + %2 = load void (i32, ...)*, void (i32, ...)** %1, align 8 + tail call void (i32, ...) %2(i32 signext 0, <4 x i32> zeroinitializer) + ret void +} + +; CHECK: .gnu_attribute 8, 1 Index: llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-11.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-11.ll @@ -0,0 +1,13 @@ +; RUN: llc -mtriple=s390x-linux-gnu -mcpu=arch13 < %s | FileCheck %s +; +; Test the emission of a .gnu_attribute describing the vector abi. + +; Passing vector argument to varargs function via local function pointer. + +define void @fun(void (i32, ...)* nocapture %fptr) { +entry: + tail call void (i32, ...) %fptr(i32 signext 0, <4 x i32> zeroinitializer) + ret void +} + +; CHECK: .gnu_attribute 8, 2 Index: llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-12.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-12.ll @@ -0,0 +1,18 @@ +; RUN: llc -mtriple=s390x-linux-gnu -mcpu=arch13 < %s | FileCheck %s +; +; Test the emission of a .gnu_attribute describing the vector abi. + +; Passing a vararg vector argument should always emit the gnu attribute as +; the va_list may potentially be passed on to another (global) function. + +define internal void @foo(i32 %a, ...) { +entry: + ret void +} + +define internal void @fun() { + call void (i32, ...) @foo(i32 1, <4 x i32> zeroinitializer); + ret void +} + +; CHECK: .gnu_attribute 8, 2 Index: llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-13.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-13.ll @@ -0,0 +1,11 @@ +; RUN: llc -mtriple=s390x-linux-gnu -mcpu=arch13 < %s | FileCheck %s +; +; Test the emission of a .gnu_attribute describing the vector abi. + +; Defining globally visible function with vector arguments. +define <4 x i32> @fun(<4 x i32> %Arg) { +entry: + ret <4 x i32> %Arg +} + +; CHECK: .gnu_attribute 8, 2 Index: llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-14.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-14.ll @@ -0,0 +1,8 @@ +; RUN: llc -mtriple=s390x-linux-gnu -mcpu=arch10 < %s | FileCheck %s +; +; Test the emission of a .gnu_attribute describing the vector abi. + +; Globally visible vector variable. +@VisibleVector = global <4 x i32> zeroinitializer + +; CHECK: .gnu_attribute 8, 1 Index: llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-15.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-15.ll @@ -0,0 +1,14 @@ +; RUN: llc -mtriple=s390x-linux-gnu -mcpu=arch13 < %s +; +; Test the emission of a .gnu_attribute describing the vector abi. + +; Using external vector variable. +@ExtVector = external global <4 x i32> + +define void @fun() { +entry: + store <4 x i32> zeroinitializer, <4 x i32>* @ExtVector + ret void +} + +; CHECK: .gnu_attribute 8, 2 Index: llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-16.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/SystemZ/vec-abi-gnuattr-16.ll @@ -0,0 +1,14 @@ +; RUN: llc -mtriple=s390x-linux-gnu -mcpu=arch13 < %s | FileCheck %s +; +; Test the emission of a .gnu_attribute describing the vector abi. + +; Using *local* vector variable. +@LocVector = internal global <4 x i32> zeroinitializer + +define void @fun() { +entry: + store volatile <4 x i32> zeroinitializer, <4 x i32>* @LocVector + ret void +} + +; CHECK-NOT: .gnu_attribute 8 Index: llvm/test/MC/SystemZ/gnu-attributes.s =================================================================== --- /dev/null +++ llvm/test/MC/SystemZ/gnu-attributes.s @@ -0,0 +1,17 @@ +# RUN: llvm-mc -triple s390x-linux-gnu -filetype=asm %s | \ +# RUN: FileCheck %s --check-prefix=ASM +# RUN: llvm-mc -triple s390x-linux-gnu -filetype=obj %s | \ +# RUN: llvm-objdump --mcpu=z14 -D - | FileCheck %s --check-prefix=OBJ + +#ASM: .text +#ASM: .gnu_attribute 8, 2 + +#OBJ: 0000000000000000 <.gnu.attributes>: +#OBJ: 0: 41 00 00 00 +#OBJ: 4: 0f 67 +#OBJ: 6: 6e 75 00 01 +#OBJ: a: 00 00 +#OBJ: c: 00 07 +#OBJ: e: 08 02 + + .gnu_attribute 8, 2