[clang] [clang][CodeGen][X86_64] Honor per-function AVX ABI in C/C++ call paths, maintain old psABI for PlayStation. (PR #193298)
Benjamin Luke via cfe-commits
cfe-commits at lists.llvm.org
Sun May 10 02:41:39 PDT 2026
https://github.com/freaknbigpanda updated https://github.com/llvm/llvm-project/pull/193298
>From 48cdce0b6627161faa3509f5dd9d0d509bbde95b Mon Sep 17 00:00:00 2001
From: Benjamin Luke <benjamin.luke at sony.com>
Date: Mon, 9 Mar 2026 14:00:09 -0700
Subject: [PATCH] [clang][CodeGen][X86_64] Honor per-function AVX ABI in C/C++
call paths, maintain old psABI for PlayStation.
Wire per fucntion x86 AVX ABI level into CodeGen arrangement methods so target("avx") /
target("avx512f") on methods, ctors, and free-functions affects ABI lowering consistently.
Specifically:
- Added X86AVXABILevel member to CGFunctionInfo.
- Populated X86AVXABILevel member in CGFunctionInfo objects via arrangement methods declared
in CodeGenTypes.h.
- Respect CGFunctionInfo AVX Level in X86_64ABIInfo::computeInfo.
- Call site ABI is determined by caller AVX level, matching GCC behaviour
- Add/extend regression tests for:
- free-function target-attribute AVX ABI lowering
- C++ method/ctor target-attribute AVX ABI lowering
- PS4/PS5 legacy ABI behavior (no per-function AVX ABI change)
---
clang/include/clang/Basic/ABIVersions.def | 5 +
clang/include/clang/CodeGen/CGFunctionInfo.h | 16 +-
clang/include/clang/CodeGen/CodeGenABITypes.h | 6 +-
clang/lib/CodeGen/CGCall.cpp | 147 ++++++++++++++----
clang/lib/CodeGen/CGClass.cpp | 7 +-
clang/lib/CodeGen/CGExpr.cpp | 2 +-
clang/lib/CodeGen/CGExprCXX.cpp | 11 +-
clang/lib/CodeGen/CodeGenABITypes.cpp | 13 +-
clang/lib/CodeGen/CodeGenFunction.cpp | 7 +
clang/lib/CodeGen/CodeGenFunction.h | 1 +
clang/lib/CodeGen/CodeGenModule.cpp | 38 ++++-
clang/lib/CodeGen/CodeGenModule.h | 3 +
clang/lib/CodeGen/CodeGenTypes.h | 34 +++-
clang/lib/CodeGen/Targets/X86.cpp | 13 ++
clang/test/CodeGen/sysv_abi.c | 18 +++
clang/test/CodeGen/target-avx-function-abi.c | 71 +++++++++
.../test/CodeGenCXX/target-avx-method-abi.cpp | 121 ++++++++++++++
.../unittests/CodeGen/CodeGenExternalTest.cpp | 136 ++++++++++++++--
clang/unittests/CodeGen/TestCompiler.h | 16 +-
19 files changed, 589 insertions(+), 76 deletions(-)
create mode 100644 clang/test/CodeGen/target-avx-function-abi.c
create mode 100644 clang/test/CodeGenCXX/target-avx-method-abi.cpp
diff --git a/clang/include/clang/Basic/ABIVersions.def b/clang/include/clang/Basic/ABIVersions.def
index 92edcd830f031..bc083b19dd4a5 100644
--- a/clang/include/clang/Basic/ABIVersions.def
+++ b/clang/include/clang/Basic/ABIVersions.def
@@ -135,6 +135,11 @@ ABI_VER_MAJOR(20)
/// operator delete.
ABI_VER_MAJOR(21)
+/// Attempt to be ABI-compatible with code generated by Clang 22.0.x.
+/// This causes clang to:
+/// - Not ignore function __attribute(__target(...)) directives that change the ABI
+ABI_VER_MAJOR(22)
+
/// Conform to the underlying platform's C and C++ ABIs as closely as we can.
ABI_VER_LATEST(Latest)
diff --git a/clang/include/clang/CodeGen/CGFunctionInfo.h b/clang/include/clang/CodeGen/CGFunctionInfo.h
index 713b52a4cc2b8..a394d18780e6e 100644
--- a/clang/include/clang/CodeGen/CGFunctionInfo.h
+++ b/clang/include/clang/CodeGen/CGFunctionInfo.h
@@ -653,6 +653,10 @@ class CGFunctionInfo final
/// Log 2 of the maximum vector width.
unsigned MaxVectorWidth : 4;
+ /// Effective x86 AVX ABI level used during ABI classification. Used to
+ /// support target attributes.
+ unsigned X86AVXABILevel : 2;
+
RequiredArgs Required;
/// The struct representing all arguments passed in memory. Only used when
@@ -683,7 +687,8 @@ class CGFunctionInfo final
public:
static CGFunctionInfo *
create(unsigned llvmCC, bool instanceMethod, bool chainCall,
- bool delegateCall, const FunctionType::ExtInfo &extInfo,
+ bool delegateCall, unsigned X86AVXABILevel,
+ const FunctionType::ExtInfo &extInfo,
ArrayRef<ExtParameterInfo> paramInfos, CanQualType resultType,
ArrayRef<CanQualType> argTypes, RequiredArgs required);
void operator delete(void *p) { ::operator delete(p); }
@@ -809,6 +814,12 @@ class CGFunctionInfo final
MaxVectorWidth = llvm::countr_zero(Width) + 1;
}
+ unsigned getX86AVXABILevel() const { return X86AVXABILevel; }
+ void setX86AVXABILevel(unsigned Level) {
+ assert(Level <= 2 && "invalid AVX ABI level");
+ X86AVXABILevel = Level;
+ }
+
void Profile(llvm::FoldingSetNodeID &ID) {
ID.AddInteger(getASTCallingConvention());
ID.AddBoolean(InstanceMethod);
@@ -821,6 +832,7 @@ class CGFunctionInfo final
ID.AddInteger(RegParm);
ID.AddBoolean(NoCfCheck);
ID.AddBoolean(CmseNSCall);
+ ID.AddInteger(X86AVXABILevel);
ID.AddInteger(Required.getOpaqueData());
ID.AddBoolean(HasExtParameterInfos);
if (HasExtParameterInfos) {
@@ -833,6 +845,7 @@ class CGFunctionInfo final
}
static void Profile(llvm::FoldingSetNodeID &ID, bool InstanceMethod,
bool ChainCall, bool IsDelegateCall,
+ unsigned X86AVXABILevel,
const FunctionType::ExtInfo &info,
ArrayRef<ExtParameterInfo> paramInfos,
RequiredArgs required, CanQualType resultType,
@@ -848,6 +861,7 @@ class CGFunctionInfo final
ID.AddInteger(info.getRegParm());
ID.AddBoolean(info.getNoCfCheck());
ID.AddBoolean(info.getCmseNSCall());
+ ID.AddInteger(X86AVXABILevel);
ID.AddInteger(required.getOpaqueData());
ID.AddBoolean(!paramInfos.empty());
if (!paramInfos.empty()) {
diff --git a/clang/include/clang/CodeGen/CodeGenABITypes.h b/clang/include/clang/CodeGen/CodeGenABITypes.h
index 627dd0f0453ac..b9b9a8a23c7b2 100644
--- a/clang/include/clang/CodeGen/CodeGenABITypes.h
+++ b/clang/include/clang/CodeGen/CodeGenABITypes.h
@@ -81,13 +81,13 @@ const CGFunctionInfo &
arrangeCXXMethodCall(CodeGenModule &CGM, CanQualType returnType,
ArrayRef<CanQualType> argTypes, FunctionType::ExtInfo info,
ArrayRef<FunctionProtoType::ExtParameterInfo> paramInfos,
- RequiredArgs args);
+ RequiredArgs args, const FunctionDecl *CallerFD = nullptr);
const CGFunctionInfo &arrangeFreeFunctionCall(
CodeGenModule &CGM, CanQualType returnType, ArrayRef<CanQualType> argTypes,
FunctionType::ExtInfo info,
- ArrayRef<FunctionProtoType::ExtParameterInfo> paramInfos,
- RequiredArgs args);
+ ArrayRef<FunctionProtoType::ExtParameterInfo> paramInfos, RequiredArgs args,
+ const FunctionDecl *CallerFD = nullptr);
// An overload with an empty `paramInfos`
inline const CGFunctionInfo &
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index a2b9c945788ee..5a1979816b04e 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -357,10 +357,16 @@ CodeGenTypes::arrangeCXXMethodType(const CXXRecordDecl *RD,
// Add the 'this' pointer.
argTypes.push_back(DeriveThisType(RD, MD));
-
- return ::arrangeLLVMFunctionInfo(
- *this, /*instanceMethod=*/true, argTypes,
- FTP->getCanonicalTypeUnqualified().getAs<FunctionProtoType>());
+ auto CanonicalFTP =
+ FTP->getCanonicalTypeUnqualified().getAs<FunctionProtoType>();
+ ExtParameterInfoList paramInfos;
+ RequiredArgs required = RequiredArgs::forPrototypePlus(
+ CanonicalFTP.getTypePtr(), argTypes.size());
+ appendParameterTypes(*this, argTypes, paramInfos, CanonicalFTP);
+ return arrangeLLVMFunctionInfo(
+ CanonicalFTP->getReturnType().getUnqualifiedType(),
+ FnInfoOpts::IsInstanceMethod, argTypes, CanonicalFTP->getExtInfo(),
+ paramInfos, required, MD);
}
/// Set calling convention for CUDA/HIP kernel.
@@ -393,7 +399,13 @@ CodeGenTypes::arrangeCXXMethodDeclaration(const CXXMethodDecl *MD) {
return arrangeCXXMethodType(ThisType, prototype.getTypePtr(), MD);
}
- return arrangeFreeFunctionType(prototype);
+ CanQualTypeList argTypes;
+ ExtParameterInfoList paramInfos;
+ appendParameterTypes(*this, argTypes, paramInfos, prototype);
+ return arrangeLLVMFunctionInfo(
+ prototype->getReturnType().getUnqualifiedType(), FnInfoOpts::None,
+ argTypes, prototype->getExtInfo(), paramInfos,
+ RequiredArgs::forPrototypePlus(prototype.getTypePtr(), 0), MD);
}
bool CodeGenTypes::inheritingCtorHasParams(
@@ -452,7 +464,7 @@ CodeGenTypes::arrangeCXXStructorDeclaration(GlobalDecl GD) {
? CGM.getContext().VoidPtrTy
: Context.VoidTy;
return arrangeLLVMFunctionInfo(resultType, FnInfoOpts::IsInstanceMethod,
- argTypes, extInfo, paramInfos, required);
+ argTypes, extInfo, paramInfos, required, MD);
}
static CanQualTypeList getArgTypesForCall(ASTContext &ctx,
@@ -492,6 +504,15 @@ getExtParameterInfosForCall(const FunctionProtoType *proto, unsigned prefixArgs,
const CGFunctionInfo &CodeGenTypes::arrangeCXXConstructorCall(
const CallArgList &args, const CXXConstructorDecl *D, CXXCtorType CtorKind,
unsigned ExtraPrefixArgs, unsigned ExtraSuffixArgs, bool PassProtoArgs) {
+ return arrangeCXXConstructorCall(
+ args, D, CtorKind, ExtraPrefixArgs, ExtraSuffixArgs,
+ CGM.getDefaultX86AVXABILevel(), PassProtoArgs);
+}
+
+const CGFunctionInfo &CodeGenTypes::arrangeCXXConstructorCall(
+ const CallArgList &args, const CXXConstructorDecl *D, CXXCtorType CtorKind,
+ unsigned ExtraPrefixArgs, unsigned ExtraSuffixArgs, unsigned X86AVXABILevel,
+ bool PassProtoArgs) {
CanQualTypeList ArgTypes;
for (const auto &Arg : args)
ArgTypes.push_back(Context.getCanonicalParamType(Arg.Ty));
@@ -522,7 +543,8 @@ const CGFunctionInfo &CodeGenTypes::arrangeCXXConstructorCall(
}
return arrangeLLVMFunctionInfo(ResultType, FnInfoOpts::IsInstanceMethod,
- ArgTypes, Info, ParamInfos, Required);
+ ArgTypes, Info, ParamInfos, Required,
+ X86AVXABILevel);
}
/// Arrange the argument and result information for the declaration or
@@ -551,10 +573,17 @@ CodeGenTypes::arrangeFunctionDeclaration(const GlobalDecl GD) {
if (CanQual<FunctionNoProtoType> noProto = FTy.getAs<FunctionNoProtoType>()) {
return arrangeLLVMFunctionInfo(noProto->getReturnType(), FnInfoOpts::None,
{}, noProto->getExtInfo(), {},
- RequiredArgs::All);
+ RequiredArgs::All, FD);
}
- return arrangeFreeFunctionType(FTy.castAs<FunctionProtoType>());
+ CanQual<FunctionProtoType> FTP = FTy.castAs<FunctionProtoType>();
+ CanQualTypeList argTypes;
+ ExtParameterInfoList paramInfos;
+ appendParameterTypes(*this, argTypes, paramInfos, FTP);
+ return arrangeLLVMFunctionInfo(FTP->getReturnType().getUnqualifiedType(),
+ FnInfoOpts::None, argTypes, FTP->getExtInfo(),
+ paramInfos,
+ RequiredArgs::forPrototypePlus(FTP, 0), FD);
}
/// Arrange the argument and result information for the declaration or
@@ -664,7 +693,8 @@ CodeGenTypes::arrangeMSCtorClosure(const CXXConstructorDecl *CD,
static const CGFunctionInfo &
arrangeFreeFunctionLikeCall(CodeGenTypes &CGT, CodeGenModule &CGM,
const CallArgList &args, const FunctionType *fnType,
- unsigned numExtraRequiredArgs, bool chainCall) {
+ unsigned numExtraRequiredArgs, bool chainCall,
+ unsigned X86AVXABILevel) {
assert(args.size() >= numExtraRequiredArgs);
ExtParameterInfoList paramInfos;
@@ -697,7 +727,7 @@ arrangeFreeFunctionLikeCall(CodeGenTypes &CGT, CodeGenModule &CGM,
FnInfoOpts opts = chainCall ? FnInfoOpts::IsChainCall : FnInfoOpts::None;
return CGT.arrangeLLVMFunctionInfo(GetReturnType(fnType->getReturnType()),
opts, argTypes, fnType->getExtInfo(),
- paramInfos, required);
+ paramInfos, required, X86AVXABILevel);
}
/// Figure out the rules for calling a function with the given formal
@@ -706,8 +736,17 @@ arrangeFreeFunctionLikeCall(CodeGenTypes &CGT, CodeGenModule &CGM,
/// target-dependent in crazy ways.
const CGFunctionInfo &CodeGenTypes::arrangeFreeFunctionCall(
const CallArgList &args, const FunctionType *fnType, bool chainCall) {
- return arrangeFreeFunctionLikeCall(*this, CGM, args, fnType,
- chainCall ? 1 : 0, chainCall);
+ return arrangeFreeFunctionCall(
+ args, fnType, chainCall,
+ static_cast<unsigned>(CGM.getDefaultX86AVXABILevel()));
+}
+
+const CGFunctionInfo &
+CodeGenTypes::arrangeFreeFunctionCall(const CallArgList &args,
+ const FunctionType *fnType,
+ bool chainCall, unsigned X86AVXABILevel) {
+ return arrangeFreeFunctionLikeCall(
+ *this, CGM, args, fnType, chainCall ? 1 : 0, chainCall, X86AVXABILevel);
}
/// A block function is essentially a free function with an
@@ -715,8 +754,10 @@ const CGFunctionInfo &CodeGenTypes::arrangeFreeFunctionCall(
const CGFunctionInfo &
CodeGenTypes::arrangeBlockFunctionCall(const CallArgList &args,
const FunctionType *fnType) {
- return arrangeFreeFunctionLikeCall(*this, CGM, args, fnType, 1,
- /*chainCall=*/false);
+ return arrangeFreeFunctionLikeCall(
+ *this, CGM, args, fnType, 1,
+ /*chainCall=*/false,
+ static_cast<unsigned>(CGM.getDefaultX86AVXABILevel()));
}
const CGFunctionInfo &
@@ -777,6 +818,14 @@ const CGFunctionInfo &CodeGenTypes::arrangeDeviceKernelCallerDeclaration(
const CGFunctionInfo &CodeGenTypes::arrangeCXXMethodCall(
const CallArgList &args, const FunctionProtoType *proto,
RequiredArgs required, unsigned numPrefixArgs) {
+ return arrangeCXXMethodCall(
+ args, proto, required, numPrefixArgs,
+ static_cast<unsigned>(CGM.getDefaultX86AVXABILevel()));
+}
+
+const CGFunctionInfo &CodeGenTypes::arrangeCXXMethodCall(
+ const CallArgList &args, const FunctionProtoType *proto,
+ RequiredArgs required, unsigned numPrefixArgs, unsigned X86AVXABILevel) {
assert(numPrefixArgs + 1 <= args.size() &&
"Emitting a call with less args than the required prefix?");
// Add one to account for `this`. It's a bit awkward here, but we don't count
@@ -789,7 +838,7 @@ const CGFunctionInfo &CodeGenTypes::arrangeCXXMethodCall(
FunctionType::ExtInfo info = proto->getExtInfo();
return arrangeLLVMFunctionInfo(GetReturnType(proto->getReturnType()),
FnInfoOpts::IsInstanceMethod, argTypes, info,
- paramInfos, required);
+ paramInfos, required, X86AVXABILevel);
}
const CGFunctionInfo &CodeGenTypes::arrangeNullaryFunction() {
@@ -800,6 +849,13 @@ const CGFunctionInfo &CodeGenTypes::arrangeNullaryFunction() {
const CGFunctionInfo &CodeGenTypes::arrangeCall(const CGFunctionInfo &signature,
const CallArgList &args) {
+ return arrangeCall(signature, args,
+ static_cast<unsigned>(CGM.getDefaultX86AVXABILevel()));
+}
+
+const CGFunctionInfo &CodeGenTypes::arrangeCall(const CGFunctionInfo &signature,
+ const CallArgList &args,
+ unsigned X86AVXABILevel) {
assert(signature.arg_size() <= args.size());
if (signature.arg_size() == args.size())
return signature;
@@ -821,9 +877,13 @@ const CGFunctionInfo &CodeGenTypes::arrangeCall(const CGFunctionInfo &signature,
opts |= FnInfoOpts::IsChainCall;
if (signature.isDelegateCall())
opts |= FnInfoOpts::IsDelegateCall;
- return arrangeLLVMFunctionInfo(signature.getReturnType(), opts, argTypes,
- signature.getExtInfo(), paramInfos,
- signature.getRequiredArgs());
+
+ const CGFunctionInfo *newFI = findOrInsertCGFunctionInfo(
+ signature.isInstanceMethod(), signature.isChainCall(),
+ signature.isDelegateCall(), X86AVXABILevel, signature.getExtInfo(),
+ paramInfos, signature.getRequiredArgs(), signature.getReturnType(),
+ argTypes);
+ return *newFI;
}
namespace clang {
@@ -893,6 +953,16 @@ ABIArgInfo CodeGenModule::convertABIArgInfo(const llvm::abi::ArgInfo &AbiInfo,
llvm_unreachable("Unexpected llvm::abi::ArgInfo kind");
}
+const CGFunctionInfo &CodeGenTypes::arrangeLLVMFunctionInfo(
+ CanQualType resultType, FnInfoOpts opts, ArrayRef<CanQualType> argTypes,
+ FunctionType::ExtInfo info,
+ ArrayRef<FunctionProtoType::ExtParameterInfo> paramInfos,
+ RequiredArgs required, const FunctionDecl *FD) {
+ return arrangeLLVMFunctionInfo(
+ resultType, opts, argTypes, info, paramInfos, required,
+ static_cast<unsigned>(CGM.getEffectiveX86AVXABILevel(FD)));
+}
+
/// Arrange the argument and result information for an abstract value
/// of a given function type. This is the method which all of the
/// above functions ultimately defer to.
@@ -900,7 +970,7 @@ const CGFunctionInfo &CodeGenTypes::arrangeLLVMFunctionInfo(
CanQualType resultType, FnInfoOpts opts, ArrayRef<CanQualType> argTypes,
FunctionType::ExtInfo info,
ArrayRef<FunctionProtoType::ExtParameterInfo> paramInfos,
- RequiredArgs required) {
+ RequiredArgs required, unsigned X86AVXABILevel) {
assert(llvm::all_of(argTypes,
[](CanQualType T) { return T.isCanonicalAsParam(); }));
@@ -912,19 +982,35 @@ const CGFunctionInfo &CodeGenTypes::arrangeLLVMFunctionInfo(
(opts & FnInfoOpts::IsChainCall) == FnInfoOpts::IsChainCall;
bool isDelegateCall =
(opts & FnInfoOpts::IsDelegateCall) == FnInfoOpts::IsDelegateCall;
+
+ const CGFunctionInfo *newFI = findOrInsertCGFunctionInfo(
+ isInstanceMethod, isChainCall, isDelegateCall, X86AVXABILevel, info,
+ paramInfos, required, resultType, argTypes);
+ return *newFI;
+}
+
+CGFunctionInfo *CodeGenTypes::findOrInsertCGFunctionInfo(
+ bool isInstanceMethod, bool isChainCall, bool isDelegateCall,
+ unsigned X86AVXABILevel, const FunctionType::ExtInfo &info,
+ ArrayRef<FunctionProtoType::ExtParameterInfo> paramInfos,
+ RequiredArgs required, CanQualType resultType,
+ ArrayRef<CanQualType> argTypes) {
+ llvm::FoldingSetNodeID ID;
CGFunctionInfo::Profile(ID, isInstanceMethod, isChainCall, isDelegateCall,
- info, paramInfos, required, resultType, argTypes);
+ X86AVXABILevel, info, paramInfos, required,
+ resultType, argTypes);
void *insertPos = nullptr;
CGFunctionInfo *FI = FunctionInfos.FindNodeOrInsertPos(ID, insertPos);
if (FI)
- return *FI;
+ return FI;
unsigned CC = ClangCallConvToLLVMCallConv(info.getCC());
// Construct the function info. We co-allocate the ArgInfos.
FI = CGFunctionInfo::create(CC, isInstanceMethod, isChainCall, isDelegateCall,
- info, paramInfos, resultType, argTypes, required);
+ X86AVXABILevel, info, paramInfos, resultType,
+ argTypes, required);
FunctionInfos.InsertNode(FI, insertPos);
bool inserted = FunctionsBeingProcessed.insert(FI).second;
@@ -963,16 +1049,14 @@ const CGFunctionInfo &CodeGenTypes::arrangeLLVMFunctionInfo(
(void)erased;
assert(erased && "Not in set?");
- return *FI;
+ return FI;
}
-CGFunctionInfo *CGFunctionInfo::create(unsigned llvmCC, bool instanceMethod,
- bool chainCall, bool delegateCall,
- const FunctionType::ExtInfo &info,
- ArrayRef<ExtParameterInfo> paramInfos,
- CanQualType resultType,
- ArrayRef<CanQualType> argTypes,
- RequiredArgs required) {
+CGFunctionInfo *CGFunctionInfo::create(
+ unsigned llvmCC, bool instanceMethod, bool chainCall, bool delegateCall,
+ unsigned X86AVXABILevel, const FunctionType::ExtInfo &info,
+ ArrayRef<ExtParameterInfo> paramInfos, CanQualType resultType,
+ ArrayRef<CanQualType> argTypes, RequiredArgs required) {
assert(paramInfos.empty() || paramInfos.size() == argTypes.size());
assert(!required.allowsOptionalArgs() ||
required.getNumRequiredArgs() <= argTypes.size());
@@ -995,6 +1079,7 @@ CGFunctionInfo *CGFunctionInfo::create(unsigned llvmCC, bool instanceMethod,
FI->Required = required;
FI->HasRegParm = info.getHasRegParm();
FI->RegParm = info.getRegParm();
+ FI->X86AVXABILevel = X86AVXABILevel;
FI->ArgStruct = nullptr;
FI->ArgStructAlign = 0;
FI->NumArgs = argTypes.size();
diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp
index c0482fb13ec79..dbea76528ae97 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -2316,8 +2316,8 @@ static bool canEmitDelegateCallArgs(CodeGenFunction &CGF,
return false;
// Likewise if they're inalloca.
- const CGFunctionInfo &Info =
- CGF.CGM.getTypes().arrangeCXXConstructorCall(Args, Ctor, Type, 0, 0);
+ const CGFunctionInfo &Info = CGF.CGM.getTypes().arrangeCXXConstructorCall(
+ Args, Ctor, Type, 0, 0, CGF.getCurrentFunctionX86AVXABILevel());
if (Info.usesInAlloca())
return false;
}
@@ -2377,7 +2377,8 @@ void CodeGenFunction::EmitCXXConstructorCall(
// Emit the call.
llvm::Constant *CalleePtr = CGM.getAddrOfCXXStructor(GlobalDecl(D, Type));
const CGFunctionInfo &Info = CGM.getTypes().arrangeCXXConstructorCall(
- Args, D, Type, ExtraArgs.Prefix, ExtraArgs.Suffix, PassPrototypeArgs);
+ Args, D, Type, ExtraArgs.Prefix, ExtraArgs.Suffix,
+ getCurrentFunctionX86AVXABILevel(), PassPrototypeArgs);
CGCallee Callee = CGCallee::forDirect(CalleePtr, GlobalDecl(D, Type));
EmitCall(Info, Callee, ReturnValueSlot(), Args, CallOrInvoke, false, Loc);
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 9107553652688..c8450e4c98d97 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -7083,7 +7083,7 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType,
E->getDirectCallee(), /*ParamsToSkip=*/0, Order);
const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeFreeFunctionCall(
- Args, FnType, /*ChainCall=*/Chain);
+ Args, FnType, /*ChainCall=*/Chain, getCurrentFunctionX86AVXABILevel());
if (ResolvedFnInfo)
*ResolvedFnInfo = &FnInfo;
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index c585523f2718f..a2fe64dcdda2e 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -92,7 +92,8 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorCall(
MemberCallInfo CallInfo = commonEmitCXXMemberOrOperatorCall(
*this, MD, This, ImplicitParam, ImplicitParamTy, CE, Args, RtlArgs);
auto &FnInfo = CGM.getTypes().arrangeCXXMethodCall(
- Args, FPT, CallInfo.ReqArgs, CallInfo.PrefixSize);
+ Args, FPT, CallInfo.ReqArgs, CallInfo.PrefixSize,
+ getCurrentFunctionX86AVXABILevel());
return EmitCall(FnInfo, Callee, ReturnValue, Args, CallOrInvoke,
CE && CE == MustTailCall,
CE ? CE->getExprLoc() : SourceLocation());
@@ -482,8 +483,9 @@ CodeGenFunction::EmitCXXMemberPointerCallExpr(const CXXMemberCallExpr *E,
// And the rest of the call args
EmitCallArgs(Args, FPT, E->arguments());
- return EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, required,
- /*PrefixSize=*/0),
+ return EmitCall(CGM.getTypes().arrangeCXXMethodCall(
+ Args, FPT, required,
+ /*PrefixSize=*/0, getCurrentFunctionX86AVXABILevel()),
Callee, ReturnValue, Args, CallOrInvoke, E == MustTailCall,
E->getExprLoc());
}
@@ -1342,7 +1344,8 @@ static RValue EmitNewDeleteCall(CodeGenFunction &CGF,
llvm::Constant *CalleePtr = CGF.CGM.GetAddrOfFunction(CalleeDecl);
CGCallee Callee = CGCallee::forDirect(CalleePtr, GlobalDecl(CalleeDecl));
RValue RV = CGF.EmitCall(CGF.CGM.getTypes().arrangeFreeFunctionCall(
- Args, CalleeType, /*ChainCall=*/false),
+ Args, CalleeType, /*ChainCall=*/false,
+ CGF.getCurrentFunctionX86AVXABILevel()),
Callee, ReturnValueSlot(), Args, &CallOrInvoke);
/// C++1y [expr.new]p10:
diff --git a/clang/lib/CodeGen/CodeGenABITypes.cpp b/clang/lib/CodeGen/CodeGenABITypes.cpp
index aad286f3de53c..5713f9be37b60 100644
--- a/clang/lib/CodeGen/CodeGenABITypes.cpp
+++ b/clang/lib/CodeGen/CodeGenABITypes.cpp
@@ -60,20 +60,21 @@ CodeGen::arrangeCXXMethodType(CodeGenModule &CGM,
const CGFunctionInfo &CodeGen::arrangeCXXMethodCall(
CodeGenModule &CGM, CanQualType returnType, ArrayRef<CanQualType> argTypes,
FunctionType::ExtInfo info,
- ArrayRef<FunctionProtoType::ExtParameterInfo> paramInfos,
- RequiredArgs args) {
+ ArrayRef<FunctionProtoType::ExtParameterInfo> paramInfos, RequiredArgs args,
+ const FunctionDecl *CallerFD) {
return CGM.getTypes().arrangeLLVMFunctionInfo(
returnType, FnInfoOpts::IsInstanceMethod, argTypes, info, paramInfos,
- args);
+ args, static_cast<unsigned>(CGM.getEffectiveX86AVXABILevel(CallerFD)));
}
const CGFunctionInfo &CodeGen::arrangeFreeFunctionCall(
CodeGenModule &CGM, CanQualType returnType, ArrayRef<CanQualType> argTypes,
FunctionType::ExtInfo info,
- ArrayRef<FunctionProtoType::ExtParameterInfo> paramInfos,
- RequiredArgs args) {
+ ArrayRef<FunctionProtoType::ExtParameterInfo> paramInfos, RequiredArgs args,
+ const FunctionDecl *CallerFD) {
return CGM.getTypes().arrangeLLVMFunctionInfo(
- returnType, FnInfoOpts::None, argTypes, info, paramInfos, args);
+ returnType, FnInfoOpts::None, argTypes, info, paramInfos, args,
+ static_cast<unsigned>(CGM.getEffectiveX86AVXABILevel(CallerFD)));
}
ImplicitCXXConstructorArgs
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index b920266b59808..53cc191150e95 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -90,6 +90,13 @@ CodeGenFunction::CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext)
SetFastMathFlags(CurFPFeatures);
}
+unsigned CodeGenFunction::getCurrentFunctionX86AVXABILevel() const {
+ const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CurCodeDecl);
+ if (!FD)
+ FD = dyn_cast_or_null<FunctionDecl>(CurFuncDecl);
+ return static_cast<unsigned>(CGM.getEffectiveX86AVXABILevel(FD));
+}
+
CodeGenFunction::~CodeGenFunction() {
assert(LifetimeExtendedCleanupStack.empty() && "failed to emit a cleanup");
assert(DeferredDeactivationCleanupStack.empty() &&
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 29b87a0616992..9688763dead24 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -2234,6 +2234,7 @@ class CodeGenFunction : public CodeGenTypeCache {
const TargetCodeGenInfo &getTargetHooks() const {
return CGM.getTargetCodeGenInfo();
}
+ unsigned getCurrentFunctionX86AVXABILevel() const;
//===--------------------------------------------------------------------===//
// Cleanups
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 106f1e63cd904..ecfe00c43ad92 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -111,6 +111,8 @@ static CGCXXABI *createCXXABI(CodeGenModule &CGM) {
llvm_unreachable("invalid C++ ABI kind");
}
+static X86AVXABILevel getDefaultX86AVXABILevel(const TargetInfo &Target);
+
static std::unique_ptr<TargetCodeGenInfo>
createTargetCodeGenInfo(CodeGenModule &CGM) {
const TargetInfo &Target = CGM.getTarget();
@@ -268,10 +270,7 @@ createTargetCodeGenInfo(CodeGenModule &CGM) {
}
case llvm::Triple::x86_64: {
- StringRef ABI = Target.getABI();
- X86AVXABILevel AVXLevel = (ABI == "avx512" ? X86AVXABILevel::AVX512
- : ABI == "avx" ? X86AVXABILevel::AVX
- : X86AVXABILevel::None);
+ X86AVXABILevel AVXLevel = getDefaultX86AVXABILevel(Target);
switch (Triple.getOS()) {
case llvm::Triple::UEFI:
@@ -333,6 +332,18 @@ createTargetCodeGenInfo(CodeGenModule &CGM) {
}
}
+static X86AVXABILevel getDefaultX86AVXABILevel(const TargetInfo &Target) {
+ X86AVXABILevel Level = X86AVXABILevel::None;
+ if (Target.getTriple().isX86_64()) {
+ StringRef ABI = Target.getABI();
+ if (ABI == "avx512")
+ Level = X86AVXABILevel::AVX512;
+ else if (ABI == "avx")
+ Level = X86AVXABILevel::AVX;
+ }
+ return Level;
+}
+
const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() {
if (!TheTargetCodeGenInfo)
TheTargetCodeGenInfo = createTargetCodeGenInfo(*this);
@@ -6200,6 +6211,25 @@ const ABIInfo &CodeGenModule::getABIInfo() {
return getTargetCodeGenInfo().getABIInfo();
}
+unsigned CodeGenModule::getDefaultX86AVXABILevel() const {
+ return static_cast<unsigned>(::getDefaultX86AVXABILevel(Target));
+}
+
+unsigned
+CodeGenModule::getEffectiveX86AVXABILevel(const FunctionDecl *FD) const {
+ X86AVXABILevel Level = ::getDefaultX86AVXABILevel(Target);
+ if (!FD)
+ return static_cast<unsigned>(Level);
+
+ llvm::StringMap<bool> FeatureMap;
+ Context.getFunctionFeatureMap(FeatureMap, FD);
+ if (FeatureMap.lookup("avx512f"))
+ return static_cast<unsigned>(std::max(Level, X86AVXABILevel::AVX512));
+ if (FeatureMap.lookup("avx"))
+ return static_cast<unsigned>(std::max(Level, X86AVXABILevel::AVX));
+ return static_cast<unsigned>(Level);
+}
+
/// Pass IsTentative as true if you want to create a tentative definition.
void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
bool IsTentative) {
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index b7dab4ef583ff..ff909194d4322 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -914,6 +914,9 @@ class CodeGenModule : public CodeGenTypeCache {
/// in classification, and write the results back into FI.
void computeABIInfoUsingLib(CGFunctionInfo &FI);
+ unsigned getDefaultX86AVXABILevel() const;
+ unsigned getEffectiveX86AVXABILevel(const FunctionDecl *FD) const;
+
CGCXXABI &getCXXABI() const { return *ABI; }
llvm::LLVMContext &getLLVMContext() { return VMContext; }
diff --git a/clang/lib/CodeGen/CodeGenTypes.h b/clang/lib/CodeGen/CodeGenTypes.h
index 9de7e0a83579d..4d8627bc32276 100644
--- a/clang/lib/CodeGen/CodeGenTypes.h
+++ b/clang/lib/CodeGen/CodeGenTypes.h
@@ -89,9 +89,18 @@ class CodeGenTypes {
llvm::DenseMap<const Type *, llvm::Type *> RecordsWithOpaqueMemberPointers;
static constexpr unsigned FunctionInfosLog2InitSize = 9;
+
/// Helper for ConvertType.
llvm::Type *ConvertFunctionTypeInternal(QualType FT);
+ // Helper to insert CGFunctionInfo objects
+ CGFunctionInfo *findOrInsertCGFunctionInfo(
+ bool isInstanceMethod, bool isChainCall, bool isDelegateCall,
+ unsigned X86AVXABILevel, const FunctionType::ExtInfo &info,
+ ArrayRef<FunctionProtoType::ExtParameterInfo> paramInfos,
+ RequiredArgs required, CanQualType resultType,
+ ArrayRef<CanQualType> argTypes);
+
public:
CodeGenTypes(CodeGenModule &cgm);
~CodeGenTypes();
@@ -204,6 +213,9 @@ class CodeGenTypes {
/// Often this will be able to simply return the declaration info.
const CGFunctionInfo &arrangeCall(const CGFunctionInfo &declFI,
const CallArgList &args);
+ const CGFunctionInfo &arrangeCall(const CGFunctionInfo &declFI,
+ const CallArgList &args,
+ unsigned X86AVXABILevel);
/// Free functions are functions that are compatible with an ordinary
/// C function pointer type.
@@ -211,6 +223,10 @@ class CodeGenTypes {
const CGFunctionInfo &arrangeFreeFunctionCall(const CallArgList &Args,
const FunctionType *Ty,
bool ChainCall);
+ const CGFunctionInfo &arrangeFreeFunctionCall(const CallArgList &Args,
+ const FunctionType *Ty,
+ bool ChainCall,
+ unsigned X86AVXABILevel);
const CGFunctionInfo &arrangeFreeFunctionType(CanQual<FunctionProtoType> Ty);
const CGFunctionInfo &arrangeFreeFunctionType(CanQual<FunctionNoProtoType> Ty);
@@ -261,10 +277,21 @@ class CodeGenTypes {
unsigned ExtraSuffixArgs,
bool PassProtoArgs = true);
+ const CGFunctionInfo &
+ arrangeCXXConstructorCall(const CallArgList &Args,
+ const CXXConstructorDecl *D, CXXCtorType CtorKind,
+ unsigned ExtraPrefixArgs, unsigned ExtraSuffixArgs,
+ unsigned X86AVXABILevel, bool PassProtoArgs = true);
+
const CGFunctionInfo &arrangeCXXMethodCall(const CallArgList &args,
const FunctionProtoType *type,
RequiredArgs required,
unsigned numPrefixArgs);
+ const CGFunctionInfo &arrangeCXXMethodCall(const CallArgList &args,
+ const FunctionProtoType *type,
+ RequiredArgs required,
+ unsigned numPrefixArgs,
+ unsigned X86AVXABILevel);
const CGFunctionInfo &
arrangeUnprototypedMustTailThunk(const CXXMethodDecl *MD);
const CGFunctionInfo &arrangeMSCtorClosure(const CXXConstructorDecl *CD,
@@ -283,7 +310,12 @@ class CodeGenTypes {
CanQualType returnType, FnInfoOpts opts, ArrayRef<CanQualType> argTypes,
FunctionType::ExtInfo info,
ArrayRef<FunctionProtoType::ExtParameterInfo> paramInfos,
- RequiredArgs args);
+ RequiredArgs args, unsigned X86AVXABILevel);
+ const CGFunctionInfo &arrangeLLVMFunctionInfo(
+ CanQualType returnType, FnInfoOpts opts, ArrayRef<CanQualType> argTypes,
+ FunctionType::ExtInfo info,
+ ArrayRef<FunctionProtoType::ExtParameterInfo> paramInfos,
+ RequiredArgs args, const FunctionDecl *FD = nullptr);
/// Compute a new LLVM record layout object for the given record.
std::unique_ptr<CGRecordLayout> ComputeRecordLayout(const RecordDecl *D,
diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp
index 4a57ca7767bd2..6c6fb1dfef8a4 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -2961,6 +2961,19 @@ X86_64ABIInfo::classifyRegCallStructType(QualType Ty, unsigned &NeededInt,
}
void X86_64ABIInfo::computeInfo(CGFunctionInfo &FI) const {
+ bool shouldRespectFunctionAVXLevel =
+ !getTarget().getTriple().isPS() &&
+ getContext().getLangOpts().getClangABICompat() >
+ LangOptions::ClangABI::Ver22;
+
+ if (shouldRespectFunctionAVXLevel &&
+ FI.getX86AVXABILevel() != static_cast<unsigned>(AVXLevel)) {
+ auto EffectiveAVXLevel =
+ static_cast<X86AVXABILevel>(FI.getX86AVXABILevel());
+ X86_64ABIInfo EffectiveABIInfo(CGT, EffectiveAVXLevel);
+ EffectiveABIInfo.computeInfo(FI);
+ return;
+ }
const unsigned CallingConv = FI.getCallingConvention();
// It is possible to force Win64 calling convention on any x86_64 target by
diff --git a/clang/test/CodeGen/sysv_abi.c b/clang/test/CodeGen/sysv_abi.c
index a66ecc6e26242..81d2b26a09629 100644
--- a/clang/test/CodeGen/sysv_abi.c
+++ b/clang/test/CodeGen/sysv_abi.c
@@ -53,3 +53,21 @@ void use_vectors(void) {
// NOAVX: call {{(x86_64_sysvcc )?}}void @take_m256(ptr noundef byval(<8 x float>) align 32 %{{.*}})
// NOAVX: call {{(x86_64_sysvcc )?}}<16 x float> @get_m512()
// NOAVX: call {{(x86_64_sysvcc )?}}void @take_m512(ptr noundef byval(<16 x float>) align 64 %{{.*}})
+
+// Added test to explicitly cover the case when __attribute__((target("avx"))) is used
+// with __attribute__((sysv_abi))
+
+__attribute__((target("avx"))) my_m256 SYSV_CC get_avx_m256(void);
+__attribute__((target("avx"))) void SYSV_CC take_avx_m256(my_m256);
+
+__attribute__((target("avx")))
+void use_target_attr_vectors(void) {
+ my_m256 v = get_avx_m256();
+ take_avx_m256(v);
+}
+
+// CHECK: define {{(dso_local )?}}void @use_target_attr_vectors()
+// AVX: call {{(x86_64_sysvcc )?}}<8 x float> @get_avx_m256()
+// AVX: call {{(x86_64_sysvcc )?}}void @take_avx_m256(<8 x float> noundef %{{.*}})
+// NOAVX: call {{(x86_64_sysvcc )?}}<8 x float> @get_avx_m256()
+// NOAVX: call {{(x86_64_sysvcc )?}}void @take_avx_m256(<8 x float> noundef %{{.*}})
diff --git a/clang/test/CodeGen/target-avx-function-abi.c b/clang/test/CodeGen/target-avx-function-abi.c
new file mode 100644
index 0000000000000..6ea0fd39a29f1
--- /dev/null
+++ b/clang/test/CodeGen/target-avx-function-abi.c
@@ -0,0 +1,71 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=SYSV
+// RUN: %clang_cc1 -triple x86_64-scei-ps4 -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=PS
+// RUN: %clang_cc1 -triple x86_64-sie-ps5 -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=PS
+
+typedef float v8f __attribute__((vector_size(32)));
+typedef float v16f __attribute__((vector_size(64)));
+
+v8f g256(v8f x) { return x; }
+v16f g512(v16f x) { return x; }
+
+__attribute__((target("avx"))) v8f l256(v8f x) { return x; }
+__attribute__((target("avx512f"))) v16f l512(v16f x) { return x; }
+
+__attribute__((target("avx"))) v8f call_l256(v8f x) { return l256(x); }
+__attribute__((target("avx512f"))) v16f call_l512(v16f x) { return l512(x); }
+
+__attribute__((target("avx"))) v8f call_ptr_l256(v8f x) {
+ v8f (*fp)(v8f) = l256;
+ return fp(x);
+}
+
+__attribute__((target("avx512f"))) v16f call_ptr_l512(v16f x) {
+ v16f (*fp)(v16f) = l512;
+ return fp(x);
+}
+
+// SYSV-LABEL: define dso_local <8 x float> @g256(
+// SYSV: byval(<8 x float>) align 32
+
+// SYSV-LABEL: define dso_local <16 x float> @g512(
+// SYSV: byval(<16 x float>) align 64
+
+// SYSV-LABEL: define dso_local <8 x float> @l256(<8 x float> noundef %x)
+// SYSV-LABEL: define dso_local <16 x float> @l512(<16 x float> noundef %x)
+
+// SYSV-LABEL: define dso_local <8 x float> @call_l256(<8 x float> noundef %x)
+// SYSV: call <8 x float> @l256(<8 x float> noundef
+
+// SYSV-LABEL: define dso_local <16 x float> @call_l512(<16 x float> noundef %x)
+// SYSV: call <16 x float> @l512(<16 x float> noundef
+
+// SYSV-LABEL: define dso_local <8 x float> @call_ptr_l256(<8 x float> noundef %x)
+// SYSV: call <8 x float> %{{.*}}(<8 x float> noundef
+
+// SYSV-LABEL: define dso_local <16 x float> @call_ptr_l512(<16 x float> noundef %x)
+// SYSV: call <16 x float> %{{.*}}(<16 x float> noundef
+
+// PlayStation keeps the legacy ABI which always returns AVX vectors in registers & only uses AVX level from module/TU level even with AVX target attributes.
+// PS-LABEL: define dso_local <8 x float> @g256(
+// PS: byval(<8 x float>) align 32
+
+// PS-LABEL: define dso_local <16 x float> @g512(
+// PS: byval(<16 x float>) align 64
+
+// PS-LABEL: define dso_local <8 x float> @l256(
+// PS: byval(<8 x float>) align 32
+
+// PS-LABEL: define dso_local <16 x float> @l512(
+// PS: byval(<16 x float>) align 64
+
+// PS-LABEL: define dso_local <8 x float> @call_l256(
+// PS: call <8 x float> @l256(ptr noundef byval(<8 x float>) align 32
+
+// PS-LABEL: define dso_local <16 x float> @call_l512(
+// PS: call <16 x float> @l512(ptr noundef byval(<16 x float>) align 64
+
+// PS-LABEL: define dso_local <8 x float> @call_ptr_l256(
+// PS: call <8 x float> %{{.*}}(ptr noundef byval(<8 x float>) align 32
+
+// PS-LABEL: define dso_local <16 x float> @call_ptr_l512(
+// PS: call <16 x float> %{{.*}}(ptr noundef byval(<16 x float>) align 64
diff --git a/clang/test/CodeGenCXX/target-avx-method-abi.cpp b/clang/test/CodeGenCXX/target-avx-method-abi.cpp
new file mode 100644
index 0000000000000..47bd1d3ca8c83
--- /dev/null
+++ b/clang/test/CodeGenCXX/target-avx-method-abi.cpp
@@ -0,0 +1,121 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=SYSV
+// RUN: %clang_cc1 -triple x86_64-scei-ps4 -std=c++20 -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=PS
+// RUN: %clang_cc1 -triple x86_64-sie-ps5 -std=c++20 -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=PS
+
+typedef float v8f __attribute__((vector_size(32)));
+typedef float v16f __attribute__((vector_size(64)));
+
+struct S {
+ __attribute__((target("avx")))
+ v8f m(v8f x) { return x; }
+};
+
+struct C {
+ v8f field;
+ __attribute__((target("avx")))
+ C(v8f x) : field(x) {}
+};
+
+__attribute__((target("avx")))
+v8f callm(S *s, v8f x) {
+ return s->m(x);
+}
+
+__attribute__((target("avx")))
+v8f callctor(v8f x) {
+ C c(x);
+ return c.field;
+}
+
+__attribute__((target("avx")))
+v8f callm_ptr(S *s, v8f x) {
+ v8f (S::*pmf)(v8f) = &S::m;
+ return (s->*pmf)(x);
+}
+
+struct S512 {
+ __attribute__((target("avx512f")))
+ v16f m512(v16f x) { return x; }
+};
+
+struct D512 {
+ v16f field;
+ __attribute__((target("avx512f")))
+ D512(v16f x) : field(x) {}
+};
+
+__attribute__((target("avx512f")))
+v16f callm512(S512 *s, v16f x) {
+ return s->m512(x);
+}
+
+__attribute__((target("avx512f")))
+v16f callctor512(v16f x) {
+ D512 c(x);
+ return c.field;
+}
+
+__attribute__((target("avx512f")))
+v16f callm512_ptr(S512 *s, v16f x) {
+ v16f (S512::*pmf)(v16f) = &S512::m512;
+ return (s->*pmf)(x);
+}
+
+// Desired ABI behavior: AVX-targeted member functions should pass/return AVX
+// vectors directly, just like AVX-targeted free functions.
+// SYSV-LABEL: define dso_local noundef <8 x float> @_Z5callmP1SDv8_f(
+// SYSV: call noundef <8 x float> @_ZN1S1mEDv8_f(ptr noundef
+// SYSV-LABEL: define linkonce_odr noundef <8 x float> @_ZN1S1mEDv8_f(
+// SYSV-SAME: ptr noundef nonnull align 1 dereferenceable(1) %this, <8 x float> noundef %x)
+// SYSV-LABEL: define dso_local noundef <8 x float> @_Z8callctorDv8_f(
+// SYSV: call void @_ZN1CC1EDv8_f(ptr noundef nonnull align 32 dereferenceable(32)
+// SYSV-SAME: <8 x float> noundef
+// SYSV-LABEL: define linkonce_odr void @_ZN1CC1EDv8_f(
+// SYSV-SAME: ptr noundef nonnull align 32 dereferenceable(32) %this, <8 x float> noundef %x)
+// SYSV-LABEL: define dso_local noundef <8 x float> @_Z9callm_ptrP1SDv8_f(
+// SYSV-SAME: ptr noundef %s, <8 x float> noundef %x)
+// SYSV: call noundef <8 x float> %{{.*}}(ptr noundef nonnull align 1 dereferenceable(1) %{{.*}}, <8 x float> noundef
+// SYSV-LABEL: define dso_local noundef <16 x float> @_Z8callm512P4S512Dv16_f(
+// SYSV: call noundef <16 x float> @_ZN4S5124m512EDv16_f(ptr noundef
+// SYSV-LABEL: define linkonce_odr noundef <16 x float> @_ZN4S5124m512EDv16_f(
+// SYSV-SAME: ptr noundef nonnull align 1 dereferenceable(1) %this, <16 x float> noundef %x)
+// SYSV-LABEL: define dso_local noundef <16 x float> @_Z11callctor512Dv16_f(
+// SYSV: call void @_ZN4D512C1EDv16_f(ptr noundef nonnull align 64 dereferenceable(64)
+// SYSV-SAME: <16 x float> noundef
+// SYSV-LABEL: define linkonce_odr void @_ZN4D512C1EDv16_f(
+// SYSV-SAME: ptr noundef nonnull align 64 dereferenceable(64) %this, <16 x float> noundef %x)
+// SYSV-LABEL: define dso_local noundef <16 x float> @_Z12callm512_ptrP4S512Dv16_f(
+// SYSV-SAME: ptr noundef %s, <16 x float> noundef %x)
+// SYSV: call noundef <16 x float> %{{.*}}(ptr noundef nonnull align 1 dereferenceable(1) %{{.*}}, <16 x float> noundef
+// SYSV-LABEL: define linkonce_odr void @_ZN1CC2EDv8_f(
+// SYSV-SAME: ptr noundef nonnull align 32 dereferenceable(32) %this, <8 x float> noundef %x)
+// SYSV-LABEL: define linkonce_odr void @_ZN4D512C2EDv16_f(
+// SYSV-SAME: ptr noundef nonnull align 64 dereferenceable(64) %this, <16 x float> noundef %x)
+
+// PlayStation keeps the legacy ABI which always returns AVX vectors in registers & only uses AVX level from module/TU level even with AVX target attributes.
+// PS-LABEL: define dso_local noundef <8 x float> @_Z5callmP1SDv8_f(
+// PS: byval(<8 x float>) align 32
+// PS: call noundef <8 x float> @_ZN1S1mEDv8_f(
+// PS-LABEL: define linkonce_odr noundef <8 x float> @_ZN1S1mEDv8_f(
+// PS: byval(<8 x float>) align 32
+// PS-LABEL: define dso_local noundef <8 x float> @_Z8callctorDv8_f(
+// PS-LABEL: define linkonce_odr void @_ZN1CC1EDv8_f(
+// PS-SAME: ptr noundef nonnull align 32 dereferenceable(32) %this, ptr noundef byval(<8 x float>) align 32
+// PS-LABEL: define dso_local noundef <8 x float> @_Z9callm_ptrP1SDv8_f(
+// PS-SAME: ptr noundef %s, ptr noundef byval(<8 x float>) align 32
+// PS: call noundef <8 x float> %{{.*}}(ptr noundef nonnull align 1 dereferenceable(1) %{{.*}}, ptr noundef byval(<8 x float>) align 32
+// PS-LABEL: define dso_local noundef <16 x float> @_Z8callm512P4S512Dv16_f(
+// PS: byval(<16 x float>) align 64
+// PS: call noundef <16 x float> @_ZN4S5124m512EDv16_f(
+// PS-LABEL: define linkonce_odr noundef <16 x float> @_ZN4S5124m512EDv16_f(
+// PS: byval(<16 x float>) align 64
+// PS-LABEL: define dso_local noundef <16 x float> @_Z11callctor512Dv16_f(
+// PS-LABEL: define linkonce_odr void @_ZN4D512C1EDv16_f(
+// PS-SAME: ptr noundef nonnull align 64 dereferenceable(64) %this, ptr noundef byval(<16 x float>) align 64
+// PS-LABEL: define dso_local noundef <16 x float> @_Z12callm512_ptrP4S512Dv16_f(
+// PS-SAME: ptr noundef %s, ptr noundef byval(<16 x float>) align 64
+// PS: call noundef <16 x float> %{{.*}}(ptr noundef nonnull align 1 dereferenceable(1) %{{.*}}, ptr noundef byval(<16 x float>) align 64
+// PS-LABEL: define linkonce_odr void @_ZN1CC2EDv8_f(
+// PS-SAME: ptr noundef nonnull align 32 dereferenceable(32) %this, ptr noundef byval(<8 x float>) align 32
+// PS-LABEL: define linkonce_odr void @_ZN4D512C2EDv16_f(
+// PS-SAME: ptr noundef nonnull align 64 dereferenceable(64) %this, ptr noundef byval(<16 x float>) align 64
diff --git a/clang/unittests/CodeGen/CodeGenExternalTest.cpp b/clang/unittests/CodeGen/CodeGenExternalTest.cpp
index be3be147460f3..2f622474ed98b 100644
--- a/clang/unittests/CodeGen/CodeGenExternalTest.cpp
+++ b/clang/unittests/CodeGen/CodeGenExternalTest.cpp
@@ -10,6 +10,7 @@
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
#include "clang/AST/GlobalDecl.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/TargetInfo.h"
@@ -42,8 +43,10 @@ static const bool DebugThisTest = false;
// forward declarations
struct MyASTConsumer;
-static void test_codegen_fns(MyASTConsumer *my);
-static bool test_codegen_fns_ran;
+static void test_generic_codegen_fns(MyASTConsumer *my);
+static void test_x86_avx_abi_codegen_fns(MyASTConsumer *my);
+static bool test_generic_codegen_fns_ran;
+static bool test_x86_avx_abi_codegen_fns_ran;
// This forwards the calls to the Clang CodeGenerator
// so that we can test CodeGen functions while it is open.
@@ -52,13 +55,15 @@ static bool test_codegen_fns_ran;
// before forwarding that function to the CodeGenerator.
struct MyASTConsumer : public ASTConsumer {
+ using TestFn = void (*)(MyASTConsumer *);
+
std::unique_ptr<CodeGenerator> Builder;
+ TestFn TestFunction;
std::vector<Decl*> toplevel_decls;
- MyASTConsumer(std::unique_ptr<CodeGenerator> Builder_in)
- : ASTConsumer(), Builder(std::move(Builder_in))
- {
- }
+ MyASTConsumer(std::unique_ptr<CodeGenerator> Builder_in, TestFn TestFunction)
+ : ASTConsumer(), Builder(std::move(Builder_in)),
+ TestFunction(TestFunction) {}
~MyASTConsumer() { }
@@ -104,7 +109,7 @@ void MyASTConsumer::HandleInterestingDecl(DeclGroupRef D) {
}
void MyASTConsumer::HandleTranslationUnit(ASTContext &Context) {
- test_codegen_fns(this);
+ TestFunction(this);
// HandleTranslationUnit can close the module
Builder->HandleTranslationUnit(Context);
}
@@ -161,13 +166,30 @@ bool MyASTConsumer::shouldSkipFunctionBody(Decl *D) {
return Builder->shouldSkipFunctionBody(D);
}
-const char TestProgram[] =
+const char GenericTestProgram[] =
"struct mytest_struct { char x; short y; char p; long z; };\n"
"int mytest_fn(int x) { return x; }\n";
-// This function has the real test code here
-static void test_codegen_fns(MyASTConsumer *my) {
-
+const char X86AVXABITestProgram[] =
+ "typedef float mytest_v8f __attribute__((vector_size(32)));\n"
+ "struct mytest_avx_method_holder {\n"
+ " __attribute__((target(\"avx\"))) mytest_v8f method(mytest_v8f x) {\n"
+ " return x;\n"
+ " }\n"
+ "};\n"
+ "__attribute__((target(\"avx\"))) mytest_v8f mytest_avx_fn(mytest_v8f x) "
+ "{\n"
+ " return x;\n"
+ "}\n"
+ "__attribute__((target(\"avx\"))) void caller() "
+ "{\n"
+ " mytest_v8f hello = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0};\n"
+ " mytest_avx_fn(hello);\n"
+ " mytest_avx_method_holder holder = mytest_avx_method_holder();\n"
+ " holder.method(hello);\n"
+ "}\n";
+
+static void test_generic_codegen_fns(MyASTConsumer *my) {
bool mytest_fn_ok = false;
bool mytest_struct_ok = false;
@@ -254,7 +276,73 @@ static void test_codegen_fns(MyASTConsumer *my) {
ASSERT_TRUE(mytest_fn_ok);
ASSERT_TRUE(mytest_struct_ok);
- test_codegen_fns_ran = true;
+ test_generic_codegen_fns_ran = true;
+}
+
+static void test_x86_avx_abi_codegen_fns(MyASTConsumer *my) {
+ bool mytest_avx_fn_ok = false;
+ bool mytest_avx_method_ok = false;
+
+ CodeGen::CodeGenModule &CGM = my->Builder->CGM();
+ const ASTContext &Ctx = my->toplevel_decls.front()->getASTContext();
+
+ // First get the caller decl, which we need to determine call ABI
+ FunctionDecl *callerDecl = nullptr;
+ for (auto decl : my->toplevel_decls) {
+ if (FunctionDecl *fd = dyn_cast<FunctionDecl>(decl)) {
+ if (fd->getName() != "caller")
+ continue;
+
+ callerDecl = fd;
+ break;
+ }
+ }
+
+ for (auto decl : my->toplevel_decls) {
+ if (FunctionDecl *fd = dyn_cast<FunctionDecl>(decl)) {
+ if (fd->getName() != "mytest_avx_fn")
+ continue;
+
+ const auto *FPT = fd->getType()->castAs<FunctionProtoType>();
+ SmallVector<CanQualType, 4> ArgTypes;
+ for (const ParmVarDecl *Param : fd->parameters())
+ ArgTypes.push_back(Ctx.getCanonicalParamType(Param->getType()));
+
+ const CodeGen::CGFunctionInfo &FnInfo = CodeGen::arrangeFreeFunctionCall(
+ CGM, Ctx.getCanonicalType(FPT->getReturnType()), ArgTypes,
+ FPT->getExtInfo(), {},
+ CodeGen::RequiredArgs::forPrototypePlus(FPT, 0), callerDecl);
+ ASSERT_EQ(FnInfo.getX86AVXABILevel(), 1U);
+ ASSERT_TRUE(FnInfo.getReturnInfo().isDirect());
+ ASSERT_TRUE(FnInfo.arg_begin()->info.isDirect());
+ mytest_avx_fn_ok = true;
+ } else if (RecordDecl *rd = dyn_cast<RecordDecl>(decl)) {
+ if (rd->getName() != "mytest_avx_method_holder")
+ continue;
+
+ const auto *MethodRD = cast<CXXRecordDecl>(rd->getDefinition());
+ const auto *MD = cast<CXXMethodDecl>(*MethodRD->method_begin());
+ const auto *FPT = MD->getType()->castAs<FunctionProtoType>();
+ SmallVector<CanQualType, 4> ArgTypes;
+ ArgTypes.push_back(Ctx.getCanonicalParamType(MD->getThisType()));
+ for (const ParmVarDecl *Param : MD->parameters())
+ ArgTypes.push_back(Ctx.getCanonicalParamType(Param->getType()));
+
+ const CodeGen::CGFunctionInfo &FnInfo = CodeGen::arrangeCXXMethodCall(
+ CGM, Ctx.getCanonicalType(FPT->getReturnType()), ArgTypes,
+ FPT->getExtInfo(), {},
+ CodeGen::RequiredArgs::forPrototypePlus(FPT, 1), callerDecl);
+ ASSERT_EQ(FnInfo.getX86AVXABILevel(), 1u);
+ ASSERT_TRUE(FnInfo.getReturnInfo().isDirect());
+ ASSERT_TRUE(FnInfo.arg_begin()[1].info.isDirect());
+ mytest_avx_method_ok = true;
+ }
+ }
+
+ ASSERT_TRUE(mytest_avx_fn_ok);
+ ASSERT_TRUE(mytest_avx_method_ok);
+
+ test_x86_avx_abi_codegen_fns_ran = true;
}
TEST(CodeGenExternalTest, CodeGenExternalTest) {
@@ -262,14 +350,30 @@ TEST(CodeGenExternalTest, CodeGenExternalTest) {
LO.CPlusPlus = 1;
LO.CPlusPlus11 = 1;
TestCompiler Compiler(LO);
- auto CustomASTConsumer
- = std::make_unique<MyASTConsumer>(std::move(Compiler.CG));
+ auto CustomASTConsumer = std::make_unique<MyASTConsumer>(
+ std::move(Compiler.CG), test_generic_codegen_fns);
+
+ Compiler.init(GenericTestProgram, std::move(CustomASTConsumer));
+
+ clang::ParseAST(Compiler.compiler.getSema(), false, false);
+
+ ASSERT_TRUE(test_generic_codegen_fns_ran);
+}
+
+TEST(CodeGenExternalTest, X86AVXABIQuery) {
+ clang::LangOptions LO;
+ LO.CPlusPlus = 1;
+ LO.CPlusPlus11 = 1;
+ TestCompiler Compiler(LO, clang::CodeGenOptions(),
+ "x86_64-unknown-linux-gnu");
+ auto CustomASTConsumer = std::make_unique<MyASTConsumer>(
+ std::move(Compiler.CG), test_x86_avx_abi_codegen_fns);
- Compiler.init(TestProgram, std::move(CustomASTConsumer));
+ Compiler.init(X86AVXABITestProgram, std::move(CustomASTConsumer));
clang::ParseAST(Compiler.compiler.getSema(), false, false);
- ASSERT_TRUE(test_codegen_fns_ran);
+ ASSERT_TRUE(test_x86_avx_abi_codegen_fns_ran);
}
} // end anonymous namespace
diff --git a/clang/unittests/CodeGen/TestCompiler.h b/clang/unittests/CodeGen/TestCompiler.h
index 18947584bd0b3..3ba839979b867 100644
--- a/clang/unittests/CodeGen/TestCompiler.h
+++ b/clang/unittests/CodeGen/TestCompiler.h
@@ -33,17 +33,21 @@ struct TestCompiler {
unsigned PtrSize = 0;
TestCompiler(clang::LangOptions LO,
- clang::CodeGenOptions CGO = clang::CodeGenOptions()) {
+ clang::CodeGenOptions CGO = clang::CodeGenOptions(),
+ llvm::StringRef TripleStr = "") {
compiler.getLangOpts() = LO;
compiler.getCodeGenOpts() = CGO;
compiler.setVirtualFileSystem(llvm::vfs::getRealFileSystem());
compiler.createDiagnostics();
- std::string TrStr = llvm::Triple::normalize(llvm::sys::getProcessTriple());
- llvm::Triple Tr(TrStr);
- Tr.setOS(Triple::Linux);
- Tr.setVendor(Triple::VendorType::UnknownVendor);
- Tr.setEnvironment(Triple::EnvironmentType::UnknownEnvironment);
+ llvm::Triple Tr(TripleStr.empty()
+ ? llvm::Triple::normalize(llvm::sys::getProcessTriple())
+ : llvm::Triple::normalize(TripleStr));
+ if (TripleStr.empty()) {
+ Tr.setOS(Triple::Linux);
+ Tr.setVendor(Triple::VendorType::UnknownVendor);
+ Tr.setEnvironment(Triple::EnvironmentType::UnknownEnvironment);
+ }
compiler.getTargetOpts().Triple = Tr.getTriple();
compiler.setTarget(clang::TargetInfo::CreateTargetInfo(
compiler.getDiagnostics(), compiler.getTargetOpts()));
More information about the cfe-commits
mailing list