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