Index: clang/lib/CodeGen/TargetInfo.cpp =================================================================== --- clang/lib/CodeGen/TargetInfo.cpp +++ clang/lib/CodeGen/TargetInfo.cpp @@ -7566,6 +7566,10 @@ bool IsVector = false; CharUnits UnpaddedSize; CharUnits DirectAlign; + llvm::Module &M = CGF.CGM.getModule(); + if (!CGF.CurFn->hasLocalLinkage() && ArgTy->isVectorTy() && + !M.getModuleFlag("visible-vector-ABI")) + M.addModuleFlag(llvm::Module::Warning, "visible-vector-ABI", 1); 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,26 @@ +// 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: !llvm.module.flags = !{!0, !1} +//ARCH10-ATTR: !0 = !{i32 2, !"visible-vector-ABI", i32 1} +//ARCH13-ATTR: !llvm.module.flags = !{!0, !1} +//ARCH13-ATTR: !0 = !{i32 2, !"visible-vector-ABI", i32 1} + +//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 @@ -432,6 +432,7 @@ bool ParseDirectiveInsn(SMLoc L); bool ParseDirectiveMachine(SMLoc L); + bool ParseGNUAttribute(SMLoc L); OperandMatchResultTy parseAddress(OperandVector &Operands, MemoryKind MemKind, @@ -1224,6 +1225,8 @@ return ParseDirectiveInsn(DirectiveID.getLoc()); if (IDVal == ".machine") return ParseDirectiveMachine(DirectiveID.getLoc()); + if (IDVal.startswith(".gnu_attribute")) + return ParseGNUAttribute(DirectiveID.getLoc()); return true; } @@ -1358,6 +1361,24 @@ 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 @@ -57,6 +57,7 @@ StringRef getPassName() const override { return "SystemZ Assembly Printer"; } void emitInstruction(const MachineInstr *MI) override; void emitMachineConstantPoolValue(MachineConstantPoolValue *MCPV) override; + void emitEndOfAsmFile(Module &M) override; bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, const char *ExtraCode, raw_ostream &OS) override; bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo, @@ -74,6 +75,7 @@ void LowerFENTRY_CALL(const MachineInstr &MI, SystemZMCInstLower &MCIL); void LowerSTACKMAP(const MachineInstr &MI); void LowerPATCHPOINT(const MachineInstr &MI, SystemZMCInstLower &Lower); + 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 @@ -15,6 +15,7 @@ #include "MCTargetDesc/SystemZInstPrinter.h" #include "SystemZConstantPoolValue.h" #include "SystemZMCInstLower.h" +#include "SystemZMachineFunctionInfo.h" #include "TargetInfo/SystemZTargetInfo.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/CodeGen/MachineModuleInfoImpls.h" @@ -759,6 +760,63 @@ getSubtargetInfo()); } +// Returns true (the first time) when Ty is found to be or contain a vector +// type. +bool llvm::SystemZ::isVectorTypeBased(Type *Ty) { + // Opaque pointers could potentially always involve vectors. + if (Ty->isVectorTy() || Ty->isPointerTy()) + return true; + if (auto *ArrayTy = dyn_cast(Ty)) + return isVectorTypeBased(ArrayTy->getElementType()); + if (auto *STy = dyn_cast(Ty)) { + for (StructType::element_iterator I = STy->element_begin(), + E = STy->element_end(); + I != E; ++I) + if (isVectorTypeBased(*I)) + return true; + } + if (auto *FTy = dyn_cast(Ty)) { + if (isVectorTypeBased(FTy->getReturnType())) + return true; + for (auto I = FTy->param_begin(), E = FTy->param_end(); I != E; ++I) + if (isVectorTypeBased(*I)) + return true; + } + return false; +} + +// The *alignment* of 128-bit vector types is different between the software +// and hardware vector ABIs. If the there is an externally visible use of a +// vector type in the module it should be annotated with an attribute. +void SystemZAsmPrinter::emitAttributes(Module &M) { + bool HasVectorABI = M.getModuleFlag("visible-vector-ABI"); + + if (!HasVectorABI) { + for (const GlobalValue &GV : M.global_values()) { + // Externally visible function definitions. + if (const Function *F = dyn_cast(&GV)) + if (!F->hasLocalLinkage() && !F->isDeclaration() && + SystemZ::isVectorTypeBased(F->getFunctionType())) { + HasVectorABI = true; + break; + } + // Externally visible variables. + if (isa(GV) && !GV.hasLocalLinkage() && + (!GV.isDeclaration() || !GV.use_empty()) && + SystemZ::isVectorTypeBased(GV.getValueType())) { + 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 @@ -817,6 +875,10 @@ return false; } +void SystemZAsmPrinter::emitEndOfAsmFile(Module &M) { + emitAttributes(M); +} + void SystemZAsmPrinter::emitFunctionBodyEnd() { if (TM.getTargetTriple().isOSzOS()) { // Emit symbol for the end of function if the z/OS target streamer Index: llvm/lib/Target/SystemZ/SystemZISelLowering.cpp =================================================================== --- llvm/lib/Target/SystemZ/SystemZISelLowering.cpp +++ llvm/lib/Target/SystemZ/SystemZISelLowering.cpp @@ -1658,6 +1658,31 @@ return true; } +static bool hasVisibleVectorABI(SystemZTargetLowering::CallLoweringInfo &CLI) { + Value *Callee = CLI.CB->getCalledOperand(); + bool LocalCallee = false; + if (const Function *F = dyn_cast(Callee)) + LocalCallee = F->hasLocalLinkage(); + + if (!LocalCallee && + SystemZ::isVectorTypeBased(CLI.CB->getFunctionType())) + // External or indirect function call. + return true; + else if (CLI.IsVarArg) { + // Check the passed varargs. Do this as well in case of a local callee as + // va_list may be passed on by it but cannot be identified as a parameter + // by isVectorTypeBased(). + for (unsigned Idx = CLI.CB->getFunctionType()->getNumParams(); + Idx < CLI.CB->arg_size(); Idx++) { + Type *ArgTy = CLI.CB->getArgOperand(Idx)->getType(); + if (SystemZ::isVectorTypeBased(ArgTy)) + return true; + } + } + + return false; +} + SDValue SystemZTargetLowering::LowerCall(CallLoweringInfo &CLI, SmallVectorImpl &InVals) const { @@ -1672,6 +1697,7 @@ CallingConv::ID CallConv = CLI.CallConv; bool IsVarArg = CLI.IsVarArg; MachineFunction &MF = DAG.getMachineFunction(); + Module &M = *MF.getFunction().getParent(); EVT PtrVT = getPointerTy(MF.getDataLayout()); LLVMContext &Ctx = *DAG.getContext(); SystemZCallingConventionRegisters *Regs = Subtarget.getSpecialRegisters(); @@ -1686,6 +1712,10 @@ VerifyVectorTypes(Ins); } + if (!M.getModuleFlag("visible-vector-ABI") && CLI.CB && + hasVisibleVectorABI(CLI)) + M.addModuleFlag(llvm::Module::Warning, "visible-vector-ABI", 1); + // 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); } class SystemZMachineFunctionInfo : public MachineFunctionInfo { 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, ptr %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, ptr %indirect-arg-temp + call void @foo(ptr nonnull %indirect-arg-temp) + ret void +} + +define internal void @foo(ptr) { 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 (ptr)* null, align 8 + +define internal void @fun() { +entry: + %V = alloca <4 x i32>, align 16 + store <4 x i32> zeroinitializer, ptr %V, align 16 + %FuncPtr = load void (ptr)*, void (ptr)** @foo, align 8 + call void %FuncPtr(ptr 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 internal void @InternalFun(<4 x i32> %V) { +entry: + store <4 x i32> %V, ptr undef + ret void +} + +declare void @GlobFun(ptr) + +define void @fun() { +entry: + tail call void @GlobFun(ptr nonnull @InternalFun) + 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,16 @@ +; RUN: llc -mtriple=s390x-linux-gnu -mcpu=arch13 < %s | FileCheck %s +; +; Test the emission of a .gnu_attribute describing the vector abi. + +; Passing an array of vectors. +define internal void @fun() { +entry: + %V = alloca [2 x <4 x i32>], align 16 + %L = load [2 x <4 x i32>], ptr %V + call void ([2 x <4 x i32>]) @foo([2 x <4 x i32>] %L) + 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,17 @@ +; 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 + %L = load %struct.S, ptr %V + call void (%struct.S) @foo(%struct.S %L) + 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 ptr null, align 8 + +define void @fun() { + %1 = load ptr, void (i32, ...)*** @fptrp, align 8 + %2 = load ptr, 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(ptr 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, ptr @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, ptr @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