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