• 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 "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