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 "runtime_calls.h"
17 #include "llvm_ark_interface.h"
18 #include "transforms/transform_utils.h"
19
20 #include <llvm/IR/IRBuilder.h>
21
22 namespace ark::llvmbackend::runtime_calls {
23
GetAddressToTLS(llvm::IRBuilder<> * builder,LLVMArkInterface * arkInterface,uintptr_t tlsOffset)24 llvm::Value *GetAddressToTLS(llvm::IRBuilder<> *builder, LLVMArkInterface *arkInterface, uintptr_t tlsOffset)
25 {
26 auto threadRegValue = GetThreadRegValue(builder, arkInterface);
27 auto threadRegPtr = builder->CreateIntToPtr(threadRegValue, builder->getPtrTy());
28 return builder->CreateConstInBoundsGEP1_64(builder->getInt8Ty(), threadRegPtr, tlsOffset);
29 }
30
LoadTLSValue(llvm::IRBuilder<> * builder,LLVMArkInterface * arkInterface,uintptr_t tlsOffset,llvm::Type * type)31 llvm::Value *LoadTLSValue(llvm::IRBuilder<> *builder, LLVMArkInterface *arkInterface, uintptr_t tlsOffset,
32 llvm::Type *type)
33 {
34 auto addr = GetAddressToTLS(builder, arkInterface, tlsOffset);
35 return builder->CreateLoad(type, addr);
36 }
37
GetPandaRuntimeFunctionCallee(int entrypoint,llvm::FunctionType * functionProto,llvm::IRBuilder<> * builder,llvm::StringRef prefix)38 llvm::FunctionCallee GetPandaRuntimeFunctionCallee(int entrypoint, llvm::FunctionType *functionProto,
39 llvm::IRBuilder<> *builder, llvm::StringRef prefix)
40 {
41 auto module = builder->GetInsertBlock()->getModule();
42 auto table = module->getGlobalVariable("__aot_got");
43 auto arrayType = llvm::ArrayType::get(builder->getInt64Ty(), 0);
44 auto epAddrPtr = builder->CreateConstInBoundsGEP2_32(arrayType, table, 0, entrypoint);
45 auto entrypointAddress = builder->CreateLoad(builder->getInt64Ty(), epAddrPtr, prefix + "_addr");
46 auto calleePointer = builder->CreateIntToPtr(entrypointAddress, llvm::PointerType::get(functionProto, 0));
47 return llvm::FunctionCallee(functionProto, calleePointer);
48 }
49
CreateEntrypointCallCommon(llvm::IRBuilder<> * builder,llvm::Value * threadRegValue,LLVMArkInterface * arkInterface,EntrypointId eid,llvm::ArrayRef<llvm::Value * > arguments,llvm::ArrayRef<llvm::OperandBundleDef> bundle)50 llvm::CallInst *CreateEntrypointCallCommon(llvm::IRBuilder<> *builder, llvm::Value *threadRegValue,
51 LLVMArkInterface *arkInterface, EntrypointId eid,
52 llvm::ArrayRef<llvm::Value *> arguments,
53 llvm::ArrayRef<llvm::OperandBundleDef> bundle)
54 {
55 ASSERT(arkInterface->DeoptsEnabled() || bundle.empty());
56 auto tlsOffset = arkInterface->GetEntrypointTlsOffset(eid);
57 auto [functionProto, functionName] = arkInterface->GetEntrypointCallee(eid);
58
59 auto threadRegPtr = builder->CreateIntToPtr(threadRegValue, builder->getPtrTy());
60 auto addr = builder->CreateConstInBoundsGEP1_64(builder->getInt8Ty(), threadRegPtr, tlsOffset);
61 auto callee = builder->CreateLoad(builder->getPtrTy(), addr, functionName + "_addr");
62
63 auto calleeFuncTy = llvm::cast<llvm::FunctionType>(functionProto);
64 auto call = builder->CreateCall(calleeFuncTy, callee, arguments, bundle);
65
66 auto bridgeType = arkInterface->GetBridgeType(eid);
67 ASSERT(bridgeType != BridgeType::SLOW_PATH && bridgeType != BridgeType::ODD_SAVED);
68 ASSERT(call->getCallingConv() == llvm::CallingConv::C);
69
70 // Entrypoint bridges preserve a lot of registers, so we can put appropriate ArkFast convention for them.
71 if (bridgeType == BridgeType::ENTRYPOINT) {
72 llvm::CallingConv::ID cc = llvm::CallingConv::C;
73 switch (arguments.size()) {
74 case 0U:
75 cc = llvm::CallingConv::ArkFast0;
76 break;
77 case 1U:
78 case 2U:
79 cc = llvm::CallingConv::ArkFast2;
80 break;
81 case 3U:
82 case 4U:
83 cc = llvm::CallingConv::ArkFast4;
84 break;
85 case 5U:
86 case 6U:
87 cc = llvm::CallingConv::ArkFast6;
88 break;
89 default:
90 llvm_unreachable("Entrypoints with 7 and more arguments are not supported");
91 }
92 call->setCallingConv(cc);
93 }
94
95 return call;
96 }
97
GetThreadRegValue(llvm::IRBuilder<> * builder,LLVMArkInterface * arkInterface)98 llvm::Value *GetThreadRegValue(llvm::IRBuilder<> *builder, LLVMArkInterface *arkInterface)
99 {
100 ASSERT(!arkInterface->IsIrtocMode());
101 auto func = builder->GetInsertBlock()->getParent();
102 auto &ctx = func->getContext();
103 auto regMd = llvm::MDNode::get(ctx, {llvm::MDString::get(ctx, arkInterface->GetThreadRegister())});
104 auto threadReg = llvm::MetadataAsValue::get(ctx, regMd);
105 auto readReg =
106 llvm::Intrinsic::getDeclaration(func->getParent(), llvm::Intrinsic::read_register, builder->getInt64Ty());
107 return builder->CreateCall(readReg, {threadReg});
108 }
109
GetRealFrameRegValue(llvm::IRBuilder<> * builder,LLVMArkInterface * arkInterface)110 llvm::Value *GetRealFrameRegValue(llvm::IRBuilder<> *builder, LLVMArkInterface *arkInterface)
111 {
112 ASSERT(!arkInterface->IsIrtocMode());
113 auto func = builder->GetInsertBlock()->getParent();
114 auto &ctx = func->getContext();
115 auto regMd = llvm::MDNode::get(ctx, {llvm::MDString::get(ctx, arkInterface->GetFramePointerRegister())});
116 auto frameReg = llvm::MetadataAsValue::get(ctx, regMd);
117 auto readReg =
118 llvm::Intrinsic::getDeclaration(func->getParent(), llvm::Intrinsic::read_register, builder->getInt64Ty());
119 return builder->CreateCall(readReg, {frameReg});
120 }
121
122 } // namespace ark::llvmbackend::runtime_calls
123