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/optimizer/ir/runtime_interface.h"
17 #include "runtime/include/method.h"
18 #include "events/events.h"
19 #include "compiler_options.h"
20 #include "utils/cframe_layout.h"
21 #include "llvm_ark_interface.h"
22 #include "llvm_logger.h"
23
24 #include <llvm/IR/Function.h>
25 #include <llvm/IR/IntrinsicInst.h>
26 #include <llvm/Support/raw_ostream.h>
27 #include "llvm_options.h"
28
29 using panda::compiler::RuntimeInterface;
30
31 namespace {
LLVMArchToArkArch(llvm::Triple::ArchType arch)32 constexpr panda::Arch LLVMArchToArkArch(llvm::Triple::ArchType arch)
33 {
34 switch (arch) {
35 case llvm::Triple::ArchType::aarch64:
36 return panda::Arch::AARCH64;
37 case llvm::Triple::ArchType::x86_64:
38 return panda::Arch::X86_64;
39 case llvm::Triple::ArchType::x86:
40 return panda::Arch::X86;
41 case llvm::Triple::ArchType::arm:
42 return panda::Arch::AARCH32;
43 default:
44 UNREACHABLE();
45 return panda::Arch::NONE;
46 }
47 }
48 #include <entrypoints_gen.inl>
49 #include <intrinsics_gen.inl>
50 #include <intrinsic_names_gen.inl>
51 #include <entrypoints_llvm_ark_interface_gen.inl>
52 } // namespace
53
54 namespace panda::llvmbackend {
55
LLVMArkInterface(RuntimeInterface * runtime,llvm::Triple triple)56 panda::llvmbackend::LLVMArkInterface::LLVMArkInterface(RuntimeInterface *runtime, llvm::Triple triple)
57 : runtime_(runtime), triple_(std::move(triple))
58 {
59 ASSERT(runtime != nullptr);
60 }
61
GetThreadRegister() const62 const char *LLVMArkInterface::GetThreadRegister() const
63 {
64 switch (LLVMArchToArkArch(triple_.getArch())) {
65 case Arch::AARCH64: {
66 constexpr auto EXPECTED_REGISTER = 28;
67 static_assert(ArchTraits<Arch::AARCH64>::THREAD_REG == EXPECTED_REGISTER);
68 return "x28";
69 }
70 case Arch::X86_64: {
71 constexpr auto EXPECTED_REGISTER = 15;
72 static_assert(ArchTraits<Arch::X86_64>::THREAD_REG == EXPECTED_REGISTER);
73 return "r15";
74 }
75 default:
76 UNREACHABLE();
77 }
78 }
79
GetFramePointerRegister() const80 const char *LLVMArkInterface::GetFramePointerRegister() const
81 {
82 switch (LLVMArchToArkArch(triple_.getArch())) {
83 case Arch::AARCH64:
84 return "x29";
85 case Arch::X86_64:
86 return "rbp";
87 default:
88 UNREACHABLE();
89 }
90 }
91
GetEntrypointTlsOffset(EntrypointId id) const92 uintptr_t LLVMArkInterface::GetEntrypointTlsOffset(EntrypointId id) const
93 {
94 Arch arkArch = LLVMArchToArkArch(triple_.getArch());
95 using PandaEntrypointId = panda::compiler::RuntimeInterface::EntrypointId;
96 return runtime_->GetEntrypointTlsOffset(arkArch, static_cast<PandaEntrypointId>(id));
97 }
98
GetTlsPreWrbEntrypointOffset() const99 size_t LLVMArkInterface::GetTlsPreWrbEntrypointOffset() const
100 {
101 Arch arkArch = LLVMArchToArkArch(triple_.getArch());
102 return runtime_->GetTlsPreWrbEntrypointOffset(arkArch);
103 }
104
GetManagedThreadPostWrbOneObjectOffset() const105 uint32_t LLVMArkInterface::GetManagedThreadPostWrbOneObjectOffset() const
106 {
107 Arch arkArch = LLVMArchToArkArch(triple_.getArch());
108 return cross_values::GetManagedThreadPostWrbOneObjectOffset(arkArch);
109 }
110
GetRuntimeFunctionName(LLVMArkInterface::RuntimeCallType callType,LLVMArkInterface::IntrinsicId id)111 llvm::StringRef LLVMArkInterface::GetRuntimeFunctionName(LLVMArkInterface::RuntimeCallType callType,
112 LLVMArkInterface::IntrinsicId id)
113 {
114 if (callType == LLVMArkInterface::RuntimeCallType::INTRINSIC) {
115 return llvm::StringRef(GetIntrinsicRuntimeFunctionName(id));
116 }
117 // sanity check
118 ASSERT(callType == LLVMArkInterface::RuntimeCallType::ENTRYPOINT);
119 return llvm::StringRef(GetEntrypointRuntimeFunctionName(id));
120 }
121
GetRuntimeFunctionType(llvm::StringRef name) const122 llvm::FunctionType *LLVMArkInterface::GetRuntimeFunctionType(llvm::StringRef name) const
123 {
124 return runtimeFunctionTypes_.lookup(name);
125 }
126
GetOrCreateRuntimeFunctionType(llvm::LLVMContext & ctx,llvm::Module * module,LLVMArkInterface::RuntimeCallType callType,LLVMArkInterface::IntrinsicId id)127 llvm::FunctionType *LLVMArkInterface::GetOrCreateRuntimeFunctionType(llvm::LLVMContext &ctx, llvm::Module *module,
128 LLVMArkInterface::RuntimeCallType callType,
129 LLVMArkInterface::IntrinsicId id)
130 {
131 auto rtFunctionName = GetRuntimeFunctionName(callType, id);
132 auto rtFunctionTy = GetRuntimeFunctionType(rtFunctionName);
133 if (rtFunctionTy != nullptr) {
134 return rtFunctionTy;
135 }
136
137 if (callType == RuntimeCallType::INTRINSIC) {
138 rtFunctionTy = GetIntrinsicDeclaration(ctx, static_cast<RuntimeInterface::IntrinsicId>(id));
139 } else {
140 // sanity check
141 ASSERT(callType == RuntimeCallType::ENTRYPOINT);
142 rtFunctionTy = GetEntrypointDeclaration(ctx, module, static_cast<RuntimeInterface::EntrypointId>(id));
143 }
144
145 ASSERT(rtFunctionTy != nullptr);
146 runtimeFunctionTypes_.insert({rtFunctionName, rtFunctionTy});
147 return rtFunctionTy;
148 }
149
RememberFunctionOrigin(const llvm::Function * function,MethodPtr methodPtr)150 void LLVMArkInterface::RememberFunctionOrigin(const llvm::Function *function, MethodPtr methodPtr)
151 {
152 ASSERT(function != nullptr);
153 ASSERT(methodPtr != nullptr);
154
155 auto file = static_cast<panda_file::File *>(runtime_->GetBinaryFileForMethod(methodPtr));
156 [[maybe_unused]] auto insertionResult = functionOrigins_.insert({function, file});
157 ASSERT(insertionResult.second);
158 LLVM_LOG(DEBUG, INFRA) << function->getName().data() << " defined in " << runtime_->GetFileName(methodPtr);
159 }
160
GetEntrypointCallee(EntrypointId id) const161 LLVMArkInterface::RuntimeCallee LLVMArkInterface::GetEntrypointCallee(EntrypointId id) const
162 {
163 using PandaEntrypointId = panda::compiler::RuntimeInterface::EntrypointId;
164 auto eid = static_cast<PandaEntrypointId>(id);
165 auto functionName = GetEntrypointInternalName(eid);
166 auto functionProto = GetRuntimeFunctionType(functionName);
167 ASSERT(functionProto != nullptr);
168 return {functionProto, functionName};
169 }
170
GetFunctionByMethodPtr(LLVMArkInterface::MethodPtr method) const171 llvm::Function *LLVMArkInterface::GetFunctionByMethodPtr(LLVMArkInterface::MethodPtr method) const
172 {
173 ASSERT(method != nullptr);
174
175 return functions_.lookup(method);
176 }
177
PutFunction(LLVMArkInterface::MethodPtr methodPtr,llvm::Function * function)178 void LLVMArkInterface::PutFunction(LLVMArkInterface::MethodPtr methodPtr, llvm::Function *function)
179 {
180 ASSERT(function != nullptr);
181 ASSERT(methodPtr != nullptr);
182 // We are not expecting `tan` functions other than we are adding in LLVMIrConstructor::EmitTan()
183 ASSERT(function->getName() != "tan");
184
185 [[maybe_unused]] auto fInsertionResult = functions_.insert({methodPtr, function});
186 ASSERT_PRINT(fInsertionResult.first->second == function,
187 std::string("Attempt to map '") + GetUniqMethodName(methodPtr) + "' to a function = '" +
188 function->getName().str() + "', but the method_ptr already mapped to '" +
189 fInsertionResult.first->second->getName().str() + "'");
190 auto sourceLang = static_cast<uint8_t>(runtime_->GetMethodSourceLanguage(methodPtr));
191 [[maybe_unused]] auto slInsertionResult = sourceLanguages_.insert({function, sourceLang});
192 ASSERT(slInsertionResult.first->second == sourceLang);
193 }
194
GetIntrinsicRuntimeFunctionName(LLVMArkInterface::IntrinsicId id) const195 const char *LLVMArkInterface::GetIntrinsicRuntimeFunctionName(LLVMArkInterface::IntrinsicId id) const
196 {
197 ASSERT(id >= 0 && id < static_cast<IntrinsicId>(RuntimeInterface::IntrinsicId::COUNT));
198 return GetIntrinsicInternalName(static_cast<RuntimeInterface::IntrinsicId>(id));
199 }
200
GetEntrypointRuntimeFunctionName(LLVMArkInterface::EntrypointId id) const201 const char *LLVMArkInterface::GetEntrypointRuntimeFunctionName(LLVMArkInterface::EntrypointId id) const
202 {
203 ASSERT(id >= 0 && id < static_cast<EntrypointId>(RuntimeInterface::EntrypointId::COUNT));
204 return GetEntrypointInternalName(static_cast<RuntimeInterface::EntrypointId>(id));
205 }
206
GetUniqMethodName(LLVMArkInterface::MethodPtr methodPtr) const207 std::string LLVMArkInterface::GetUniqMethodName(LLVMArkInterface::MethodPtr methodPtr) const
208 {
209 ASSERT(methodPtr != nullptr);
210
211 if (IsIrtocMode()) {
212 return runtime_->GetMethodName(methodPtr);
213 }
214 #ifndef NDEBUG
215 auto uniqName = std::string(runtime_->GetMethodFullName(methodPtr, true));
216 uniqName.append("_id_");
217 uniqName.append(std::to_string(runtime_->GetUniqMethodId(methodPtr)));
218 #else
219 std::stringstream ssUniqName;
220 ssUniqName << "f_" << std::hex << methodPtr;
221 auto uniqName = ssUniqName.str();
222 #endif
223 return uniqName;
224 }
225
GetUniqMethodName(const Method * method) const226 std::string LLVMArkInterface::GetUniqMethodName(const Method *method) const
227 {
228 ASSERT(method != nullptr);
229
230 auto casted = const_cast<Method *>(method);
231 return GetUniqMethodName(static_cast<MethodPtr>(casted));
232 }
233
GetUniqueBasicBlockName(const std::string & bbName,const std::string & uniqueSuffix)234 std::string LLVMArkInterface::GetUniqueBasicBlockName(const std::string &bbName, const std::string &uniqueSuffix)
235 {
236 std::stringstream uniqueName;
237 const std::string nameDelimiter = "..";
238 auto first = bbName.find(nameDelimiter);
239 if (first == std::string::npos) {
240 uniqueName << bbName << "_" << uniqueSuffix;
241 return uniqueName.str();
242 }
243
244 auto second = bbName.rfind(nameDelimiter);
245 ASSERT(second != std::string::npos);
246
247 uniqueName << bbName.substr(0, first + 2U) << uniqueSuffix << bbName.substr(second, bbName.size());
248
249 return uniqueName.str();
250 }
251
IsIrtocMode() const252 bool LLVMArkInterface::IsIrtocMode() const
253 {
254 return true;
255 }
256
AppendIrtocReturnHandler(llvm::StringRef returnHandler)257 void LLVMArkInterface::AppendIrtocReturnHandler(llvm::StringRef returnHandler)
258 {
259 irtocReturnHandlers_.push_back(returnHandler);
260 }
261
IsIrtocReturnHandler(const llvm::Function & function) const262 bool LLVMArkInterface::IsIrtocReturnHandler(const llvm::Function &function) const
263 {
264 return std::find(irtocReturnHandlers_.cbegin(), irtocReturnHandlers_.cend(), function.getName()) !=
265 irtocReturnHandlers_.cend();
266 }
267
268 } // namespace panda::llvmbackend
269