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