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 "optimizer/ir/graph.h"
17 #include "compiler_options.h"
18
19 #include "llvm_compiler.h"
20 #include "llvm_ark_interface.h"
21 #include "llvm_logger.h"
22 #include "llvm_options.h"
23
24 #include <llvm/Config/llvm-config.h>
25 #include <llvm/ExecutionEngine/Interpreter.h>
26 #include <llvm/InitializePasses.h>
27 #include <llvm/MC/TargetRegistry.h>
28 #include <llvm/PassRegistry.h>
29 #include <llvm/Support/CommandLine.h>
30 #include <llvm/Support/StringSaver.h>
31
32 namespace panda::llvmbackend {
33
LLVMCompiler(Arch arch)34 LLVMCompiler::LLVMCompiler(Arch arch) : arch_(arch)
35 {
36 #ifndef REQUIRED_LLVM_VERSION
37 #error Internal build error, missing cmake variable
38 #else
39 #define STRINGIFY(s) STR(s) // NOLINT(cppcoreguidelines-macro-usage)
40 #define STR(s) #s // NOLINT(cppcoreguidelines-macro-usage)
41 // LLVM_VERSION_STRING is defined in llvm-config.h
42 // REQUIRED_LLVM_VERSION is defined in libllvmbackend/CMakeLists.txt
43 static_assert(std::string_view {LLVM_VERSION_STRING} == STRINGIFY(REQUIRED_LLVM_VERSION));
44
45 const std::string currentLlvmLibVersion = llvm::LLVMContext::getLLVMVersion();
46 if (currentLlvmLibVersion != STRINGIFY(REQUIRED_LLVM_VERSION)) {
47 llvm::report_fatal_error(llvm::Twine("Incompatible LLVM version " + currentLlvmLibVersion + ". " +
48 std::string(STRINGIFY(REQUIRED_LLVM_VERSION)) + " is required."),
49 false);
50 }
51 #undef STR
52 #undef STRINGIFY
53 #endif
54 InitializeLLVMTarget(arch);
55 InitializeLLVMPasses();
56 InitializeLLVMOptions();
57 #ifdef NDEBUG
58 context_.setDiscardValueNames(true);
59 #endif
60 context_.setOpaquePointers(true);
61
62 LLVMLogger::Init(g_options.GetLlvmLog());
63 }
64
InitializeLLVMCompilerOptions()65 panda::llvmbackend::LLVMCompilerOptions LLVMCompiler::InitializeLLVMCompilerOptions()
66 {
67 panda::llvmbackend::LLVMCompilerOptions llvmCompilerOptions {};
68 llvmCompilerOptions.optimize = !panda::compiler::g_options.IsCompilerNonOptimizing();
69 llvmCompilerOptions.optlevel = llvmCompilerOptions.optimize ? 2U : 0U;
70 llvmCompilerOptions.dumpModuleAfterOptimizations = g_options.IsLlvmDumpAfter();
71 llvmCompilerOptions.dumpModuleBeforeOptimizations = g_options.IsLlvmDumpBefore();
72 llvmCompilerOptions.inlineModuleFile = g_options.GetLlvmInlineModule();
73 llvmCompilerOptions.pipelineFile = g_options.GetLlvmPipeline();
74
75 llvmCompilerOptions.doIrtocInline = !llvmCompilerOptions.inlineModuleFile.empty();
76
77 return llvmCompilerOptions;
78 }
79
InitializeDefaultLLVMOptions()80 void LLVMCompiler::InitializeDefaultLLVMOptions()
81 {
82 if (panda::compiler::g_options.IsCompilerNonOptimizing()) {
83 constexpr auto DISABLE = llvm::cl::boolOrDefault::BOU_FALSE;
84 SetLLVMOption("fast-isel", DISABLE);
85 SetLLVMOption("global-isel", DISABLE);
86 }
87 }
88
InitializeLLVMOptions()89 void LLVMCompiler::InitializeLLVMOptions()
90 {
91 llvm::cl::ResetAllOptionOccurrences();
92 InitializeDefaultLLVMOptions();
93 auto llvmOptionsStr = g_options.GetLlvmOptions();
94 if (llvmOptionsStr.length() == 0) {
95 return;
96 }
97
98 llvm::BumpPtrAllocator alloc;
99 llvm::StringSaver stringSaver(alloc);
100 llvm::SmallVector<const char *, 0> parsedArgv;
101 parsedArgv.emplace_back(""); // First argument is an executable
102 llvm::cl::TokenizeGNUCommandLine(llvmOptionsStr, stringSaver, parsedArgv);
103 llvm::cl::ParseCommandLineOptions(parsedArgv.size(), parsedArgv.data());
104 }
105
106 template <typename T>
SetLLVMOption(const char * option,T val)107 void LLVMCompiler::SetLLVMOption(const char *option, T val)
108 {
109 auto opts = llvm::cl::getRegisteredOptions();
110 auto entry = opts.find(option);
111 ASSERT(entry != opts.end());
112 static_cast<llvm::cl::opt<T> *>(entry->second)->setValue(val);
113 }
114
115 // Instantiate for irtoc_compiler.cpp
116 template void LLVMCompiler::SetLLVMOption(const char *option, bool val);
117
GetTripleForArch(Arch arch)118 llvm::Triple LLVMCompiler::GetTripleForArch(Arch arch)
119 {
120 std::string error;
121 std::string tripleName;
122 switch (arch) {
123 case Arch::AARCH64:
124 tripleName = g_options.GetLlvmTriple();
125 break;
126 case Arch::X86_64:
127 #ifdef PANDA_TARGET_LINUX
128 tripleName = g_options.WasSetLlvmTriple() ? g_options.GetLlvmTriple() : "x86_64-unknown-linux-gnu";
129 #elif defined(PANDA_TARGET_MACOS)
130 tripleName = g_options.WasSetLlvmTriple() ? g_options.GetLlvmTriple() : "x86_64-apple-darwin-gnu";
131 #elif defined(PANDA_TARGET_WINDOWS)
132 tripleName = g_options.WasSetLlvmTriple() ? g_options.GetLlvmTriple() : "x86_64-unknown-windows-unknown";
133 #else
134 tripleName = g_options.WasSetLlvmTriple() ? g_options.GetLlvmTriple() : "x86_64-unknown-unknown-unknown";
135 #endif
136 break;
137
138 default:
139 UNREACHABLE();
140 }
141 llvm::Triple triple(llvm::Triple::normalize(tripleName));
142 [[maybe_unused]] auto target = llvm::TargetRegistry::lookupTarget("", triple, error);
143 ASSERT_PRINT(target != nullptr, error);
144 return triple;
145 }
146
GetCPUForArch(Arch arch)147 std::string LLVMCompiler::GetCPUForArch(Arch arch)
148 {
149 std::string cpu;
150 switch (arch) {
151 case Arch::AARCH64:
152 cpu = g_options.GetLlvmCpu();
153 break;
154 case Arch::X86_64:
155 cpu = g_options.WasSetLlvmCpu() ? g_options.GetLlvmCpu() : "";
156 break;
157 default:
158 UNREACHABLE();
159 }
160 return cpu;
161 }
162
InitializeLLVMTarget(Arch arch)163 void LLVMCompiler::InitializeLLVMTarget(Arch arch)
164 {
165 switch (arch) {
166 case Arch::AARCH64: {
167 LLVMInitializeAArch64TargetInfo();
168 LLVMInitializeAArch64Target();
169 LLVMInitializeAArch64TargetMC();
170 LLVMInitializeAArch64AsmPrinter();
171 LLVMInitializeAArch64AsmParser();
172 break;
173 }
174 #ifdef PANDA_TARGET_AMD64
175 case Arch::X86_64: {
176 LLVMInitializeX86TargetInfo();
177 LLVMInitializeX86Target();
178 LLVMInitializeX86TargetMC();
179 LLVMInitializeX86AsmPrinter();
180 LLVMInitializeX86AsmParser();
181 break;
182 }
183 #endif
184 default:
185 LLVM_LOG(FATAL, INFRA) << GetArchString(arch) << std::string(" is unsupported. ");
186 }
187 LLVM_LOG(DEBUG, INFRA) << "Available targets";
188 for (auto target : llvm::TargetRegistry::targets()) {
189 LLVM_LOG(DEBUG, INFRA) << "\t" << target.getName();
190 }
191 }
192
InitializeLLVMPasses()193 void LLVMCompiler::InitializeLLVMPasses()
194 {
195 llvm::PassRegistry ®istry = *llvm::PassRegistry::getPassRegistry();
196 initializeCore(registry);
197 initializeScalarOpts(registry);
198 initializeObjCARCOpts(registry);
199 initializeVectorization(registry);
200 initializeIPO(registry);
201 initializeAnalysis(registry);
202 initializeTransformUtils(registry);
203 initializeInstCombine(registry);
204 initializeAggressiveInstCombine(registry);
205 initializeInstrumentation(registry);
206 initializeTarget(registry);
207 initializeCodeGen(registry);
208 initializeGlobalISel(registry);
209 }
210
211 } // namespace panda::llvmbackend
212