1 /*
2 * Copyright (c) 2023 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_optimizer.h"
17
18 #include "passes/ark_inlining.h"
19 #include "passes/ark_speculation.h"
20 #include "passes/expand_atomics.h"
21
22 #include "passes/inline_ir/cleanup_inline_module.h"
23 #include "passes/inline_ir/discard_inline_module.h"
24 #include "passes/inline_ir/mark_always_inline.h"
25 #include "passes/inline_ir/mark_inline_module.h"
26 #include "passes/inline_ir/remove_unused_functions.h"
27
28 #include "llvm_ark_interface.h"
29
30 #include <llvm/Analysis/AliasAnalysis.h>
31 #include <llvm/Analysis/GlobalsModRef.h>
32 #include <llvm/Analysis/TargetLibraryInfo.h>
33 #include <llvm/Analysis/ProfileSummaryInfo.h>
34 #include <llvm/Transforms/Utils/CanonicalizeAliases.h>
35 #include <llvm/Transforms/Utils/NameAnonGlobals.h>
36 #include <llvm/Passes/PassBuilder.h>
37 #include <llvm/Passes/StandardInstrumentations.h>
38 #include <llvm/IR/Verifier.h>
39 #include <llvm/Target/TargetMachine.h>
40
41 #include <fstream>
42 #include <streambuf>
43 #include <type_traits>
44
45 #include "transforms/transform_utils.h"
46
47 namespace {
48
49 template <typename PassManagerT, typename PassT>
AddPassIf(PassManagerT & passManager,PassT && pass,bool needInsert=true)50 void AddPassIf(PassManagerT &passManager, PassT &&pass, bool needInsert = true)
51 {
52 if (!needInsert) {
53 return;
54 }
55 passManager.addPass(std::forward<PassT>(pass));
56 #ifndef NDEBUG
57 // VerifierPass can be insterted only in ModulePassManager or FunctionPassManager
58 constexpr auto IS_MODULE_PM = std::is_same_v<llvm::ModulePassManager, PassManagerT>;
59 constexpr auto IS_FUNCTION_PM = std::is_same_v<llvm::FunctionPassManager, PassManagerT>;
60 // Disable checks due to clang-tidy bug https://bugs.llvm.org/show_bug.cgi?id=32203
61 // NOLINTNEXTLINE(readability-braces-around-statements, hicpp-braces-around-statements)
62 if constexpr (IS_MODULE_PM || IS_FUNCTION_PM) { // NOLINT(bugprone-suspicious-semicolon)
63 passManager.addPass(llvm::VerifierPass());
64 }
65 #endif
66 }
67
PreprocessPipelineFile(const std::string & filename)68 std::string PreprocessPipelineFile(const std::string &filename)
69 {
70 std::ifstream file(filename);
71 if (!file.is_open()) {
72 llvm::report_fatal_error(llvm::Twine("Cant open pipeline file: `") + filename + "`", false);
73 }
74 std::string rawData((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
75
76 // Remove comments
77 size_t insertPos = 0;
78 size_t copyPos = 0;
79 constexpr auto COMMENT_SYMBOL = '#';
80 constexpr auto ENDLINE_SYMBOL = '\n';
81 while (copyPos < rawData.size()) {
82 while (copyPos < rawData.size() && rawData[copyPos] != COMMENT_SYMBOL) {
83 rawData[insertPos++] = rawData[copyPos++];
84 }
85 while (copyPos < rawData.size() && rawData[copyPos] != ENDLINE_SYMBOL) {
86 copyPos++;
87 }
88 }
89 rawData.resize(insertPos);
90
91 // Remove space symbols
92 auto predicate = [](char sym) { return std::isspace(sym); };
93 rawData.erase(std::remove_if(rawData.begin(), rawData.end(), predicate), rawData.end());
94
95 return rawData;
96 }
97
98 #include <pipeline_irtoc_gen.inc>
99
GetOptimizationPipeline(const std::string & filename)100 std::string GetOptimizationPipeline(const std::string &filename)
101 {
102 std::string pipeline;
103 if (!filename.empty()) {
104 pipeline = PreprocessPipelineFile(filename);
105 } else {
106 // PIPELINE_IRTOC variable is defined in pipeline_irtoc_gen.inc
107 pipeline = std::string {PIPELINE_IRTOC};
108 }
109 return pipeline;
110 }
111
112 } // namespace
113
114 #include <llvm_passes.inl>
115
116 namespace panda::llvmbackend {
117
LLVMOptimizer(panda::llvmbackend::LLVMCompilerOptions options,LLVMArkInterface * arkInterface,std::shared_ptr<llvm::TargetMachine> targetMachine)118 LLVMOptimizer::LLVMOptimizer(panda::llvmbackend::LLVMCompilerOptions options, LLVMArkInterface *arkInterface,
119 std::shared_ptr<llvm::TargetMachine> targetMachine)
120 : options_(std::move(options)), arkInterface_(arkInterface), targetMachine_(std::move(targetMachine))
121 {
122 }
123
ProcessInlineModule(llvm::Module * inlineModule) const124 void LLVMOptimizer::ProcessInlineModule(llvm::Module *inlineModule) const
125 {
126 namespace pass = panda::llvmbackend::passes;
127 llvm::ModulePassManager modulePm;
128 llvm::LoopAnalysisManager loopAm;
129 llvm::FunctionAnalysisManager functionAm;
130 llvm::CGSCCAnalysisManager cgsccAm;
131 llvm::ModuleAnalysisManager moduleAm;
132
133 llvm::PassBuilder passBuilder(targetMachine_.get());
134 passBuilder.registerModuleAnalyses(moduleAm);
135 passBuilder.registerCGSCCAnalyses(cgsccAm);
136 passBuilder.registerFunctionAnalyses(functionAm);
137 passBuilder.registerLoopAnalyses(loopAm);
138 passBuilder.crossRegisterProxies(loopAm, functionAm, cgsccAm, moduleAm);
139
140 AddPassIf(modulePm, llvm::CanonicalizeAliasesPass(), true);
141 AddPassIf(modulePm, llvm::NameAnonGlobalPass(), true);
142 AddPassIf(modulePm, pass::MarkInlineModule(), true);
143 AddPassIf(modulePm, pass::CleanupInlineModule(), true);
144
145 modulePm.run(*inlineModule, moduleAm);
146 }
147
OptimizeModule(llvm::Module * module) const148 void LLVMOptimizer::OptimizeModule(llvm::Module *module) const
149 {
150 ASSERT(arkInterface_ != nullptr);
151
152 if (options_.dumpModuleBeforeOptimizations) {
153 llvm::errs() << "; =========================================\n";
154 llvm::errs() << "; LLVM IR module BEFORE LLVM optimizations:\n";
155 llvm::errs() << "; =========================================\n";
156 llvm::errs() << *module << '\n';
157 }
158
159 DoOptimizeModule(module);
160
161 if (options_.dumpModuleAfterOptimizations) {
162 llvm::errs() << "; =========================================\n";
163 llvm::errs() << "; LLVM IR module AFTER LLVM optimizations: \n";
164 llvm::errs() << "; =========================================\n";
165 llvm::errs() << *module << '\n';
166 }
167 }
168
DoOptimizeModule(llvm::Module * module) const169 void LLVMOptimizer::DoOptimizeModule(llvm::Module *module) const
170 {
171 // Create the analysis managers.
172 llvm::LoopAnalysisManager loopAm;
173 llvm::FunctionAnalysisManager functionAm;
174 llvm::CGSCCAnalysisManager cgsccAm;
175 llvm::ModuleAnalysisManager moduleAm;
176
177 llvm::StandardInstrumentations instrumentation(false);
178 llvm::PassInstrumentationCallbacks passInstrumentation;
179 instrumentation.registerCallbacks(passInstrumentation);
180 panda::libllvmbackend::RegisterPasses(passInstrumentation);
181
182 llvm::PassBuilder passBuilder(targetMachine_.get(), llvm::PipelineTuningOptions(), llvm::None,
183 &passInstrumentation);
184
185 // Register the AA manager first so that our version is the one used.
186 functionAm.registerPass([&passBuilder] { return passBuilder.buildDefaultAAPipeline(); });
187 // Register the target library analysis directly.
188 functionAm.registerPass(
189 [&] { return llvm::TargetLibraryAnalysis(llvm::TargetLibraryInfoImpl(targetMachine_->getTargetTriple())); });
190 // Register all the basic analyses with the managers.
191 passBuilder.registerModuleAnalyses(moduleAm);
192 passBuilder.registerCGSCCAnalyses(cgsccAm);
193 passBuilder.registerFunctionAnalyses(functionAm);
194 passBuilder.registerLoopAnalyses(loopAm);
195 passBuilder.crossRegisterProxies(loopAm, functionAm, cgsccAm, moduleAm);
196
197 panda::libllvmbackend::PassParser passParser(arkInterface_);
198 passParser.RegisterParserCallbacks(passBuilder, options_);
199
200 llvm::ModulePassManager modulePm;
201 if (options_.optimize) {
202 cantFail(passBuilder.parsePassPipeline(modulePm, GetOptimizationPipeline(options_.pipelineFile)));
203 } else {
204 namespace pass = panda::llvmbackend::passes;
205 llvm::FunctionPassManager functionPm;
206 AddPassIf(functionPm, pass::ExpandAtomics());
207 modulePm.addPass(createModuleToFunctionPassAdaptor(std::move(functionPm)));
208 }
209 modulePm.run(*module, moduleAm);
210 }
211
212 } // namespace panda::llvmbackend
213