Skip to content

Commit

Permalink
[SystemZ] Emit a .gnu_attribute for an externally visible vector abi.
Browse files Browse the repository at this point in the history
On SystemZ, the vector ABI changes depending on the presence of hardware
vector support. Therefore, each binary compiled with a visible vector ABI
(e.g. one that calls an external function with a vector argument) should be
marked with a .gnu_attribute describing this.

Reviewed By: uweigand

Differential Revision: https://reviews.llvm.org/D105067
  • Loading branch information
JonPsson committed Dec 6, 2022
1 parent fc46d6e commit 481bb44
Show file tree
Hide file tree
Showing 31 changed files with 641 additions and 7 deletions.
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CodeGenTypes.h
Expand Up @@ -109,6 +109,7 @@ class CodeGenTypes {
const llvm::DataLayout &getDataLayout() const {
return TheModule.getDataLayout();
}
CodeGenModule &getCGM() const { return CGM; }
ASTContext &getContext() const { return Context; }
const ABIInfo &getABIInfo() const { return TheABIInfo; }
const TargetInfo &getTarget() const { return Target; }
Expand Down
96 changes: 89 additions & 7 deletions clang/lib/CodeGen/TargetInfo.cpp
Expand Up @@ -7402,18 +7402,20 @@ class SystemZABIInfo : public ABIInfo {
ABIArgInfo classifyReturnType(QualType RetTy) const;
ABIArgInfo classifyArgumentType(QualType ArgTy) const;

void computeInfo(CGFunctionInfo &FI) const override {
if (!getCXXABI().classifyReturnType(FI))
FI.getReturnInfo() = classifyReturnType(FI.getReturnType());
for (auto &I : FI.arguments())
I.info = classifyArgumentType(I.type);
}

void computeInfo(CGFunctionInfo &FI) const override;
Address EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
QualType Ty) const override;
};

class SystemZTargetCodeGenInfo : public TargetCodeGenInfo {
// These are used for speeding up the search for a visible vector ABI.
mutable bool HasVisibleVecABIFlag = false;
mutable std::set<const Type *> 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;

public:
SystemZTargetCodeGenInfo(CodeGenTypes &CGT, bool HasVector, bool SoftFloatABI)
: TargetCodeGenInfo(
Expand All @@ -7422,6 +7424,37 @@ class SystemZTargetCodeGenInfo : public TargetCodeGenInfo {
std::make_unique<SwiftABIInfo>(CGT, /*SwiftErrorInRegister=*/false);
}

// The vector ABI is different when the vector facility is present and when
// a module e.g. defines an externally visible vector variable, a flag
// 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)) {
M.getModule().addModuleFlag(llvm::Module::Warning,
"s390x-visible-vector-ABI", 1);
HasVisibleVecABIFlag = true;
}
}

void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV,
CodeGen::CodeGenModule &M) const override {
if (!D)
return;

// Check if the vector ABI becomes visible by an externally visible
// variable or function.
if (const auto *VD = dyn_cast<VarDecl>(D)) {
if (VD->isExternallyVisible())
handleExternallyVisibleObjABI(VD->getType().getTypePtr(), M);
}
else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
if (FD->isExternallyVisible())
handleExternallyVisibleObjABI(FD->getType().getTypePtr(), M);
}
}

