• 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 "frame_lowering.h"
17 #include "frame_builder.h"
18 #include "llvm_ark_interface.h"
19 #include "compiler/optimizer/code_generator/target_info.h"
20 
21 #include <llvm/CodeGen/MachineFrameInfo.h>
22 #include <llvm/MC/MCRegisterInfo.h>
23 #include <llvm/CodeGen/TargetInstrInfo.h>
24 #include <llvm/CodeGen/MachineFunctionPass.h>
25 
26 #define DEBUG_TYPE "frame-builder"
27 
28 namespace {
29 // See https://github.com/ARM-software/abi-aa/blob/main/aadwarf64/aadwarf64.rst#dwarf-register-names
30 constexpr int32_t X0 = 0;
31 constexpr int32_t D0 = 64;
32 constexpr int32_t D31 = 95;
33 
34 // See amd64 abi spec
35 constexpr int32_t RAX = 0;
36 constexpr int32_t XMM0 = 17;
37 constexpr int32_t XMM15 = 32;
38 
ToDwarfReg(ark::Arch arch,bool fp,size_t arkReg)39 constexpr size_t ToDwarfReg(ark::Arch arch, bool fp, size_t arkReg)
40 {
41     ASSERT(arkReg <= 32U);
42     if (arch == ark::Arch::AARCH64) {
43         return !fp ? arkReg : D0 + arkReg;
44     }
45     ASSERT(arch == ark::Arch::X86_64);
46     constexpr int32_t SIXTEEN = 16U;
47     ASSERT(!fp || arkReg < SIXTEEN);
48     return fp ? 1U + SIXTEEN + arkReg : ark::llvmbackend::LLVMArkInterface::ToDwarfRegNumX86(arkReg);
49 }
50 
51 class FrameLoweringPass : public llvm::MachineFunctionPass {
52 public:
53     static constexpr llvm::StringRef PASS_NAME = "ARK-LLVM Frame Lowering";
54     static constexpr llvm::StringRef ARG_NAME = "ark-llvm-frame-lowering";
55 
FrameLoweringPass(ark::llvmbackend::LLVMArkInterface * arkInterface=nullptr)56     explicit FrameLoweringPass(ark::llvmbackend::LLVMArkInterface *arkInterface = nullptr)
57         : llvm::MachineFunctionPass(ID), arkInterface_ {arkInterface}
58     {
59     }
60 
getPassName() const61     llvm::StringRef getPassName() const override
62     {
63         return PASS_NAME;
64     }
65 
runOnMachineFunction(llvm::MachineFunction & mfunc)66     bool runOnMachineFunction(llvm::MachineFunction &mfunc) override
67     {
68         if (mfunc.getFunction().getMetadata("use-ark-frame") == nullptr) {
69             return false;
70         }
71         // Collect information about current function
72         FrameInfo frameInfo;
73         frameInfo.regMasks = GetUsedRegs(mfunc);
74         frameInfo.hasCalls = HasCalls(mfunc);
75         frameInfo.stackSize = mfunc.getFrameInfo().getStackSize();
76         frameInfo.soOffset = -arkInterface_->GetStackOverflowCheckOffset();
77         frameInfo.usesStack = IsStackUsed(&mfunc);
78         frameInfo.usesFloatRegs = FloatRegsUsed(&mfunc);
79         if (arkInterface_->IsArm64()) {
80             constexpr uint32_t SLOT_SIZE = 8U;
81             ASSERT(frameInfo.stackSize >= SLOT_SIZE);
82             frameInfo.stackSize -= 2U * SLOT_SIZE;
83         }
84         if (arkInterface_->IsArm64()) {
85             ARM64FrameBuilder frameBuilder(
86                 frameInfo, [this](FrameConstantDescriptor descr) { return GetConstantFromRuntime(descr); });
87             return VisitMachineFunction(mfunc, &frameBuilder);
88         }
89         AMD64FrameBuilder frameBuilder(frameInfo,
90                                        [this](FrameConstantDescriptor descr) { return GetConstantFromRuntime(descr); });
91         return VisitMachineFunction(mfunc, &frameBuilder);
92     }
93 
94 private:
GetConstantFromRuntime(FrameConstantDescriptor descr)95     ssize_t GetConstantFromRuntime(FrameConstantDescriptor descr)
96     {
97         switch (descr) {
98             case FrameConstantDescriptor::TLS_FRAME_OFFSET:
99                 return arkInterface_->GetTlsFrameKindOffset();
100             case FrameConstantDescriptor::FRAME_FLAGS:
101                 return arkInterface_->GetCFrameHasFloatRegsFlagMask();
102             default:
103                 llvm_unreachable("Undefined FrameConstantDescriptor in constant_pool_handler");
104         }
105     }
106 
VisitMachineFunction(llvm::MachineFunction & mfunc,FrameBuilderInterface * frameBuilder)107     bool VisitMachineFunction(llvm::MachineFunction &mfunc, FrameBuilderInterface *frameBuilder)
108     {
109         // Remove generated prolog/epilog and insert custom one
110         for (auto &bb : mfunc) {
111             auto isPrologue = frameBuilder->RemovePrologue(bb);
112             auto isEpilogue = frameBuilder->RemoveEpilogue(bb);
113             if (isPrologue) {
114                 frameBuilder->InsertPrologue(bb);
115             }
116             if (isEpilogue) {
117                 frameBuilder->InsertEpilogue(bb);
118             }
119         }
120 
121         // Remember info for CodeInfoProducer
122         auto &func = mfunc.getFunction();
123         arkInterface_->PutMethodStackSize(&func, mfunc.getFrameInfo().getStackSize());
124         arkInterface_->PutCalleeSavedRegistersMask(func.getName(), frameBuilder->GetFrameInfo().regMasks);
125 
126         return true;
127     }
128 
GetDwarfRegNum(llvm::Register reg,const llvm::TargetRegisterInfo * regInfo) const129     int32_t GetDwarfRegNum(llvm::Register reg, const llvm::TargetRegisterInfo *regInfo) const
130     {
131         ASSERT(reg.isPhysical());
132         // dwarfId < 0 means, there is no dwarf mapping.
133         // E.g. for status registers.
134         // But need to check separately for x86
135         int dwarfId = regInfo->getDwarfRegNum(reg, false);
136         if (dwarfId < 0 && !arkInterface_->IsArm64()) {
137             // Check super regs as X86 dwarf info in LLVM is weird
138             for (llvm::MCSuperRegIterator sreg(reg, regInfo); sreg.isValid(); ++sreg) {
139                 dwarfId = regInfo->getDwarfRegNum(*sreg, false);
140                 if (dwarfId >= 0) {
141                     break;
142                 }
143             }
144         }
145 
146         ASSERT(!regInfo->isConstantPhysReg(reg.asMCReg()));
147         return dwarfId;
148     }
149 
150     // first -- X-regs, second -- V-regs
GetUsedRegs(const llvm::MachineFunction & mfunc) const151     FrameInfo::RegMasks GetUsedRegs(const llvm::MachineFunction &mfunc) const
152     {
153         FrameInfo::RegMasks masks {};
154         llvm::SmallVector<llvm::Register> usedRegisters;
155 
156         auto arch = arkInterface_->IsArm64() ? ark::Arch::AARCH64 : ark::Arch::X86_64;
157         auto csrMask = ark::GetCalleeRegsMask(arch, false);
158         auto csrVMask = ark::GetCalleeRegsMask(arch, true);
159         auto regInfo = mfunc.getSubtarget().getRegisterInfo();
160         // Convert ark mask of csr to vector of llvm registers
161         for (size_t i = 0; i < csrMask.Size(); i++) {
162             if (csrMask.Test(i)) {
163                 auto dwarf = ToDwarfReg(arch, false, i);
164                 auto llvm = *regInfo->getLLVMRegNum(dwarf, false);
165                 usedRegisters.push_back(llvm::Register {llvm});
166             }
167         }
168         for (size_t i = 0; i < csrVMask.Size(); i++) {
169             if (csrVMask.Test(i)) {
170                 auto dwarf = ToDwarfReg(arch, true, i);
171                 auto llvm = *regInfo->getLLVMRegNum(dwarf, false);
172                 usedRegisters.push_back(llvm::Register {llvm});
173             }
174         }
175 
176         for (auto &block : mfunc) {
177             for (auto &inst : block) {
178                 FillMaskForInst(&masks, usedRegisters, inst);
179             }
180         }
181 
182         // Drop thread register from the mask since it is reserved
183         auto threadReg = ark::GetThreadReg(arch);
184         std::get<0>(masks) &= ~(1U << threadReg);
185 
186         return masks;
187     }
188 
FillMaskForInst(FrameInfo::RegMasks * masks,const llvm::SmallVector<llvm::Register> & usedRegisters,const llvm::MachineInstr & inst) const189     void FillMaskForInst(FrameInfo::RegMasks *masks, const llvm::SmallVector<llvm::Register> &usedRegisters,
190                          const llvm::MachineInstr &inst) const
191     {
192         auto registerInfo = inst.getMF()->getSubtarget().getRegisterInfo();
193         for (auto reg : usedRegisters) {
194             if (inst.getFlag(llvm::MachineInstr::FrameSetup) || inst.getFlag(llvm::MachineInstr::FrameDestroy) ||
195                 !inst.modifiesRegister(reg, registerInfo) || registerInfo->isConstantPhysReg(reg)) {
196                 continue;
197             }
198             auto dwarfRegNum = GetDwarfRegNum(reg, registerInfo);
199             ASSERT(dwarfRegNum >= 0);
200             FillMask(dwarfRegNum, masks);
201         }
202     }
203 
HasCalls(const llvm::MachineFunction & mfunc)204     bool HasCalls(const llvm::MachineFunction &mfunc)
205     {
206         for (auto &mblock : mfunc) {
207             for (auto &minst : mblock) {
208                 bool isFrameSetup = minst.getFlag(llvm::MachineInstr::FrameSetup);
209                 bool isFrameDestroy = minst.getFlag(llvm::MachineInstr::FrameDestroy);
210                 if (isFrameSetup || isFrameDestroy) {
211                     continue;
212                 }
213                 auto desc = minst.getDesc();
214                 if (!desc.isCall() || desc.isReturn()) {
215                     continue;
216                 }
217                 return true;
218             }
219         }
220         return false;
221     }
222 
IsStackUsed(llvm::MachineFunction * machineFunction)223     bool IsStackUsed(llvm::MachineFunction *machineFunction)
224     {
225         for (auto &basicBlock : *machineFunction) {
226             for (auto &instruction : basicBlock) {
227                 if (instruction.getFlag(llvm::MachineInstr::FrameSetup) ||
228                     instruction.getFlag(llvm::MachineInstr::FrameDestroy)) {
229                     continue;
230                 }
231                 if (HasOperandUsingStack(&instruction)) {
232                     return true;
233                 }
234             }
235         }
236         return false;
237     }
238 
HasOperandUsingStack(llvm::MachineInstr * machineInstr)239     bool HasOperandUsingStack(llvm::MachineInstr *machineInstr)
240     {
241         auto regInfo = machineInstr->getMF()->getSubtarget().getRegisterInfo();
242         auto arch = arkInterface_->IsArm64() ? ark::Arch::AARCH64 : ark::Arch::X86_64;
243         auto sp = GetDwarfSP(arch);
244         auto fp = GetDwarfFP(arch);
245 
246         for (auto operand : machineInstr->operands()) {
247             if (operand.isReg()) {
248                 llvm::MCRegister reg = operand.getReg().asMCReg();
249                 size_t dwarfId = regInfo->getDwarfRegNum(reg, false);
250                 // Aarch64 zero registers (xzr/wzr) have same dwarfId as sp, and should be skipped
251                 if (regInfo->isConstantPhysReg(reg)) {
252                     ASSERT(arkInterface_->IsArm64() && dwarfId == sp);
253                     continue;
254                 }
255                 if (dwarfId == sp || dwarfId == fp) {
256                     return true;
257                 }
258             }
259         }
260         return false;
261     }
262 
FillMask(int32_t index,FrameInfo::RegMasks * regMasks) const263     void FillMask(int32_t index, FrameInfo::RegMasks *regMasks) const
264     {
265         if (arkInterface_->IsArm64()) {
266             // Dwarf numbers from llvm/lib/Target/AArch64/AArch64RegisterInfo.td
267             if (index >= D0) {
268                 std::get<1>(*regMasks) |= 1U << static_cast<unsigned>(index - D0);
269             } else {
270                 std::get<0>(*regMasks) |= 1U << static_cast<unsigned>(index - X0);
271             }
272         } else {
273             // Dwarf numbers from llvm/lib/Target/X86/X86RegisterInfo.td
274             if (index >= XMM0 && index <= XMM15) {
275                 std::get<1>(*regMasks) |= 1U << static_cast<unsigned>(index - XMM0);
276             } else {
277                 index = ark::llvmbackend::LLVMArkInterface::X86RegNumberConvert(index);
278                 std::get<0>(*regMasks) |= 1U << static_cast<unsigned>(index - RAX);
279             }
280         }
281     }
282 
FloatRegsUsed(llvm::MachineFunction * function)283     bool FloatRegsUsed(llvm::MachineFunction *function)
284     {
285         // Check if any float register is used in this function.
286         // Usage here means either read or write to register
287         for (auto &basicBlock : *function) {
288             for (auto &inst : basicBlock) {
289                 if (inst.getFlag(llvm::MachineInstr::FrameSetup) || inst.getFlag(llvm::MachineInstr::FrameDestroy)) {
290                     continue;
291                 }
292                 if (HasOperandUsingFloatReg(&inst)) {
293                     return true;
294                 }
295             }
296         }
297         return false;
298     }
299 
HasOperandUsingFloatReg(llvm::MachineInstr * instr)300     bool HasOperandUsingFloatReg(llvm::MachineInstr *instr)
301     {
302         auto regInfo = instr->getMF()->getSubtarget().getRegisterInfo();
303         for (auto operand : instr->operands()) {
304             if (operand.isReg() && operand.getReg().isPhysical() &&
305                 !regInfo->isConstantPhysReg(operand.getReg().asMCReg())) {
306                 auto dwarf = GetDwarfRegNum(operand.getReg(), regInfo);
307                 if (arkInterface_->IsArm64() && dwarf >= D0 && dwarf < D31) {
308                     return true;
309                 }
310                 if (!arkInterface_->IsArm64() && dwarf >= XMM0 && dwarf <= XMM15) {
311                     return true;
312                 }
313             }
314         }
315         return false;
316     }
317 
318 public:
319     static inline char ID = 0;  // NOLINT(readability-identifier-naming)
320 private:
321     ark::llvmbackend::LLVMArkInterface *arkInterface_ {nullptr};
322 };
323 }  // namespace
324 
CreateFrameLoweringPass(ark::llvmbackend::LLVMArkInterface * arkInterface)325 llvm::MachineFunctionPass *ark::llvmbackend::CreateFrameLoweringPass(ark::llvmbackend::LLVMArkInterface *arkInterface)
326 {
327     return new FrameLoweringPass(arkInterface);
328 }
329 
330 // NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
331 static llvm::RegisterPass<FrameLoweringPass> g_fl(FrameLoweringPass::ARG_NAME, FrameLoweringPass::PASS_NAME, false,
332                                                   false);
333