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: Prolog/epilog should be stackified too. This pass runs after register
40 //       stackification, so we'll have to do it manually.
41 // TODO: Emit TargetOpcode::CFI_INSTRUCTION instructions
42 
43 /// Return true if the specified function should have a dedicated frame pointer
44 /// register.
45 bool WebAssemblyFrameLowering::hasFP(const MachineFunction &MF) const {
46   const MachineFrameInfo *MFI = MF.getFrameInfo();
47   assert(!MFI->isFrameAddressTaken());
48   const auto *RegInfo =
49       MF.getSubtarget<WebAssemblySubtarget>().getRegisterInfo();
50   return MFI->hasVarSizedObjects() || MFI->hasStackMap() ||
51          MFI->hasPatchPoint() || RegInfo->needsStackRealignment(MF);
52 }
53 
54 /// Under normal circumstances, when a frame pointer is not required, we reserve
55 /// argument space for call sites in the function immediately on entry to the
56 /// current function. This eliminates the need for add/sub sp brackets around
57 /// call sites. Returns true if the call frame is included as part of the stack
58 /// frame.
59 bool WebAssemblyFrameLowering::hasReservedCallFrame(
60     const MachineFunction &MF) const {
61   return !MF.getFrameInfo()->hasVarSizedObjects();
62 }
63 
64 void WebAssemblyFrameLowering::eliminateCallFramePseudoInstr(
65     MachineFunction &MF, MachineBasicBlock &MBB,
66     MachineBasicBlock::iterator I) const {
67   // TODO: can we avoid using call frame pseudos altogether?
68   assert(!I->getOperand(0).getImm() &&
69          "Stack should not be adjusted around calls");
70   MBB.erase(I);
71 }
72 
73 void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF,
74                                             MachineBasicBlock &MBB) const {
75   // TODO: Do ".setMIFlag(MachineInstr::FrameSetup)" on emitted instructions
76   auto *MFI = MF.getFrameInfo();
77   assert(MFI->getCalleeSavedInfo().empty() &&
78          "WebAssembly should not have callee-saved registers");
79 
80   uint64_t StackSize = MFI->getStackSize();
81   if (!StackSize && !MFI->adjustsStack() && !hasFP(MF)) return;
82 
83   const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
84   auto &MRI = MF.getRegInfo();
85 
86   auto InsertPt = MBB.begin();
87   DebugLoc DL;
88 
89   unsigned SPReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
90   auto *SPSymbol = MF.createExternalSymbolName("__stack_pointer");
91   BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), SPReg)
92       .addExternalSymbol(SPSymbol);
93   // This MachinePointerInfo should reference __stack_pointer as well but
94   // doesn't because MachinePointerInfo() takes a GV which we don't have for
95   // __stack_pointer. TODO: check if PseudoSourceValue::ExternalSymbolCallEntry
96   // is appropriate instead. (likewise for EmitEpologue below)
97   auto *LoadMMO = new MachineMemOperand(MachinePointerInfo(),
98                                         MachineMemOperand::MOLoad, 4, 4);
99   // Load the SP value.
100   BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::LOAD_I32),
101           StackSize ? SPReg : (unsigned)WebAssembly::SP32)
102       .addImm(0)      // offset
103       .addReg(SPReg)  // addr
104       .addImm(2)      // p2align
105       .addMemOperand(LoadMMO);
106 
107   unsigned OffsetReg = 0;
108   if (StackSize) {
109     // Subtract the frame size
110     OffsetReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
111     BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg)
112         .addImm(StackSize);
113     BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::SUB_I32),
114             WebAssembly::SP32)
115         .addReg(SPReg)
116         .addReg(OffsetReg);
117   }
118   if (hasFP(MF)) {
119     // Unlike most conventional targets (where FP points to the saved FP),
120     // FP points to the bottom of the fixed-size locals, so we can use positive
121     // offsets in load/store instructions.
122     BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::COPY_LOCAL_I32),
123             WebAssembly::FP32)
124         .addReg(WebAssembly::SP32);
125   }
126   if (StackSize) {
127     assert(OffsetReg);
128     // The SP32 register now has the new stacktop. Also write it back to memory.
129     BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg)
130         .addExternalSymbol(SPSymbol);
131     auto *MMO = new MachineMemOperand(MachinePointerInfo(),
132                                       MachineMemOperand::MOStore, 4, 4);
133     BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::STORE_I32),
134             WebAssembly::SP32)
135         .addImm(0)
136         .addReg(OffsetReg)
137         .addImm(2)  // p2align
138         .addReg(WebAssembly::SP32)
139         .addMemOperand(MMO);
140   }
141 }
142 
143 void WebAssemblyFrameLowering::emitEpilogue(MachineFunction &MF,
144                                             MachineBasicBlock &MBB) const {
145   auto *MFI = MF.getFrameInfo();
146   uint64_t StackSize = MFI->getStackSize();
147   if (!StackSize && !MFI->adjustsStack() && !hasFP(MF)) return;
148   const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
149   auto &MRI = MF.getRegInfo();
150   unsigned OffsetReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
151   auto InsertPt = MBB.getFirstTerminator();
152   DebugLoc DL;
153 
154   if (InsertPt != MBB.end()) {
155     DL = InsertPt->getDebugLoc();
156   }
157 
158   // Restore the stack pointer. If we had fixed-size locals, add the offset
159   // subtracted in the prolog.
160   if (StackSize) {
161     BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg)
162         .addImm(StackSize);
163     BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::ADD_I32),
164             WebAssembly::SP32)
165         .addReg(hasFP(MF) ? WebAssembly::FP32 : WebAssembly::SP32)
166         .addReg(OffsetReg);
167   }
168 
169   auto *SPSymbol = MF.createExternalSymbolName("__stack_pointer");
170   // Re-use OffsetReg to hold the address of the stacktop
171   BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg)
172       .addExternalSymbol(SPSymbol);
173   auto *MMO = new MachineMemOperand(MachinePointerInfo(),
174                                     MachineMemOperand::MOStore, 4, 4);
175   BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::STORE_I32),
176           WebAssembly::SP32)
177       .addImm(0)
178       .addReg(OffsetReg)
179       .addImm(2)  // p2align
180       .addReg((!StackSize && hasFP(MF)) ? WebAssembly::FP32 : WebAssembly::SP32)
181       .addMemOperand(MMO);
182 }
183