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