• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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 "llvm_ark_interface.h"
17 #include "patch_return_handler_stack_adjustment.h"
18 
19 #include <llvm/ADT/SmallVector.h>
20 #include <llvm/CodeGen/GlobalISel/MachineIRBuilder.h>
21 #include <llvm/CodeGen/MachineFrameInfo.h>
22 #include <llvm/CodeGen/MachineFunctionPass.h>
23 #include <llvm/CodeGen/MachineModuleInfo.h>
24 #include <llvm/IR/IRBuilder.h>
25 #include <llvm/Support/Debug.h>
26 
27 #include "transforms/transform_utils.h"
28 
29 using llvm::MachineFunction;
30 using llvm::MachineFunctionPass;
31 using llvm::RegisterPass;
32 using llvm::report_fatal_error;
33 using llvm::StringRef;
34 using panda::llvmbackend::LLVMArkInterface;
35 
36 #define DEBUG_TYPE "patch-return-handler-stack-adjustment"
37 
38 namespace {
39 
40 /**
41  * Patch stack adjustment value in return handler
42  *
43  * We generate inline assembly with hardcoded constant for return handlers in LLVMEntry::EmitInterpreterReturn
44  *
45  * The inline assembly uses stack pointer and inserts own return. Examples of inline assemblies:
46  *
47  * 1. leaq  $0, %rsp - x86. We add a hardcoded value to %rsp and retq then
48  * 2. add  sp, sp, $0 - aarch64. We add hardcoded value to sp and ret then
49  *
50  * LLVM does not know about our rets
51  *
52  * We use stack pointer in inline assemblies assuming that llvm does not touch sp itself.
53  * For example, we assume that llvm does not spill any register value onto the stack
54  * But llvm can do it, example: 'sub    $0x10,%rsp' in function prologue.
55  * LLVM will insert corresponding 'add    $0x10,%rsp' before its own rets but not for ours.
56  *
57  * So we add the stack size of machine function to our "hardcoded value" in inline assemblies.
58  * To find such assemblies the pass looks for a comment in the inline assembly template -
59  * LLVMArkInterface::PATCH_STACK_ADJUSTMENT_COMMENT
60  */
61 class PatchReturnHandlerStackAdjustment : public MachineFunctionPass {
62 public:
PatchReturnHandlerStackAdjustment(LLVMArkInterface * arkInterface=nullptr)63     explicit PatchReturnHandlerStackAdjustment(LLVMArkInterface *arkInterface = nullptr)
64         : MachineFunctionPass(ID), arkInterface_(arkInterface)
65     {
66     }
67 
runOnMachineFunction(MachineFunction & machineFunction)68     bool runOnMachineFunction(MachineFunction &machineFunction) override
69     {
70         ASSERT(arkInterface_ != nullptr);
71         if (!arkInterface_->IsIrtocReturnHandler(machineFunction.getFunction())) {
72             return false;
73         }
74 
75         auto &frameInfo = machineFunction.getFrameInfo();
76         if (frameInfo.hasVarSizedObjects()) {
77             report_fatal_error(StringRef("Return handler '") + machineFunction.getName() + "' uses var sized objects");
78             return false;
79         }
80         auto stackSize = frameInfo.getStackSize();
81         if (stackSize == 0) {
82             return false;
83         }
84 
85         bool changed = false;
86         for (auto &basicBlock : machineFunction) {
87             for (auto &instruction : basicBlock) {
88                 if (!instruction.isInlineAsm()) {
89                     continue;
90                 }
91                 static constexpr unsigned INLINE_ASM_INDEX = 0;
92                 static constexpr unsigned STACK_ADJUSTMENT_INDEX = 3;
93 
94                 std::string_view inlineAsm {instruction.getOperand(INLINE_ASM_INDEX).getSymbolName()};
95                 if (inlineAsm.find(LLVMArkInterface::PATCH_STACK_ADJUSTMENT_COMMENT) != std::string::npos) {
96                     auto &stackAdjustment = instruction.getOperand(STACK_ADJUSTMENT_INDEX);
97                     ASSERT(stackAdjustment.isImm());
98                     auto oldStackSize = stackAdjustment.getImm();
99                     auto newStackSize = oldStackSize + stackSize;
100                     LLVM_DEBUG(llvm::dbgs() << "Replaced old_stack_size = " << oldStackSize
101                                             << " with new_stack_size = " << newStackSize << " in inline_asm = '"
102                                             << inlineAsm << "' because llvm used " << stackSize
103                                             << " bytes of stack in function = '" << machineFunction.getName() << "'\n");
104                     stackAdjustment.setImm(newStackSize);
105                     changed = true;
106                 }
107             }
108         }
109 
110         return changed;
111     }
112 
getPassName() const113     StringRef getPassName() const override
114     {
115         return PASS_NAME;
116     }
117 
118     static inline char ID = 0;  // NOLINT(readability-identifier-naming)
119     static constexpr StringRef PASS_NAME = "ARK-LLVM patch stack adjustment";
120     static constexpr StringRef ARG_NAME = "patch-return-handler-stack-adjustment";
121 
122 private:
123     LLVMArkInterface *arkInterface_;
124 };
125 
126 }  // namespace
127 
128 namespace panda::llvmbackend {
129 
CreatePatchReturnHandlerStackAdjustmentPass(LLVMArkInterface * arkInterface)130 MachineFunctionPass *CreatePatchReturnHandlerStackAdjustmentPass(LLVMArkInterface *arkInterface)
131 {
132     return new PatchReturnHandlerStackAdjustment(arkInterface);
133 }
134 
135 }  // namespace panda::llvmbackend
136 
137 // NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
138 static RegisterPass<PatchReturnHandlerStackAdjustment> g_p1(PatchReturnHandlerStackAdjustment::ARG_NAME,
139                                                             PatchReturnHandlerStackAdjustment::PASS_NAME, false, false);
140