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