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