llvm::Value *testFPKind(llvm::Value *V, unsigned BuiltinID,
CGBuilderTy &Builder,
CodeGenModule &CGM) const override {
Expand Down Expand Up @@ -7579,6 +7612,9 @@ Address SystemZABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
// Every non-vector argument occupies 8 bytes and is passed by preference
// in either GPRs or FPRs. Vector arguments occupy 8 or 16 bytes and are
// always passed on the stack.
const SystemZTargetCodeGenInfo &SZCGI =
static_cast<const SystemZTargetCodeGenInfo &>(
CGT.getCGM().getTargetCodeGenInfo());
Ty = getContext().getCanonicalType(Ty);
auto TyInfo = getContext().getTypeInfoInChars(Ty);
llvm::Type *ArgTy = CGF.ConvertTypeForMem(Ty);
Expand All @@ -7589,6 +7625,7 @@ Address SystemZABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
bool IsVector = false;
CharUnits UnpaddedSize;
CharUnits DirectAlign;
SZCGI.handleExternallyVisibleObjABI(Ty.getTypePtr(), CGT.getCGM());
if (IsIndirect) {
DirectTy = llvm::PointerType::getUnqual(DirectTy);
UnpaddedSize = DirectAlign = CharUnits::fromQuantity(8);
Expand Down Expand Up @@ -7783,6 +7820,51 @@ ABIArgInfo SystemZABIInfo::classifyArgumentType(QualType Ty) const {
return ABIArgInfo::getDirect(nullptr);
}

void SystemZABIInfo::computeInfo(CGFunctionInfo &FI) const {
const SystemZTargetCodeGenInfo &SZCGI =
static_cast<const SystemZTargetCodeGenInfo &>(
CGT.getCGM().getTargetCodeGenInfo());
if (!getCXXABI().classifyReturnType(FI))
FI.getReturnInfo() = classifyReturnType(FI.getReturnType());
unsigned Idx = 0;
for (auto &I : FI.arguments()) {
I.info = classifyArgumentType(I.type);
if (FI.isVariadic() && Idx++ >= FI.getNumRequiredArgs())
// 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());
}
}

bool SystemZTargetCodeGenInfo::isVectorTypeBased(const Type *Ty) const {
while (Ty->isPointerType() || Ty->isArrayType())
Ty = Ty->getPointeeOrArrayElementType();
if (!SeenTypes.insert(Ty).second)
return false;
if (Ty->isVectorType())
return true;
if (const auto *RecordTy = Ty->getAs<RecordType>()) {
const RecordDecl *RD = RecordTy->getDecl();
if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD))
if (CXXRD->hasDefinition())
for (const auto &I : CXXRD->bases())
if (isVectorTypeBased(I.getType().getTypePtr()))
return true;
for (const auto *FD : RD->fields())
if (isVectorTypeBased(FD->getType().getTypePtr()))
return true;
}
if (const auto *FT = Ty->getAs<FunctionType>())
if (isVectorTypeBased(FT->getReturnType().getTypePtr()))
return true;
if (const FunctionProtoType *Proto = Ty->getAs<FunctionProtoType>())
for (auto ParamType : Proto->getParamTypes())
if (isVectorTypeBased(ParamType.getTypePtr()))
return true;
return false;
}

//===----------------------------------------------------------------------===//
// MSP430 ABI Implementation
//===----------------------------------------------------------------------===//
Expand Down
28 changes: 28 additions & 0 deletions clang/test/CodeGen/SystemZ/vec-abi-gnuattr-00.c
@@ -0,0 +1,28 @@
// RUN: %clang_cc1 -triple s390x-ibm-linux -target-cpu arch10 -emit-llvm \
// RUN: -fzvector -o - %s 2>&1 | FileCheck %s --check-prefix=MODFLAG
//
// Test the emission of the "s390x-visible-vector-ABI" module flag.

// 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
//
// Test the emission of a gnu attribute describing the vector ABI.

// Call to external function with vector argument.

typedef __attribute__((vector_size(16))) int v4i32;

void bar(v4i32 Arg);

void foo() {
v4i32 Var = {0, 0, 0, 0};
bar(Var);
}

//MODFLAG: !llvm.module.flags = !{!0, !1}
//MODFLAG: !0 = !{i32 2, !"s390x-visible-vector-ABI", i32 1}

//ARCH10-ASM: .gnu_attribute 8, 1
//ARCH13-ASM: .gnu_attribute 8, 2
18 changes: 18 additions & 0 deletions clang/test/CodeGen/SystemZ/vec-abi-gnuattr-01.c
@@ -0,0 +1,18 @@
// RUN: %clang_cc1 -triple s390x-ibm-linux -target-cpu arch13 -emit-llvm \
// RUN: -fzvector -o - %s 2>&1 | FileCheck %s
//
// Test the emission of the "s390x-visible-vector-ABI" module flag.

// Call to external function with vector return value.

typedef __attribute__((vector_size(16))) int v4i32;

v4i32 bar(void);

void foo(v4i32 *Dst) {
v4i32 Var = bar();
*Dst = Var;
}

//CHECK: !llvm.module.flags = !{!0, !1}
//CHECK: !0 = !{i32 2, !"s390x-visible-vector-ABI", i32 1}
15 changes: 15 additions & 0 deletions clang/test/CodeGen/SystemZ/vec-abi-gnuattr-02.c
@@ -0,0 +1,15 @@
// 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 not emitted.

// Call to external function *without* vector argument.

void bar(int arg);

void foo() {
int Var = 0;
bar(Var);
}

//CHECK-NOT: !{i32 2, !"s390x-visible-vector-ABI", i32 1}
17 changes: 17 additions & 0 deletions clang/test/CodeGen/SystemZ/vec-abi-gnuattr-03.c
@@ -0,0 +1,17 @@
// RUN: %clang_cc1 -triple s390x-ibm-linux -emit-llvm -fzvector -o - %s \
// RUN: -Wno-undefined-internal 2>&1 | FileCheck %s
//
// Test that the "s390x-visible-vector-ABI" module flag is not emitted.

// Calling *local* function with vector argument.

