1 //===-- WebAssemblyFrameLowering.cpp - WebAssembly Frame Lowering ----------==//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 ///
10 /// \file
11 /// \brief This file contains the WebAssembly implementation of
12 /// TargetFrameLowering class.
13 ///
14 /// On WebAssembly, there aren't a lot of things to do here. There are no
15 /// callee-saved registers to save, and no spill slots.
16 ///
17 /// The stack grows downward.
18 ///
19 //===----------------------------------------------------------------------===//
20 
21 #include "WebAssemblyFrameLowering.h"
22 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
23 #include "WebAssemblyInstrInfo.h"
24 #include "WebAssemblyMachineFunctionInfo.h"
25 #include "WebAssemblySubtarget.h"
26 #include "WebAssemblyTargetMachine.h"
27 #include "llvm/CodeGen/MachineFrameInfo.h"
28 #include "llvm/CodeGen/MachineFunction.h"
29 #include "llvm/CodeGen/MachineInstrBuilder.h"
30 #include "llvm/CodeGen/MachineModuleInfo.h"
31 #include "llvm/CodeGen/MachineRegisterInfo.h"
32 #include "llvm/Support/Debug.h"
33 using namespace llvm;
34 
35 #define DEBUG_TYPE "wasm-frame-info"
36 
37 // TODO: Implement a red zone?
38 // TODO: wasm64
39 // TODO: Emit TargetOpcode::CFI_INSTRUCTION instructions
40 
41 /// Return true if the specified function should have a dedicated frame pointer
42 /// register.
43 bool WebAssemblyFrameLowering::hasFP(const MachineFunction &MF) const {
44   const MachineFrameInfo *MFI = MF.getFrameInfo();
45   const auto *RegInfo =
46       MF.getSubtarget<WebAssemblySubtarget>().getRegisterInfo();
47   return MFI->isFrameAddressTaken() || MFI->hasVarSizedObjects() ||
48          MFI->hasStackMap() || MFI->hasPatchPoint() ||
49          RegInfo->needsStackRealignment(MF);
50 }
51 
52 /// Under normal circumstances, when a frame pointer is not required, we reserve
53 /// argument space for call sites in the function immediately on entry to the
54 /// current function. This eliminates the need for add/sub sp brackets around
55 /// call sites. Returns true if the call frame is included as part of the stack
56 /// frame.
57 bool WebAssemblyFrameLowering::hasReservedCallFrame(
58     const MachineFunction &MF) const {
59   return !MF.getFrameInfo()->hasVarSizedObjects();
60 }
61 
62 static void writeSPToMemory(unsigned SrcReg, MachineFunction &MF,
63                             MachineBasicBlock &MBB,
64                             MachineBasicBlock::iterator &InsertPt,
65                             DebugLoc DL) {
66   auto *SPSymbol = MF.createExternalSymbolName("__stack_pointer");
67   unsigned SPAddr =
68       MF.getRegInfo().createVirtualRegister(&WebAssembly::I32RegClass);
69   const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
70 
71   BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), SPAddr)
72       .addExternalSymbol(SPSymbol);
73   auto *MMO = new MachineMemOperand(MachinePointerInfo(),
74                                     MachineMemOperand::MOStore, 4, 4);
75   BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::STORE_I32),
76           WebAssembly::SP32)
77       .addImm(0)
78       .addReg(SPAddr)
79       .addImm(2)  // p2align
80       .addReg(SrcReg)
81       .addMemOperand(MMO);
82   MF.getInfo<WebAssemblyFunctionInfo>()->stackifyVReg(SPAddr);
83 }
84 
85 void WebAssemblyFrameLowering::eliminateCallFramePseudoInstr(
86     MachineFunction &MF, MachineBasicBlock &MBB,
87     MachineBasicBlock::iterator I) const {
88   assert(!I->getOperand(0).getImm() && hasFP(MF) &&
89          "Call frame pseudos should only be used for dynamic stack adjustment");
90   const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
91   if (I->getOpcode() == TII->getCallFrameDestroyOpcode()) {
92     DebugLoc DL = I->getDebugLoc();
93     writeSPToMemory(WebAssembly::SP32, MF, MBB, I, DL);
94   }
95   MBB.erase(I);
96 }
97 
98 void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF,
99                                             MachineBasicBlock &MBB) const {
100   // TODO: Do ".setMIFlag(MachineInstr::FrameSetup)" on emitted instructions
101   auto *MFI = MF.getFrameInfo();
102   assert(MFI->getCalleeSavedInfo().empty() &&
103          "WebAssembly should not have callee-saved registers");
104   auto *WFI = MF.getInfo<WebAssemblyFunctionInfo>();
105 
106   uint64_t StackSize = MFI->getStackSize();
107   if (!StackSize && !MFI->adjustsStack() && !hasFP(MF)) return;
108 
109   const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
110   auto &MRI = MF.getRegInfo();
111 
112   auto InsertPt = MBB.begin();
113   DebugLoc DL;
114 
115   unsigned SPAddr = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
116   unsigned SPReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
117   auto *SPSymbol = MF.createExternalSymbolName("__stack_pointer");
118   BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), SPAddr)
119       .addExternalSymbol(SPSymbol);
120   // This MachinePointerInfo should reference __stack_pointer as well but
121   // doesn't because MachinePointerInfo() takes a GV which we don't have for
122   // __stack_pointer. TODO: check if PseudoSourceValue::ExternalSymbolCallEntry
123   // is appropriate instead. (likewise for EmitEpologue below)
124   auto *LoadMMO = new MachineMemOperand(MachinePointerInfo(),
125                                         MachineMemOperand::MOLoad, 4, 4);
126   // Load the SP value.
127   BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::LOAD_I32),
128           StackSize ? SPReg : (unsigned)WebAssembly::SP32)
129       .addImm(0)       // offset
130       .addReg(SPAddr)  // addr
131       .addImm(2)       // p2align
132       .addMemOperand(LoadMMO);
133   WFI->stackifyVReg(SPAddr);
134 
135   if (StackSize) {
136     // Subtract the frame size
137     unsigned OffsetReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
138     BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg)
139         .addImm(StackSize);
140     BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::SUB_I32),
141             WebAssembly::SP32)
142         .addReg(SPReg)
143         .addReg(OffsetReg);
144     WFI->stackifyVReg(OffsetReg);
145     WFI->stackifyVReg(SPReg);
146   }
147   if (hasFP(MF)) {
148     // Unlike most conventional targets (where FP points to the saved FP),
149     // FP points to the bottom of the fixed-size locals, so we can use positive
150     // offsets in load/store instructions.
151     BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::COPY_LOCAL_I32),
152             WebAssembly::FP32)
153         .addReg(WebAssembly::SP32);
154   }
155   if (StackSize) {
156     writeSPToMemory(WebAssembly::SP32, MF, MBB, InsertPt, DL);
157   }
158 }
159 
160 void WebAssemblyFrameLowering::emitEpilogue(MachineFunction &MF,
161                                             MachineBasicBlock &MBB) const {
162   auto *MFI = MF.getFrameInfo();
163   uint64_t StackSize = MFI->getStackSize();
164   if (!StackSize && !MFI->adjustsStack() && !hasFP(MF)) return;
165   auto *WFI = MF.getInfo<WebAssemblyFunctionInfo>();
166   const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
167   auto &MRI = MF.getRegInfo();
168   auto InsertPt = MBB.getFirstTerminator();
169   DebugLoc DL;
170 
171   if (InsertPt != MBB.end()) {
172     DL = InsertPt->getDebugLoc();
173   }
174 
175   // Restore the stack pointer. If we had fixed-size locals, add the offset
176   // subtracted in the prolog.
177   if (StackSize) {
178     unsigned OffsetReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
179     BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg)
180         .addImm(StackSize);
181     BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::ADD_I32),
182             WebAssembly::SP32)
183         .addReg(hasFP(MF) ? WebAssembly::FP32 : WebAssembly::SP32)
184         .addReg(OffsetReg);
185     WFI->stackifyVReg(OffsetReg);
186   }
187 
188   writeSPToMemory(
189       (!StackSize && hasFP(MF)) ? WebAssembly::FP32 : WebAssembly::SP32, MF,
190       MBB, InsertPt, DL);
191 }
192