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 3710e730a2SDan Gohman // TODO: Implement a red zone? 389769debfSDerek Schuff // TODO: wasm64 399769debfSDerek Schuff // TODO: Prolog/epilog should be stackified too. This pass runs after register 409769debfSDerek Schuff // stackification, so we'll have to do it manually. 419769debfSDerek Schuff // TODO: Emit TargetOpcode::CFI_INSTRUCTION instructions 4210e730a2SDan Gohman 4310e730a2SDan Gohman /// Return true if the specified function should have a dedicated frame pointer 4410e730a2SDan Gohman /// register. 4510e730a2SDan Gohman bool WebAssemblyFrameLowering::hasFP(const MachineFunction &MF) const { 46b9073fb2SJF Bastien const MachineFrameInfo *MFI = MF.getFrameInfo(); 47e419a7c3SDan Gohman const auto *RegInfo = 48e419a7c3SDan Gohman MF.getSubtarget<WebAssemblySubtarget>().getRegisterInfo(); 499769debfSDerek Schuff return MFI->hasVarSizedObjects() || MFI->isFrameAddressTaken() || 509769debfSDerek Schuff MFI->hasStackMap() || MFI->hasPatchPoint() || 519769debfSDerek Schuff RegInfo->needsStackRealignment(MF); 5210e730a2SDan Gohman } 5310e730a2SDan Gohman 5410e730a2SDan Gohman /// Under normal circumstances, when a frame pointer is not required, we reserve 5510e730a2SDan Gohman /// argument space for call sites in the function immediately on entry to the 5610e730a2SDan Gohman /// current function. This eliminates the need for add/sub sp brackets around 5710e730a2SDan Gohman /// call sites. Returns true if the call frame is included as part of the stack 5810e730a2SDan Gohman /// frame. 5910e730a2SDan Gohman bool WebAssemblyFrameLowering::hasReservedCallFrame( 6010e730a2SDan Gohman const MachineFunction &MF) const { 6110e730a2SDan Gohman return !MF.getFrameInfo()->hasVarSizedObjects(); 6210e730a2SDan Gohman } 6310e730a2SDan Gohman 6410e730a2SDan Gohman 658bb5f292SDerek Schuff /// Adjust the stack pointer by a constant amount. 668bb5f292SDerek Schuff static void adjustStackPointer(unsigned StackSize, 678bb5f292SDerek Schuff bool AdjustUp, 688bb5f292SDerek Schuff MachineFunction& MF, 698bb5f292SDerek Schuff MachineBasicBlock& MBB, 708bb5f292SDerek Schuff const TargetInstrInfo* TII, 718bb5f292SDerek Schuff MachineBasicBlock::iterator InsertPt, 728bb5f292SDerek Schuff const DebugLoc& DL) { 736ea637afSDerek Schuff assert((StackSize || !AdjustUp) && "Adjusting up by 0"); 749769debfSDerek Schuff auto &MRI = MF.getRegInfo(); 759769debfSDerek Schuff unsigned SPReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); 769769debfSDerek Schuff auto *SPSymbol = MF.createExternalSymbolName("__stack_pointer"); 779769debfSDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), SPReg) 789769debfSDerek Schuff .addExternalSymbol(SPSymbol); 799769debfSDerek Schuff // This MachinePointerInfo should reference __stack_pointer as well but 809769debfSDerek Schuff // doesn't because MachinePointerInfo() takes a GV which we don't have for 819769debfSDerek Schuff // __stack_pointer. TODO: check if PseudoSourceValue::ExternalSymbolCallEntry 829769debfSDerek Schuff // is appropriate instead. (likewise for EmitEpologue below) 839769debfSDerek Schuff auto *LoadMMO = new MachineMemOperand(MachinePointerInfo(), 849769debfSDerek Schuff MachineMemOperand::MOLoad, 4, 4); 859769debfSDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::LOAD_I32), SPReg) 866ea637afSDerek Schuff .addImm(0) // offset 876ea637afSDerek Schuff .addReg(SPReg) // addr 88bb372243SDan Gohman .addImm(2) // p2align 899769debfSDerek Schuff .addMemOperand(LoadMMO); 908bb5f292SDerek Schuff // Add/Subtract the frame size 919769debfSDerek Schuff unsigned OffsetReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); 929769debfSDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg) 939769debfSDerek Schuff .addImm(StackSize); 948bb5f292SDerek Schuff BuildMI(MBB, InsertPt, DL, 958bb5f292SDerek Schuff TII->get(AdjustUp ? WebAssembly::ADD_I32 : WebAssembly::SUB_I32), 968bb5f292SDerek Schuff WebAssembly::SP32) 979769debfSDerek Schuff .addReg(SPReg) 989769debfSDerek Schuff .addReg(OffsetReg); 999769debfSDerek Schuff // The SP32 register now has the new stacktop. Also write it back to memory. 1009769debfSDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg) 1019769debfSDerek Schuff .addExternalSymbol(SPSymbol); 1029769debfSDerek Schuff auto *MMO = new MachineMemOperand(MachinePointerInfo(), 1039769debfSDerek Schuff MachineMemOperand::MOStore, 4, 4); 1049769debfSDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::STORE_I32), WebAssembly::SP32) 1059769debfSDerek Schuff .addImm(0) 1069769debfSDerek Schuff .addReg(OffsetReg) 107bb372243SDan Gohman .addImm(2) // p2align 1089769debfSDerek Schuff .addReg(WebAssembly::SP32) 1099769debfSDerek Schuff .addMemOperand(MMO); 11010e730a2SDan Gohman } 11110e730a2SDan Gohman 1128bb5f292SDerek Schuff void WebAssemblyFrameLowering::eliminateCallFramePseudoInstr( 1138bb5f292SDerek Schuff MachineFunction &MF, MachineBasicBlock &MBB, 1148bb5f292SDerek Schuff MachineBasicBlock::iterator I) const { 1153196650bSDan Gohman const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 1168bb5f292SDerek Schuff DebugLoc DL = I->getDebugLoc(); 1178bb5f292SDerek Schuff unsigned Opc = I->getOpcode(); 1188bb5f292SDerek Schuff bool IsDestroy = Opc == TII->getCallFrameDestroyOpcode(); 1198bb5f292SDerek Schuff unsigned Amount = I->getOperand(0).getImm(); 1206ea637afSDerek Schuff // TODO(dschuff): After we switch varargs to passing an explicit pointer 1216ea637afSDerek Schuff // rather than using an implicit call frame, assert here that Amount is 0 1226ea637afSDerek Schuff // and remove adjustStackPointer altogether. 1238bb5f292SDerek Schuff if (Amount) 1248bb5f292SDerek Schuff adjustStackPointer(Amount, IsDestroy, MF, MBB, 1258bb5f292SDerek Schuff TII, I, DL); 1268bb5f292SDerek Schuff MBB.erase(I); 1278bb5f292SDerek Schuff } 1288bb5f292SDerek Schuff 1298bb5f292SDerek Schuff void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF, 1308bb5f292SDerek Schuff MachineBasicBlock &MBB) const { 1318bb5f292SDerek Schuff // TODO: Do ".setMIFlag(MachineInstr::FrameSetup)" on emitted instructions 1328bb5f292SDerek Schuff auto *MFI = MF.getFrameInfo(); 1338bb5f292SDerek Schuff assert(MFI->getCalleeSavedInfo().empty() && 1348bb5f292SDerek Schuff "WebAssembly should not have callee-saved registers"); 1356ea637afSDerek Schuff 1368bb5f292SDerek Schuff uint64_t StackSize = MFI->getStackSize(); 1376ea637afSDerek Schuff if (!StackSize && !MFI->adjustsStack()) 1388bb5f292SDerek Schuff return; 1398bb5f292SDerek Schuff 1403196650bSDan Gohman const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 1416ea637afSDerek Schuff auto &MRI = MF.getRegInfo(); 1428bb5f292SDerek Schuff 1438bb5f292SDerek Schuff auto InsertPt = MBB.begin(); 1448bb5f292SDerek Schuff DebugLoc DL; 1458bb5f292SDerek Schuff 1466ea637afSDerek Schuff unsigned SPReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); 1476ea637afSDerek Schuff auto *SPSymbol = MF.createExternalSymbolName("__stack_pointer"); 1486ea637afSDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), SPReg) 1496ea637afSDerek Schuff .addExternalSymbol(SPSymbol); 1506ea637afSDerek Schuff // This MachinePointerInfo should reference __stack_pointer as well but 1516ea637afSDerek Schuff // doesn't because MachinePointerInfo() takes a GV which we don't have for 1526ea637afSDerek Schuff // __stack_pointer. TODO: check if PseudoSourceValue::ExternalSymbolCallEntry 1536ea637afSDerek Schuff // is appropriate instead. (likewise for EmitEpologue below) 1546ea637afSDerek Schuff auto *LoadMMO = new MachineMemOperand(MachinePointerInfo(), 1556ea637afSDerek Schuff MachineMemOperand::MOLoad, 4, 4); 1566ea637afSDerek Schuff // Load the SP value. 1576ea637afSDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::LOAD_I32), 158*3ca3ea69SJF Bastien StackSize ? SPReg : (unsigned)WebAssembly::SP32) 1596ea637afSDerek Schuff .addImm(0) // offset 1606ea637afSDerek Schuff .addReg(SPReg) // addr 1616ea637afSDerek Schuff .addImm(2) // p2align 1626ea637afSDerek Schuff .addMemOperand(LoadMMO); 1636ea637afSDerek Schuff 1646ea637afSDerek Schuff unsigned OffsetReg = 0; 1656ea637afSDerek Schuff if (StackSize) { 1666ea637afSDerek Schuff // Subtract the frame size 1676ea637afSDerek Schuff OffsetReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); 1686ea637afSDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg) 1696ea637afSDerek Schuff .addImm(StackSize); 1706ea637afSDerek Schuff BuildMI(MBB, InsertPt, DL, 1716ea637afSDerek Schuff TII->get(WebAssembly::SUB_I32), 1726ea637afSDerek Schuff WebAssembly::SP32) 1736ea637afSDerek Schuff .addReg(SPReg) 1746ea637afSDerek Schuff .addReg(OffsetReg); 1756ea637afSDerek Schuff } 1766ea637afSDerek Schuff if (hasFP(MF)) { 1776ea637afSDerek Schuff // Unlike most conventional targets (where FP points to the saved FP), 1786ea637afSDerek Schuff // FP points to the bottom of the fixed-size locals, so we can use positive 1796ea637afSDerek Schuff // offsets in load/store instructions. 1806ea637afSDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::COPY_LOCAL_I32), 1816ea637afSDerek Schuff WebAssembly::FP32) 1826ea637afSDerek Schuff .addReg(WebAssembly::SP32); 1836ea637afSDerek Schuff } 1846ea637afSDerek Schuff if (StackSize) { 1856ea637afSDerek Schuff assert(OffsetReg); 1866ea637afSDerek Schuff // The SP32 register now has the new stacktop. Also write it back to memory. 1876ea637afSDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg) 1886ea637afSDerek Schuff .addExternalSymbol(SPSymbol); 1896ea637afSDerek Schuff auto *MMO = new MachineMemOperand(MachinePointerInfo(), 1906ea637afSDerek Schuff MachineMemOperand::MOStore, 4, 4); 1916ea637afSDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::STORE_I32), WebAssembly::SP32) 1926ea637afSDerek Schuff .addImm(0) 1936ea637afSDerek Schuff .addReg(OffsetReg) 1946ea637afSDerek Schuff .addImm(2) // p2align 1956ea637afSDerek Schuff .addReg(WebAssembly::SP32) 1966ea637afSDerek Schuff .addMemOperand(MMO); 1976ea637afSDerek Schuff } 1988bb5f292SDerek Schuff } 1998bb5f292SDerek Schuff 2009769debfSDerek Schuff void WebAssemblyFrameLowering::emitEpilogue(MachineFunction &MF, 2019769debfSDerek Schuff MachineBasicBlock &MBB) const { 2026ea637afSDerek Schuff auto *MFI = MF.getFrameInfo(); 2036ea637afSDerek Schuff uint64_t StackSize = MFI->getStackSize(); 2046ea637afSDerek Schuff if (!StackSize && !MFI->adjustsStack()) 2059769debfSDerek Schuff return; 2063196650bSDan Gohman const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 2079769debfSDerek Schuff auto &MRI = MF.getRegInfo(); 2089769debfSDerek Schuff unsigned OffsetReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); 2099769debfSDerek Schuff auto InsertPt = MBB.getFirstTerminator(); 2109769debfSDerek Schuff DebugLoc DL; 2119769debfSDerek Schuff 2129769debfSDerek Schuff if (InsertPt != MBB.end()) { 2139769debfSDerek Schuff DL = InsertPt->getDebugLoc(); 2149769debfSDerek Schuff } 2159769debfSDerek Schuff 2166ea637afSDerek Schuff // Restore the stack pointer. If we had fixed-size locals, add the offset 2176ea637afSDerek Schuff // subtracted in the prolog. 2186ea637afSDerek Schuff if (StackSize) { 2199769debfSDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg) 2209769debfSDerek Schuff .addImm(StackSize); 2219769debfSDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::ADD_I32), WebAssembly::SP32) 2226ea637afSDerek Schuff .addReg(hasFP(MF) ? WebAssembly::FP32 : WebAssembly::SP32) 2239769debfSDerek Schuff .addReg(OffsetReg); 2246ea637afSDerek Schuff } 2256ea637afSDerek Schuff 2266ea637afSDerek Schuff auto *SPSymbol = MF.createExternalSymbolName("__stack_pointer"); 2279769debfSDerek Schuff // Re-use OffsetReg to hold the address of the stacktop 2289769debfSDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg) 2299769debfSDerek Schuff .addExternalSymbol(SPSymbol); 2309769debfSDerek Schuff auto *MMO = new MachineMemOperand(MachinePointerInfo(), 2319769debfSDerek Schuff MachineMemOperand::MOStore, 4, 4); 2329769debfSDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::STORE_I32), WebAssembly::SP32) 2339769debfSDerek Schuff .addImm(0) 2349769debfSDerek Schuff .addReg(OffsetReg) 235bb372243SDan Gohman .addImm(2) // p2align 2366ea637afSDerek Schuff .addReg((!StackSize && hasFP(MF)) ? WebAssembly::FP32 : WebAssembly::SP32) 2379769debfSDerek Schuff .addMemOperand(MMO); 23810e730a2SDan Gohman } 239