• 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 "optimizer/ir/basicblock.h"
17 #include "optimizer/ir/graph.h"
18 #include "compiler_options.h"
19 
20 #include "llvm_compiler.h"
21 #include "llvm_ark_interface.h"
22 #include "llvm_logger.h"
23 #include "llvm_options.h"
24 
25 #include <llvm/Config/llvm-config.h>
26 #include <llvm/ExecutionEngine/Interpreter.h>
27 #include <llvm/InitializePasses.h>
28 #include <llvm/MC/TargetRegistry.h>
29 #include <llvm/PassRegistry.h>
30 #include <llvm/Support/CommandLine.h>
31 #include <llvm/Support/StringSaver.h>
32 
33 namespace ark::llvmbackend {
34 
35 static llvm::llvm_shutdown_obj g_shutdown {};
36 
LLVMCompiler(Arch arch)37 LLVMCompiler::LLVMCompiler(Arch arch) : arch_(arch)
38 {
39 #ifndef REQUIRED_LLVM_VERSION
40 #error Internal build error, missing cmake variable
41 #else
42 #define STRINGIFY(s) STR(s)  // NOLINT(cppcoreguidelines-macro-usage)
43 #define STR(s) #s            // NOLINT(cppcoreguidelines-macro-usage)
44     // LLVM_VERSION_STRING is defined in llvm-config.h
45     // REQUIRED_LLVM_VERSION is defined in libllvmbackend/CMakeLists.txt
46     static_assert(std::string_view {LLVM_VERSION_STRING} == STRINGIFY(REQUIRED_LLVM_VERSION));
47 
48     const std::string currentLlvmLibVersion = llvm::LLVMContext::getLLVMVersion();
49     if (currentLlvmLibVersion != STRINGIFY(REQUIRED_LLVM_VERSION)) {
50         llvm::report_fatal_error(llvm::Twine("Incompatible LLVM version " + currentLlvmLibVersion + ". " +
51                                              std::string(STRINGIFY(REQUIRED_LLVM_VERSION)) + " is required."),
52                                  false); /* gen_crash_diag = false */
53     }
54 #undef STR
55 #undef STRINGIFY
56 #endif
57     InitializeLLVMTarget(arch);
58     InitializeLLVMPasses();
59 #ifdef NDEBUG
60     context_.setDiscardValueNames(true);
61 #endif
62     context_.setOpaquePointers(true);
63 
64     LLVMLogger::Init(g_options.GetLlvmLog());
65 }
66 
IsInliningDisabled()67 bool LLVMCompiler::IsInliningDisabled()
68 {
69     if (ark::compiler::g_options.WasSetCompilerInlining() && !g_options.WasSetLlvmInlining()) {
70         return !ark::compiler::g_options.IsCompilerInlining();
71     }
72     return !g_options.IsLlvmInlining();
73 }
74 
IsInliningDisabled(compiler::Graph * graph)75 bool LLVMCompiler::IsInliningDisabled(compiler::Graph *graph)
76 {
77     ASSERT(graph != nullptr);
78 
79     // Erased end block means infinite loop in the 'Graph', such method should not be inlined.
80     if (graph->GetEndBlock() == nullptr) {
81         return true;
82     }
83 
84     // Since we do not generate code for 'Catch' blocks, LLVM can not inline function with
85     // 'Try' blocks. Otherwise, if the inlined code throws, execution is on another frame and
86     // it is impossible to find the 'Catch' block. The approach here is same with Ark Compiler.
87     // Also, we can not just check 'graph->GetTryBeginBlocks().empty()', because 'TryCatchResolving'
88     // pass may remove 'TryBegin' blocks, but keep some 'Try' blocks.
89     for (auto block : graph->GetBlocksRPO()) {
90         if (block->IsTry()) {
91             return true;
92         }
93     }
94 
95     // NB! LLVM does not follow '--compiler-inlining-skip-always-throw-methods=false' option,
96     // as it may lead to incorrect 'NoReturn' attribute propagation.
97     // Loop below checks if the function exits only using 'Throw' or 'Deoptimize' opcode
98     bool alwaysThrowOrDeopt = true;
99     for (auto pred : graph->GetEndBlock()->GetPredsBlocks()) {
100         if (pred->GetLastInst() == nullptr || !(pred->GetLastInst()->GetOpcode() == compiler::Opcode::Throw ||
101                                                 pred->GetLastInst()->GetOpcode() == compiler::Opcode::Deoptimize)) {
102             alwaysThrowOrDeopt = false;
103             break;
104         }
105     }
106     return alwaysThrowOrDeopt || IsInliningDisabled(graph->GetRuntime(), graph->GetMethod());
107 }
108 
IsInliningDisabled(compiler::RuntimeInterface * runtime,compiler::RuntimeInterface::MethodPtr method)109 bool LLVMCompiler::IsInliningDisabled(compiler::RuntimeInterface *runtime, compiler::RuntimeInterface::MethodPtr method)
110 {
111     ASSERT(runtime != nullptr);
112     ASSERT(method != nullptr);
113 
114     if (IsInliningDisabled()) {
115         return true;
116     }
117 
118     auto skipList = ark::compiler::g_options.GetCompilerInliningBlacklist();
119     if (!skipList.empty()) {
120         std::string methodName = runtime->GetMethodFullName(method);
121         if (std::find(skipList.begin(), skipList.end(), methodName) != skipList.end()) {
122             return true;
123         }
124     }
125 
126     return (runtime->GetMethodName(method).find("__noinline__") != std::string::npos);
127 }
128 
InitializeLLVMCompilerOptions()129 ark::llvmbackend::LLVMCompilerOptions LLVMCompiler::InitializeLLVMCompilerOptions()
130 {
131     ark::llvmbackend::LLVMCompilerOptions llvmCompilerOptions {};
132     llvmCompilerOptions.optimize = !ark::compiler::g_options.IsCompilerNonOptimizing();
133     llvmCompilerOptions.optlevel = llvmCompilerOptions.optimize ? 2U : 0U;
134     llvmCompilerOptions.gcIntrusionChecks = g_options.IsLlvmGcCheck();
135     llvmCompilerOptions.useSafepoint = ark::compiler::g_options.IsCompilerUseSafepoint();
136     llvmCompilerOptions.dumpModuleAfterOptimizations = g_options.IsLlvmDumpAfter();
137     llvmCompilerOptions.dumpModuleBeforeOptimizations = g_options.IsLlvmDumpBefore();
138     llvmCompilerOptions.inlineModuleFile = g_options.GetLlvmInlineModule();
139     llvmCompilerOptions.pipelineFile = g_options.GetLlvmPipeline();
140 
141     llvmCompilerOptions.inlining = !IsInliningDisabled();
142     llvmCompilerOptions.recursiveInlining = g_options.IsLlvmRecursiveInlining();
143     llvmCompilerOptions.doIrtocInline = !llvmCompilerOptions.inlineModuleFile.empty();
144     llvmCompilerOptions.doVirtualInline = !ark::compiler::g_options.IsCompilerNoVirtualInlining();
145 
146     return llvmCompilerOptions;
147 }
148 
InitializeDefaultLLVMOptions()149 void LLVMCompiler::InitializeDefaultLLVMOptions()
150 {
151     if (ark::compiler::g_options.IsCompilerNonOptimizing()) {
152         SetLLVMOption("fast-isel", "false");
153         SetLLVMOption("global-isel", "false");
154     } else {
155         SetLLVMOption("fixup-allow-gcptr-in-csr", "true");
156         SetLLVMOption("max-registers-for-gc-values", std::to_string(std::numeric_limits<int>::max()));
157     }
158 }
159 
InitializeLLVMOptions()160 void LLVMCompiler::InitializeLLVMOptions()
161 {
162     llvm::cl::ResetAllOptionOccurrences();
163     InitializeDefaultLLVMOptions();
164     auto llvmOptionsStr = llvmPreOptions_ + " " + g_options.GetLlvmOptions();
165     LLVM_LOG(DEBUG, INFRA) << "Using the following llvm options: '" << llvmPreOptions_ << "' (set internally), and '"
166                            << g_options.GetLlvmOptions() << "' (from the option to aot compiler)";
167     llvm::BumpPtrAllocator alloc;
168     llvm::StringSaver stringSaver(alloc);
169     llvm::SmallVector<const char *, 0> parsedArgv;
170     parsedArgv.emplace_back("");  // First argument is an executable
171     llvm::cl::TokenizeGNUCommandLine(llvmOptionsStr, stringSaver, parsedArgv);
172     llvm::cl::ParseCommandLineOptions(parsedArgv.size(), parsedArgv.data());
173 #ifndef NDEBUG
174     parsedOptions_ = true;
175 #endif
176 }
177 
SetLLVMOption(const char * option,const std::string & value)178 void LLVMCompiler::SetLLVMOption(const char *option, const std::string &value)
179 {
180 #ifndef NDEBUG
181     ASSERT_PRINT(!parsedOptions_, "Should call SetLLVMOptions earlier");
182 #endif
183     std::string prefix {llvmPreOptions_.empty() ? "" : " "};
184     llvmPreOptions_ += prefix + "--" + option + (value.empty() ? "" : "=") + value;
185 }
186 
GetTripleForArch(Arch arch)187 llvm::Triple LLVMCompiler::GetTripleForArch(Arch arch)
188 {
189     std::string error;
190     std::string tripleName;
191     switch (arch) {
192         case Arch::AARCH64:
193             tripleName = g_options.GetLlvmTriple();
194             break;
195         case Arch::X86_64:
196 #ifdef PANDA_TARGET_LINUX
197             tripleName = g_options.WasSetLlvmTriple() ? g_options.GetLlvmTriple() : "x86_64-unknown-linux-gnu";
198 #elif defined(PANDA_TARGET_MACOS)
199             tripleName = g_options.WasSetLlvmTriple() ? g_options.GetLlvmTriple() : "x86_64-apple-darwin-gnu";
200 #elif defined(PANDA_TARGET_WINDOWS)
201             tripleName = g_options.WasSetLlvmTriple() ? g_options.GetLlvmTriple() : "x86_64-unknown-windows-unknown";
202 #else
203             tripleName = g_options.WasSetLlvmTriple() ? g_options.GetLlvmTriple() : "x86_64-unknown-unknown-unknown";
204 #endif
205             break;
206 
207         default:
208             UNREACHABLE();
209     }
210     llvm::Triple triple(llvm::Triple::normalize(tripleName));
211     [[maybe_unused]] auto target = llvm::TargetRegistry::lookupTarget("", triple, error);
212     ASSERT_PRINT(target != nullptr, error);
213     return triple;
214 }
215 
GetCPUForArch(Arch arch)216 std::string LLVMCompiler::GetCPUForArch(Arch arch)
217 {
218     std::string cpu;
219     switch (arch) {
220         case Arch::AARCH64:
221 #if defined(PANDA_TARGET_ARM64) && defined(PANDA_TARGET_LINUX)
222             // Avoid specifying default cortex for arm64-linux
223             cpu = g_options.WasSetLlvmCpu() ? g_options.GetLlvmCpu() : "";
224 #else
225             cpu = g_options.GetLlvmCpu();
226 #endif
227             break;
228         case Arch::X86_64:
229             cpu = g_options.WasSetLlvmCpu() ? g_options.GetLlvmCpu() : "";
230             break;
231         default:
232             UNREACHABLE();
233     }
234     return cpu;
235 }
236 
InitializeLLVMTarget(Arch arch)237 void LLVMCompiler::InitializeLLVMTarget(Arch arch)
238 {
239     switch (arch) {
240         case Arch::AARCH64: {
241             LLVMInitializeAArch64TargetInfo();
242             LLVMInitializeAArch64Target();
243             LLVMInitializeAArch64TargetMC();
244             LLVMInitializeAArch64AsmPrinter();
245             LLVMInitializeAArch64AsmParser();
246             break;
247         }
248 #ifdef PANDA_TARGET_AMD64
249         case Arch::X86_64: {
250             LLVMInitializeX86TargetInfo();
251             LLVMInitializeX86Target();
252             LLVMInitializeX86TargetMC();
253             LLVMInitializeX86AsmPrinter();
254             LLVMInitializeX86AsmParser();
255             break;
256         }
257 #endif
258         default:
259             LLVM_LOG(FATAL, INFRA) << GetArchString(arch) << std::string(" is unsupported. ");
260     }
261     LLVM_LOG(DEBUG, INFRA) << "Available targets";
262     for (auto target : llvm::TargetRegistry::targets()) {
263         LLVM_LOG(DEBUG, INFRA) << "\t" << target.getName();
264     }
265 }
266 
InitializeLLVMPasses()267 void LLVMCompiler::InitializeLLVMPasses()
268 {
269     llvm::PassRegistry &registry = *llvm::PassRegistry::getPassRegistry();
270     initializeCore(registry);
271     initializeScalarOpts(registry);
272     initializeObjCARCOpts(registry);
273     initializeVectorization(registry);
274     initializeIPO(registry);
275     initializeAnalysis(registry);
276     initializeTransformUtils(registry);
277     initializeInstCombine(registry);
278     initializeAggressiveInstCombine(registry);
279     initializeInstrumentation(registry);
280     initializeTarget(registry);
281     initializeCodeGen(registry);
282     initializeGlobalISel(registry);
283 }
284 
285 }  // namespace ark::llvmbackend
286