110e730a2SDan Gohman //===-- WebAssemblyFrameLowering.cpp - WebAssembly Frame Lowering ----------==// 210e730a2SDan Gohman // 3*2946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*2946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 5*2946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 610e730a2SDan Gohman // 710e730a2SDan Gohman //===----------------------------------------------------------------------===// 810e730a2SDan Gohman /// 910e730a2SDan Gohman /// \file 105f8f34e4SAdrian Prantl /// This file contains the WebAssembly implementation of 1110e730a2SDan Gohman /// TargetFrameLowering class. 1210e730a2SDan Gohman /// 1310e730a2SDan Gohman /// On WebAssembly, there aren't a lot of things to do here. There are no 1410e730a2SDan Gohman /// callee-saved registers to save, and no spill slots. 1510e730a2SDan Gohman /// 1610e730a2SDan Gohman /// The stack grows downward. 1710e730a2SDan Gohman /// 1810e730a2SDan Gohman //===----------------------------------------------------------------------===// 1910e730a2SDan Gohman 2010e730a2SDan Gohman #include "WebAssemblyFrameLowering.h" 2110e730a2SDan Gohman #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" 2210e730a2SDan Gohman #include "WebAssemblyInstrInfo.h" 2310e730a2SDan Gohman #include "WebAssemblyMachineFunctionInfo.h" 2410e730a2SDan Gohman #include "WebAssemblySubtarget.h" 2510e730a2SDan Gohman #include "WebAssemblyTargetMachine.h" 26d934cb88SDan Gohman #include "WebAssemblyUtilities.h" 2710e730a2SDan Gohman #include "llvm/CodeGen/MachineFrameInfo.h" 2810e730a2SDan Gohman #include "llvm/CodeGen/MachineFunction.h" 2910e730a2SDan Gohman #include "llvm/CodeGen/MachineInstrBuilder.h" 3082607f56SDan Gohman #include "llvm/CodeGen/MachineModuleInfoImpls.h" 3110e730a2SDan Gohman #include "llvm/CodeGen/MachineRegisterInfo.h" 3278d19108SHeejin Ahn #include "llvm/MC/MCAsmInfo.h" 3310e730a2SDan Gohman #include "llvm/Support/Debug.h" 3410e730a2SDan Gohman using namespace llvm; 3510e730a2SDan Gohman 3603855df1SJF Bastien #define DEBUG_TYPE "wasm-frame-info" 3710e730a2SDan Gohman 389769debfSDerek Schuff // TODO: wasm64 399769debfSDerek Schuff // TODO: Emit TargetOpcode::CFI_INSTRUCTION instructions 4010e730a2SDan Gohman 410d41b7b3SDerek Schuff /// We need a base pointer in the case of having items on the stack that 420d41b7b3SDerek Schuff /// require stricter alignment than the stack pointer itself. Because we need 430d41b7b3SDerek Schuff /// to shift the stack pointer by some unknown amount to force the alignment, 440d41b7b3SDerek Schuff /// we need to record the value of the stack pointer on entry to the function. 45f208f631SHeejin Ahn bool WebAssemblyFrameLowering::hasBP(const MachineFunction &MF) const { 460d41b7b3SDerek Schuff const auto *RegInfo = 470d41b7b3SDerek Schuff MF.getSubtarget<WebAssemblySubtarget>().getRegisterInfo(); 480d41b7b3SDerek Schuff return RegInfo->needsStackRealignment(MF); 490d41b7b3SDerek Schuff } 500d41b7b3SDerek Schuff 5110e730a2SDan Gohman /// Return true if the specified function should have a dedicated frame pointer 5210e730a2SDan Gohman /// register. 5310e730a2SDan Gohman bool WebAssemblyFrameLowering::hasFP(const MachineFunction &MF) const { 54941a705bSMatthias Braun const MachineFrameInfo &MFI = MF.getFrameInfo(); 550d41b7b3SDerek Schuff 560d41b7b3SDerek Schuff // When we have var-sized objects, we move the stack pointer by an unknown 570d41b7b3SDerek Schuff // amount, and need to emit a frame pointer to restore the stack to where we 580d41b7b3SDerek Schuff // were on function entry. 590d41b7b3SDerek Schuff // If we already need a base pointer, we use that to fix up the stack pointer. 600d41b7b3SDerek Schuff // If there are no fixed-size objects, we would have no use of a frame 610d41b7b3SDerek Schuff // pointer, and thus should not emit one. 620d41b7b3SDerek Schuff bool HasFixedSizedObjects = MFI.getStackSize() > 0; 630d41b7b3SDerek Schuff bool NeedsFixedReference = !hasBP(MF) || HasFixedSizedObjects; 640d41b7b3SDerek Schuff 650d41b7b3SDerek Schuff return MFI.isFrameAddressTaken() || 660d41b7b3SDerek Schuff (MFI.hasVarSizedObjects() && NeedsFixedReference) || 670d41b7b3SDerek Schuff MFI.hasStackMap() || MFI.hasPatchPoint(); 6810e730a2SDan Gohman } 6910e730a2SDan Gohman 7010e730a2SDan Gohman /// Under normal circumstances, when a frame pointer is not required, we reserve 7110e730a2SDan Gohman /// argument space for call sites in the function immediately on entry to the 7210e730a2SDan Gohman /// current function. This eliminates the need for add/sub sp brackets around 7310e730a2SDan Gohman /// call sites. Returns true if the call frame is included as part of the stack 7410e730a2SDan Gohman /// frame. 7510e730a2SDan Gohman bool WebAssemblyFrameLowering::hasReservedCallFrame( 7610e730a2SDan Gohman const MachineFunction &MF) const { 77941a705bSMatthias Braun return !MF.getFrameInfo().hasVarSizedObjects(); 7810e730a2SDan Gohman } 7910e730a2SDan Gohman 80972fc358SHeejin Ahn // Returns true if this function needs a local user-space stack pointer for its 81972fc358SHeejin Ahn // local frame (not for exception handling). 82972fc358SHeejin Ahn bool WebAssemblyFrameLowering::needsSPForLocalFrame( 83972fc358SHeejin Ahn const MachineFunction &MF) const { 84972fc358SHeejin Ahn auto &MFI = MF.getFrameInfo(); 85972fc358SHeejin Ahn return MFI.getStackSize() || MFI.adjustsStack() || hasFP(MF); 86972fc358SHeejin Ahn } 87972fc358SHeejin Ahn 8878d19108SHeejin Ahn // In function with EH pads, we need to make a copy of the value of 8978d19108SHeejin Ahn // __stack_pointer global in SP32 register, in order to use it when restoring 9078d19108SHeejin Ahn // __stack_pointer after an exception is caught. 9178d19108SHeejin Ahn bool WebAssemblyFrameLowering::needsPrologForEH( 9278d19108SHeejin Ahn const MachineFunction &MF) const { 9378d19108SHeejin Ahn auto EHType = MF.getTarget().getMCAsmInfo()->getExceptionHandlingType(); 9478d19108SHeejin Ahn return EHType == ExceptionHandling::Wasm && 9578d19108SHeejin Ahn MF.getFunction().hasPersonalityFn() && MF.getFrameInfo().hasCalls(); 9678d19108SHeejin Ahn } 974b3bb213SDerek Schuff 984b3bb213SDerek Schuff /// Returns true if this function needs a local user-space stack pointer. 994b3bb213SDerek Schuff /// Unlike a machine stack pointer, the wasm user stack pointer is a global 1004b3bb213SDerek Schuff /// variable, so it is loaded into a register in the prolog. 101bc6d8970SHeejin Ahn bool WebAssemblyFrameLowering::needsSP(const MachineFunction &MF) const { 102972fc358SHeejin Ahn return needsSPForLocalFrame(MF) || needsPrologForEH(MF); 1034b3bb213SDerek Schuff } 1044b3bb213SDerek Schuff 1054b3bb213SDerek Schuff /// Returns true if the local user-space stack pointer needs to be written back 106c4df1d18SHeejin Ahn /// to __stack_pointer global by this function (this is not meaningful if 107c4df1d18SHeejin Ahn /// needsSP is false). If false, the stack red zone can be used and only a local 108c4df1d18SHeejin Ahn /// SP is needed. 1094b3bb213SDerek Schuff bool WebAssemblyFrameLowering::needsSPWriteback( 110bc6d8970SHeejin Ahn const MachineFunction &MF) const { 111bc6d8970SHeejin Ahn auto &MFI = MF.getFrameInfo(); 112bc6d8970SHeejin Ahn assert(needsSP(MF)); 113972fc358SHeejin Ahn // When we don't need a local stack pointer for its local frame but only to 114972fc358SHeejin Ahn // support EH, we don't need to write SP back in the epilog, because we don't 115972fc358SHeejin Ahn // bump down the stack pointer in the prolog. We need to write SP back in the 116972fc358SHeejin Ahn // epilog only if 117972fc358SHeejin Ahn // 1. We need SP not only for EH support but also because we actually use 118972fc358SHeejin Ahn // stack or we have a frame address taken. 119972fc358SHeejin Ahn // 2. We cannot use the red zone. 120972fc358SHeejin Ahn bool CanUseRedZone = MFI.getStackSize() <= RedZoneSize && !MFI.hasCalls() && 121972fc358SHeejin Ahn !MF.getFunction().hasFnAttribute(Attribute::NoRedZone); 122972fc358SHeejin Ahn return needsSPForLocalFrame(MF) && !CanUseRedZone; 1234b3bb213SDerek Schuff } 1244b3bb213SDerek Schuff 12578d19108SHeejin Ahn void WebAssemblyFrameLowering::writeSPToGlobal( 12678d19108SHeejin Ahn unsigned SrcReg, MachineFunction &MF, MachineBasicBlock &MBB, 12778d19108SHeejin Ahn MachineBasicBlock::iterator &InsertStore, const DebugLoc &DL) const { 12882607f56SDan Gohman const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 12982607f56SDan Gohman 130d08cd15fSDan Gohman const char *ES = "__stack_pointer"; 131d08cd15fSDan Gohman auto *SPSymbol = MF.createExternalSymbolName(ES); 1326a87ddacSThomas Lively BuildMI(MBB, InsertStore, DL, TII->get(WebAssembly::GLOBAL_SET_I32)) 133e408a89aSNicholas Wilson .addExternalSymbol(SPSymbol, WebAssemblyII::MO_SYMBOL_GLOBAL) 13482607f56SDan Gohman .addReg(SrcReg); 13582607f56SDan Gohman } 13627e3b8a6SDerek Schuff 137e1a2e90fSHans Wennborg MachineBasicBlock::iterator 138e1a2e90fSHans Wennborg WebAssemblyFrameLowering::eliminateCallFramePseudoInstr( 1398bb5f292SDerek Schuff MachineFunction &MF, MachineBasicBlock &MBB, 1408bb5f292SDerek Schuff MachineBasicBlock::iterator I) const { 1410d41b7b3SDerek Schuff assert(!I->getOperand(0).getImm() && (hasFP(MF) || hasBP(MF)) && 14227e3b8a6SDerek Schuff "Call frame pseudos should only be used for dynamic stack adjustment"); 14327e3b8a6SDerek Schuff const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 1444b3bb213SDerek Schuff if (I->getOpcode() == TII->getCallFrameDestroyOpcode() && 145bc6d8970SHeejin Ahn needsSPWriteback(MF)) { 14627e3b8a6SDerek Schuff DebugLoc DL = I->getDebugLoc(); 14720c9c443SHeejin Ahn writeSPToGlobal(WebAssembly::SP32, MF, MBB, I, DL); 14827e3b8a6SDerek Schuff } 149e1a2e90fSHans Wennborg return MBB.erase(I); 1508bb5f292SDerek Schuff } 1518bb5f292SDerek Schuff 1528bb5f292SDerek Schuff void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF, 1538bb5f292SDerek Schuff MachineBasicBlock &MBB) const { 1548bb5f292SDerek Schuff // TODO: Do ".setMIFlag(MachineInstr::FrameSetup)" on emitted instructions 155941a705bSMatthias Braun auto &MFI = MF.getFrameInfo(); 156941a705bSMatthias Braun assert(MFI.getCalleeSavedInfo().empty() && 1578bb5f292SDerek Schuff "WebAssembly should not have callee-saved registers"); 1586ea637afSDerek Schuff 159f208f631SHeejin Ahn if (!needsSP(MF)) 160f208f631SHeejin Ahn return; 161941a705bSMatthias Braun uint64_t StackSize = MFI.getStackSize(); 1628bb5f292SDerek Schuff 1633196650bSDan Gohman const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 1646ea637afSDerek Schuff auto &MRI = MF.getRegInfo(); 1658bb5f292SDerek Schuff 1668bb5f292SDerek Schuff auto InsertPt = MBB.begin(); 167d934cb88SDan Gohman while (InsertPt != MBB.end() && WebAssembly::isArgument(*InsertPt)) 168d934cb88SDan Gohman ++InsertPt; 1698bb5f292SDerek Schuff DebugLoc DL; 1708bb5f292SDerek Schuff 1710cfb5f85SDan Gohman const TargetRegisterClass *PtrRC = 1720cfb5f85SDan Gohman MRI.getTargetRegisterInfo()->getPointerRegClass(MF); 1730d41b7b3SDerek Schuff unsigned SPReg = WebAssembly::SP32; 1740d41b7b3SDerek Schuff if (StackSize) 1750d41b7b3SDerek Schuff SPReg = MRI.createVirtualRegister(PtrRC); 1769d24fb7fSSam Clegg 177d08cd15fSDan Gohman const char *ES = "__stack_pointer"; 178d08cd15fSDan Gohman auto *SPSymbol = MF.createExternalSymbolName(ES); 1796a87ddacSThomas Lively BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::GLOBAL_GET_I32), SPReg) 180e408a89aSNicholas Wilson .addExternalSymbol(SPSymbol, WebAssemblyII::MO_SYMBOL_GLOBAL); 1816ea637afSDerek Schuff 1820d41b7b3SDerek Schuff bool HasBP = hasBP(MF); 1830d41b7b3SDerek Schuff if (HasBP) { 1840d41b7b3SDerek Schuff auto FI = MF.getInfo<WebAssemblyFunctionInfo>(); 1850d41b7b3SDerek Schuff unsigned BasePtr = MRI.createVirtualRegister(PtrRC); 1860d41b7b3SDerek Schuff FI->setBasePointerVreg(BasePtr); 1870d41b7b3SDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::COPY), BasePtr) 1880d41b7b3SDerek Schuff .addReg(SPReg); 1890d41b7b3SDerek Schuff } 1906ea637afSDerek Schuff if (StackSize) { 1916ea637afSDerek Schuff // Subtract the frame size 1920cfb5f85SDan Gohman unsigned OffsetReg = MRI.createVirtualRegister(PtrRC); 1936ea637afSDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg) 1946ea637afSDerek Schuff .addImm(StackSize); 1953f063295SDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::SUB_I32), 1966ea637afSDerek Schuff WebAssembly::SP32) 1976ea637afSDerek Schuff .addReg(SPReg) 1986ea637afSDerek Schuff .addReg(OffsetReg); 1996ea637afSDerek Schuff } 2000d41b7b3SDerek Schuff if (HasBP) { 2010d41b7b3SDerek Schuff unsigned BitmaskReg = MRI.createVirtualRegister(PtrRC); 2020d41b7b3SDerek Schuff unsigned Alignment = MFI.getMaxAlignment(); 203f295cc8fSDan Gohman assert((1u << countTrailingZeros(Alignment)) == Alignment && 2040d41b7b3SDerek Schuff "Alignment must be a power of 2"); 2050d41b7b3SDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), BitmaskReg) 2060d41b7b3SDerek Schuff .addImm((int)~(Alignment - 1)); 2070d41b7b3SDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::AND_I32), 2080d41b7b3SDerek Schuff WebAssembly::SP32) 2090d41b7b3SDerek Schuff .addReg(WebAssembly::SP32) 2100d41b7b3SDerek Schuff .addReg(BitmaskReg); 2110d41b7b3SDerek Schuff } 2126ea637afSDerek Schuff if (hasFP(MF)) { 2136ea637afSDerek Schuff // Unlike most conventional targets (where FP points to the saved FP), 2146ea637afSDerek Schuff // FP points to the bottom of the fixed-size locals, so we can use positive 2156ea637afSDerek Schuff // offsets in load/store instructions. 216f208f631SHeejin Ahn BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::COPY), WebAssembly::FP32) 2176ea637afSDerek Schuff .addReg(WebAssembly::SP32); 2186ea637afSDerek Schuff } 219bc6d8970SHeejin Ahn if (StackSize && needsSPWriteback(MF)) { 22020c9c443SHeejin Ahn writeSPToGlobal(WebAssembly::SP32, MF, MBB, InsertPt, DL); 2216ea637afSDerek Schuff } 2228bb5f292SDerek Schuff } 2238bb5f292SDerek Schuff 2249769debfSDerek Schuff void WebAssemblyFrameLowering::emitEpilogue(MachineFunction &MF, 2259769debfSDerek Schuff MachineBasicBlock &MBB) const { 226bc6d8970SHeejin Ahn uint64_t StackSize = MF.getFrameInfo().getStackSize(); 227f208f631SHeejin Ahn if (!needsSP(MF) || !needsSPWriteback(MF)) 228f208f631SHeejin Ahn return; 2293196650bSDan Gohman const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 2309769debfSDerek Schuff auto &MRI = MF.getRegInfo(); 2319769debfSDerek Schuff auto InsertPt = MBB.getFirstTerminator(); 2329769debfSDerek Schuff DebugLoc DL; 2339769debfSDerek Schuff 2340cfb5f85SDan Gohman if (InsertPt != MBB.end()) 2359769debfSDerek Schuff DL = InsertPt->getDebugLoc(); 236450a8075SDan Gohman 2376ea637afSDerek Schuff // Restore the stack pointer. If we had fixed-size locals, add the offset 2386ea637afSDerek Schuff // subtracted in the prolog. 239d4207ba0SDerek Schuff unsigned SPReg = 0; 2400d41b7b3SDerek Schuff if (hasBP(MF)) { 2410d41b7b3SDerek Schuff auto FI = MF.getInfo<WebAssemblyFunctionInfo>(); 2420d41b7b3SDerek Schuff SPReg = FI->getBasePointerVreg(); 2430d41b7b3SDerek Schuff } else if (StackSize) { 2440cfb5f85SDan Gohman const TargetRegisterClass *PtrRC = 2450cfb5f85SDan Gohman MRI.getTargetRegisterInfo()->getPointerRegClass(MF); 2460cfb5f85SDan Gohman unsigned OffsetReg = MRI.createVirtualRegister(PtrRC); 2479769debfSDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg) 2489769debfSDerek Schuff .addImm(StackSize); 249d4207ba0SDerek Schuff // In the epilog we don't need to write the result back to the SP32 physreg 250d4207ba0SDerek Schuff // because it won't be used again. We can use a stackified register instead. 2510cfb5f85SDan Gohman SPReg = MRI.createVirtualRegister(PtrRC); 252d4207ba0SDerek Schuff BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::ADD_I32), SPReg) 2536ea637afSDerek Schuff .addReg(hasFP(MF) ? WebAssembly::FP32 : WebAssembly::SP32) 2549769debfSDerek Schuff .addReg(OffsetReg); 255d4207ba0SDerek Schuff } else { 256d4207ba0SDerek Schuff SPReg = hasFP(MF) ? WebAssembly::FP32 : WebAssembly::SP32; 2576ea637afSDerek Schuff } 2586ea637afSDerek Schuff 25920c9c443SHeejin Ahn writeSPToGlobal(SPReg, MF, MBB, InsertPt, DL); 26010e730a2SDan Gohman } 261