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 callee->getIntrinsicID() == llvm::Intrinsic::experimental_deoptimize);
65
66 // LLVM is able to process pure recursive calls, but they may confuse StackWalker after deoptimization
67 if (callee == &function && !hasDeopt) {
68 continue;
69 }
70
71 if (callee->getIntrinsicID() == llvm::Intrinsic::experimental_deoptimize) {
72 LowerDeoptimizeIntrinsic(call);
73 } else if (callee->getSectionPrefix() && callee->getSectionPrefix()->equals(builtins::BUILTIN_SECTION)) {
74 LowerBuiltin(call);
75 } else if (arkInterface_->IsRememberedCall(&function, callee)) {
76 LowerCallStatic(call);
77 } else if (call->hasFnAttr("original-method-id")) {
78 LowerCallVirtual(call);
79 } else {
80 llvm_unreachable("Do not know how to lower a call - unknown call type.");
81 }
82 changed = true;
83 }
84 }
85 return changed ? llvm::PreservedAnalyses::none() : llvm::PreservedAnalyses::all();
86 }
87
LowerCallStatic(llvm::CallInst * inst)88 void PandaRuntimeLowering::LowerCallStatic(llvm::CallInst *inst)
89 {
90 auto epOffset = llvm::ConstantInt::get(llvm::Type::getInt64Ty(inst->getContext()),
91 arkInterface_->GetCompiledEntryPointOffset());
92
93 auto builder = llvm::IRBuilder<>(inst);
94 auto calleePtr = GetMethodOrResolverPtr(&builder, inst);
95 // Calculate address of entry point
96 auto calleeAddr = builder.CreateInBoundsGEP(builder.getInt8Ty(), calleePtr, epOffset, "ep_addr");
97 // Cast entry point address to a pointer to callee function pointer
98 auto ftype = inst->getFunctionType();
99 // Load function pointer
100 ASSERT(inst->getCalledFunction() != nullptr);
101 auto calleeExec = builder.CreateLoad(builder.getPtrTy(), calleeAddr, {inst->getCalledFunction()->getName(), "_p"});
102 // Update call inst
103 inst->setCalledFunction(ftype, calleeExec);
104 inst->setArgOperand(0, calleePtr);
105 if (!arkInterface_->IsArm64()) {
106 inst->setCallingConv(llvm::CallingConv::ArkPlt);
107 }
108 inst->addFnAttr(llvm::Attribute::get(inst->getContext(), "use-ark-spills"));
109 }
110
LowerCallVirtual(llvm::CallInst * inst)111 void PandaRuntimeLowering::LowerCallVirtual(llvm::CallInst *inst)
112 {
113 ASSERT(!arkInterface_->IsInterfaceMethod(inst));
114 auto builder = llvm::IRBuilder<>(inst);
115 llvm::Value *thiz = inst->getArgOperand(1);
116 auto methodId = ark::llvmbackend::utils::GetMethodIdFromAttr(inst);
117 auto func = inst->getFunction();
118 auto method = ark::llvmbackend::utils::CreateLoadMethodUsingVTable(thiz, func, methodId, &builder, arkInterface_);
119
120 auto offset = arkInterface_->GetCompiledEntryPointOffset();
121 auto calleeAdr = builder.CreateConstInBoundsGEP1_32(builder.getInt8Ty(), method, offset);
122 auto calleeP = builder.CreateLoad(builder.getPtrTy(), calleeAdr);
123
124 inst->setCalledFunction(inst->getFunctionType(), calleeP);
125 inst->setArgOperand(0, method);
126 }
127
GetMethodOrResolverPtr(llvm::IRBuilder<> * builder,llvm::CallInst * inst)128 llvm::Value *PandaRuntimeLowering::GetMethodOrResolverPtr(llvm::IRBuilder<> *builder, llvm::CallInst *inst)
129 {
130 auto slot = arkInterface_->GetPltSlotId(inst->getCaller(), inst->getCalledFunction());
131 auto block = builder->GetInsertBlock();
132 auto aotGot = block->getModule()->getGlobalVariable("__aot_got");
133 auto arrayType = llvm::ArrayType::get(builder->getInt64Ty(), 0);
134 auto methodPtr = builder->CreateConstInBoundsGEP2_64(arrayType, aotGot, 0, slot);
135 auto cachedMethodAddr = builder->CreateLoad(builder->getInt64Ty(), methodPtr);
136 return builder->CreateIntToPtr(cachedMethodAddr, builder->getPtrTy(), "method_ptr");
137 }
138
LowerBuiltin(llvm::CallInst * inst)139 void PandaRuntimeLowering::LowerBuiltin(llvm::CallInst *inst)
140 {
141 auto builder = llvm::IRBuilder<>(inst);
142 auto lowered = builtins::LowerBuiltin(&builder, inst, arkInterface_);
143 if (lowered != nullptr) {
144 llvm::BasicBlock::iterator ii(inst);
145 ReplaceInstWithValue(inst->getParent()->getInstList(), ii, lowered);
146 } else {
147 ASSERT(inst->use_empty());
148 ASSERT(inst->getFunctionType()->getReturnType()->isVoidTy());
149 inst->eraseFromParent();
150 }
151 }
152
NeedsToBeLowered(llvm::CallInst * call)153 bool PandaRuntimeLowering::NeedsToBeLowered(llvm::CallInst *call)
154 {
155 auto callee = call->getCalledFunction();
156 if (callee == nullptr ||
157 (callee->isIntrinsic() && callee->getIntrinsicID() != llvm::Intrinsic::experimental_deoptimize)) {
158 return false;
159 }
160 ASSERT((callee->getSectionPrefix() && callee->getSectionPrefix()->equals(builtins::BUILTIN_SECTION)) ||
161 arkInterface_->IsRememberedCall(call->getCaller(), callee) || call->hasFnAttr("original-method-id") ||
162 call->getIntrinsicID() == llvm::Intrinsic::experimental_deoptimize);
163 return true;
164 }
165
LowerDeoptimizeIntrinsic(llvm::CallInst * deoptimize)166 void PandaRuntimeLowering::LowerDeoptimizeIntrinsic(llvm::CallInst *deoptimize)
167 {
168 ASSERT(deoptimize->getIntrinsicID() == llvm::Intrinsic::experimental_deoptimize);
169
170 llvm::IRBuilder<> builder {deoptimize};
171 ASSERT(deoptimize->arg_size() > 0);
172
173 // The last argument is the entrypoint id
174 llvm::Value *entrypointIdValue = deoptimize->getArgOperand(deoptimize->arg_size() - 1U);
175 uint64_t entrypointId = llvm::cast<llvm::ConstantInt>(entrypointIdValue)->getZExtValue();
176
177 // Drop last argument
178 llvm::SmallVector<llvm::Value *> args;
179 for (auto &arg : llvm::drop_end(deoptimize->args())) {
180 args.push_back(arg.get());
181 }
182
183 auto entrypointCall = llvmbackend::runtime_calls::CreateEntrypointCallCommon(
184 &builder, runtime_calls::GetThreadRegValue(&builder, arkInterface_), arkInterface_,
185 static_cast<llvmbackend::runtime_calls::EntrypointId>(entrypointId), args, utils::CopyDeoptBundle(deoptimize));
186 // Copy attributes
187 entrypointCall->setDebugLoc(deoptimize->getDebugLoc());
188
189 // Remove return after llvm.experimental.deoptimize call
190 auto basicBlock = entrypointCall->getParent();
191 auto terminator = basicBlock->getTerminator();
192 ASSERT(llvm::isa<llvm::ReturnInst>(terminator));
193 terminator->eraseFromParent();
194
195 // Erase the llvm.experimental.deoptimize call
196 ASSERT(deoptimize->getNumUses() == 0);
197 deoptimize->eraseFromParent();
198
199 // Create unreachable instead of removed return
200 builder.SetInsertPoint(basicBlock);
201 builder.CreateUnreachable();
202 }
203
204 } // namespace ark::llvmbackend::passes
205