• 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 "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