LLVM 20.0.0git
WebAssemblyFrameLowering.cpp
Go to the documentation of this file.
1//===-- WebAssemblyFrameLowering.cpp - WebAssembly Frame Lowering ----------==//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// This file contains the WebAssembly implementation of
11/// TargetFrameLowering class.
12///
13/// On WebAssembly, there aren't a lot of things to do here. There are no
14/// callee-saved registers to save, and no spill slots.
15///
16/// The stack grows downward.
17///
18//===----------------------------------------------------------------------===//
19
23#include "WebAssembly.h"
35#include "llvm/MC/MCAsmInfo.h"
36#include "llvm/Support/Debug.h"
37using namespace llvm;
38
39#define DEBUG_TYPE "wasm-frame-info"
40
41// TODO: wasm64
42// TODO: Emit TargetOpcode::CFI_INSTRUCTION instructions
43
44// In an ideal world, when objects are added to the MachineFrameInfo by
45// FunctionLoweringInfo::set, we could somehow hook into target-specific code to
46// ensure they are assigned the right stack ID. However there isn't a hook that
47// runs between then and DAG building time, though, so instead we hoist stack
48// objects lazily when they are first used, and comprehensively after the DAG is
49// built via the PreprocessISelDAG hook, called by the
50// SelectionDAGISel::runOnMachineFunction. We have to do it in two places
51// because we want to do it while building the selection DAG for uses of alloca,
52// but not all alloca instructions are used so we have to follow up afterwards.
53std::optional<unsigned>
55 int FrameIndex) {
57
58 // If already hoisted to a local, done.
59 if (MFI.getStackID(FrameIndex) == TargetStackID::WasmLocal)
60 return static_cast<unsigned>(MFI.getObjectOffset(FrameIndex));
61
62 // If not allocated in the object address space, this object will be in
63 // linear memory.
64 const AllocaInst *AI = MFI.getObjectAllocation(FrameIndex);
66 return std::nullopt;
67
68 // Otherwise, allocate this object in the named value stack, outside of linear
69 // memory.
70 SmallVector<EVT, 4> ValueVTs;
71 const WebAssemblyTargetLowering &TLI =
72 *MF.getSubtarget<WebAssemblySubtarget>().getTargetLowering();
74 ComputeValueVTs(TLI, MF.getDataLayout(), AI->getAllocatedType(), ValueVTs);
75 MFI.setStackID(FrameIndex, TargetStackID::WasmLocal);
76 // Abuse SP offset to record the index of the first local in the object.
77 unsigned Local = FuncInfo->getParams().size() + FuncInfo->getLocals().size();
78 MFI.setObjectOffset(FrameIndex, Local);
79 // Allocate WebAssembly locals for each non-aggregate component of the
80 // allocation.
81 for (EVT ValueVT : ValueVTs)
82 FuncInfo->addLocal(ValueVT.getSimpleVT());
83 // Abuse object size to record number of WebAssembly locals allocated to
84 // this object.
85 MFI.setObjectSize(FrameIndex, ValueVTs.size());
86 return static_cast<unsigned>(Local);
87}
88
89/// We need a base pointer in the case of having items on the stack that
90/// require stricter alignment than the stack pointer itself. Because we need
91/// to shift the stack pointer by some unknown amount to force the alignment,
92/// we need to record the value of the stack pointer on entry to the function.
93bool WebAssemblyFrameLowering::hasBP(const MachineFunction &MF) const {
94 const auto *RegInfo =
95 MF.getSubtarget<WebAssemblySubtarget>().getRegisterInfo();
96 return RegInfo->hasStackRealignment(MF);
97}
98
99/// Return true if the specified function should have a dedicated frame pointer
100/// register.
102 const MachineFrameInfo &MFI = MF.getFrameInfo();
103
104 // When we have var-sized objects, we move the stack pointer by an unknown
105 // amount, and need to emit a frame pointer to restore the stack to where we
106 // were on function entry.
107 // If we already need a base pointer, we use that to fix up the stack pointer.
108 // If there are no fixed-size objects, we would have no use of a frame
109 // pointer, and thus should not emit one.
110 bool HasFixedSizedObjects = MFI.getStackSize() > 0;
111 bool NeedsFixedReference = !hasBP(MF) || HasFixedSizedObjects;
112
113 return MFI.isFrameAddressTaken() ||
114 (MFI.hasVarSizedObjects() && NeedsFixedReference) ||
115 MFI.hasStackMap() || MFI.hasPatchPoint();
116}
117
118/// Under normal circumstances, when a frame pointer is not required, we reserve
119/// argument space for call sites in the function immediately on entry to the
120/// current function. This eliminates the need for add/sub sp brackets around
121/// call sites. Returns true if the call frame is included as part of the stack
122/// frame.
124 const MachineFunction &MF) const {
125 return !MF.getFrameInfo().hasVarSizedObjects();
126}
127
128// Returns true if this function needs a local user-space stack pointer for its
129// local frame (not for exception handling).
130bool WebAssemblyFrameLowering::needsSPForLocalFrame(
131 const MachineFunction &MF) const {
132 auto &MFI = MF.getFrameInfo();
133 auto &MRI = MF.getRegInfo();
134 // llvm.stacksave can explicitly read SP register and it can appear without
135 // dynamic alloca.
136 bool HasExplicitSPUse =
137 any_of(MRI.use_operands(getSPReg(MF)),
138 [](MachineOperand &MO) { return !MO.isImplicit(); });
139
140 return MFI.getStackSize() || MFI.adjustsStack() || hasFP(MF) ||
141 HasExplicitSPUse;
142}
143
144// In function with EH pads, we need to make a copy of the value of
145// __stack_pointer global in SP32/64 register, in order to use it when
146// restoring __stack_pointer after an exception is caught.
148 const MachineFunction &MF) const {
149 auto EHType = MF.getTarget().getMCAsmInfo()->getExceptionHandlingType();
150 return EHType == ExceptionHandling::Wasm &&
152}
153
154/// Returns true if this function needs a local user-space stack pointer.
155/// Unlike a machine stack pointer, the wasm user stack pointer is a global
156/// variable, so it is loaded into a register in the prolog.
157bool WebAssemblyFrameLowering::needsSP(const MachineFunction &MF) const {
158 return needsSPForLocalFrame(MF) || needsPrologForEH(MF);
159}
160
161/// Returns true if the local user-space stack pointer needs to be written back
162/// to __stack_pointer global by this function (this is not meaningful if
163/// needsSP is false). If false, the stack red zone can be used and only a local
164/// SP is needed.
165bool WebAssemblyFrameLowering::needsSPWriteback(
166 const MachineFunction &MF) const {
167 auto &MFI = MF.getFrameInfo();
168 assert(needsSP(MF));
169 // When we don't need a local stack pointer for its local frame but only to
170 // support EH, we don't need to write SP back in the epilog, because we don't
171 // bump down the stack pointer in the prolog. We need to write SP back in the
172 // epilog only if
173 // 1. We need SP not only for EH support but also because we actually use
174 // stack or we have a frame address taken.
175 // 2. We cannot use the red zone.
176 bool CanUseRedZone = MFI.getStackSize() <= RedZoneSize && !MFI.hasCalls() &&
177 !MF.getFunction().hasFnAttribute(Attribute::NoRedZone);
178 return needsSPForLocalFrame(MF) && !CanUseRedZone;
179}
180
182 return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()
183 ? WebAssembly::SP64
184 : WebAssembly::SP32;
185}
186
188 return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()
189 ? WebAssembly::FP64
190 : WebAssembly::FP32;
191}
192
193unsigned
195 return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()
196 ? WebAssembly::CONST_I64
197 : WebAssembly::CONST_I32;
198}
199
201 return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()
202 ? WebAssembly::ADD_I64
203 : WebAssembly::ADD_I32;
204}
205
207 return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()
208 ? WebAssembly::SUB_I64
209 : WebAssembly::SUB_I32;
210}
211
213 return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()
214 ? WebAssembly::AND_I64
215 : WebAssembly::AND_I32;
216}
217
218unsigned
220 return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()
221 ? WebAssembly::GLOBAL_GET_I64
222 : WebAssembly::GLOBAL_GET_I32;
223}
224
225unsigned
227 return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()
228 ? WebAssembly::GLOBAL_SET_I64
229 : WebAssembly::GLOBAL_SET_I32;
230}
231
233 unsigned SrcReg, MachineFunction &MF, MachineBasicBlock &MBB,
234 MachineBasicBlock::iterator &InsertStore, const DebugLoc &DL) const {
235 const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
236
237 const char *ES = "__stack_pointer";
238 auto *SPSymbol = MF.createExternalSymbolName(ES);
239
240 BuildMI(MBB, InsertStore, DL, TII->get(getOpcGlobSet(MF)))
241 .addExternalSymbol(SPSymbol)
242 .addReg(SrcReg);
243}
244
249 assert(!I->getOperand(0).getImm() && (hasFP(MF) || hasBP(MF)) &&
250 "Call frame pseudos should only be used for dynamic stack adjustment");
251 auto &ST = MF.getSubtarget<WebAssemblySubtarget>();
252 const auto *TII = ST.getInstrInfo();
253 if (I->getOpcode() == TII->getCallFrameDestroyOpcode() &&
254 needsSPWriteback(MF)) {
255 DebugLoc DL = I->getDebugLoc();
256 writeSPToGlobal(getSPReg(MF), MF, MBB, I, DL);
257 }
258 return MBB.erase(I);
259}
260
262 MachineBasicBlock &MBB) const {
263 // TODO: Do ".setMIFlag(MachineInstr::FrameSetup)" on emitted instructions
264 auto &MFI = MF.getFrameInfo();
265 assert(MFI.getCalleeSavedInfo().empty() &&
266 "WebAssembly should not have callee-saved registers");
267
268 if (!needsSP(MF))
269 return;
270 uint64_t StackSize = MFI.getStackSize();
271
272 auto &ST = MF.getSubtarget<WebAssemblySubtarget>();
273 const auto *TII = ST.getInstrInfo();
274 auto &MRI = MF.getRegInfo();
275
276 auto InsertPt = MBB.begin();
277 while (InsertPt != MBB.end() &&
278 WebAssembly::isArgument(InsertPt->getOpcode()))
279 ++InsertPt;
280 DebugLoc DL;
281
282 const TargetRegisterClass *PtrRC =
283 MRI.getTargetRegisterInfo()->getPointerRegClass(MF);
284 unsigned SPReg = getSPReg(MF);
285 if (StackSize)
286 SPReg = MRI.createVirtualRegister(PtrRC);
287
288 const char *ES = "__stack_pointer";
289 auto *SPSymbol = MF.createExternalSymbolName(ES);
290 BuildMI(MBB, InsertPt, DL, TII->get(getOpcGlobGet(MF)), SPReg)
291 .addExternalSymbol(SPSymbol);
292
293 bool HasBP = hasBP(MF);
294 if (HasBP) {
295 auto FI = MF.getInfo<WebAssemblyFunctionInfo>();
296 Register BasePtr = MRI.createVirtualRegister(PtrRC);
297 FI->setBasePointerVreg(BasePtr);
298 BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::COPY), BasePtr)
299 .addReg(SPReg);
300 }
301 if (StackSize) {
302 // Subtract the frame size
303 Register OffsetReg = MRI.createVirtualRegister(PtrRC);
304 BuildMI(MBB, InsertPt, DL, TII->get(getOpcConst(MF)), OffsetReg)
305 .addImm(StackSize);
306 BuildMI(MBB, InsertPt, DL, TII->get(getOpcSub(MF)), getSPReg(MF))
307 .addReg(SPReg)
308 .addReg(OffsetReg);
309 }
310 if (HasBP) {
311 Register BitmaskReg = MRI.createVirtualRegister(PtrRC);
312 Align Alignment = MFI.getMaxAlign();
313 BuildMI(MBB, InsertPt, DL, TII->get(getOpcConst(MF)), BitmaskReg)
314 .addImm((int64_t) ~(Alignment.value() - 1));
315 BuildMI(MBB, InsertPt, DL, TII->get(getOpcAnd(MF)), getSPReg(MF))
316 .addReg(getSPReg(MF))
317 .addReg(BitmaskReg);
318 }
319 if (hasFP(MF)) {
320 // Unlike most conventional targets (where FP points to the saved FP),
321 // FP points to the bottom of the fixed-size locals, so we can use positive
322 // offsets in load/store instructions.
323 BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::COPY), getFPReg(MF))
324 .addReg(getSPReg(MF));
325 }
326 if (StackSize && needsSPWriteback(MF)) {
327 writeSPToGlobal(getSPReg(MF), MF, MBB, InsertPt, DL);
328 }
329}
330
332 MachineBasicBlock &MBB) const {
333 uint64_t StackSize = MF.getFrameInfo().getStackSize();
334 if (!needsSP(MF) || !needsSPWriteback(MF))
335 return;
336 auto &ST = MF.getSubtarget<WebAssemblySubtarget>();
337 const auto *TII = ST.getInstrInfo();
338 auto &MRI = MF.getRegInfo();
339 auto InsertPt = MBB.getFirstTerminator();
340 DebugLoc DL;
341
342 if (InsertPt != MBB.end())
343 DL = InsertPt->getDebugLoc();
344
345 // Restore the stack pointer. If we had fixed-size locals, add the offset
346 // subtracted in the prolog.
347 unsigned SPReg = 0;
348 unsigned SPFPReg = hasFP(MF) ? getFPReg(MF) : getSPReg(MF);
349 if (hasBP(MF)) {
350 auto FI = MF.getInfo<WebAssemblyFunctionInfo>();
351 SPReg = FI->getBasePointerVreg();
352 } else if (StackSize) {
353 const TargetRegisterClass *PtrRC =
354 MRI.getTargetRegisterInfo()->getPointerRegClass(MF);
355 Register OffsetReg = MRI.createVirtualRegister(PtrRC);
356 BuildMI(MBB, InsertPt, DL, TII->get(getOpcConst(MF)), OffsetReg)
357 .addImm(StackSize);
358 // In the epilog we don't need to write the result back to the SP32/64
359 // physreg because it won't be used again. We can use a stackified register
360 // instead.
361 SPReg = MRI.createVirtualRegister(PtrRC);
362 BuildMI(MBB, InsertPt, DL, TII->get(getOpcAdd(MF)), SPReg)
363 .addReg(SPFPReg)
364 .addReg(OffsetReg);
365 } else {
366 SPReg = SPFPReg;
367 }
368
369 writeSPToGlobal(SPReg, MF, MBB, InsertPt, DL);
370}
371
373 TargetStackID::Value ID) const {
374 // Use the Object stack for WebAssembly locals which can only be accessed
375 // by name, not via an address in linear memory.
377 return true;
378
380}
381
384 DwarfFrameBase Loc;
385 Loc.Kind = DwarfFrameBase::WasmFrameBase;
387 if (needsSP(MF) && MFI.isFrameBaseVirtual()) {
388 unsigned LocalNum = MFI.getFrameBaseLocal();
390 } else {
391 // TODO: This should work on a breakpoint at a function with no frame,
392 // but probably won't work for traversing up the stack.
394 }
395 return Loc;
396}
unsigned const MachineRegisterInfo * MRI
MachineBasicBlock & MBB
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
const HexagonInstrInfo * TII
#define I(x, y, z)
Definition: MD5.cpp:58
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
This class implements WebAssembly-specific bits of TargetFrameLowering class.
This file contains the WebAssembly implementation of the TargetInstrInfo class.
This file provides WebAssembly-specific target descriptions.
This file declares WebAssembly-specific per-machine-function information.
This file declares the WebAssembly-specific subclass of TargetSubtarget.
This file declares the WebAssembly-specific subclass of TargetMachine.
This file contains the declaration of the WebAssembly-specific type parsing utility functions.
This file contains the entry points for global functions defined in the LLVM WebAssembly back-end.
an instruction to allocate memory on the stack
Definition: Instructions.h:61
Type * getAllocatedType() const
Return the type that is being allocated by the instruction.
Definition: Instructions.h:115
unsigned getAddressSpace() const
Return the address space for the allocation.
Definition: Instructions.h:102
A debug info location.
Definition: DebugLoc.h:33
bool hasPersonalityFn() const
Check whether this function has a personality function.
Definition: Function.h:903
bool hasFnAttribute(Attribute::AttrKind Kind) const
Return true if the function has the attribute.
Definition: Function.cpp:743
ExceptionHandling getExceptionHandlingType() const
Definition: MCAsmInfo.h:740
iterator getFirstTerminator()
Returns an iterator to the first terminator instruction of this basic block.
instr_iterator erase(instr_iterator I)
Remove an instruction from the instruction list and delete it.
The MachineFrameInfo class represents an abstract stack frame until prolog/epilog code is inserted.
bool hasVarSizedObjects() const
This method may be called any time after instruction selection is complete to determine if the stack ...
uint64_t getStackSize() const
Return the number of bytes that must be allocated to hold all of the fixed size frame objects.
const AllocaInst * getObjectAllocation(int ObjectIdx) const
Return the underlying Alloca of the specified stack object if it exists.
bool hasCalls() const
Return true if the current function has any function calls.
bool isFrameAddressTaken() const
This method may be called any time after instruction selection is complete to determine if there is a...
void setObjectOffset(int ObjectIdx, int64_t SPOffset)
Set the stack frame offset of the specified object.
bool hasPatchPoint() const
This method may be called any time after instruction selection is complete to determine if there is a...
void setObjectSize(int ObjectIdx, int64_t Size)
Change the size of the specified stack object.
void setStackID(int ObjectIdx, uint8_t ID)
bool hasStackMap() const
This method may be called any time after instruction selection is complete to determine if there is a...
uint8_t getStackID(int ObjectIdx) const
int64_t getObjectOffset(int ObjectIdx) const
Return the assigned stack offset of the specified object from the incoming stack pointer.
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
MachineFrameInfo & getFrameInfo()
getFrameInfo - Return the frame info object for the current function.
const char * createExternalSymbolName(StringRef Name)
Allocate a string and populate it with the given external symbol name.
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
const DataLayout & getDataLayout() const
Return the DataLayout attached to the Module associated to this MF.
Function & getFunction()
Return the LLVM function that this machine code represents.
const LLVMTargetMachine & getTarget() const
getTarget - Return the target machine this machine code is compiled with
Ty * getInfo()
getInfo - Keep track of various per-function pieces of information for backends that would like to do...
const MachineInstrBuilder & addExternalSymbol(const char *FnName, unsigned TargetFlags=0) const
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
const MachineInstrBuilder & addReg(Register RegNo, unsigned flags=0, unsigned SubReg=0) const
Add a new virtual register operand.
MachineOperand class - Representation of each machine instruction operand.
bool isImplicit() const
Wrapper class representing virtual and physical registers.
Definition: Register.h:19
size_t size() const
Definition: SmallVector.h:91
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1209
virtual bool isSupportedStackID(TargetStackID::Value ID) const
const MCAsmInfo * getMCAsmInfo() const
Return target specific asm information.
static unsigned getOpcAdd(const MachineFunction &MF)
static unsigned getFPReg(const MachineFunction &MF)
bool needsPrologForEH(const MachineFunction &MF) const
static unsigned getOpcGlobSet(const MachineFunction &MF)
bool hasReservedCallFrame(const MachineFunction &MF) const override
Under normal circumstances, when a frame pointer is not required, we reserve argument space for call ...
static unsigned getOpcAnd(const MachineFunction &MF)
static unsigned getSPReg(const MachineFunction &MF)
static unsigned getOpcGlobGet(const MachineFunction &MF)
DwarfFrameBase getDwarfFrameBase(const MachineFunction &MF) const override
Return the frame base information to be encoded in the DWARF subprogram debug info.
bool isSupportedStackID(TargetStackID::Value ID) const override
MachineBasicBlock::iterator eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB, MachineBasicBlock::iterator I) const override
This method is called during prolog/epilog code insertion to eliminate call frame setup and destroy p...
static unsigned getOpcConst(const MachineFunction &MF)
void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override
These methods insert prolog and epilog code into the function.
void writeSPToGlobal(unsigned SrcReg, MachineFunction &MF, MachineBasicBlock &MBB, MachineBasicBlock::iterator &InsertStore, const DebugLoc &DL) const
Write SP back to __stack_pointer global.
bool hasFP(const MachineFunction &MF) const override
Return true if the specified function should have a dedicated frame pointer register.
void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override
static const size_t RedZoneSize
Size of the red zone for the user stack (leaf functions can use this much space below the stack point...
static std::optional< unsigned > getLocalForStackObject(MachineFunction &MF, int FrameIndex)
static unsigned getOpcSub(const MachineFunction &MF)
This class is derived from MachineFunctionInfo and contains private WebAssembly-specific information ...
const std::vector< MVT > & getLocals() const
const std::vector< MVT > & getParams() const
bool isArgument(unsigned Opc)
bool isWasmVarAddressSpace(unsigned AS)
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
@ Wasm
WebAssembly Exception Handling.
bool any_of(R &&range, UnaryPredicate P)
Provide wrappers to std::any_of which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1729
void ComputeValueVTs(const TargetLowering &TLI, const DataLayout &DL, Type *Ty, SmallVectorImpl< EVT > &ValueVTs, SmallVectorImpl< EVT > *MemVTs, SmallVectorImpl< TypeSize > *Offsets=nullptr, TypeSize StartingOffset=TypeSize::getZero())
ComputeValueVTs - Given an LLVM IR type, compute a sequence of EVTs that represent all the individual...
Definition: Analysis.cpp:79
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition: Alignment.h:39
uint64_t value() const
This is a hole in the type system and should not be abused.
Definition: Alignment.h:85
Extended Value Type.
Definition: ValueTypes.h:35
union llvm::TargetFrameLowering::DwarfFrameBase::@246 Location
enum llvm::TargetFrameLowering::DwarfFrameBase::FrameBaseKind Kind