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 <[email protected]> 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())); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