typedef __attribute__((vector_size(16))) int v4i32;

static void bar(v4i32 arg);

void foo() {
v4i32 Var = {0, 0, 0, 0};
bar(Var);
}

//CHECK-NOT: !{i32 2, !"s390x-visible-vector-ABI", i32 1}
20 changes: 20 additions & 0 deletions clang/test/CodeGen/SystemZ/vec-abi-gnuattr-04.c
@@ -0,0 +1,20 @@
// 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 via global function pointer in internal function, with vector argument.

typedef __attribute__((vector_size(16))) int v4i32;

void (*bar)(v4i32 Arg);

static void foo() {
v4i32 Var = {0, 0, 0, 0};
(*bar)(Var);
}

void fun() { foo(); }

//CHECK: !llvm.module.flags = !{!0, !1}
//CHECK: !0 = !{i32 2, !"s390x-visible-vector-ABI", i32 1}
20 changes: 20 additions & 0 deletions clang/test/CodeGen/SystemZ/vec-abi-gnuattr-05.c
@@ -0,0 +1,20 @@
// 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 via global function pointer in internal function, with vector return
// value.

typedef __attribute__((vector_size(16))) int v4i32;

v4i32 (*bar)(int);

static int foo() {
(*bar)(0)[0];
}

int fun() { return foo(); }

//CHECK: !llvm.module.flags = !{!0, !1}
//CHECK: !0 = !{i32 2, !"s390x-visible-vector-ABI", i32 1}
21 changes: 21 additions & 0 deletions clang/test/CodeGen/SystemZ/vec-abi-gnuattr-06.c
@@ -0,0 +1,21 @@
// 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 address of local function with vector arg to global function.

typedef __attribute__((vector_size(16))) int v4i32;

void GlobFun(v4i32 (*f)(v4i32));

static v4i32 foo(v4i32 Arg) {
return Arg;
}

void fun() {
GlobFun(foo);
}

//CHECK: !llvm.module.flags = !{!0, !1}
//CHECK: !0 = !{i32 2, !"s390x-visible-vector-ABI", i32 1}
18 changes: 18 additions & 0 deletions clang/test/CodeGen/SystemZ/vec-abi-gnuattr-07.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.

// Passing an array of vectors.

typedef __attribute__((vector_size(16))) int v4i32;

void bar(v4i32 Arg[2]);

void foo() {
v4i32 Var[2] = {{0, 0, 0, 0}, {0, 0, 0, 0}};
bar(Var);
}

//CHECK: !llvm.module.flags = !{!0, !1}
//CHECK: !0 = !{i32 2, !"s390x-visible-vector-ABI", i32 1}
23 changes: 23 additions & 0 deletions clang/test/CodeGen/SystemZ/vec-abi-gnuattr-08.c
@@ -0,0 +1,23 @@
// 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 struct containing a vector element.

typedef __attribute__((vector_size(16))) int v4i32;

struct S {
int A;
v4i32 B;
};

void bar(struct S Arg);

void foo() {
struct S Var = {0, {0, 0, 0, 0}};
bar(Var);
}

//CHECK: !llvm.module.flags = !{!0, !1}
//CHECK: !0 = !{i32 2, !"s390x-visible-vector-ABI", i32 1}
18 changes: 18 additions & 0 deletions clang/test/CodeGen/SystemZ/vec-abi-gnuattr-09.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 vector argument.

typedef __attribute__((vector_size(16))) int v4i32;

void bar(int N, ...);

void foo() {
v4i32 Var = {0, 0, 0, 0};
bar(0, Var);
}

//CHECK: !llvm.module.flags = !{!0, !1}
//CHECK: !0 = !{i32 2, !"s390x-visible-vector-ABI", i32 1}
17 changes: 17 additions & 0 deletions clang/test/CodeGen/SystemZ/vec-abi-gnuattr-10.c
@@ -0,0 +1,17 @@
// 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 *without* any vector argument.

typedef __attribute__((vector_size(16))) int v4i32;

void bar(int N, ...);

void foo() {
int Var = 0;
bar(0, Var);
}

//CHECK-NOT: !{i32 2, !"s390x-visible-vector-ABI", i32 1}
18 changes: 18 additions & 0 deletions clang/test/CodeGen/SystemZ/vec-abi-gnuattr-11.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 varargs function via global pointer to pointer to function.

typedef __attribute__((vector_size(16))) int v4i32;

void (**bar)(int N, ...);

void foo() {
v4i32 Var = {0, 0, 0, 0};
(**bar)(0, Var);
}

//CHECK: !llvm.module.flags = !{!0, !1}
//CHECK: !0 = !{i32 2, !"s390x-visible-vector-ABI", i32 1}

0 comments on commit 481bb44

Please sign in to comment.