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