WebAssemblyFrameLowering.cpp revision 309124
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"
33using namespace llvm;
34
35#define DEBUG_TYPE "wasm-frame-info"
36
37// TODO: wasm64
38// TODO: Emit TargetOpcode::CFI_INSTRUCTION instructions
39
40/// Return true if the specified function should have a dedicated frame pointer
41/// register.
42bool WebAssemblyFrameLowering::hasFP(const MachineFunction &MF) const {
43  const MachineFrameInfo *MFI = MF.getFrameInfo();
44  const auto *RegInfo =
45      MF.getSubtarget<WebAssemblySubtarget>().getRegisterInfo();
46  return MFI->isFrameAddressTaken() || MFI->hasVarSizedObjects() ||
47         MFI->hasStackMap() || MFI->hasPatchPoint() ||
48         RegInfo->needsStackRealignment(MF);
49}
50
51/// Under normal circumstances, when a frame pointer is not required, we reserve
52/// argument space for call sites in the function immediately on entry to the
53/// current function. This eliminates the need for add/sub sp brackets around
54/// call sites. Returns true if the call frame is included as part of the stack
55/// frame.
56bool WebAssemblyFrameLowering::hasReservedCallFrame(
57    const MachineFunction &MF) const {
58  return !MF.getFrameInfo()->hasVarSizedObjects();
59}
60
61
62/// Returns true if this function needs a local user-space stack pointer.
63/// Unlike a machine stack pointer, the wasm user stack pointer is a global
64/// variable, so it is loaded into a register in the prolog.
65bool WebAssemblyFrameLowering::needsSP(const MachineFunction &MF,
66                                       const MachineFrameInfo &MFI) const {
67  return MFI.getStackSize() || MFI.adjustsStack() || hasFP(MF);
68}
69
70/// Returns true if the local user-space stack pointer needs to be written back
71/// to memory by this function (this is not meaningful if needsSP is false). If
72/// false, the stack red zone can be used and only a local SP is needed.
73bool WebAssemblyFrameLowering::needsSPWriteback(
74    const MachineFunction &MF, const MachineFrameInfo &MFI) const {
75  assert(needsSP(MF, MFI));
76  return MFI.getStackSize() > RedZoneSize || MFI.hasCalls() ||
77         MF.getFunction()->hasFnAttribute(Attribute::NoRedZone);
78}
79
80static void writeSPToMemory(unsigned SrcReg, MachineFunction &MF,
81                            MachineBasicBlock &MBB,
82                            MachineBasicBlock::iterator &InsertAddr,
83                            MachineBasicBlock::iterator &InsertStore,
84                            const DebugLoc &DL) {
85  const char *ES = "__stack_pointer";
86  auto *SPSymbol = MF.createExternalSymbolName(ES);
87  MachineRegisterInfo &MRI = MF.getRegInfo();
88  const TargetRegisterClass *PtrRC =
89      MRI.getTargetRegisterInfo()->getPointerRegClass(MF);
90  unsigned Zero = MRI.createVirtualRegister(PtrRC);
91  unsigned Drop = MRI.createVirtualRegister(PtrRC);
92  const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
93
94  BuildMI(MBB, InsertAddr, DL, TII->get(WebAssembly::CONST_I32), Zero)
95      .addImm(0);
96  auto *MMO = new MachineMemOperand(MachinePointerInfo(MF.getPSVManager()
97                                        .getExternalSymbolCallEntry(ES)),
98                                    MachineMemOperand::MOStore, 4, 4);
99  BuildMI(MBB, InsertStore, DL, TII->get(WebAssembly::STORE_I32), Drop)
100      .addExternalSymbol(SPSymbol)
101      .addReg(Zero)
102      .addImm(2)  // p2align
103      .addReg(SrcReg)
104      .addMemOperand(MMO);
105}
106
107MachineBasicBlock::iterator
108WebAssemblyFrameLowering::eliminateCallFramePseudoInstr(
109    MachineFunction &MF, MachineBasicBlock &MBB,
110    MachineBasicBlock::iterator I) const {
111  assert(!I->getOperand(0).getImm() && hasFP(MF) &&
112         "Call frame pseudos should only be used for dynamic stack adjustment");
113  const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
114  if (I->getOpcode() == TII->getCallFrameDestroyOpcode() &&
115      needsSPWriteback(MF, *MF.getFrameInfo())) {
116    DebugLoc DL = I->getDebugLoc();
117    writeSPToMemory(WebAssembly::SP32, MF, MBB, I, I, DL);
118  }
119  return MBB.erase(I);
120}
121
122void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF,
123                                            MachineBasicBlock &MBB) const {
124  // TODO: Do ".setMIFlag(MachineInstr::FrameSetup)" on emitted instructions
125  auto *MFI = MF.getFrameInfo();
126  assert(MFI->getCalleeSavedInfo().empty() &&
127         "WebAssembly should not have callee-saved registers");
128
129  if (!needsSP(MF, *MFI)) return;
130  uint64_t StackSize = MFI->getStackSize();
131
132  const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
133  auto &MRI = MF.getRegInfo();
134
135  auto InsertPt = MBB.begin();
136  DebugLoc DL;
137
138  const TargetRegisterClass *PtrRC =
139      MRI.getTargetRegisterInfo()->getPointerRegClass(MF);
140  unsigned Zero = MRI.createVirtualRegister(PtrRC);
141  unsigned SPReg = MRI.createVirtualRegister(PtrRC);
142  const char *ES = "__stack_pointer";
143  auto *SPSymbol = MF.createExternalSymbolName(ES);
144  BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), Zero)
145      .addImm(0);
146  auto *LoadMMO = new MachineMemOperand(MachinePointerInfo(MF.getPSVManager()
147                                            .getExternalSymbolCallEntry(ES)),
148                                        MachineMemOperand::MOLoad, 4, 4);
149  // Load the SP value.
150  BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::LOAD_I32),
151          StackSize ? SPReg : (unsigned)WebAssembly::SP32)
152      .addExternalSymbol(SPSymbol)
153      .addReg(Zero)    // addr
154      .addImm(2)       // p2align
155      .addMemOperand(LoadMMO);
156
157  if (StackSize) {
158    // Subtract the frame size
159    unsigned OffsetReg = MRI.createVirtualRegister(PtrRC);
160    BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg)
161        .addImm(StackSize);
162    BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::SUB_I32),
163            WebAssembly::SP32)
164        .addReg(SPReg)
165        .addReg(OffsetReg);
166  }
167  if (hasFP(MF)) {
168    // Unlike most conventional targets (where FP points to the saved FP),
169    // FP points to the bottom of the fixed-size locals, so we can use positive
170    // offsets in load/store instructions.
171    BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::COPY),
172            WebAssembly::FP32)
173        .addReg(WebAssembly::SP32);
174  }
175  if (StackSize && needsSPWriteback(MF, *MFI)) {
176    writeSPToMemory(WebAssembly::SP32, MF, MBB, InsertPt, InsertPt, DL);
177  }
178}
179
180void WebAssemblyFrameLowering::emitEpilogue(MachineFunction &MF,
181                                            MachineBasicBlock &MBB) const {
182  auto *MFI = MF.getFrameInfo();
183  uint64_t StackSize = MFI->getStackSize();
184  if (!needsSP(MF, *MFI) || !needsSPWriteback(MF, *MFI)) return;
185  const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
186  auto &MRI = MF.getRegInfo();
187  auto InsertPt = MBB.getFirstTerminator();
188  DebugLoc DL;
189
190  if (InsertPt != MBB.end())
191    DL = InsertPt->getDebugLoc();
192
193  // Restore the stack pointer. If we had fixed-size locals, add the offset
194  // subtracted in the prolog.
195  unsigned SPReg = 0;
196  MachineBasicBlock::iterator InsertAddr = InsertPt;
197  if (StackSize) {
198    const TargetRegisterClass *PtrRC =
199        MRI.getTargetRegisterInfo()->getPointerRegClass(MF);
200    unsigned OffsetReg = MRI.createVirtualRegister(PtrRC);
201    InsertAddr =
202        BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg)
203            .addImm(StackSize);
204    // In the epilog we don't need to write the result back to the SP32 physreg
205    // because it won't be used again. We can use a stackified register instead.
206    SPReg = MRI.createVirtualRegister(PtrRC);
207    BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::ADD_I32), SPReg)
208        .addReg(hasFP(MF) ? WebAssembly::FP32 : WebAssembly::SP32)
209        .addReg(OffsetReg);
210  } else {
211    SPReg = hasFP(MF) ? WebAssembly::FP32 : WebAssembly::SP32;
212  }
213
214  writeSPToMemory(SPReg, MF, MBB, InsertAddr, InsertPt, DL);
215}
216