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