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