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/optimizer/ir/runtime_interface.h"
17 #include "compiler/optimizer/ir/aot_data.h"
18 #include "runtime/include/method.h"
19 #include "events/events.h"
20 #include "compiler_options.h"
21 #include "utils/cframe_layout.h"
22 #include "llvm_ark_interface.h"
23 #include "llvm_logger.h"
24 #include "utils.h"
25
26 #include <llvm/IR/Function.h>
27 #include <llvm/Support/raw_ostream.h>
28 #include "llvm_options.h"
29
30 #include "aot/aot_builder/aot_builder.h"
31
32 using ark::compiler::AotBuilder;
33 using ark::compiler::AotData;
34 using ark::compiler::RuntimeInterface;
35
36 static constexpr auto MEMCPY_BIG_SIZE_FOR_X86_64_SSE = 128;
37 static constexpr auto MEMCPY_BIG_SIZE_FOR_X86_64_NO_SSE = 64;
38 static constexpr auto MEMCPY_BIG_SIZE_FOR_AARCH64 = 1024;
39
40 static constexpr auto MEMSET_SMALL_SIZE_FOR_AARCH64 = 1024;
41 static constexpr auto MEMSET_SMALL_SIZE_FOR_X86_64_SSE = 512;
42 static constexpr auto MEMSET_SMALL_SIZE_FOR_X86_64_NO_SSE = 256;
43
44 static constexpr auto MEMMOVE_SMALL_SIZE_FOR_AARCH64 = 30;
45 static constexpr auto MEMMOVE_BIG_SIZE_FOR_AARCH64 = 64;
46 static constexpr auto MEMMOVE_SMALL_SIZE_FOR_X86_64_SSE = 94;
47 static constexpr auto MEMMOVE_BIG_SIZE_FOR_X86_64_SSE = 128;
48 static constexpr auto MEMMOVE_SMALL_SIZE_FOR_X86_64_NO_SSE = 48;
49 static constexpr auto MEMMOVE_BIG_SIZE_FOR_X86_64_NO_SSE = 64;
50
51 static constexpr auto X86_THREAD_REG = 15;
52 static constexpr auto AARCH64_THREAD_REG = 28U;
53
54 namespace {
LLVMArchToArkArch(llvm::Triple::ArchType arch)55 constexpr ark::Arch LLVMArchToArkArch(llvm::Triple::ArchType arch)
56 {
57 switch (arch) {
58 case llvm::Triple::ArchType::aarch64:
59 return ark::Arch::AARCH64;
60 case llvm::Triple::ArchType::x86_64:
61 return ark::Arch::X86_64;
62 case llvm::Triple::ArchType::x86:
63 return ark::Arch::X86;
64 case llvm::Triple::ArchType::arm:
65 return ark::Arch::AARCH32;
66 default:
67 UNREACHABLE();
68 return ark::Arch::NONE;
69 }
70 }
71 #include <entrypoints_gen.inl>
72 #include <intrinsics_gen.inl>
73 #include <intrinsic_names_gen.inl>
74 #include <entrypoints_llvm_ark_interface_gen.inl>
75 } // namespace
76
77 namespace ark::llvmbackend {
78
DeoptsEnabled()79 bool LLVMArkInterface::DeoptsEnabled()
80 {
81 return g_options.IsLlvmDeopts();
82 }
83
GetAotDataFromBuilder(const class ark::panda_file::File * file,AotBuilder * aotBuilder)84 AotData GetAotDataFromBuilder(const class ark::panda_file::File *file, AotBuilder *aotBuilder)
85 {
86 return AotData(file, nullptr, 0, aotBuilder->GetIntfInlineCacheIndex(), aotBuilder->GetGotPlt(),
87 aotBuilder->GetGotVirtIndexes(), aotBuilder->GetGotClass(), aotBuilder->GetGotString(),
88 aotBuilder->GetGotIntfInlineCache(), aotBuilder->GetGotCommon(), nullptr);
89 }
90
LLVMArkInterface(RuntimeInterface * runtime,llvm::Triple triple,AotBuilder * aotBuilder,llvm::sys::Mutex * lock)91 ark::llvmbackend::LLVMArkInterface::LLVMArkInterface(RuntimeInterface *runtime, llvm::Triple triple,
92 AotBuilder *aotBuilder, llvm::sys::Mutex *lock)
93 : runtime_(runtime), triple_(std::move(triple)), aotBuilder_(aotBuilder), lock_ {lock}
94 {
95 ASSERT(runtime != nullptr);
96 }
97
GetCompiledEntryPointOffset() const98 intptr_t LLVMArkInterface::GetCompiledEntryPointOffset() const
99 {
100 return runtime_->GetCompiledEntryPointOffset(LLVMArchToArkArch(triple_.getArch()));
101 }
102
GetThreadRegister() const103 const char *LLVMArkInterface::GetThreadRegister() const
104 {
105 switch (LLVMArchToArkArch(triple_.getArch())) {
106 case Arch::AARCH64: {
107 static_assert(ArchTraits<Arch::AARCH64>::THREAD_REG == AARCH64_THREAD_REG);
108 return "x28";
109 }
110 case Arch::X86_64: {
111 static_assert(ArchTraits<Arch::X86_64>::THREAD_REG == X86_THREAD_REG);
112 return "r15";
113 }
114 default:
115 UNREACHABLE();
116 }
117 }
118
CreateIntfInlineCacheSlotId(const llvm::Function * caller) const119 int32_t LLVMArkInterface::CreateIntfInlineCacheSlotId(const llvm::Function *caller) const
120 {
121 llvm::sys::ScopedLock scopedLock {*lock_};
122 auto callerName = caller->getName();
123 auto callerOriginFile = functionOrigins_.lookup(callerName);
124 ASSERT_PRINT(callerOriginFile != nullptr,
125 std::string("No origin for function = '") + callerName.str() +
126 "'. Use RememberFunctionOrigin to store the origin before calling CreateIntfInlineCacheId");
127 auto aotData = GetAotDataFromBuilder(callerOriginFile, aotBuilder_);
128 uint64_t intfInlineCacheIndex = aotData.GetIntfInlineCacheIndex();
129 int32_t slot = aotData.GetIntfInlineCacheSlotId(intfInlineCacheIndex);
130 intfInlineCacheIndex++;
131 aotData.SetIntfInlineCacheIndex(intfInlineCacheIndex);
132 return slot;
133 }
134
GetFramePointerRegister() const135 const char *LLVMArkInterface::GetFramePointerRegister() const
136 {
137 switch (LLVMArchToArkArch(triple_.getArch())) {
138 case Arch::AARCH64:
139 return "x29";
140 case Arch::X86_64:
141 return "rbp";
142 default:
143 UNREACHABLE();
144 }
145 }
146
PutCalleeSavedRegistersMask(llvm::StringRef method,RegMasks masks)147 void LLVMArkInterface::PutCalleeSavedRegistersMask(llvm::StringRef method, RegMasks masks)
148 {
149 calleeSavedRegisters_.insert({method, masks});
150 }
151
GetCalleeSavedRegistersMask(llvm::StringRef method)152 LLVMArkInterface::RegMasks LLVMArkInterface::GetCalleeSavedRegistersMask(llvm::StringRef method)
153 {
154 return calleeSavedRegisters_.lookup(method);
155 }
156
GetEntrypointTlsOffset(EntrypointId id) const157 uintptr_t LLVMArkInterface::GetEntrypointTlsOffset(EntrypointId id) const
158 {
159 Arch arkArch = LLVMArchToArkArch(triple_.getArch());
160 using PandaEntrypointId = ark::compiler::RuntimeInterface::EntrypointId;
161 return runtime_->GetEntrypointTlsOffset(arkArch, static_cast<PandaEntrypointId>(id));
162 }
163
GetTlsFrameKindOffset() const164 size_t LLVMArkInterface::GetTlsFrameKindOffset() const
165 {
166 return runtime_->GetTlsFrameKindOffset(LLVMArchToArkArch(triple_.getArch()));
167 }
168
GetTlsPreWrbEntrypointOffset() const169 size_t LLVMArkInterface::GetTlsPreWrbEntrypointOffset() const
170 {
171 Arch arkArch = LLVMArchToArkArch(triple_.getArch());
172 return runtime_->GetTlsPreWrbEntrypointOffset(arkArch);
173 }
174
GetClassOffset() const175 uint32_t LLVMArkInterface::GetClassOffset() const
176 {
177 Arch arkArch = LLVMArchToArkArch(triple_.getArch());
178 return runtime_->GetClassOffset(arkArch);
179 }
180
GetManagedThreadPostWrbOneObjectOffset() const181 uint32_t LLVMArkInterface::GetManagedThreadPostWrbOneObjectOffset() const
182 {
183 Arch arkArch = LLVMArchToArkArch(triple_.getArch());
184 return cross_values::GetManagedThreadPostWrbOneObjectOffset(arkArch);
185 }
186
GetStackOverflowCheckOffset() const187 size_t LLVMArkInterface::GetStackOverflowCheckOffset() const
188 {
189 return runtime_->GetStackOverflowCheckOffset();
190 }
191
GetValueAsString(llvm::Value * value)192 std::string GetValueAsString(llvm::Value *value)
193 {
194 std::string name;
195 auto stream = llvm::raw_string_ostream(name);
196 value->print(stream);
197 return stream.str();
198 }
199
MustLowerMemSet(const llvm::IntrinsicInst * inst,llvm::Triple::ArchType arch)200 static bool MustLowerMemSet(const llvm::IntrinsicInst *inst, llvm::Triple::ArchType arch)
201 {
202 ASSERT(inst->getIntrinsicID() == llvm::Intrinsic::memset ||
203 inst->getIntrinsicID() == llvm::Intrinsic::memset_inline);
204
205 auto sizeArg = inst->getArgOperand(2U);
206 if (!llvm::isa<llvm::ConstantInt>(sizeArg)) {
207 return true;
208 }
209
210 auto arraySize = llvm::cast<llvm::ConstantInt>(sizeArg)->getZExtValue();
211 size_t smallSize;
212
213 if (arch == llvm::Triple::ArchType::aarch64) {
214 smallSize = MEMSET_SMALL_SIZE_FOR_AARCH64;
215 } else {
216 ASSERT(arch == llvm::Triple::ArchType::x86_64);
217 bool sse42 = ark::compiler::g_options.IsCpuFeatureEnabled(ark::compiler::SSE42);
218 smallSize = sse42 ? MEMSET_SMALL_SIZE_FOR_X86_64_SSE : MEMSET_SMALL_SIZE_FOR_X86_64_NO_SSE;
219 }
220 return arraySize > smallSize;
221 }
222
MustLowerMemCpy(const llvm::IntrinsicInst * inst,llvm::Triple::ArchType arch)223 static bool MustLowerMemCpy(const llvm::IntrinsicInst *inst, llvm::Triple::ArchType arch)
224 {
225 ASSERT(inst->getIntrinsicID() == llvm::Intrinsic::memcpy ||
226 inst->getIntrinsicID() == llvm::Intrinsic::memcpy_inline);
227
228 auto sizeArg = inst->getArgOperand(2U);
229 if (!llvm::isa<llvm::ConstantInt>(sizeArg)) {
230 return true;
231 }
232
233 auto arraySize = llvm::cast<llvm::ConstantInt>(sizeArg)->getZExtValue();
234 if (arch == llvm::Triple::ArchType::x86_64) {
235 bool sse42 = ark::compiler::g_options.IsCpuFeatureEnabled(ark::compiler::SSE42);
236 if (sse42) {
237 return arraySize > MEMCPY_BIG_SIZE_FOR_X86_64_SSE;
238 }
239 return arraySize > MEMCPY_BIG_SIZE_FOR_X86_64_NO_SSE;
240 }
241
242 ASSERT(arch == llvm::Triple::ArchType::aarch64);
243
244 return arraySize > MEMCPY_BIG_SIZE_FOR_AARCH64;
245 }
246
MustLowerMemMove(const llvm::IntrinsicInst * inst,llvm::Triple::ArchType arch)247 static bool MustLowerMemMove(const llvm::IntrinsicInst *inst, llvm::Triple::ArchType arch)
248 {
249 ASSERT(inst->getIntrinsicID() == llvm::Intrinsic::memmove);
250
251 auto sizeArg = inst->getArgOperand(2U);
252 if (!llvm::isa<llvm::ConstantInt>(sizeArg)) {
253 return true;
254 }
255
256 auto arraySize = llvm::cast<llvm::ConstantInt>(sizeArg)->getZExtValue();
257
258 size_t bigSize;
259 size_t smallSize;
260 int maxPopcount;
261 if (arch == llvm::Triple::ArchType::aarch64) {
262 smallSize = MEMMOVE_SMALL_SIZE_FOR_AARCH64;
263 bigSize = MEMMOVE_BIG_SIZE_FOR_AARCH64;
264 maxPopcount = 3U;
265 } else {
266 ASSERT(arch == llvm::Triple::ArchType::x86_64);
267 if (ark::compiler::g_options.IsCpuFeatureEnabled(ark::compiler::SSE42)) {
268 smallSize = MEMMOVE_SMALL_SIZE_FOR_X86_64_SSE;
269 bigSize = MEMMOVE_BIG_SIZE_FOR_X86_64_SSE;
270 } else {
271 smallSize = MEMMOVE_SMALL_SIZE_FOR_X86_64_NO_SSE;
272 bigSize = MEMMOVE_BIG_SIZE_FOR_X86_64_NO_SSE;
273 }
274 maxPopcount = 4U;
275 }
276 if (arraySize <= smallSize) {
277 return false;
278 }
279 if (arraySize > bigSize) {
280 return true;
281 }
282 return Popcount(arraySize) > maxPopcount;
283 }
284
GetLLVMIntrinsicId(const llvm::Instruction * inst) const285 llvm::Intrinsic::ID LLVMArkInterface::GetLLVMIntrinsicId(const llvm::Instruction *inst) const
286 {
287 ASSERT(inst != nullptr);
288
289 auto intrinsicInst = llvm::dyn_cast<llvm::IntrinsicInst>(inst);
290 if (intrinsicInst == nullptr) {
291 return llvm::Intrinsic::not_intrinsic;
292 }
293
294 auto llvmId = intrinsicInst->getIntrinsicID();
295 if (llvmId == llvm::Intrinsic::memcpy && !MustLowerMemCpy(intrinsicInst, triple_.getArch())) {
296 return llvm::Intrinsic::memcpy_inline;
297 }
298 if (llvmId == llvm::Intrinsic::memset && !MustLowerMemSet(intrinsicInst, triple_.getArch())) {
299 return llvm::Intrinsic::memset_inline;
300 }
301
302 return llvm::Intrinsic::not_intrinsic;
303 }
304
X86NoSSE(llvm::Triple::ArchType arch)305 [[maybe_unused]] static bool X86NoSSE(llvm::Triple::ArchType arch)
306 {
307 return arch == llvm::Triple::ArchType::x86_64 &&
308 !ark::compiler::g_options.IsCpuFeatureEnabled(ark::compiler::SSE42);
309 }
310
311 #include "get_intrinsic_id_llvm_ark_interface_gen.inl"
312
GetIntrinsicId(const llvm::Instruction * inst) const313 LLVMArkInterface::IntrinsicId LLVMArkInterface::GetIntrinsicId(const llvm::Instruction *inst) const
314 {
315 ASSERT(inst != nullptr);
316 auto opcode = inst->getOpcode();
317 auto type = inst->getType();
318
319 if (opcode == llvm::Instruction::FRem) {
320 ASSERT(inst->getNumOperands() == 2U);
321 ASSERT(type->isFloatTy() || type->isDoubleTy());
322 auto id = type->isFloatTy() ? RuntimeInterface::IntrinsicId::LIB_CALL_FMODF
323 : RuntimeInterface::IntrinsicId::LIB_CALL_FMOD;
324 return static_cast<IntrinsicId>(id);
325 }
326
327 auto pluginResult = GetPluginIntrinsicId(inst);
328 if (pluginResult != NO_INTRINSIC_ID_CONTINUE) {
329 return pluginResult;
330 }
331
332 if (const auto *call = llvm::dyn_cast<llvm::CallInst>(inst)) {
333 if (const llvm::Function *func = call->getCalledFunction()) {
334 // Can be generated in instcombine for `pow` intrinsic
335 if (func->getName() == "ldexp" || func->getName() == "ldexpf") {
336 ASSERT(type->isDoubleTy() || type->isFloatTy());
337 EVENT_PAOC("Lowering @ldexp");
338 auto id = type->isDoubleTy() ? RuntimeInterface::IntrinsicId::LIB_CALL_LDEXP
339 : RuntimeInterface::IntrinsicId::LIB_CALL_LDEXPF;
340 return static_cast<IntrinsicId>(id);
341 }
342 }
343 }
344
345 if (llvm::isa<llvm::IntrinsicInst>(inst)) {
346 return GetIntrinsicIdSwitch(llvm::cast<llvm::IntrinsicInst>(inst));
347 }
348 return NO_INTRINSIC_ID;
349 }
350
GetIntrinsicIdSwitch(const llvm::IntrinsicInst * inst) const351 LLVMArkInterface::IntrinsicId LLVMArkInterface::GetIntrinsicIdSwitch(const llvm::IntrinsicInst *inst) const
352 {
353 switch (inst->getIntrinsicID()) {
354 case llvm::Intrinsic::memcpy:
355 case llvm::Intrinsic::memcpy_inline:
356 case llvm::Intrinsic::memmove:
357 case llvm::Intrinsic::memset:
358 case llvm::Intrinsic::memset_inline:
359 return GetIntrinsicIdMemory(inst);
360 case llvm::Intrinsic::minimum:
361 case llvm::Intrinsic::maximum:
362 case llvm::Intrinsic::sin:
363 case llvm::Intrinsic::cos:
364 case llvm::Intrinsic::pow:
365 case llvm::Intrinsic::exp2:
366 return GetIntrinsicIdMath(inst);
367 case llvm::Intrinsic::ceil:
368 case llvm::Intrinsic::floor:
369 case llvm::Intrinsic::rint:
370 case llvm::Intrinsic::round:
371 case llvm::Intrinsic::lround:
372 case llvm::Intrinsic::exp:
373 case llvm::Intrinsic::log:
374 case llvm::Intrinsic::log2:
375 case llvm::Intrinsic::log10:
376 UNREACHABLE();
377 default:
378 return NO_INTRINSIC_ID;
379 }
380 UNREACHABLE();
381 return NO_INTRINSIC_ID;
382 }
383
GetIntrinsicIdMemory(const llvm::IntrinsicInst * inst) const384 LLVMArkInterface::IntrinsicId LLVMArkInterface::GetIntrinsicIdMemory(const llvm::IntrinsicInst *inst) const
385 {
386 auto arch = triple_.getArch();
387 auto llvmId = inst->getIntrinsicID();
388 switch (llvmId) {
389 case llvm::Intrinsic::memcpy:
390 case llvm::Intrinsic::memcpy_inline:
391 if (!ark::compiler::g_options.IsCompilerNonOptimizing() && !MustLowerMemCpy(inst, arch)) {
392 ASSERT(llvmId != llvm::Intrinsic::memcpy);
393 EVENT_PAOC("Skip lowering for @llvm.memcpy");
394 return NO_INTRINSIC_ID;
395 }
396 EVENT_PAOC("Lowering @llvm.memcpy");
397 return static_cast<IntrinsicId>(RuntimeInterface::IntrinsicId::LIB_CALL_MEM_COPY);
398 case llvm::Intrinsic::memmove:
399 if (!ark::compiler::g_options.IsCompilerNonOptimizing() && !MustLowerMemMove(inst, arch)) {
400 EVENT_PAOC("Skip lowering for @llvm.memmove, size is: " + GetValueAsString(inst->getArgOperand(2U)));
401 return NO_INTRINSIC_ID;
402 }
403 EVENT_PAOC("Lowering @llvm.memmove, size is: " + GetValueAsString(inst->getArgOperand(2U)));
404 return static_cast<IntrinsicId>(RuntimeInterface::IntrinsicId::LIB_CALL_MEM_MOVE);
405 case llvm::Intrinsic::memset:
406 case llvm::Intrinsic::memset_inline:
407 if (!ark::compiler::g_options.IsCompilerNonOptimizing() && !MustLowerMemSet(inst, arch)) {
408 ASSERT(llvmId != llvm::Intrinsic::memset);
409 EVENT_PAOC("Skip lowering for @llvm.memset");
410 return NO_INTRINSIC_ID;
411 }
412 EVENT_PAOC("Lowering @llvm.memset");
413 return static_cast<IntrinsicId>(RuntimeInterface::IntrinsicId::LIB_CALL_MEM_SET);
414 default:
415 UNREACHABLE();
416 return NO_INTRINSIC_ID;
417 }
418 return NO_INTRINSIC_ID;
419 }
420
GetIntrinsicIdMath(const llvm::IntrinsicInst * inst) const421 LLVMArkInterface::IntrinsicId LLVMArkInterface::GetIntrinsicIdMath(const llvm::IntrinsicInst *inst) const
422 {
423 auto arch = triple_.getArch();
424 auto type = inst->getType();
425 auto llvmId = inst->getIntrinsicID();
426 auto etype = !type->isVectorTy() ? type : llvm::cast<llvm::VectorType>(type)->getElementType();
427 ASSERT(etype->isFloatTy() || etype->isDoubleTy());
428 switch (llvmId) {
429 case llvm::Intrinsic::minimum: {
430 if (arch == llvm::Triple::ArchType::aarch64) {
431 return NO_INTRINSIC_ID;
432 }
433 auto id = etype->isFloatTy() ? RuntimeInterface::IntrinsicId::INTRINSIC_MATH_MIN_F32
434 : RuntimeInterface::IntrinsicId::INTRINSIC_MATH_MIN_F64;
435 return static_cast<IntrinsicId>(id);
436 }
437 case llvm::Intrinsic::maximum: {
438 if (arch == llvm::Triple::ArchType::aarch64) {
439 return NO_INTRINSIC_ID;
440 }
441 auto id = etype->isFloatTy() ? RuntimeInterface::IntrinsicId::INTRINSIC_MATH_MAX_F32
442 : RuntimeInterface::IntrinsicId::INTRINSIC_MATH_MAX_F64;
443 return static_cast<IntrinsicId>(id);
444 }
445 case llvm::Intrinsic::sin: {
446 auto id = etype->isFloatTy() ? RuntimeInterface::IntrinsicId::INTRINSIC_MATH_SIN_F32
447 : RuntimeInterface::IntrinsicId::INTRINSIC_MATH_SIN_F64;
448 return static_cast<IntrinsicId>(id);
449 }
450 case llvm::Intrinsic::cos: {
451 auto id = etype->isFloatTy() ? RuntimeInterface::IntrinsicId::INTRINSIC_MATH_COS_F32
452 : RuntimeInterface::IntrinsicId::INTRINSIC_MATH_COS_F64;
453 return static_cast<IntrinsicId>(id);
454 }
455 case llvm::Intrinsic::pow: {
456 auto id = etype->isFloatTy() ? RuntimeInterface::IntrinsicId::INTRINSIC_MATH_POW_F32
457 : RuntimeInterface::IntrinsicId::INTRINSIC_MATH_POW_F64;
458 return static_cast<IntrinsicId>(id);
459 }
460 case llvm::Intrinsic::exp2: {
461 auto id = etype->isFloatTy() ? RuntimeInterface::IntrinsicId::LIB_CALL_EXP2F
462 : RuntimeInterface::IntrinsicId::LIB_CALL_EXP2;
463 return static_cast<IntrinsicId>(id);
464 }
465 default:
466 UNREACHABLE();
467 return NO_INTRINSIC_ID;
468 }
469 return NO_INTRINSIC_ID;
470 }
471
GetRuntimeFunctionName(LLVMArkInterface::RuntimeCallType callType,LLVMArkInterface::IntrinsicId id)472 llvm::StringRef LLVMArkInterface::GetRuntimeFunctionName(LLVMArkInterface::RuntimeCallType callType,
473 LLVMArkInterface::IntrinsicId id)
474 {
475 if (callType == LLVMArkInterface::RuntimeCallType::INTRINSIC) {
476 return llvm::StringRef(GetIntrinsicRuntimeFunctionName(id));
477 }
478 // sanity check
479 ASSERT(callType == LLVMArkInterface::RuntimeCallType::ENTRYPOINT);
480 return llvm::StringRef(GetEntrypointRuntimeFunctionName(id));
481 }
482
GetRuntimeFunctionType(llvm::StringRef name) const483 llvm::FunctionType *LLVMArkInterface::GetRuntimeFunctionType(llvm::StringRef name) const
484 {
485 #ifndef NDEBUG
486 ASSERT_PRINT(!compiled_, "Attempt to call GetRuntimeFunctionType after module is compiled");
487 #endif
488 return runtimeFunctionTypes_.lookup(name);
489 }
490
GetOrCreateRuntimeFunctionType(llvm::LLVMContext & ctx,llvm::Module * module,LLVMArkInterface::RuntimeCallType callType,LLVMArkInterface::IntrinsicId id)491 llvm::FunctionType *LLVMArkInterface::GetOrCreateRuntimeFunctionType(llvm::LLVMContext &ctx, llvm::Module *module,
492 LLVMArkInterface::RuntimeCallType callType,
493 LLVMArkInterface::IntrinsicId id)
494 {
495 #ifndef NDEBUG
496 ASSERT_PRINT(!compiled_, "Attempt to call GetOrCreateRuntimeFunctionType after module is compiled");
497 #endif
498 auto rtFunctionName = GetRuntimeFunctionName(callType, id);
499 auto rtFunctionTy = GetRuntimeFunctionType(rtFunctionName);
500 if (rtFunctionTy != nullptr) {
501 return rtFunctionTy;
502 }
503
504 if (callType == RuntimeCallType::INTRINSIC) {
505 rtFunctionTy = GetIntrinsicDeclaration(ctx, static_cast<RuntimeInterface::IntrinsicId>(id));
506 } else {
507 // sanity check
508 ASSERT(callType == RuntimeCallType::ENTRYPOINT);
509 rtFunctionTy = GetEntrypointDeclaration(ctx, module, static_cast<RuntimeInterface::EntrypointId>(id));
510 }
511
512 ASSERT(rtFunctionTy != nullptr);
513 runtimeFunctionTypes_.insert({rtFunctionName, rtFunctionTy});
514 return rtFunctionTy;
515 }
516
517 #ifndef NDEBUG
MarkAsCompiled()518 void LLVMArkInterface::MarkAsCompiled()
519 {
520 runtimeFunctionTypes_.clear();
521 compiled_ = true;
522 }
523 #endif
524
CreateRequiredIntrinsicFunctionTypes(llvm::LLVMContext & ctx)525 void LLVMArkInterface::CreateRequiredIntrinsicFunctionTypes(llvm::LLVMContext &ctx)
526 {
527 constexpr std::array REQUIRED_INTRINSIC_IDS = {
528 RuntimeInterface::IntrinsicId::INTRINSIC_MATH_MIN_F64, RuntimeInterface::IntrinsicId::INTRINSIC_MATH_MIN_F32,
529 RuntimeInterface::IntrinsicId::INTRINSIC_MATH_MAX_F64, RuntimeInterface::IntrinsicId::INTRINSIC_MATH_MAX_F32,
530 RuntimeInterface::IntrinsicId::INTRINSIC_MATH_POW_F64, RuntimeInterface::IntrinsicId::INTRINSIC_MATH_POW_F32,
531 RuntimeInterface::IntrinsicId::INTRINSIC_MATH_SIN_F64, RuntimeInterface::IntrinsicId::INTRINSIC_MATH_SIN_F32,
532 RuntimeInterface::IntrinsicId::INTRINSIC_MATH_COS_F64, RuntimeInterface::IntrinsicId::INTRINSIC_MATH_COS_F32,
533 RuntimeInterface::IntrinsicId::LIB_CALL_FMODF, RuntimeInterface::IntrinsicId::LIB_CALL_FMOD,
534 RuntimeInterface::IntrinsicId::LIB_CALL_LDEXP, RuntimeInterface::IntrinsicId::LIB_CALL_LDEXPF,
535 RuntimeInterface::IntrinsicId::LIB_CALL_EXP2, RuntimeInterface::IntrinsicId::LIB_CALL_EXP2F,
536 RuntimeInterface::IntrinsicId::LIB_CALL_MEM_COPY, RuntimeInterface::IntrinsicId::LIB_CALL_MEM_MOVE,
537 RuntimeInterface::IntrinsicId::LIB_CALL_MEM_SET};
538 for (auto id : REQUIRED_INTRINSIC_IDS) {
539 auto rtFunctionName =
540 GetRuntimeFunctionName(RuntimeCallType::INTRINSIC, static_cast<LLVMArkInterface::IntrinsicId>(id));
541 auto rtFunctionTy = GetIntrinsicDeclaration(ctx, id);
542 ASSERT(rtFunctionTy != nullptr);
543 runtimeFunctionTypes_.insert({rtFunctionName, rtFunctionTy});
544 }
545 }
546
RememberFunctionOrigin(const llvm::Function * function,MethodPtr methodPtr)547 void LLVMArkInterface::RememberFunctionOrigin(const llvm::Function *function, MethodPtr methodPtr)
548 {
549 ASSERT(function != nullptr);
550 ASSERT(methodPtr != nullptr);
551 auto funcName = function->getName();
552 auto file = static_cast<panda_file::File *>(runtime_->GetBinaryFileForMethod(methodPtr));
553 [[maybe_unused]] auto insertionResultFunctionOrigins = functionOrigins_.insert({funcName, file});
554 ASSERT(insertionResultFunctionOrigins.second);
555 LLVM_LOG(DEBUG, INFRA) << funcName.data() << " defined in " << runtime_->GetFileName(methodPtr);
556 [[maybe_unused]] auto insertionResultMethodPtrs = methodPtrs_.insert({funcName, methodPtr});
557 ASSERT(insertionResultMethodPtrs.second);
558 }
559
GetMethodId(const llvm::Function * caller,const llvm::Function * callee) const560 LLVMArkInterface::MethodId LLVMArkInterface::GetMethodId(const llvm::Function *caller,
561 const llvm::Function *callee) const
562 {
563 ASSERT(callee != nullptr);
564 ASSERT(caller != nullptr);
565 auto callerName = caller->getName();
566 auto callerOriginFile = functionOrigins_.lookup(callerName);
567 ASSERT_PRINT(callerOriginFile != nullptr,
568 std::string("No origin for function = '") + callerName.str() +
569 "'. Use RememberFunctionOrigin to store the origin before calling GetMethodId");
570 auto calleeName = callee->getName();
571 auto callerOriginFileIterator = methodIds_.find(callerOriginFile);
572 ASSERT_PRINT(callerOriginFileIterator != methodIds_.end(), "No method ids were declared in callerOriginFile");
573 auto methodIdIterator = callerOriginFileIterator->second.find(calleeName);
574 ASSERT_PRINT(methodIdIterator != callerOriginFileIterator->second.end(),
575 std::string(calleeName) + " was not declared in callerOriginFile");
576
577 return methodIdIterator->second;
578 }
579
RememberFunctionCall(const llvm::Function * caller,const llvm::Function * callee,MethodId methodId)580 void LLVMArkInterface::RememberFunctionCall(const llvm::Function *caller, const llvm::Function *callee,
581 MethodId methodId)
582 {
583 ASSERT(callee != nullptr);
584 ASSERT(caller != nullptr);
585 auto callerName = caller->getName();
586 auto pandaFile = functionOrigins_.lookup(callerName);
587 ASSERT_PRINT(pandaFile != nullptr,
588 std::string("No origin for function = '") + callerName.str() +
589 "'. Use RememberFunctionOrigin to store the origin before calling RememberFunctionCall");
590 // Assuming it is ok to declare extern function twice
591 auto &pandaFileMap = methodIds_[pandaFile];
592 pandaFileMap.insert({callee->getName(), methodId});
593 }
594
IsRememberedCall(const llvm::Function * caller,const llvm::Function * callee) const595 bool ark::llvmbackend::LLVMArkInterface::IsRememberedCall(const llvm::Function *caller,
596 const llvm::Function *callee) const
597 {
598 ASSERT(caller != nullptr);
599 ASSERT(callee != nullptr);
600 auto callerName = caller->getName();
601 auto callerOriginFile = functionOrigins_.find(callerName);
602 ASSERT_PRINT(callerOriginFile != functionOrigins_.end(),
603 callerName.str() + " has no origin panda file. Use RememberFunctionOrigin for " + callerName.str() +
604 " before calling IsRememberedCall");
605 auto callerOriginFileIterator = methodIds_.find(callerOriginFile->second);
606 return callerOriginFileIterator != methodIds_.end() &&
607 callerOriginFileIterator->second.find(callee->getName()) != callerOriginFileIterator->second.end();
608 }
609
GetObjectClassOffset() const610 int32_t ark::llvmbackend::LLVMArkInterface::GetObjectClassOffset() const
611 {
612 auto arch = LLVMArchToArkArch(triple_.getArch());
613 return runtime_->GetObjClassOffset(arch);
614 }
615
IsExternal(llvm::CallInst * call)616 bool ark::llvmbackend::LLVMArkInterface::IsExternal(llvm::CallInst *call)
617 {
618 auto caller = methodPtrs_.find(call->getFunction()->getName());
619 ASSERT(caller != methodPtrs_.end());
620 auto callee = methodPtrs_.find(call->getCalledFunction()->getName());
621 ASSERT(callee != methodPtrs_.end());
622 return runtime_->IsMethodExternal(caller->second, callee->second);
623 }
624
ResolveVirtual(uint32_t classId,llvm::CallInst * call)625 LLVMArkInterface::MethodPtr ark::llvmbackend::LLVMArkInterface::ResolveVirtual(uint32_t classId, llvm::CallInst *call)
626 {
627 auto caller = methodPtrs_.find(call->getFunction()->getName());
628 ASSERT(caller != methodPtrs_.end());
629 auto klass = runtime_->GetClass(caller->second, classId);
630 if (klass == nullptr) {
631 return nullptr;
632 }
633 auto callee = methodPtrs_.find(call->getCalledFunction()->getName());
634 ASSERT(callee != methodPtrs_.end());
635 auto calleeClassId = runtime_->GetClassIdForMethod(callee->second);
636 auto calleeClass = runtime_->GetClass(callee->second, calleeClassId);
637 if (!runtime_->IsAssignableFrom(calleeClass, klass)) {
638 return nullptr;
639 }
640 auto resolved = runtime_->ResolveVirtualMethod(klass, callee->second);
641 return resolved;
642 }
643
GetPltSlotId(const llvm::Function * caller,const llvm::Function * callee) const644 int32_t LLVMArkInterface::GetPltSlotId(const llvm::Function *caller, const llvm::Function *callee) const
645 {
646 ASSERT(caller != nullptr);
647 ASSERT(callee != nullptr);
648 auto callerName = caller->getName();
649 llvm::sys::ScopedLock scopedLock {*lock_};
650 auto callerOriginFile = functionOrigins_.lookup(callerName);
651 ASSERT_PRINT(callerOriginFile != nullptr,
652 std::string("No origin for function = '") + callerName.str() +
653 "'. Use RememberFunctionOrigin to store the origin before calling GetPltSlotId");
654 return GetAotDataFromBuilder(callerOriginFile, aotBuilder_).GetPltSlotId(GetMethodId(caller, callee));
655 }
656
GetClassIndexInAotGot(ark::compiler::AotData * aotData,uint32_t klassId,bool initialized)657 int32_t LLVMArkInterface::GetClassIndexInAotGot(ark::compiler::AotData *aotData, uint32_t klassId, bool initialized)
658 {
659 ASSERT(aotData != nullptr);
660 llvm::sys::ScopedLock scopedLock {*lock_};
661 auto index = aotData->GetClassSlotId(klassId);
662 if (initialized) {
663 return index - 1;
664 }
665 return index;
666 }
667
GetStringSlotId(AotData * aotData,uint32_t typeId)668 int32_t LLVMArkInterface::GetStringSlotId(AotData *aotData, uint32_t typeId)
669 {
670 ASSERT(aotData != nullptr);
671 llvm::sys::ScopedLock scopedLock {*lock_};
672 return aotData->GetStringSlotId(typeId);
673 }
674
GetEntrypointCallee(EntrypointId id) const675 LLVMArkInterface::RuntimeCallee LLVMArkInterface::GetEntrypointCallee(EntrypointId id) const
676 {
677 using PandaEntrypointId = ark::compiler::RuntimeInterface::EntrypointId;
678 auto eid = static_cast<PandaEntrypointId>(id);
679 auto functionName = GetEntrypointInternalName(eid);
680 auto functionProto = GetRuntimeFunctionType(functionName);
681 ASSERT(functionProto != nullptr);
682 return {functionProto, functionName};
683 }
684
GetBridgeType(EntrypointId id) const685 BridgeType LLVMArkInterface::GetBridgeType(EntrypointId id) const
686 {
687 return GetBridgeTypeInternal(static_cast<RuntimeInterface::EntrypointId>(id));
688 }
689
PutVirtualFunction(LLVMArkInterface::MethodPtr methodPtr,llvm::Function * function)690 void LLVMArkInterface::PutVirtualFunction(LLVMArkInterface::MethodPtr methodPtr, llvm::Function *function)
691 {
692 ASSERT(function != nullptr);
693 ASSERT(methodPtr != nullptr);
694
695 methodPtrs_.insert({function->getName(), methodPtr});
696 }
697
GetVTableOffset(llvm::Function * caller,uint32_t methodId) const698 uint32_t LLVMArkInterface::GetVTableOffset(llvm::Function *caller, uint32_t methodId) const
699 {
700 auto arkCaller = methodPtrs_.find(caller->getName());
701 auto callee = runtime_->GetMethodById(arkCaller->second, methodId);
702
703 auto arch = LLVMArchToArkArch(triple_.getArch());
704 auto vtableIndex = runtime_->GetVTableIndex(callee);
705 uint32_t offset = runtime_->GetVTableOffset(arch) + (vtableIndex << 3U);
706 return offset;
707 }
708
IsInterfaceMethod(llvm::CallInst * call)709 bool LLVMArkInterface::IsInterfaceMethod(llvm::CallInst *call)
710 {
711 auto methodId = utils::GetMethodIdFromAttr(call);
712 auto caller = methodPtrs_.find(call->getFunction()->getName());
713 auto callee = runtime_->GetMethodById(caller->second, methodId);
714
715 return runtime_->IsInterfaceMethod(callee);
716 }
717
GetIntrinsicRuntimeFunctionName(LLVMArkInterface::IntrinsicId id) const718 const char *LLVMArkInterface::GetIntrinsicRuntimeFunctionName(LLVMArkInterface::IntrinsicId id) const
719 {
720 ASSERT(id >= 0 && id < static_cast<IntrinsicId>(RuntimeInterface::IntrinsicId::COUNT));
721 return GetIntrinsicInternalName(static_cast<RuntimeInterface::IntrinsicId>(id));
722 }
723
GetEntrypointRuntimeFunctionName(LLVMArkInterface::EntrypointId id) const724 const char *LLVMArkInterface::GetEntrypointRuntimeFunctionName(LLVMArkInterface::EntrypointId id) const
725 {
726 ASSERT(id >= 0 && id < static_cast<EntrypointId>(RuntimeInterface::EntrypointId::COUNT));
727 return GetEntrypointInternalName(static_cast<RuntimeInterface::EntrypointId>(id));
728 }
729
GetUniqMethodName(LLVMArkInterface::MethodPtr methodPtr) const730 std::string LLVMArkInterface::GetUniqMethodName(LLVMArkInterface::MethodPtr methodPtr) const
731 {
732 ASSERT(methodPtr != nullptr);
733
734 if (IsIrtocMode()) {
735 return runtime_->GetMethodName(methodPtr);
736 }
737 #ifndef NDEBUG
738 auto uniqName = std::string(runtime_->GetMethodFullName(methodPtr, true));
739 uniqName.append("_id_");
740 uniqName.append(std::to_string(runtime_->GetUniqMethodId(methodPtr)));
741 #else
742 std::stringstream ssUniqName;
743 ssUniqName << "f_" << std::hex << methodPtr;
744 auto uniqName = ssUniqName.str();
745 #endif
746 return uniqName;
747 }
748
GetUniqMethodName(const Method * method) const749 std::string LLVMArkInterface::GetUniqMethodName(const Method *method) const
750 {
751 ASSERT(method != nullptr);
752
753 auto casted = const_cast<Method *>(method);
754 return GetUniqMethodName(static_cast<MethodPtr>(casted));
755 }
756
GetUniqueBasicBlockName(const std::string & bbName,const std::string & uniqueSuffix)757 std::string LLVMArkInterface::GetUniqueBasicBlockName(const std::string &bbName, const std::string &uniqueSuffix)
758 {
759 std::stringstream uniqueName;
760 const std::string nameDelimiter = "..";
761 auto first = bbName.find(nameDelimiter);
762 if (first == std::string::npos) {
763 uniqueName << bbName << "_" << uniqueSuffix;
764 return uniqueName.str();
765 }
766
767 auto second = bbName.rfind(nameDelimiter);
768 ASSERT(second != std::string::npos);
769
770 uniqueName << bbName.substr(0, first + 2U) << uniqueSuffix << bbName.substr(second, bbName.size());
771
772 return uniqueName.str();
773 }
774
GetMethodStackSize(LLVMArkInterface::MethodPtr method) const775 uint64_t LLVMArkInterface::GetMethodStackSize(LLVMArkInterface::MethodPtr method) const
776 {
777 std::string name = GetUniqMethodName(method);
778 ASSERT(llvmStackSizes_.find(name) != llvmStackSizes_.end());
779 return llvmStackSizes_.lookup(name);
780 }
781
PutMethodStackSize(const llvm::Function * method,uint64_t size)782 void LLVMArkInterface::PutMethodStackSize(const llvm::Function *method, uint64_t size)
783 {
784 std::string name = method->getName().str();
785 ASSERT_PRINT(llvmStackSizes_.find(name) == llvmStackSizes_.end(), "Double inserted stack size for '" + name + "'");
786 llvmStackSizes_[name] = size;
787 }
788
GetVirtualRegistersCount(LLVMArkInterface::MethodPtr method) const789 uint32_t LLVMArkInterface::GetVirtualRegistersCount(LLVMArkInterface::MethodPtr method) const
790 {
791 // Registers used by user, arguments and accumulator
792 return runtime_->GetMethodRegistersCount(method) + runtime_->GetMethodArgumentsCount(method) + 1;
793 }
794
GetCFrameHasFloatRegsFlagMask() const795 uint32_t LLVMArkInterface::GetCFrameHasFloatRegsFlagMask() const
796 {
797 return 1U << ark::CFrameLayout::HasFloatRegsFlag::START_BIT;
798 }
799
IsIrtocMode() const800 bool LLVMArkInterface::IsIrtocMode() const
801 {
802 return aotBuilder_ == nullptr;
803 }
804
AppendIrtocReturnHandler(llvm::StringRef returnHandler)805 void LLVMArkInterface::AppendIrtocReturnHandler(llvm::StringRef returnHandler)
806 {
807 irtocReturnHandlers_.push_back(returnHandler);
808 }
809
IsIrtocReturnHandler(const llvm::Function & function) const810 bool LLVMArkInterface::IsIrtocReturnHandler(const llvm::Function &function) const
811 {
812 return std::find(irtocReturnHandlers_.cbegin(), irtocReturnHandlers_.cend(), function.getName()) !=
813 irtocReturnHandlers_.cend();
814 }
815
816 } // namespace ark::llvmbackend
817