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