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 { 43b9073fb2SJF Bastien const MachineFrameInfo *MFI = MF.getFrameInfo(); 44e419a7c3SDan Gohman const auto *RegInfo = 45e419a7c3SDan Gohman MF.getSubtarget<WebAssemblySubtarget>().getRegisterInfo(); 4694c65660SDan Gohman return MFI->isFrameAddressTaken() || MFI->hasVarSizedObjects() || 4794c65660SDan Gohman 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 { 5810e730a2SDan Gohman 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, 8427e3b8a6SDerek Schuff DebugLoc DL) { 85*d08cd15fSDan Gohman const char *ES = "__stack_pointer"; 86*d08cd15fSDan Gohman auto *SPSymbol = MF.createExternalSymbolName(ES); 870cfb5f85SDan Gohman MachineRegisterInfo &MRI = MF.getRegInfo(); 880cfb5f85SDan Gohman const TargetRegisterClass *PtrRC = 890cfb5f85SDan Gohman MRI.getTargetRegisterInfo()->getPointerRegClass(MF); 900cfb5f85SDan Gohman unsigned SPAddr = MRI.createVirtualRegister(PtrRC); 910cfb5f85SDan Gohman unsigned Discard = MRI.createVirtualRegister(PtrRC); 9227e3b8a6SDerek Schuff const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 9327e3b8a6SDerek Schuff 94d4207ba0SDerek Schuff BuildMI(MBB, InsertAddr, DL, TII->get(WebAssembly::CONST_I32), SPAddr) 9527e3b8a6SDerek Schuff .addExternalSymbol(SPSymbol); 96*d08cd15fSDan Gohman auto *MMO = new MachineMemOperand(MachinePointerInfo(MF.getPSVManager() 97*d08cd15fSDan Gohman .getExternalSymbolCallEntry(ES)), 9827e3b8a6SDerek Schuff MachineMemOperand::MOStore, 4, 4); 99d4207ba0SDerek Schuff BuildMI(MBB, InsertStore, DL, TII->get(WebAssembly::STORE_I32), 1000cfb5f85SDan Gohman Discard) 10127e3b8a6SDerek Schuff .addImm(0) 10227e3b8a6SDerek Schuff .addReg(SPAddr) 10327e3b8a6SDerek Schuff .addImm(2) // p2align 10427e3b8a6SDerek Schuff .addReg(SrcReg) 10527e3b8a6SDerek Schuff .addMemOperand(MMO); 10627e3b8a6SDerek Schuff } 10727e3b8a6SDerek Schuff 108e1a2e90fSHans Wennborg MachineBasicBlock::iterator 109e1a2e90fSHans Wennborg WebAssemblyFrameLowering::eliminateCallFramePseudoInstr( 1108bb5f292SDerek Schuff MachineFunction &MF, MachineBasicBlock &MBB, 1118bb5f292SDerek Schuff MachineBasicBlock::iterator I) const { 11227e3b8a6SDerek Schuff assert(!I->getOperand(0).getImm() && hasFP(MF) && 11327e3b8a6SDerek Schuff "Call frame pseudos should only be used for dynamic stack adjustment"); 11427e3b8a6SDerek Schuff const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 1154b3bb213SDerek Schuff if (I->getOpcode() == TII->getCallFrameDestroyOpcode() && 1164b3bb213SDerek Schuff needsSPWriteback(MF, *MF.getFrameInfo())) { 11727e3b8a6SDerek Schuff DebugLoc DL = I->getDebugLoc(); 118d4207ba0SDerek Schuff writeSPToMemory(WebAssembly::SP32, MF, MBB, I, I, DL); 11927e3b8a6SDerek Schuff } 120e1a2e90fSHans Wennborg return MBB.erase(I); 1218bb5f292SDerek Schuff } 1228bb5f292SDerek Schuff 1238bb5f292SDerek Schuff void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF, 1248bb5f292SDerek Schuff MachineBasicBlock &MBB) const { 1258bb5f292SDerek Schuff // TODO: Do ".setMIFlag(MachineInstr::FrameSetup)" on emitted instructions 1268bb5f292SDerek Schuff auto *MFI = MF.getFrameInfo(); 1278bb5f292SDerek Schuff assert(MFI->getCalleeSavedInfo().empty() && 1288bb5f292SDerek Schuff "WebAssembly should not have callee-saved registers"); 1296ea637afSDerek Schuff 1304b3bb213SDerek Schuff if (!needsSP(MF, *MFI)) return; 1318bb5f292SDerek Schuff uint64_t StackSize = MFI->getStackSize(); 1328bb5f292SDerek Schuff 1333196650bSDan Gohman const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 1346ea637afSDerek Schuff auto &MRI = MF.getRegInfo(); 1358bb5f292SDerek Schuff 1368bb5f292SDerek Schuff auto InsertPt = MBB.begin(); 1378bb5f292SDerek Schuff DebugLoc DL; 1388bb5f292SDerek Schuff 1390cfb5f85SDan Gohman const TargetRegisterClass *PtrRC = 1400cfb5f85SDan Gohman MRI.getTargetRegisterInfo()->getPointerRegClass(MF); 1410cfb5f85SDan Gohman unsigned SPAddr = MRI.createVirtualRegister(PtrRC); 1420cfb5f85SDan Gohman unsigned SPReg = MRI.createVirtualRegister(PtrRC); 143*d08cd15fSDan Gohman const char *ES = "__stack_pointer"; 144*d08cd15fSDan Gohman auto *SPSymbol = MF.createExternalSymbolName(ES); 145dc5f6aa4SDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), SPAddr) 1466ea637afSDerek Schuff .addExternalSymbol(SPSymbol); 147*d08cd15fSDan Gohman auto *LoadMMO = new MachineMemOperand(MachinePointerInfo(MF.getPSVManager() 148*d08cd15fSDan Gohman .getExternalSymbolCallEntry(ES)), 1496ea637afSDerek Schuff MachineMemOperand::MOLoad, 4, 4); 1506ea637afSDerek Schuff // Load the SP value. 1516ea637afSDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::LOAD_I32), 1523ca3ea69SJF Bastien StackSize ? SPReg : (unsigned)WebAssembly::SP32) 1536ea637afSDerek Schuff .addImm(0) // offset 154dc5f6aa4SDerek Schuff .addReg(SPAddr) // addr 1556ea637afSDerek Schuff .addImm(2) // p2align 1566ea637afSDerek Schuff .addMemOperand(LoadMMO); 1576ea637afSDerek Schuff 1586ea637afSDerek Schuff if (StackSize) { 1596ea637afSDerek Schuff // Subtract the frame size 1600cfb5f85SDan Gohman unsigned OffsetReg = MRI.createVirtualRegister(PtrRC); 1616ea637afSDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg) 1626ea637afSDerek Schuff .addImm(StackSize); 1633f063295SDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::SUB_I32), 1646ea637afSDerek Schuff WebAssembly::SP32) 1656ea637afSDerek Schuff .addReg(SPReg) 1666ea637afSDerek Schuff .addReg(OffsetReg); 1676ea637afSDerek Schuff } 1686ea637afSDerek Schuff if (hasFP(MF)) { 1696ea637afSDerek Schuff // Unlike most conventional targets (where FP points to the saved FP), 1706ea637afSDerek Schuff // FP points to the bottom of the fixed-size locals, so we can use positive 1716ea637afSDerek Schuff // offsets in load/store instructions. 1720cfb5f85SDan Gohman BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::COPY), 1736ea637afSDerek Schuff WebAssembly::FP32) 1746ea637afSDerek Schuff .addReg(WebAssembly::SP32); 1756ea637afSDerek Schuff } 1764b3bb213SDerek Schuff if (StackSize && needsSPWriteback(MF, *MFI)) { 177d4207ba0SDerek Schuff writeSPToMemory(WebAssembly::SP32, MF, MBB, InsertPt, InsertPt, DL); 1786ea637afSDerek Schuff } 1798bb5f292SDerek Schuff } 1808bb5f292SDerek Schuff 1819769debfSDerek Schuff void WebAssemblyFrameLowering::emitEpilogue(MachineFunction &MF, 1829769debfSDerek Schuff MachineBasicBlock &MBB) const { 1836ea637afSDerek Schuff auto *MFI = MF.getFrameInfo(); 1846ea637afSDerek Schuff uint64_t StackSize = MFI->getStackSize(); 1854b3bb213SDerek Schuff if (!needsSP(MF, *MFI) || !needsSPWriteback(MF, *MFI)) return; 1863196650bSDan Gohman const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 1879769debfSDerek Schuff auto &MRI = MF.getRegInfo(); 1889769debfSDerek Schuff auto InsertPt = MBB.getFirstTerminator(); 1899769debfSDerek Schuff DebugLoc DL; 1909769debfSDerek Schuff 1910cfb5f85SDan Gohman if (InsertPt != MBB.end()) 1929769debfSDerek Schuff DL = InsertPt->getDebugLoc(); 193450a8075SDan Gohman 1946ea637afSDerek Schuff // Restore the stack pointer. If we had fixed-size locals, add the offset 1956ea637afSDerek Schuff // subtracted in the prolog. 196d4207ba0SDerek Schuff unsigned SPReg = 0; 197d4207ba0SDerek Schuff MachineBasicBlock::iterator InsertAddr = InsertPt; 1986ea637afSDerek Schuff if (StackSize) { 1990cfb5f85SDan Gohman const TargetRegisterClass *PtrRC = 2000cfb5f85SDan Gohman MRI.getTargetRegisterInfo()->getPointerRegClass(MF); 2010cfb5f85SDan Gohman unsigned OffsetReg = MRI.createVirtualRegister(PtrRC); 202d4207ba0SDerek Schuff InsertAddr = 2039769debfSDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg) 2049769debfSDerek Schuff .addImm(StackSize); 205d4207ba0SDerek Schuff // In the epilog we don't need to write the result back to the SP32 physreg 206d4207ba0SDerek Schuff // because it won't be used again. We can use a stackified register instead. 2070cfb5f85SDan Gohman SPReg = MRI.createVirtualRegister(PtrRC); 208d4207ba0SDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::ADD_I32), SPReg) 2096ea637afSDerek Schuff .addReg(hasFP(MF) ? WebAssembly::FP32 : WebAssembly::SP32) 2109769debfSDerek Schuff .addReg(OffsetReg); 211d4207ba0SDerek Schuff } else { 212d4207ba0SDerek Schuff SPReg = hasFP(MF) ? WebAssembly::FP32 : WebAssembly::SP32; 2136ea637afSDerek Schuff } 2146ea637afSDerek Schuff 215d4207ba0SDerek Schuff writeSPToMemory(SPReg, MF, MBB, InsertAddr, InsertPt, DL); 21610e730a2SDan Gohman } 217