1 /**
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "llvm_compiler_options.h"
17 #include "transforms/passes/inline_devirt.h"
18 #include "transforms/transform_utils.h"
19 #include <llvm/Analysis/CGSCCPassManager.h>
20 #include <llvm/ADT/PriorityWorklist.h>
21
22 namespace ark::llvmbackend {
23 struct LLVMCompilerOptions;
24 } // namespace ark::llvmbackend
25
26 namespace ark::llvmbackend::passes {
27
Create(LLVMArkInterface * arkInterface,const ark::llvmbackend::LLVMCompilerOptions * options)28 InlineDevirt InlineDevirt::Create(LLVMArkInterface *arkInterface, const ark::llvmbackend::LLVMCompilerOptions *options)
29 {
30 return InlineDevirt(arkInterface, options->doVirtualInline);
31 }
32
ShouldInsert(const ark::llvmbackend::LLVMCompilerOptions * options)33 bool InlineDevirt::ShouldInsert(const ark::llvmbackend::LLVMCompilerOptions *options)
34 {
35 return options->inlining;
36 }
37
InlineDevirt(LLVMArkInterface * arkInterface,bool doVirtualInline)38 InlineDevirt::InlineDevirt(LLVMArkInterface *arkInterface, bool doVirtualInline)
39 : arkInterface_ {arkInterface}, doVirtualInline_ {doVirtualInline}
40 {
41 }
42
RunCheckExternal(ark::llvmbackend::passes::CheckExternal & externalPass)43 void InlineDevirt::RunCheckExternal(ark::llvmbackend::passes::CheckExternal &externalPass)
44 {
45 for (auto &node : *currentSCC_) {
46 auto &func = node.getFunction();
47 if (func.isDeclaration()) {
48 continue;
49 }
50 [[maybe_unused]] auto shouldRunExternal =
51 passInstrumentation_->runBeforePass<llvm::Function>(externalPass, func);
52 ASSERT(shouldRunExternal);
53
54 auto preservedAnalysesExternal = externalPass.run(func, *functionAnalysisManager_);
55 passInstrumentation_->runAfterPass<llvm::Function>(externalPass, func, preservedAnalysesExternal);
56
57 if (!preservedAnalysesExternal.areAllPreserved()) {
58 currentSCC_ = &llvm::updateCGAndAnalysisManagerForCGSCCPass(
59 *callGraph_, *currentSCC_, node, *analysisManager_, *updateResult_, *functionAnalysisManager_);
60 functionAnalysisManager_->invalidate(func, preservedAnalysesExternal);
61 preservedAnalyses_.intersect(std::move(preservedAnalysesExternal));
62 }
63 }
64 }
65
RunInlining(llvm::InlinerPass & inlinePass,llvm::SmallPtrSetImpl<llvm::Function * > & changedFunctions)66 bool InlineDevirt::RunInlining(llvm::InlinerPass &inlinePass, llvm::SmallPtrSetImpl<llvm::Function *> &changedFunctions)
67 {
68 [[maybe_unused]] auto shouldRunInlining =
69 passInstrumentation_->runBeforePass<llvm::LazyCallGraph::SCC>(inlinePass, *currentSCC_);
70 ASSERT(shouldRunInlining);
71
72 auto preservedAnalysesInline = inlinePass.run(*currentSCC_, *analysisManager_, *callGraph_, *updateResult_);
73 passInstrumentation_->runAfterPass<llvm::LazyCallGraph::SCC>(inlinePass, *currentSCC_, preservedAnalysesInline);
74
75 ASSERT(updateResult_->InvalidatedSCCs.count(currentSCC_) == 0);
76 if (!preservedAnalysesInline.areAllPreserved()) {
77 for (auto &f : *currentSCC_) {
78 changedFunctions.insert(&f.getFunction());
79 }
80 if (updateResult_->UpdatedC != nullptr && updateResult_->UpdatedC != currentSCC_) {
81 currentSCC_ = updateResult_->UpdatedC;
82 }
83 // Function analyses get invalidated in inlinePass.run
84 auto *resultFAMCP =
85 &analysisManager_->getResult<llvm::FunctionAnalysisManagerCGSCCProxy>(*currentSCC_, *callGraph_);
86 resultFAMCP->updateFAM(*functionAnalysisManager_);
87 analysisManager_->invalidate(*currentSCC_, preservedAnalysesInline);
88 preservedAnalyses_.intersect(std::move(preservedAnalysesInline));
89 return true;
90 }
91 return false;
92 }
93
RunDevirt(ark::llvmbackend::passes::Devirt & devirtPass)94 bool InlineDevirt::RunDevirt(ark::llvmbackend::passes::Devirt &devirtPass)
95 {
96 auto devirtChanged = false;
97 for (auto &node : *currentSCC_) {
98 auto &func = node.getFunction();
99 if (func.isDeclaration()) {
100 continue;
101 }
102 [[maybe_unused]] auto shouldRunDevirt = passInstrumentation_->runBeforePass<llvm::Function>(devirtPass, func);
103 ASSERT(shouldRunDevirt);
104
105 auto preservedAnalysesDevirt = devirtPass.run(func, *functionAnalysisManager_);
106 passInstrumentation_->runAfterPass<llvm::Function>(devirtPass, func, preservedAnalysesDevirt);
107
108 if (!preservedAnalysesDevirt.areAllPreserved()) {
109 devirtChanged = true;
110 currentSCC_ = &llvm::updateCGAndAnalysisManagerForCGSCCPass(
111 *callGraph_, *currentSCC_, node, *analysisManager_, *updateResult_, *functionAnalysisManager_);
112 functionAnalysisManager_->invalidate(func, preservedAnalysesDevirt);
113 preservedAnalyses_.intersect(std::move(preservedAnalysesDevirt));
114 }
115 }
116 return devirtChanged;
117 }
118
run(llvm::LazyCallGraph::SCC & initialSCC,llvm::CGSCCAnalysisManager & analysisManager,llvm::LazyCallGraph & callGraph,llvm::CGSCCUpdateResult & updateResult)119 llvm::PreservedAnalyses InlineDevirt::run(llvm::LazyCallGraph::SCC &initialSCC,
120 llvm::CGSCCAnalysisManager &analysisManager, llvm::LazyCallGraph &callGraph,
121 llvm::CGSCCUpdateResult &updateResult)
122 {
123 passInstrumentation_ = &analysisManager.getResult<llvm::PassInstrumentationAnalysis>(initialSCC, callGraph);
124 functionAnalysisManager_ =
125 &analysisManager.getResult<llvm::FunctionAnalysisManagerCGSCCProxy>(initialSCC, callGraph).getManager();
126 preservedAnalyses_ = llvm::PreservedAnalyses::all();
127 updateResult_ = &updateResult;
128 currentSCC_ = &initialSCC;
129 analysisManager_ = &analysisManager;
130 callGraph_ = &callGraph;
131
132 llvm::InlinerPass inlinePass(false);
133 ark::llvmbackend::passes::Devirt devirtPass(arkInterface_);
134 ark::llvmbackend::passes::CheckExternal externalPass;
135 static constexpr auto CHANGED_FUNCTIONS_SIZE = 16U;
136 llvm::SmallPtrSet<llvm::Function *, CHANGED_FUNCTIONS_SIZE> changedFunctions;
137
138 static constexpr auto MAX_ITERATIONS = 8U;
139 for (uint32_t i = 0; i < MAX_ITERATIONS; i++) {
140 RunCheckExternal(externalPass);
141 auto inlineChanged = RunInlining(inlinePass, changedFunctions);
142 auto devirtChanged = doVirtualInline_ && RunDevirt(devirtPass);
143 // run CheckExternal and Devirt once more only if IR has changed
144 if (!inlineChanged && !devirtChanged) {
145 break;
146 }
147 }
148
149 for (auto func : changedFunctions) {
150 for (auto user : func->users()) {
151 auto call = llvm::dyn_cast<llvm::CallInst>(user);
152 if (call != nullptr && call->getCalledFunction() == func && call->getFunction() != func &&
153 arkInterface_->IsExternal(call)) {
154 auto parent = call->getFunction();
155 auto pnode = callGraph_->lookup(*parent);
156 ASSERT(pnode != nullptr);
157 auto pscc = callGraph_->lookupSCC(*pnode);
158 ASSERT(pscc != nullptr);
159 updateResult.CWorklist.insert(pscc);
160 }
161 }
162 }
163 return preservedAnalyses_;
164 }
165
166 } // namespace ark::llvmbackend::passes
167