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 "compiler/code_info/code_info.h"
17 #include "compiler/optimizer/code_generator/relocations.h"
18 #include "events/events.h"
19 #include "optimizer/ir/graph.h"
20 #include "runtime/include/method.h"
21 #include "compiler_options.h"
22 #include "target_machine_builder.h"
23
24 #include "llvm_irtoc_compiler.h"
25 #include "llvm_logger.h"
26 #include "llvm_options.h"
27 #include "mir_compiler.h"
28
29 #include "lowering/llvm_ir_constructor.h"
30 #include "transforms/passes/inline_ir/patch_return_handler_stack_adjustment.h"
31
32 #include <llvm/Bitcode/BitcodeReader.h>
33 #include <llvm/CodeGen/MachineFunctionPass.h>
34 #include <llvm/CodeGen/Passes.h>
35 // Suppress warning about forward Pass declaration defined in another namespace
36 #include <llvm/Pass.h>
37 #include <llvm/Support/FileSystem.h>
38
39 using panda::compiler::LLVMIrConstructor;
40
41 namespace panda::llvmbackend {
42
CreateLLVMIrtocCompiler(panda::compiler::RuntimeInterface * runtime,panda::ArenaAllocator * allocator,panda::Arch arch)43 std::unique_ptr<IrtocCompilerInterface> CreateLLVMIrtocCompiler(panda::compiler::RuntimeInterface *runtime,
44 panda::ArenaAllocator *allocator, panda::Arch arch)
45 {
46 return std::make_unique<LLVMIrtocCompiler>(runtime, allocator, arch, "irtoc_file_name.hack");
47 }
48
LLVMIrtocCompiler(panda::compiler::RuntimeInterface * runtime,panda::ArenaAllocator * allocator,panda::Arch arch,std::string filename)49 LLVMIrtocCompiler::LLVMIrtocCompiler(panda::compiler::RuntimeInterface *runtime, panda::ArenaAllocator *allocator,
50 panda::Arch arch, std::string filename)
51 : LLVMCompiler(arch),
52 methods_(allocator->Adapter()),
53 filename_(std::move(filename)),
54 arkInterface_(runtime, GetTripleForArch(arch))
55 {
56 InitializeSpecificLLVMOptions(arch);
57 auto llvmCompilerOptions = InitializeLLVMCompilerOptions();
58
59 // clang-format off
60 targetMachine_ = cantFail(panda::llvmbackend::TargetMachineBuilder {}
61 .SetCPU(GetCPUForArch(arch))
62 .SetOptLevel(static_cast<llvm::CodeGenOpt::Level>(llvmCompilerOptions.optlevel))
63 .SetFeatures(GetFeaturesForArch(GetArch()))
64 .SetTriple(GetTripleForArch(GetArch()))
65 .Build());
66 // clang-format on
67 mirCompiler_ = std::make_unique<MIRCompiler>(
68 targetMachine_, [this](panda::llvmbackend::InsertingPassManager *manager) -> void {
69 manager->InsertBefore(&llvm::FEntryInserterID,
70 panda::llvmbackend::CreatePatchReturnHandlerStackAdjustmentPass(&arkInterface_));
71 });
72 optimizer_ = std::make_unique<panda::llvmbackend::LLVMOptimizer>(llvmCompilerOptions, &arkInterface_,
73 mirCompiler_->GetTargetMachine());
74 InitializeModule();
75
76 debugData_ = std::make_unique<DebugDataBuilder>(module_.get(), filename_);
77 }
78
GetFeaturesForArch(Arch arch)79 std::vector<std::string> LLVMIrtocCompiler::GetFeaturesForArch(Arch arch)
80 {
81 if (arch == Arch::X86_64 && panda::compiler::g_options.IsCpuFeatureEnabled(compiler::SSE42)) {
82 return {std::string("+sse4.2")};
83 }
84 return {};
85 }
86
GetFastPathCallingConv(uint32_t numArgs)87 static llvm::CallingConv::ID GetFastPathCallingConv(uint32_t numArgs)
88 {
89 ASSERT(numArgs <= 5U);
90 switch (numArgs) {
91 case 1U:
92 return llvm::CallingConv::ArkFast1;
93 case 2U:
94 return llvm::CallingConv::ArkFast2;
95 case 3U:
96 return llvm::CallingConv::ArkFast3;
97 case 4U:
98 return llvm::CallingConv::ArkFast4;
99 case 5U:
100 return llvm::CallingConv::ArkFast5;
101 default:
102 UNREACHABLE();
103 }
104 }
105
AddGraph(compiler::Graph * graph)106 bool LLVMIrtocCompiler::AddGraph(compiler::Graph *graph)
107 {
108 ASSERT(graph != nullptr);
109 ASSERT(graph->GetArch() == GetArch());
110 ASSERT(!graph->SupportManagedCode());
111 auto method = graph->GetMethod();
112
113 LLVMIrConstructor ctor(graph, module_.get(), GetLLVMContext(), &arkInterface_, debugData_);
114 auto llvmFunction = ctor.GetFunc();
115 if (graph->GetMode().IsInterpreter()) {
116 llvmFunction->setCallingConv(llvm::CallingConv::ArkInt);
117 } else if (graph->GetMode().IsFastPath()) {
118 // Excluding fake thread and frame arguments
119 uint32_t constexpr FAKE_ARGS_NUM = 2U;
120 ASSERT(llvmFunction->arg_size() >= FAKE_ARGS_NUM);
121 if (llvmFunction->arg_size() - FAKE_ARGS_NUM == 0) {
122 if (llvmFunction->getReturnType()->isVoidTy()) {
123 llvmFunction->setCallingConv(llvm::CallingConv::ArkFast0);
124 } else {
125 llvmFunction->setCallingConv(llvm::CallingConv::ArkFast1);
126 }
127 } else {
128 llvmFunction->setCallingConv(GetFastPathCallingConv(llvmFunction->arg_size() - FAKE_ARGS_NUM));
129 }
130 llvmFunction->addFnAttr("target-features", GetFastPathFeatures());
131 }
132
133 bool builtIr = ctor.BuildIr();
134 if (!builtIr) {
135 irFailed_ = true;
136 LLVM_LOG(ERROR, INFRA) << "LLVM failed to build IR for method " << arkInterface_.GetUniqMethodName(method);
137 llvmFunction->deleteBody();
138 return false;
139 }
140
141 LLVM_LOG(DEBUG, INFRA) << "LLVM built LLVM IR for method " << arkInterface_.GetUniqMethodName(method);
142 methods_.push_back(static_cast<Method *>(method));
143 return true;
144 }
145
CanCompile(panda::compiler::Graph * graph)146 Expected<bool, std::string> LLVMIrtocCompiler::CanCompile(panda::compiler::Graph *graph)
147 {
148 LLVM_LOG(DEBUG, INFRA) << "LLVM checking graph for method " << arkInterface_.GetUniqMethodName(graph->GetMethod());
149 return LLVMIrConstructor::CanCompile(graph);
150 }
151
CompileAll()152 void LLVMIrtocCompiler::CompileAll()
153 {
154 // Compile even if there are no methods because we have to produce an object file, even an empty one
155 ASSERT_PRINT(!HasCompiledCode(), "Cannot compile twice");
156
157 optimizer_->OptimizeModule(module_.get());
158 debugData_->Finalize();
159 objectFile_ = exitOnErr_(mirCompiler_->CompileModule(*module_));
160 }
161
GetFastPathFeatures() const162 std::string LLVMIrtocCompiler::GetFastPathFeatures() const
163 {
164 std::string features;
165 for (const auto &feature : GetFeaturesForArch(GetArch())) {
166 features.append(feature).append(",");
167 }
168 // FastPath may use FP register. We should be ready for this
169 switch (GetArch()) {
170 case Arch::AARCH64:
171 features.append("+reserve-").append(arkInterface_.GetFramePointerRegister());
172 features.append(",");
173 features.append("+reserve-").append(arkInterface_.GetThreadRegister());
174 break;
175 default:
176 UNREACHABLE();
177 }
178 return features;
179 }
180
InitializeSpecificLLVMOptions(Arch arch)181 void LLVMIrtocCompiler::InitializeSpecificLLVMOptions(Arch arch)
182 {
183 if (arch == Arch::X86_64) {
184 SetLLVMOption("x86-use-base-pointer", false);
185 }
186 if (arch == Arch::AARCH64) {
187 SetLLVMOption("aarch64-enable-ptr32", true);
188 }
189 SetLLVMOption("inline-remark-attribute", true);
190 }
191
InitializeModule()192 void LLVMIrtocCompiler::InitializeModule()
193 {
194 auto moduleFile = llvmbackend::g_options.GetLlvmInlineModule();
195 auto layout = targetMachine_->createDataLayout();
196 if (moduleFile.empty()) {
197 module_ = std::make_unique<llvm::Module>("irtoc empty module", *GetLLVMContext());
198 module_->setDataLayout(layout);
199 module_->setTargetTriple(GetTripleForArch(GetArch()).getTriple());
200 return;
201 }
202 auto buffer = errorOrToExpected(llvm::MemoryBuffer::getFile(moduleFile));
203 LLVM_LOG_IF(!buffer, FATAL, INFRA) << "Could not read inline module from file = '" << moduleFile << "', error: '"
204 << toString(buffer.takeError()) << "'";
205
206 auto contents = llvm::getBitcodeFileContents(*buffer.get());
207 LLVM_LOG_IF(!contents, FATAL, INFRA) << "Could get bitcode file contents from file = '" << moduleFile
208 << "', error: '" << toString(contents.takeError()) << "'";
209
210 static constexpr int32_t EXPECTED_MODULES = 1;
211 LLVM_LOG_IF(contents->Mods.size() != EXPECTED_MODULES, FATAL, INFRA)
212 << "Inline module file '" << moduleFile << "' has unexpected number of modules = " << contents->Mods.size()
213 << ", expected " << EXPECTED_MODULES;
214 auto module = contents->Mods[0].parseModule(*GetLLVMContext());
215 LLVM_LOG_IF(!module, FATAL, INFRA) << "Could not parse inline module from file '" << moduleFile << "', error: '"
216 << toString(buffer.takeError()) << "'";
217 module_ = std::move(*module);
218 module_->setDataLayout(layout);
219 optimizer_->ProcessInlineModule(module_.get());
220 }
221
WriteObjectFile(std::string_view output)222 void LLVMIrtocCompiler::WriteObjectFile(std::string_view output)
223 {
224 ASSERT_PRINT(HasCompiledCode(), "Call CompileAll first");
225 objectFile_->WriteTo(output);
226 }
227
GetObjectFileSize()228 size_t LLVMIrtocCompiler::GetObjectFileSize()
229 {
230 return objectFile_->Size();
231 }
232
GetCompiledCode(std::string_view functionName)233 CompiledCode LLVMIrtocCompiler::GetCompiledCode(std::string_view functionName)
234 {
235 ASSERT(objectFile_ != nullptr);
236 auto reference = objectFile_->GetSectionByFunctionName(std::string {functionName});
237 CompiledCode code {};
238 code.size = reference.GetSize();
239 code.code = reference.GetMemory();
240 return code;
241 }
242 } // namespace panda::llvmbackend
243