• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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