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 "transforms/passes/panda_runtime_lowering.h"
17
18 #include "llvm_ark_interface.h"
19 #include "lowering/metadata.h"
20 #include "transforms/runtime_calls.h"
21 #include "transforms/builtins.h"
22 #include "transforms/transform_utils.h"
23 #include "utils.h"
24
25 #include <llvm/IR/DebugInfoMetadata.h>
26 #include <llvm/IR/InlineAsm.h>
27 #include <llvm/IR/MDBuilder.h>
28 #include <llvm/IR/Verifier.h>
29 #include <llvm/Transforms/Utils/BasicBlockUtils.h>
30 #include <llvm/Pass.h>
31
32 namespace ark::llvmbackend::passes {
33
Create(llvmbackend::LLVMArkInterface * arkInterface,const ark::llvmbackend::LLVMCompilerOptions * options)34 PandaRuntimeLowering PandaRuntimeLowering::Create(llvmbackend::LLVMArkInterface *arkInterface,
35 [[maybe_unused]] const ark::llvmbackend::LLVMCompilerOptions *options)
36 {
37 return PandaRuntimeLowering(arkInterface);
38 }
39
PandaRuntimeLowering(LLVMArkInterface * arkInterface)40 PandaRuntimeLowering::PandaRuntimeLowering(LLVMArkInterface *arkInterface) : arkInterface_ {arkInterface} {}
41
run(llvm::Function & function,llvm::FunctionAnalysisManager &)42 llvm::PreservedAnalyses PandaRuntimeLowering::run(llvm::Function &function, llvm::FunctionAnalysisManager & /*am*/)
43 {
44 ASSERT(arkInterface_ != nullptr);
45 bool changed = false;
46 bool hasDeopt = false;
47 for (llvm::BasicBlock &block : function) {
48 llvm::SmallVector<llvm::CallInst *> calls;
49 for (llvm::Instruction &inst : block) {
50 auto call = llvm::dyn_cast<llvm::CallInst>(&inst);
51 if (call == nullptr) {
52 continue;
53 }
54 if (call->hasFnAttr("may-deoptimize")) {
55 hasDeopt = true;
56 }
57 if (NeedsToBeLowered(call)) {
58 calls.push_back(call);
59 }
60 }
61 for (auto call : calls) {
62 auto callee = call->getCalledFunction();
63 ASSERT(callee != nullptr && !callee->isIntrinsic());
64
65 // LLVM is able to process pure recursive calls, but they may confuse StackWalker after deoptimization
66 if (callee == &function && !hasDeopt) {
67 continue;
68 }
69
70 if (callee->getSectionPrefix() && callee->getSectionPrefix()->equals(builtins::BUILTIN_SECTION)) {
71 LowerBuiltin(call);
72 } else if (arkInterface_->IsRememberedCall(&function, callee)) {
73 LowerCallStatic(call);
74 } else if (call->hasFnAttr("original-method-id")) {
75 LowerCallVirtual(call);
76 } else {
77 llvm_unreachable("Do not know how to lower a call - unknown call type.");
78 }
79 changed = true;
80 }
81 }
82 return changed ? llvm::PreservedAnalyses::none() : llvm::PreservedAnalyses::all();
83 }
84
LowerCallStatic(llvm::CallInst * inst)85 void PandaRuntimeLowering::LowerCallStatic(llvm::CallInst *inst)
86 {
87 auto epOffset = llvm::ConstantInt::get(llvm::Type::getInt64Ty(inst->getContext()),
88 arkInterface_->GetCompiledEntryPointOffset());
89
90 auto builder = llvm::IRBuilder<>(inst);
91 auto calleePtr = GetMethodOrResolverPtr(&builder, inst);
92 // Calculate address of entry point
93 auto calleeAddr = builder.CreateInBoundsGEP(builder.getInt8Ty(), calleePtr, epOffset, "ep_addr");
94 // Cast entry point address to a pointer to callee function pointer
95 auto ftype = inst->getFunctionType();
96 // Load function pointer
97 ASSERT(inst->getCalledFunction() != nullptr);
98 auto calleeExec = builder.CreateLoad(builder.getPtrTy(), calleeAddr, {inst->getCalledFunction()->getName(), "_p"});
99 // Update call inst
100 inst->setCalledFunction(ftype, calleeExec);
101 inst->setArgOperand(0, calleePtr);
102 if (!arkInterface_->IsArm64()) {
103 inst->setCallingConv(llvm::CallingConv::ArkPlt);
104 }
105 inst->addFnAttr(llvm::Attribute::get(inst->getContext(), "use-ark-spills"));
106 }
107
LowerCallVirtual(llvm::CallInst * inst)108 void PandaRuntimeLowering::LowerCallVirtual(llvm::CallInst *inst)
109 {
110 ASSERT(!arkInterface_->IsInterfaceMethod(inst));
111 auto builder = llvm::IRBuilder<>(inst);
112 llvm::Value *thiz = inst->getArgOperand(1);
113 auto methodId = ark::llvmbackend::utils::GetMethodIdFromAttr(inst);
114 auto func = inst->getFunction();
115 auto method = ark::llvmbackend::utils::CreateLoadMethodUsingVTable(thiz, func, methodId, &builder, arkInterface_);
116
117 auto offset = arkInterface_->GetCompiledEntryPointOffset();
118 auto calleeAdr = builder.CreateConstInBoundsGEP1_32(builder.getInt8Ty(), method, offset);
119 auto calleeP = builder.CreateLoad(builder.getPtrTy(), calleeAdr);
120
121 inst->setCalledFunction(inst->getFunctionType(), calleeP);
122 inst->setArgOperand(0, method);
123 }
124
GetMethodOrResolverPtr(llvm::IRBuilder<> * builder,llvm::CallInst * inst)125 llvm::Value *PandaRuntimeLowering::GetMethodOrResolverPtr(llvm::IRBuilder<> *builder, llvm::CallInst *inst)
126 {
127 auto slot = arkInterface_->GetPltSlotId(inst->getCaller(), inst->getCalledFunction());
128 auto block = builder->GetInsertBlock();
129 auto aotGot = block->getModule()->getGlobalVariable("__aot_got");
130 auto arrayType = llvm::ArrayType::get(builder->getInt64Ty(), 0);
131 auto methodPtr = builder->CreateConstInBoundsGEP2_64(arrayType, aotGot, 0, slot);
132 auto cachedMethodAddr = builder->CreateLoad(builder->getInt64Ty(), methodPtr);
133 return builder->CreateIntToPtr(cachedMethodAddr, builder->getPtrTy(), "method_ptr");
134 }
135
LowerBuiltin(llvm::CallInst * inst)136 void PandaRuntimeLowering::LowerBuiltin(llvm::CallInst *inst)
137 {
138 auto builder = llvm::IRBuilder<>(inst);
139 auto lowered = builtins::LowerBuiltin(&builder, inst, arkInterface_);
140 if (lowered != nullptr) {
141 llvm::BasicBlock::iterator ii(inst);
142 ReplaceInstWithValue(inst->getParent()->getInstList(), ii, lowered);
143 } else {
144 ASSERT(inst->use_empty());
145 ASSERT(inst->getFunctionType()->getReturnType()->isVoidTy());
146 inst->eraseFromParent();
147 }
148 }
149
NeedsToBeLowered(llvm::CallInst * call)150 bool PandaRuntimeLowering::NeedsToBeLowered(llvm::CallInst *call)
151 {
152 auto callee = call->getCalledFunction();
153 if (callee == nullptr || callee->isIntrinsic()) {
154 return false;
155 }
156 ASSERT((callee->getSectionPrefix() && callee->getSectionPrefix()->equals(builtins::BUILTIN_SECTION)) ||
157 arkInterface_->IsRememberedCall(call->getCaller(), callee) || call->hasFnAttr("original-method-id"));
158 return true;
159 }
160
161 } // namespace ark::llvmbackend::passes
162