110e730a2SDan Gohman //===-- WebAssemblyFrameLowering.cpp - WebAssembly Frame Lowering ----------==//
210e730a2SDan Gohman //
310e730a2SDan Gohman //                     The LLVM Compiler Infrastructure
410e730a2SDan Gohman //
510e730a2SDan Gohman // This file is distributed under the University of Illinois Open Source
610e730a2SDan Gohman // License. See LICENSE.TXT for details.
710e730a2SDan Gohman //
810e730a2SDan Gohman //===----------------------------------------------------------------------===//
910e730a2SDan Gohman ///
1010e730a2SDan Gohman /// \file
1110e730a2SDan Gohman /// \brief This file contains the WebAssembly implementation of
1210e730a2SDan Gohman /// TargetFrameLowering class.
1310e730a2SDan Gohman ///
1410e730a2SDan Gohman /// On WebAssembly, there aren't a lot of things to do here. There are no
1510e730a2SDan Gohman /// callee-saved registers to save, and no spill slots.
1610e730a2SDan Gohman ///
1710e730a2SDan Gohman /// The stack grows downward.
1810e730a2SDan Gohman ///
1910e730a2SDan Gohman //===----------------------------------------------------------------------===//
2010e730a2SDan Gohman 
2110e730a2SDan Gohman #include "WebAssemblyFrameLowering.h"
2210e730a2SDan Gohman #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
2310e730a2SDan Gohman #include "WebAssemblyInstrInfo.h"
2410e730a2SDan Gohman #include "WebAssemblyMachineFunctionInfo.h"
2510e730a2SDan Gohman #include "WebAssemblySubtarget.h"
2610e730a2SDan Gohman #include "WebAssemblyTargetMachine.h"
2710e730a2SDan Gohman #include "llvm/CodeGen/MachineFrameInfo.h"
2810e730a2SDan Gohman #include "llvm/CodeGen/MachineFunction.h"
2910e730a2SDan Gohman #include "llvm/CodeGen/MachineInstrBuilder.h"
3010e730a2SDan Gohman #include "llvm/CodeGen/MachineModuleInfo.h"
3110e730a2SDan Gohman #include "llvm/CodeGen/MachineRegisterInfo.h"
3210e730a2SDan Gohman #include "llvm/Support/Debug.h"
3310e730a2SDan Gohman using namespace llvm;
3410e730a2SDan Gohman 
3503855df1SJF Bastien #define DEBUG_TYPE "wasm-frame-info"
3610e730a2SDan Gohman 
379769debfSDerek Schuff // TODO: wasm64
389769debfSDerek Schuff // TODO: Emit TargetOpcode::CFI_INSTRUCTION instructions
3910e730a2SDan Gohman 
4010e730a2SDan Gohman /// Return true if the specified function should have a dedicated frame pointer
4110e730a2SDan Gohman /// register.
4210e730a2SDan Gohman bool WebAssemblyFrameLowering::hasFP(const MachineFunction &MF) const {
43941a705bSMatthias Braun   const MachineFrameInfo &MFI = MF.getFrameInfo();
44e419a7c3SDan Gohman   const auto *RegInfo =
45e419a7c3SDan Gohman       MF.getSubtarget<WebAssemblySubtarget>().getRegisterInfo();
46941a705bSMatthias Braun   return MFI.isFrameAddressTaken() || MFI.hasVarSizedObjects() ||
47941a705bSMatthias Braun          MFI.hasStackMap() || MFI.hasPatchPoint() ||
4894c65660SDan Gohman          RegInfo->needsStackRealignment(MF);
4910e730a2SDan Gohman }
5010e730a2SDan Gohman 
5110e730a2SDan Gohman /// Under normal circumstances, when a frame pointer is not required, we reserve
5210e730a2SDan Gohman /// argument space for call sites in the function immediately on entry to the
5310e730a2SDan Gohman /// current function. This eliminates the need for add/sub sp brackets around
5410e730a2SDan Gohman /// call sites. Returns true if the call frame is included as part of the stack
5510e730a2SDan Gohman /// frame.
5610e730a2SDan Gohman bool WebAssemblyFrameLowering::hasReservedCallFrame(
5710e730a2SDan Gohman     const MachineFunction &MF) const {
58941a705bSMatthias Braun   return !MF.getFrameInfo().hasVarSizedObjects();
5910e730a2SDan Gohman }
6010e730a2SDan Gohman 
614b3bb213SDerek Schuff 
624b3bb213SDerek Schuff /// Returns true if this function needs a local user-space stack pointer.
634b3bb213SDerek Schuff /// Unlike a machine stack pointer, the wasm user stack pointer is a global
644b3bb213SDerek Schuff /// variable, so it is loaded into a register in the prolog.
654b3bb213SDerek Schuff bool WebAssemblyFrameLowering::needsSP(const MachineFunction &MF,
664b3bb213SDerek Schuff                                        const MachineFrameInfo &MFI) const {
674b3bb213SDerek Schuff   return MFI.getStackSize() || MFI.adjustsStack() || hasFP(MF);
684b3bb213SDerek Schuff }
694b3bb213SDerek Schuff 
704b3bb213SDerek Schuff /// Returns true if the local user-space stack pointer needs to be written back
714b3bb213SDerek Schuff /// to memory by this function (this is not meaningful if needsSP is false). If
724b3bb213SDerek Schuff /// false, the stack red zone can be used and only a local SP is needed.
734b3bb213SDerek Schuff bool WebAssemblyFrameLowering::needsSPWriteback(
744b3bb213SDerek Schuff     const MachineFunction &MF, const MachineFrameInfo &MFI) const {
75450a8075SDan Gohman   assert(needsSP(MF, MFI));
764b3bb213SDerek Schuff   return MFI.getStackSize() > RedZoneSize || MFI.hasCalls() ||
774b3bb213SDerek Schuff          MF.getFunction()->hasFnAttribute(Attribute::NoRedZone);
784b3bb213SDerek Schuff }
794b3bb213SDerek Schuff 
8027e3b8a6SDerek Schuff static void writeSPToMemory(unsigned SrcReg, MachineFunction &MF,
8127e3b8a6SDerek Schuff                             MachineBasicBlock &MBB,
82d4207ba0SDerek Schuff                             MachineBasicBlock::iterator &InsertAddr,
83d4207ba0SDerek Schuff                             MachineBasicBlock::iterator &InsertStore,
84bdc4956bSBenjamin Kramer                             const DebugLoc &DL) {
85d08cd15fSDan Gohman   const char *ES = "__stack_pointer";
86d08cd15fSDan Gohman   auto *SPSymbol = MF.createExternalSymbolName(ES);
870cfb5f85SDan Gohman   MachineRegisterInfo &MRI = MF.getRegInfo();
880cfb5f85SDan Gohman   const TargetRegisterClass *PtrRC =
890cfb5f85SDan Gohman       MRI.getTargetRegisterInfo()->getPointerRegClass(MF);
90d530f68dSDan Gohman   unsigned Zero = MRI.createVirtualRegister(PtrRC);
9171008090SDan Gohman   unsigned Drop = MRI.createVirtualRegister(PtrRC);
9227e3b8a6SDerek Schuff   const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
9327e3b8a6SDerek Schuff 
94d530f68dSDan Gohman   BuildMI(MBB, InsertAddr, DL, TII->get(WebAssembly::CONST_I32), Zero)
95d530f68dSDan Gohman       .addImm(0);
96*9bc1b230SBenjamin Kramer   MachineMemOperand *MMO = MF.getMachineMemOperand(
97*9bc1b230SBenjamin Kramer       MachinePointerInfo(MF.getPSVManager().getExternalSymbolCallEntry(ES)),
9827e3b8a6SDerek Schuff       MachineMemOperand::MOStore, 4, 4);
9971008090SDan Gohman   BuildMI(MBB, InsertStore, DL, TII->get(WebAssembly::STORE_I32), Drop)
100d530f68dSDan Gohman       .addExternalSymbol(SPSymbol)
101d530f68dSDan Gohman       .addReg(Zero)
10227e3b8a6SDerek Schuff       .addImm(2)  // p2align
10327e3b8a6SDerek Schuff       .addReg(SrcReg)
10427e3b8a6SDerek Schuff       .addMemOperand(MMO);
10527e3b8a6SDerek Schuff }
10627e3b8a6SDerek Schuff 
107e1a2e90fSHans Wennborg MachineBasicBlock::iterator
108e1a2e90fSHans Wennborg WebAssemblyFrameLowering::eliminateCallFramePseudoInstr(
1098bb5f292SDerek Schuff     MachineFunction &MF, MachineBasicBlock &MBB,
1108bb5f292SDerek Schuff     MachineBasicBlock::iterator I) const {
11127e3b8a6SDerek Schuff   assert(!I->getOperand(0).getImm() && hasFP(MF) &&
11227e3b8a6SDerek Schuff          "Call frame pseudos should only be used for dynamic stack adjustment");
11327e3b8a6SDerek Schuff   const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
1144b3bb213SDerek Schuff   if (I->getOpcode() == TII->getCallFrameDestroyOpcode() &&
115941a705bSMatthias Braun       needsSPWriteback(MF, MF.getFrameInfo())) {
11627e3b8a6SDerek Schuff     DebugLoc DL = I->getDebugLoc();
117d4207ba0SDerek Schuff     writeSPToMemory(WebAssembly::SP32, MF, MBB, I, I, DL);
11827e3b8a6SDerek Schuff   }
119e1a2e90fSHans Wennborg   return MBB.erase(I);
1208bb5f292SDerek Schuff }
1218bb5f292SDerek Schuff 
1228bb5f292SDerek Schuff void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF,
1238bb5f292SDerek Schuff                                             MachineBasicBlock &MBB) const {
1248bb5f292SDerek Schuff   // TODO: Do ".setMIFlag(MachineInstr::FrameSetup)" on emitted instructions
125941a705bSMatthias Braun   auto &MFI = MF.getFrameInfo();
126941a705bSMatthias Braun   assert(MFI.getCalleeSavedInfo().empty() &&
1278bb5f292SDerek Schuff          "WebAssembly should not have callee-saved registers");
1286ea637afSDerek Schuff 
129941a705bSMatthias Braun   if (!needsSP(MF, MFI)) return;
130941a705bSMatthias Braun   uint64_t StackSize = MFI.getStackSize();
1318bb5f292SDerek Schuff 
1323196650bSDan Gohman   const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
1336ea637afSDerek Schuff   auto &MRI = MF.getRegInfo();
1348bb5f292SDerek Schuff 
1358bb5f292SDerek Schuff   auto InsertPt = MBB.begin();
1368bb5f292SDerek Schuff   DebugLoc DL;
1378bb5f292SDerek Schuff 
1380cfb5f85SDan Gohman   const TargetRegisterClass *PtrRC =
1390cfb5f85SDan Gohman       MRI.getTargetRegisterInfo()->getPointerRegClass(MF);
140d530f68dSDan Gohman   unsigned Zero = MRI.createVirtualRegister(PtrRC);
1410cfb5f85SDan Gohman   unsigned SPReg = MRI.createVirtualRegister(PtrRC);
142d08cd15fSDan Gohman   const char *ES = "__stack_pointer";
143d08cd15fSDan Gohman   auto *SPSymbol = MF.createExternalSymbolName(ES);
144d530f68dSDan Gohman   BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), Zero)
145d530f68dSDan Gohman       .addImm(0);
146*9bc1b230SBenjamin Kramer   MachineMemOperand *LoadMMO = MF.getMachineMemOperand(
147*9bc1b230SBenjamin Kramer       MachinePointerInfo(MF.getPSVManager().getExternalSymbolCallEntry(ES)),
1486ea637afSDerek Schuff       MachineMemOperand::MOLoad, 4, 4);
1496ea637afSDerek Schuff   // Load the SP value.
1506ea637afSDerek Schuff   BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::LOAD_I32),
1513ca3ea69SJF Bastien           StackSize ? SPReg : (unsigned)WebAssembly::SP32)
152d530f68dSDan Gohman       .addExternalSymbol(SPSymbol)
153d530f68dSDan Gohman       .addReg(Zero)    // addr
1546ea637afSDerek Schuff       .addImm(2)       // p2align
1556ea637afSDerek Schuff       .addMemOperand(LoadMMO);
1566ea637afSDerek Schuff 
1576ea637afSDerek Schuff   if (StackSize) {
1586ea637afSDerek Schuff     // Subtract the frame size
1590cfb5f85SDan Gohman     unsigned OffsetReg = MRI.createVirtualRegister(PtrRC);
1606ea637afSDerek Schuff     BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg)
1616ea637afSDerek Schuff         .addImm(StackSize);
1623f063295SDerek Schuff     BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::SUB_I32),
1636ea637afSDerek Schuff             WebAssembly::SP32)
1646ea637afSDerek Schuff         .addReg(SPReg)
1656ea637afSDerek Schuff         .addReg(OffsetReg);
1666ea637afSDerek Schuff   }
1676ea637afSDerek Schuff   if (hasFP(MF)) {
1686ea637afSDerek Schuff     // Unlike most conventional targets (where FP points to the saved FP),
1696ea637afSDerek Schuff     // FP points to the bottom of the fixed-size locals, so we can use positive
1706ea637afSDerek Schuff     // offsets in load/store instructions.
1710cfb5f85SDan Gohman     BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::COPY),
1726ea637afSDerek Schuff             WebAssembly::FP32)
1736ea637afSDerek Schuff         .addReg(WebAssembly::SP32);
1746ea637afSDerek Schuff   }
175941a705bSMatthias Braun   if (StackSize && needsSPWriteback(MF, MFI)) {
176d4207ba0SDerek Schuff     writeSPToMemory(WebAssembly::SP32, MF, MBB, InsertPt, InsertPt, DL);
1776ea637afSDerek Schuff   }
1788bb5f292SDerek Schuff }
1798bb5f292SDerek Schuff 
1809769debfSDerek Schuff void WebAssemblyFrameLowering::emitEpilogue(MachineFunction &MF,
1819769debfSDerek Schuff                                             MachineBasicBlock &MBB) const {
182941a705bSMatthias Braun   auto &MFI = MF.getFrameInfo();
183941a705bSMatthias Braun   uint64_t StackSize = MFI.getStackSize();
184941a705bSMatthias Braun   if (!needsSP(MF, MFI) || !needsSPWriteback(MF, MFI)) return;
1853196650bSDan Gohman   const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
1869769debfSDerek Schuff   auto &MRI = MF.getRegInfo();
1879769debfSDerek Schuff   auto InsertPt = MBB.getFirstTerminator();
1889769debfSDerek Schuff   DebugLoc DL;
1899769debfSDerek Schuff 
1900cfb5f85SDan Gohman   if (InsertPt != MBB.end())
1919769debfSDerek Schuff     DL = InsertPt->getDebugLoc();
192450a8075SDan Gohman 
1936ea637afSDerek Schuff   // Restore the stack pointer. If we had fixed-size locals, add the offset
1946ea637afSDerek Schuff   // subtracted in the prolog.
195d4207ba0SDerek Schuff   unsigned SPReg = 0;
196d4207ba0SDerek Schuff   MachineBasicBlock::iterator InsertAddr = InsertPt;
1976ea637afSDerek Schuff   if (StackSize) {
1980cfb5f85SDan Gohman     const TargetRegisterClass *PtrRC =
1990cfb5f85SDan Gohman         MRI.getTargetRegisterInfo()->getPointerRegClass(MF);
2000cfb5f85SDan Gohman     unsigned OffsetReg = MRI.createVirtualRegister(PtrRC);
201d4207ba0SDerek Schuff     InsertAddr =
2029769debfSDerek Schuff         BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg)
2039769debfSDerek Schuff             .addImm(StackSize);
204d4207ba0SDerek Schuff     // In the epilog we don't need to write the result back to the SP32 physreg
205d4207ba0SDerek Schuff     // because it won't be used again. We can use a stackified register instead.
2060cfb5f85SDan Gohman     SPReg = MRI.createVirtualRegister(PtrRC);
207d4207ba0SDerek Schuff     BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::ADD_I32), SPReg)
2086ea637afSDerek Schuff         .addReg(hasFP(MF) ? WebAssembly::FP32 : WebAssembly::SP32)
2099769debfSDerek Schuff         .addReg(OffsetReg);
210d4207ba0SDerek Schuff   } else {
211d4207ba0SDerek Schuff     SPReg = hasFP(MF) ? WebAssembly::FP32 : WebAssembly::SP32;
2126ea637afSDerek Schuff   }
2136ea637afSDerek Schuff 
214d4207ba0SDerek Schuff   writeSPToMemory(SPReg, MF, MBB, InsertAddr, InsertPt, DL);
21510e730a2SDan Gohman }
216