109467b48Spatrick //===--- AMDGPUPropagateAttributes.cpp --------------------------*- C++ -*-===//
209467b48Spatrick //
309467b48Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
409467b48Spatrick // See https://llvm.org/LICENSE.txt for license information.
509467b48Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
609467b48Spatrick //
709467b48Spatrick //===----------------------------------------------------------------------===//
809467b48Spatrick //
909467b48Spatrick /// \file
1009467b48Spatrick /// \brief This pass propagates attributes from kernels to the non-entry
1109467b48Spatrick /// functions. Most of the library functions were not compiled for specific ABI,
12*d415bd75Srobert /// yet will be correctly compiled if proper attributes are propagated from the
1309467b48Spatrick /// caller.
1409467b48Spatrick ///
1509467b48Spatrick /// The pass analyzes call graph and propagates ABI target features through the
1609467b48Spatrick /// call graph.
1709467b48Spatrick ///
1809467b48Spatrick /// It can run in two modes: as a function or module pass. A function pass
1909467b48Spatrick /// simply propagates attributes. A module pass clones functions if there are
20*d415bd75Srobert /// callers with different ABI. If a function is cloned all call sites will
2109467b48Spatrick /// be updated to use a correct clone.
2209467b48Spatrick ///
2309467b48Spatrick /// A function pass is limited in functionality but can run early in the
2409467b48Spatrick /// pipeline. A module pass is more powerful but has to run late, so misses
2509467b48Spatrick /// library folding opportunities.
2609467b48Spatrick //
2709467b48Spatrick //===----------------------------------------------------------------------===//
2809467b48Spatrick
2909467b48Spatrick #include "AMDGPU.h"
3009467b48Spatrick #include "MCTargetDesc/AMDGPUMCTargetDesc.h"
3109467b48Spatrick #include "Utils/AMDGPUBaseInfo.h"
3209467b48Spatrick #include "llvm/ADT/SmallSet.h"
3373471bf0Spatrick #include "llvm/CodeGen/TargetPassConfig.h"
3473471bf0Spatrick #include "llvm/CodeGen/TargetSubtargetInfo.h"
3573471bf0Spatrick #include "llvm/IR/InstrTypes.h"
3609467b48Spatrick #include "llvm/Target/TargetMachine.h"
3709467b48Spatrick #include "llvm/Transforms/Utils/Cloning.h"
3809467b48Spatrick
3909467b48Spatrick #define DEBUG_TYPE "amdgpu-propagate-attributes"
4009467b48Spatrick
4109467b48Spatrick using namespace llvm;
4209467b48Spatrick
4309467b48Spatrick namespace llvm {
4409467b48Spatrick extern const SubtargetFeatureKV AMDGPUFeatureKV[AMDGPU::NumSubtargetFeatures-1];
4509467b48Spatrick }
4609467b48Spatrick
4709467b48Spatrick namespace {
4809467b48Spatrick
49097a140dSpatrick // Target features to propagate.
50097a140dSpatrick static constexpr const FeatureBitset TargetFeatures = {
5109467b48Spatrick AMDGPU::FeatureWavefrontSize16,
5209467b48Spatrick AMDGPU::FeatureWavefrontSize32,
5309467b48Spatrick AMDGPU::FeatureWavefrontSize64
5409467b48Spatrick };
5509467b48Spatrick
56097a140dSpatrick // Attributes to propagate.
5773471bf0Spatrick // TODO: Support conservative min/max merging instead of cloning.
58*d415bd75Srobert static constexpr const char *AttributeNames[] = {"amdgpu-waves-per-eu"};
59097a140dSpatrick
60*d415bd75Srobert static constexpr unsigned NumAttr = std::size(AttributeNames);
61097a140dSpatrick
62097a140dSpatrick class AMDGPUPropagateAttributes {
63097a140dSpatrick
64097a140dSpatrick class FnProperties {
65097a140dSpatrick private:
FnProperties(const FeatureBitset && FB)66097a140dSpatrick explicit FnProperties(const FeatureBitset &&FB) : Features(FB) {}
67097a140dSpatrick
68097a140dSpatrick public:
FnProperties(const TargetMachine & TM,const Function & F)69097a140dSpatrick explicit FnProperties(const TargetMachine &TM, const Function &F) {
70097a140dSpatrick Features = TM.getSubtargetImpl(F)->getFeatureBits();
71097a140dSpatrick
72097a140dSpatrick for (unsigned I = 0; I < NumAttr; ++I)
73097a140dSpatrick if (F.hasFnAttribute(AttributeNames[I]))
74097a140dSpatrick Attributes[I] = F.getFnAttribute(AttributeNames[I]);
75097a140dSpatrick }
76097a140dSpatrick
operator ==(const FnProperties & Other) const77097a140dSpatrick bool operator == (const FnProperties &Other) const {
78097a140dSpatrick if ((Features & TargetFeatures) != (Other.Features & TargetFeatures))
79097a140dSpatrick return false;
80097a140dSpatrick for (unsigned I = 0; I < NumAttr; ++I)
81097a140dSpatrick if (Attributes[I] != Other.Attributes[I])
82097a140dSpatrick return false;
83097a140dSpatrick return true;
84097a140dSpatrick }
85097a140dSpatrick
adjustToCaller(const FnProperties & CallerProps) const86097a140dSpatrick FnProperties adjustToCaller(const FnProperties &CallerProps) const {
87097a140dSpatrick FnProperties New((Features & ~TargetFeatures) | CallerProps.Features);
88097a140dSpatrick for (unsigned I = 0; I < NumAttr; ++I)
89097a140dSpatrick New.Attributes[I] = CallerProps.Attributes[I];
90097a140dSpatrick return New;
91097a140dSpatrick }
92097a140dSpatrick
93097a140dSpatrick FeatureBitset Features;
94*d415bd75Srobert std::optional<Attribute> Attributes[NumAttr];
95097a140dSpatrick };
96097a140dSpatrick
9709467b48Spatrick class Clone {
9809467b48Spatrick public:
Clone(const FnProperties & Props,Function * OrigF,Function * NewF)99097a140dSpatrick Clone(const FnProperties &Props, Function *OrigF, Function *NewF) :
100097a140dSpatrick Properties(Props), OrigF(OrigF), NewF(NewF) {}
10109467b48Spatrick
102097a140dSpatrick FnProperties Properties;
10309467b48Spatrick Function *OrigF;
10409467b48Spatrick Function *NewF;
10509467b48Spatrick };
10609467b48Spatrick
10709467b48Spatrick const TargetMachine *TM;
10809467b48Spatrick
10909467b48Spatrick // Clone functions as needed or just set attributes.
11009467b48Spatrick bool AllowClone;
11109467b48Spatrick
11209467b48Spatrick // Option propagation roots.
11309467b48Spatrick SmallSet<Function *, 32> Roots;
11409467b48Spatrick
11509467b48Spatrick // Clones of functions with their attributes.
11609467b48Spatrick SmallVector<Clone, 32> Clones;
11709467b48Spatrick
11809467b48Spatrick // Find a clone with required features.
119097a140dSpatrick Function *findFunction(const FnProperties &PropsNeeded,
12009467b48Spatrick Function *OrigF);
12109467b48Spatrick
122097a140dSpatrick // Clone function \p F and set \p NewProps on the clone.
12309467b48Spatrick // Cole takes the name of original function.
124097a140dSpatrick Function *cloneWithProperties(Function &F, const FnProperties &NewProps);
12509467b48Spatrick
12609467b48Spatrick // Set new function's features in place.
12709467b48Spatrick void setFeatures(Function &F, const FeatureBitset &NewFeatures);
12809467b48Spatrick
129097a140dSpatrick // Set new function's attributes in place.
130*d415bd75Srobert void setAttributes(Function &F,
131*d415bd75Srobert const ArrayRef<std::optional<Attribute>> NewAttrs);
132097a140dSpatrick
13309467b48Spatrick std::string getFeatureString(const FeatureBitset &Features) const;
13409467b48Spatrick
13509467b48Spatrick // Propagate attributes from Roots.
13609467b48Spatrick bool process();
13709467b48Spatrick
13809467b48Spatrick public:
AMDGPUPropagateAttributes(const TargetMachine * TM,bool AllowClone)13909467b48Spatrick AMDGPUPropagateAttributes(const TargetMachine *TM, bool AllowClone) :
14009467b48Spatrick TM(TM), AllowClone(AllowClone) {}
14109467b48Spatrick
14209467b48Spatrick // Use F as a root and propagate its attributes.
14309467b48Spatrick bool process(Function &F);
14409467b48Spatrick
14509467b48Spatrick // Propagate attributes starting from kernel functions.
14609467b48Spatrick bool process(Module &M);
14709467b48Spatrick };
14809467b48Spatrick
149*d415bd75Srobert // Allows to propagate attributes early, but no cloning is allowed as it must
15009467b48Spatrick // be a function pass to run before any optimizations.
15109467b48Spatrick // TODO: We shall only need a one instance of module pass, but that needs to be
15209467b48Spatrick // in the linker pipeline which is currently not possible.
15309467b48Spatrick class AMDGPUPropagateAttributesEarly : public FunctionPass {
15409467b48Spatrick const TargetMachine *TM;
15509467b48Spatrick
15609467b48Spatrick public:
15709467b48Spatrick static char ID; // Pass identification
15809467b48Spatrick
AMDGPUPropagateAttributesEarly(const TargetMachine * TM=nullptr)15909467b48Spatrick AMDGPUPropagateAttributesEarly(const TargetMachine *TM = nullptr) :
16009467b48Spatrick FunctionPass(ID), TM(TM) {
16109467b48Spatrick initializeAMDGPUPropagateAttributesEarlyPass(
16209467b48Spatrick *PassRegistry::getPassRegistry());
16309467b48Spatrick }
16409467b48Spatrick
16509467b48Spatrick bool runOnFunction(Function &F) override;
16609467b48Spatrick };
16709467b48Spatrick
168*d415bd75Srobert // Allows to propagate attributes with cloning but does that late in the
16909467b48Spatrick // pipeline.
17009467b48Spatrick class AMDGPUPropagateAttributesLate : public ModulePass {
17109467b48Spatrick const TargetMachine *TM;
17209467b48Spatrick
17309467b48Spatrick public:
17409467b48Spatrick static char ID; // Pass identification
17509467b48Spatrick
AMDGPUPropagateAttributesLate(const TargetMachine * TM=nullptr)17609467b48Spatrick AMDGPUPropagateAttributesLate(const TargetMachine *TM = nullptr) :
17709467b48Spatrick ModulePass(ID), TM(TM) {
17809467b48Spatrick initializeAMDGPUPropagateAttributesLatePass(
17909467b48Spatrick *PassRegistry::getPassRegistry());
18009467b48Spatrick }
18109467b48Spatrick
18209467b48Spatrick bool runOnModule(Module &M) override;
18309467b48Spatrick };
18409467b48Spatrick
18509467b48Spatrick } // end anonymous namespace.
18609467b48Spatrick
18709467b48Spatrick char AMDGPUPropagateAttributesEarly::ID = 0;
18809467b48Spatrick char AMDGPUPropagateAttributesLate::ID = 0;
18909467b48Spatrick
19009467b48Spatrick INITIALIZE_PASS(AMDGPUPropagateAttributesEarly,
19109467b48Spatrick "amdgpu-propagate-attributes-early",
19209467b48Spatrick "Early propagate attributes from kernels to functions",
19309467b48Spatrick false, false)
19409467b48Spatrick INITIALIZE_PASS(AMDGPUPropagateAttributesLate,
19509467b48Spatrick "amdgpu-propagate-attributes-late",
19609467b48Spatrick "Late propagate attributes from kernels to functions",
19709467b48Spatrick false, false)
19809467b48Spatrick
19909467b48Spatrick Function *
findFunction(const FnProperties & PropsNeeded,Function * OrigF)200097a140dSpatrick AMDGPUPropagateAttributes::findFunction(const FnProperties &PropsNeeded,
20109467b48Spatrick Function *OrigF) {
20209467b48Spatrick // TODO: search for clone's clones.
20309467b48Spatrick for (Clone &C : Clones)
204097a140dSpatrick if (C.OrigF == OrigF && PropsNeeded == C.Properties)
20509467b48Spatrick return C.NewF;
20609467b48Spatrick
20709467b48Spatrick return nullptr;
20809467b48Spatrick }
20909467b48Spatrick
process(Module & M)21009467b48Spatrick bool AMDGPUPropagateAttributes::process(Module &M) {
21109467b48Spatrick for (auto &F : M.functions())
212*d415bd75Srobert if (AMDGPU::isKernel(F.getCallingConv()))
21309467b48Spatrick Roots.insert(&F);
21409467b48Spatrick
215*d415bd75Srobert return Roots.empty() ? false : process();
21609467b48Spatrick }
21709467b48Spatrick
process(Function & F)21809467b48Spatrick bool AMDGPUPropagateAttributes::process(Function &F) {
21909467b48Spatrick Roots.insert(&F);
22009467b48Spatrick return process();
22109467b48Spatrick }
22209467b48Spatrick
process()22309467b48Spatrick bool AMDGPUPropagateAttributes::process() {
22409467b48Spatrick bool Changed = false;
22509467b48Spatrick SmallSet<Function *, 32> NewRoots;
22609467b48Spatrick SmallSet<Function *, 32> Replaced;
22709467b48Spatrick
228*d415bd75Srobert assert(!Roots.empty());
22909467b48Spatrick Module &M = *(*Roots.begin())->getParent();
23009467b48Spatrick
23109467b48Spatrick do {
23209467b48Spatrick Roots.insert(NewRoots.begin(), NewRoots.end());
23309467b48Spatrick NewRoots.clear();
23409467b48Spatrick
23509467b48Spatrick for (auto &F : M.functions()) {
236097a140dSpatrick if (F.isDeclaration())
23709467b48Spatrick continue;
23809467b48Spatrick
239097a140dSpatrick const FnProperties CalleeProps(*TM, F);
24009467b48Spatrick SmallVector<std::pair<CallBase *, Function *>, 32> ToReplace;
241097a140dSpatrick SmallSet<CallBase *, 32> Visited;
24209467b48Spatrick
24309467b48Spatrick for (User *U : F.users()) {
24409467b48Spatrick Instruction *I = dyn_cast<Instruction>(U);
24509467b48Spatrick if (!I)
24609467b48Spatrick continue;
24709467b48Spatrick CallBase *CI = dyn_cast<CallBase>(I);
24873471bf0Spatrick // Only propagate attributes if F is the called function. Specifically,
24973471bf0Spatrick // do not propagate attributes if F is passed as an argument.
25073471bf0Spatrick // FIXME: handle bitcasted callee, e.g.
25173471bf0Spatrick // %retval = call i8* bitcast (i32* ()* @f to i8* ()*)()
25273471bf0Spatrick if (!CI || CI->getCalledOperand() != &F)
25309467b48Spatrick continue;
25409467b48Spatrick Function *Caller = CI->getCaller();
255097a140dSpatrick if (!Caller || !Visited.insert(CI).second)
25609467b48Spatrick continue;
257097a140dSpatrick if (!Roots.count(Caller) && !NewRoots.count(Caller))
25809467b48Spatrick continue;
25909467b48Spatrick
260097a140dSpatrick const FnProperties CallerProps(*TM, *Caller);
26109467b48Spatrick
262097a140dSpatrick if (CalleeProps == CallerProps) {
263097a140dSpatrick if (!Roots.count(&F))
26409467b48Spatrick NewRoots.insert(&F);
26509467b48Spatrick continue;
26609467b48Spatrick }
26709467b48Spatrick
268097a140dSpatrick Function *NewF = findFunction(CallerProps, &F);
26909467b48Spatrick if (!NewF) {
270097a140dSpatrick const FnProperties NewProps = CalleeProps.adjustToCaller(CallerProps);
27109467b48Spatrick if (!AllowClone) {
272*d415bd75Srobert // This may set different features on different iterations if
27309467b48Spatrick // there is a contradiction in callers' attributes. In this case
27409467b48Spatrick // we rely on a second pass running on Module, which is allowed
27509467b48Spatrick // to clone.
276097a140dSpatrick setFeatures(F, NewProps.Features);
277097a140dSpatrick setAttributes(F, NewProps.Attributes);
27809467b48Spatrick NewRoots.insert(&F);
27909467b48Spatrick Changed = true;
28009467b48Spatrick break;
28109467b48Spatrick }
28209467b48Spatrick
283097a140dSpatrick NewF = cloneWithProperties(F, NewProps);
284097a140dSpatrick Clones.push_back(Clone(CallerProps, &F, NewF));
28509467b48Spatrick NewRoots.insert(NewF);
28609467b48Spatrick }
28709467b48Spatrick
288*d415bd75Srobert ToReplace.push_back(std::pair(CI, NewF));
28909467b48Spatrick Replaced.insert(&F);
29009467b48Spatrick
29109467b48Spatrick Changed = true;
29209467b48Spatrick }
29309467b48Spatrick
29409467b48Spatrick while (!ToReplace.empty()) {
29509467b48Spatrick auto R = ToReplace.pop_back_val();
29609467b48Spatrick R.first->setCalledFunction(R.second);
29709467b48Spatrick }
29809467b48Spatrick }
29909467b48Spatrick } while (!NewRoots.empty());
30009467b48Spatrick
30109467b48Spatrick for (Function *F : Replaced) {
30209467b48Spatrick if (F->use_empty())
30309467b48Spatrick F->eraseFromParent();
30409467b48Spatrick }
30509467b48Spatrick
306097a140dSpatrick Roots.clear();
307097a140dSpatrick Clones.clear();
308097a140dSpatrick
30909467b48Spatrick return Changed;
31009467b48Spatrick }
31109467b48Spatrick
31209467b48Spatrick Function *
cloneWithProperties(Function & F,const FnProperties & NewProps)313097a140dSpatrick AMDGPUPropagateAttributes::cloneWithProperties(Function &F,
314097a140dSpatrick const FnProperties &NewProps) {
31509467b48Spatrick LLVM_DEBUG(dbgs() << "Cloning " << F.getName() << '\n');
31609467b48Spatrick
31709467b48Spatrick ValueToValueMapTy dummy;
31809467b48Spatrick Function *NewF = CloneFunction(&F, dummy);
319097a140dSpatrick setFeatures(*NewF, NewProps.Features);
320097a140dSpatrick setAttributes(*NewF, NewProps.Attributes);
321097a140dSpatrick NewF->setVisibility(GlobalValue::DefaultVisibility);
322097a140dSpatrick NewF->setLinkage(GlobalValue::InternalLinkage);
32309467b48Spatrick
32409467b48Spatrick // Swap names. If that is the only clone it will retain the name of now
325097a140dSpatrick // dead value. Preserve original name for externally visible functions.
326097a140dSpatrick if (F.hasName() && F.hasLocalLinkage()) {
327097a140dSpatrick std::string NewName = std::string(NewF->getName());
32809467b48Spatrick NewF->takeName(&F);
32909467b48Spatrick F.setName(NewName);
33009467b48Spatrick }
33109467b48Spatrick
33209467b48Spatrick return NewF;
33309467b48Spatrick }
33409467b48Spatrick
setFeatures(Function & F,const FeatureBitset & NewFeatures)33509467b48Spatrick void AMDGPUPropagateAttributes::setFeatures(Function &F,
33609467b48Spatrick const FeatureBitset &NewFeatures) {
33709467b48Spatrick std::string NewFeatureStr = getFeatureString(NewFeatures);
33809467b48Spatrick
33909467b48Spatrick LLVM_DEBUG(dbgs() << "Set features "
34009467b48Spatrick << getFeatureString(NewFeatures & TargetFeatures)
34109467b48Spatrick << " on " << F.getName() << '\n');
34209467b48Spatrick
34309467b48Spatrick F.removeFnAttr("target-features");
34409467b48Spatrick F.addFnAttr("target-features", NewFeatureStr);
34509467b48Spatrick }
34609467b48Spatrick
setAttributes(Function & F,const ArrayRef<std::optional<Attribute>> NewAttrs)347*d415bd75Srobert void AMDGPUPropagateAttributes::setAttributes(
348*d415bd75Srobert Function &F, const ArrayRef<std::optional<Attribute>> NewAttrs) {
349097a140dSpatrick LLVM_DEBUG(dbgs() << "Set attributes on " << F.getName() << ":\n");
350097a140dSpatrick for (unsigned I = 0; I < NumAttr; ++I) {
351097a140dSpatrick F.removeFnAttr(AttributeNames[I]);
352097a140dSpatrick if (NewAttrs[I]) {
353097a140dSpatrick LLVM_DEBUG(dbgs() << '\t' << NewAttrs[I]->getAsString() << '\n');
354097a140dSpatrick F.addFnAttr(*NewAttrs[I]);
355097a140dSpatrick }
356097a140dSpatrick }
357097a140dSpatrick }
358097a140dSpatrick
35909467b48Spatrick std::string
getFeatureString(const FeatureBitset & Features) const36009467b48Spatrick AMDGPUPropagateAttributes::getFeatureString(const FeatureBitset &Features) const
36109467b48Spatrick {
36209467b48Spatrick std::string Ret;
36309467b48Spatrick for (const SubtargetFeatureKV &KV : AMDGPUFeatureKV) {
36409467b48Spatrick if (Features[KV.Value])
36509467b48Spatrick Ret += (StringRef("+") + KV.Key + ",").str();
36609467b48Spatrick else if (TargetFeatures[KV.Value])
36709467b48Spatrick Ret += (StringRef("-") + KV.Key + ",").str();
36809467b48Spatrick }
36909467b48Spatrick Ret.pop_back(); // Remove last comma.
37009467b48Spatrick return Ret;
37109467b48Spatrick }
37209467b48Spatrick
runOnFunction(Function & F)37309467b48Spatrick bool AMDGPUPropagateAttributesEarly::runOnFunction(Function &F) {
37473471bf0Spatrick if (!TM) {
37573471bf0Spatrick auto *TPC = getAnalysisIfAvailable<TargetPassConfig>();
37673471bf0Spatrick if (!TPC)
37773471bf0Spatrick return false;
37873471bf0Spatrick
37973471bf0Spatrick TM = &TPC->getTM<TargetMachine>();
38073471bf0Spatrick }
38173471bf0Spatrick
382*d415bd75Srobert if (!AMDGPU::isKernel(F.getCallingConv()))
38309467b48Spatrick return false;
38409467b48Spatrick
38509467b48Spatrick return AMDGPUPropagateAttributes(TM, false).process(F);
38609467b48Spatrick }
38709467b48Spatrick
runOnModule(Module & M)38809467b48Spatrick bool AMDGPUPropagateAttributesLate::runOnModule(Module &M) {
38973471bf0Spatrick if (!TM) {
39073471bf0Spatrick auto *TPC = getAnalysisIfAvailable<TargetPassConfig>();
39173471bf0Spatrick if (!TPC)
39209467b48Spatrick return false;
39309467b48Spatrick
39473471bf0Spatrick TM = &TPC->getTM<TargetMachine>();
39573471bf0Spatrick }
39673471bf0Spatrick
39709467b48Spatrick return AMDGPUPropagateAttributes(TM, true).process(M);
39809467b48Spatrick }
39909467b48Spatrick
40009467b48Spatrick FunctionPass
createAMDGPUPropagateAttributesEarlyPass(const TargetMachine * TM)40109467b48Spatrick *llvm::createAMDGPUPropagateAttributesEarlyPass(const TargetMachine *TM) {
40209467b48Spatrick return new AMDGPUPropagateAttributesEarly(TM);
40309467b48Spatrick }
40409467b48Spatrick
40509467b48Spatrick ModulePass
createAMDGPUPropagateAttributesLatePass(const TargetMachine * TM)40609467b48Spatrick *llvm::createAMDGPUPropagateAttributesLatePass(const TargetMachine *TM) {
40709467b48Spatrick return new AMDGPUPropagateAttributesLate(TM);
40809467b48Spatrick }
40973471bf0Spatrick
41073471bf0Spatrick PreservedAnalyses
run(Function & F,FunctionAnalysisManager & AM)41173471bf0Spatrick AMDGPUPropagateAttributesEarlyPass::run(Function &F,
41273471bf0Spatrick FunctionAnalysisManager &AM) {
41373471bf0Spatrick if (!AMDGPU::isEntryFunctionCC(F.getCallingConv()))
41473471bf0Spatrick return PreservedAnalyses::all();
41573471bf0Spatrick
41673471bf0Spatrick return AMDGPUPropagateAttributes(&TM, false).process(F)
41773471bf0Spatrick ? PreservedAnalyses::none()
41873471bf0Spatrick : PreservedAnalyses::all();
41973471bf0Spatrick }
42073471bf0Spatrick
42173471bf0Spatrick PreservedAnalyses
run(Module & M,ModuleAnalysisManager & AM)42273471bf0Spatrick AMDGPUPropagateAttributesLatePass::run(Module &M, ModuleAnalysisManager &AM) {
42373471bf0Spatrick return AMDGPUPropagateAttributes(&TM, true).process(M)
42473471bf0Spatrick ? PreservedAnalyses::none()
42573471bf0Spatrick : PreservedAnalyses::all();
42673471bf0Spatrick }
427