1*0b57cec5SDimitry Andric //===-- WebAssemblyFrameLowering.cpp - WebAssembly Frame Lowering ----------==// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric /// 9*0b57cec5SDimitry Andric /// \file 10*0b57cec5SDimitry Andric /// This file contains the WebAssembly implementation of 11*0b57cec5SDimitry Andric /// TargetFrameLowering class. 12*0b57cec5SDimitry Andric /// 13*0b57cec5SDimitry Andric /// On WebAssembly, there aren't a lot of things to do here. There are no 14*0b57cec5SDimitry Andric /// callee-saved registers to save, and no spill slots. 15*0b57cec5SDimitry Andric /// 16*0b57cec5SDimitry Andric /// The stack grows downward. 17*0b57cec5SDimitry Andric /// 18*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 19*0b57cec5SDimitry Andric 20*0b57cec5SDimitry Andric #include "WebAssemblyFrameLowering.h" 21*0b57cec5SDimitry Andric #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" 22*0b57cec5SDimitry Andric #include "WebAssemblyInstrInfo.h" 23*0b57cec5SDimitry Andric #include "WebAssemblyMachineFunctionInfo.h" 24*0b57cec5SDimitry Andric #include "WebAssemblySubtarget.h" 25*0b57cec5SDimitry Andric #include "WebAssemblyTargetMachine.h" 26*0b57cec5SDimitry Andric #include "WebAssemblyUtilities.h" 27*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineFrameInfo.h" 28*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineFunction.h" 29*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h" 30*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineModuleInfoImpls.h" 31*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineRegisterInfo.h" 32*0b57cec5SDimitry Andric #include "llvm/MC/MCAsmInfo.h" 33*0b57cec5SDimitry Andric #include "llvm/Support/Debug.h" 34*0b57cec5SDimitry Andric using namespace llvm; 35*0b57cec5SDimitry Andric 36*0b57cec5SDimitry Andric #define DEBUG_TYPE "wasm-frame-info" 37*0b57cec5SDimitry Andric 38*0b57cec5SDimitry Andric // TODO: wasm64 39*0b57cec5SDimitry Andric // TODO: Emit TargetOpcode::CFI_INSTRUCTION instructions 40*0b57cec5SDimitry Andric 41*0b57cec5SDimitry Andric /// We need a base pointer in the case of having items on the stack that 42*0b57cec5SDimitry Andric /// require stricter alignment than the stack pointer itself. Because we need 43*0b57cec5SDimitry Andric /// to shift the stack pointer by some unknown amount to force the alignment, 44*0b57cec5SDimitry Andric /// we need to record the value of the stack pointer on entry to the function. 45*0b57cec5SDimitry Andric bool WebAssemblyFrameLowering::hasBP(const MachineFunction &MF) const { 46*0b57cec5SDimitry Andric const auto *RegInfo = 47*0b57cec5SDimitry Andric MF.getSubtarget<WebAssemblySubtarget>().getRegisterInfo(); 48*0b57cec5SDimitry Andric return RegInfo->needsStackRealignment(MF); 49*0b57cec5SDimitry Andric } 50*0b57cec5SDimitry Andric 51*0b57cec5SDimitry Andric /// Return true if the specified function should have a dedicated frame pointer 52*0b57cec5SDimitry Andric /// register. 53*0b57cec5SDimitry Andric bool WebAssemblyFrameLowering::hasFP(const MachineFunction &MF) const { 54*0b57cec5SDimitry Andric const MachineFrameInfo &MFI = MF.getFrameInfo(); 55*0b57cec5SDimitry Andric 56*0b57cec5SDimitry Andric // When we have var-sized objects, we move the stack pointer by an unknown 57*0b57cec5SDimitry Andric // amount, and need to emit a frame pointer to restore the stack to where we 58*0b57cec5SDimitry Andric // were on function entry. 59*0b57cec5SDimitry Andric // If we already need a base pointer, we use that to fix up the stack pointer. 60*0b57cec5SDimitry Andric // If there are no fixed-size objects, we would have no use of a frame 61*0b57cec5SDimitry Andric // pointer, and thus should not emit one. 62*0b57cec5SDimitry Andric bool HasFixedSizedObjects = MFI.getStackSize() > 0; 63*0b57cec5SDimitry Andric bool NeedsFixedReference = !hasBP(MF) || HasFixedSizedObjects; 64*0b57cec5SDimitry Andric 65*0b57cec5SDimitry Andric return MFI.isFrameAddressTaken() || 66*0b57cec5SDimitry Andric (MFI.hasVarSizedObjects() && NeedsFixedReference) || 67*0b57cec5SDimitry Andric MFI.hasStackMap() || MFI.hasPatchPoint(); 68*0b57cec5SDimitry Andric } 69*0b57cec5SDimitry Andric 70*0b57cec5SDimitry Andric /// Under normal circumstances, when a frame pointer is not required, we reserve 71*0b57cec5SDimitry Andric /// argument space for call sites in the function immediately on entry to the 72*0b57cec5SDimitry Andric /// current function. This eliminates the need for add/sub sp brackets around 73*0b57cec5SDimitry Andric /// call sites. Returns true if the call frame is included as part of the stack 74*0b57cec5SDimitry Andric /// frame. 75*0b57cec5SDimitry Andric bool WebAssemblyFrameLowering::hasReservedCallFrame( 76*0b57cec5SDimitry Andric const MachineFunction &MF) const { 77*0b57cec5SDimitry Andric return !MF.getFrameInfo().hasVarSizedObjects(); 78*0b57cec5SDimitry Andric } 79*0b57cec5SDimitry Andric 80*0b57cec5SDimitry Andric // Returns true if this function needs a local user-space stack pointer for its 81*0b57cec5SDimitry Andric // local frame (not for exception handling). 82*0b57cec5SDimitry Andric bool WebAssemblyFrameLowering::needsSPForLocalFrame( 83*0b57cec5SDimitry Andric const MachineFunction &MF) const { 84*0b57cec5SDimitry Andric auto &MFI = MF.getFrameInfo(); 85*0b57cec5SDimitry Andric return MFI.getStackSize() || MFI.adjustsStack() || hasFP(MF); 86*0b57cec5SDimitry Andric } 87*0b57cec5SDimitry Andric 88*0b57cec5SDimitry Andric // In function with EH pads, we need to make a copy of the value of 89*0b57cec5SDimitry Andric // __stack_pointer global in SP32 register, in order to use it when restoring 90*0b57cec5SDimitry Andric // __stack_pointer after an exception is caught. 91*0b57cec5SDimitry Andric bool WebAssemblyFrameLowering::needsPrologForEH( 92*0b57cec5SDimitry Andric const MachineFunction &MF) const { 93*0b57cec5SDimitry Andric auto EHType = MF.getTarget().getMCAsmInfo()->getExceptionHandlingType(); 94*0b57cec5SDimitry Andric return EHType == ExceptionHandling::Wasm && 95*0b57cec5SDimitry Andric MF.getFunction().hasPersonalityFn() && MF.getFrameInfo().hasCalls(); 96*0b57cec5SDimitry Andric } 97*0b57cec5SDimitry Andric 98*0b57cec5SDimitry Andric /// Returns true if this function needs a local user-space stack pointer. 99*0b57cec5SDimitry Andric /// Unlike a machine stack pointer, the wasm user stack pointer is a global 100*0b57cec5SDimitry Andric /// variable, so it is loaded into a register in the prolog. 101*0b57cec5SDimitry Andric bool WebAssemblyFrameLowering::needsSP(const MachineFunction &MF) const { 102*0b57cec5SDimitry Andric return needsSPForLocalFrame(MF) || needsPrologForEH(MF); 103*0b57cec5SDimitry Andric } 104*0b57cec5SDimitry Andric 105*0b57cec5SDimitry Andric /// Returns true if the local user-space stack pointer needs to be written back 106*0b57cec5SDimitry Andric /// to __stack_pointer global by this function (this is not meaningful if 107*0b57cec5SDimitry Andric /// needsSP is false). If false, the stack red zone can be used and only a local 108*0b57cec5SDimitry Andric /// SP is needed. 109*0b57cec5SDimitry Andric bool WebAssemblyFrameLowering::needsSPWriteback( 110*0b57cec5SDimitry Andric const MachineFunction &MF) const { 111*0b57cec5SDimitry Andric auto &MFI = MF.getFrameInfo(); 112*0b57cec5SDimitry Andric assert(needsSP(MF)); 113*0b57cec5SDimitry Andric // When we don't need a local stack pointer for its local frame but only to 114*0b57cec5SDimitry Andric // support EH, we don't need to write SP back in the epilog, because we don't 115*0b57cec5SDimitry Andric // bump down the stack pointer in the prolog. We need to write SP back in the 116*0b57cec5SDimitry Andric // epilog only if 117*0b57cec5SDimitry Andric // 1. We need SP not only for EH support but also because we actually use 118*0b57cec5SDimitry Andric // stack or we have a frame address taken. 119*0b57cec5SDimitry Andric // 2. We cannot use the red zone. 120*0b57cec5SDimitry Andric bool CanUseRedZone = MFI.getStackSize() <= RedZoneSize && !MFI.hasCalls() && 121*0b57cec5SDimitry Andric !MF.getFunction().hasFnAttribute(Attribute::NoRedZone); 122*0b57cec5SDimitry Andric return needsSPForLocalFrame(MF) && !CanUseRedZone; 123*0b57cec5SDimitry Andric } 124*0b57cec5SDimitry Andric 125*0b57cec5SDimitry Andric void WebAssemblyFrameLowering::writeSPToGlobal( 126*0b57cec5SDimitry Andric unsigned SrcReg, MachineFunction &MF, MachineBasicBlock &MBB, 127*0b57cec5SDimitry Andric MachineBasicBlock::iterator &InsertStore, const DebugLoc &DL) const { 128*0b57cec5SDimitry Andric const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 129*0b57cec5SDimitry Andric 130*0b57cec5SDimitry Andric const char *ES = "__stack_pointer"; 131*0b57cec5SDimitry Andric auto *SPSymbol = MF.createExternalSymbolName(ES); 132*0b57cec5SDimitry Andric BuildMI(MBB, InsertStore, DL, TII->get(WebAssembly::GLOBAL_SET_I32)) 133*0b57cec5SDimitry Andric .addExternalSymbol(SPSymbol) 134*0b57cec5SDimitry Andric .addReg(SrcReg); 135*0b57cec5SDimitry Andric } 136*0b57cec5SDimitry Andric 137*0b57cec5SDimitry Andric MachineBasicBlock::iterator 138*0b57cec5SDimitry Andric WebAssemblyFrameLowering::eliminateCallFramePseudoInstr( 139*0b57cec5SDimitry Andric MachineFunction &MF, MachineBasicBlock &MBB, 140*0b57cec5SDimitry Andric MachineBasicBlock::iterator I) const { 141*0b57cec5SDimitry Andric assert(!I->getOperand(0).getImm() && (hasFP(MF) || hasBP(MF)) && 142*0b57cec5SDimitry Andric "Call frame pseudos should only be used for dynamic stack adjustment"); 143*0b57cec5SDimitry Andric const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 144*0b57cec5SDimitry Andric if (I->getOpcode() == TII->getCallFrameDestroyOpcode() && 145*0b57cec5SDimitry Andric needsSPWriteback(MF)) { 146*0b57cec5SDimitry Andric DebugLoc DL = I->getDebugLoc(); 147*0b57cec5SDimitry Andric writeSPToGlobal(WebAssembly::SP32, MF, MBB, I, DL); 148*0b57cec5SDimitry Andric } 149*0b57cec5SDimitry Andric return MBB.erase(I); 150*0b57cec5SDimitry Andric } 151*0b57cec5SDimitry Andric 152*0b57cec5SDimitry Andric void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF, 153*0b57cec5SDimitry Andric MachineBasicBlock &MBB) const { 154*0b57cec5SDimitry Andric // TODO: Do ".setMIFlag(MachineInstr::FrameSetup)" on emitted instructions 155*0b57cec5SDimitry Andric auto &MFI = MF.getFrameInfo(); 156*0b57cec5SDimitry Andric assert(MFI.getCalleeSavedInfo().empty() && 157*0b57cec5SDimitry Andric "WebAssembly should not have callee-saved registers"); 158*0b57cec5SDimitry Andric 159*0b57cec5SDimitry Andric if (!needsSP(MF)) 160*0b57cec5SDimitry Andric return; 161*0b57cec5SDimitry Andric uint64_t StackSize = MFI.getStackSize(); 162*0b57cec5SDimitry Andric 163*0b57cec5SDimitry Andric const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 164*0b57cec5SDimitry Andric auto &MRI = MF.getRegInfo(); 165*0b57cec5SDimitry Andric 166*0b57cec5SDimitry Andric auto InsertPt = MBB.begin(); 167*0b57cec5SDimitry Andric while (InsertPt != MBB.end() && 168*0b57cec5SDimitry Andric WebAssembly::isArgument(InsertPt->getOpcode())) 169*0b57cec5SDimitry Andric ++InsertPt; 170*0b57cec5SDimitry Andric DebugLoc DL; 171*0b57cec5SDimitry Andric 172*0b57cec5SDimitry Andric const TargetRegisterClass *PtrRC = 173*0b57cec5SDimitry Andric MRI.getTargetRegisterInfo()->getPointerRegClass(MF); 174*0b57cec5SDimitry Andric unsigned SPReg = WebAssembly::SP32; 175*0b57cec5SDimitry Andric if (StackSize) 176*0b57cec5SDimitry Andric SPReg = MRI.createVirtualRegister(PtrRC); 177*0b57cec5SDimitry Andric 178*0b57cec5SDimitry Andric const char *ES = "__stack_pointer"; 179*0b57cec5SDimitry Andric auto *SPSymbol = MF.createExternalSymbolName(ES); 180*0b57cec5SDimitry Andric BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::GLOBAL_GET_I32), SPReg) 181*0b57cec5SDimitry Andric .addExternalSymbol(SPSymbol); 182*0b57cec5SDimitry Andric 183*0b57cec5SDimitry Andric bool HasBP = hasBP(MF); 184*0b57cec5SDimitry Andric if (HasBP) { 185*0b57cec5SDimitry Andric auto FI = MF.getInfo<WebAssemblyFunctionInfo>(); 186*0b57cec5SDimitry Andric unsigned BasePtr = MRI.createVirtualRegister(PtrRC); 187*0b57cec5SDimitry Andric FI->setBasePointerVreg(BasePtr); 188*0b57cec5SDimitry Andric BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::COPY), BasePtr) 189*0b57cec5SDimitry Andric .addReg(SPReg); 190*0b57cec5SDimitry Andric } 191*0b57cec5SDimitry Andric if (StackSize) { 192*0b57cec5SDimitry Andric // Subtract the frame size 193*0b57cec5SDimitry Andric unsigned OffsetReg = MRI.createVirtualRegister(PtrRC); 194*0b57cec5SDimitry Andric BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg) 195*0b57cec5SDimitry Andric .addImm(StackSize); 196*0b57cec5SDimitry Andric BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::SUB_I32), 197*0b57cec5SDimitry Andric WebAssembly::SP32) 198*0b57cec5SDimitry Andric .addReg(SPReg) 199*0b57cec5SDimitry Andric .addReg(OffsetReg); 200*0b57cec5SDimitry Andric } 201*0b57cec5SDimitry Andric if (HasBP) { 202*0b57cec5SDimitry Andric unsigned BitmaskReg = MRI.createVirtualRegister(PtrRC); 203*0b57cec5SDimitry Andric unsigned Alignment = MFI.getMaxAlignment(); 204*0b57cec5SDimitry Andric assert((1u << countTrailingZeros(Alignment)) == Alignment && 205*0b57cec5SDimitry Andric "Alignment must be a power of 2"); 206*0b57cec5SDimitry Andric BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), BitmaskReg) 207*0b57cec5SDimitry Andric .addImm((int)~(Alignment - 1)); 208*0b57cec5SDimitry Andric BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::AND_I32), 209*0b57cec5SDimitry Andric WebAssembly::SP32) 210*0b57cec5SDimitry Andric .addReg(WebAssembly::SP32) 211*0b57cec5SDimitry Andric .addReg(BitmaskReg); 212*0b57cec5SDimitry Andric } 213*0b57cec5SDimitry Andric if (hasFP(MF)) { 214*0b57cec5SDimitry Andric // Unlike most conventional targets (where FP points to the saved FP), 215*0b57cec5SDimitry Andric // FP points to the bottom of the fixed-size locals, so we can use positive 216*0b57cec5SDimitry Andric // offsets in load/store instructions. 217*0b57cec5SDimitry Andric BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::COPY), WebAssembly::FP32) 218*0b57cec5SDimitry Andric .addReg(WebAssembly::SP32); 219*0b57cec5SDimitry Andric } 220*0b57cec5SDimitry Andric if (StackSize && needsSPWriteback(MF)) { 221*0b57cec5SDimitry Andric writeSPToGlobal(WebAssembly::SP32, MF, MBB, InsertPt, DL); 222*0b57cec5SDimitry Andric } 223*0b57cec5SDimitry Andric } 224*0b57cec5SDimitry Andric 225*0b57cec5SDimitry Andric void WebAssemblyFrameLowering::emitEpilogue(MachineFunction &MF, 226*0b57cec5SDimitry Andric MachineBasicBlock &MBB) const { 227*0b57cec5SDimitry Andric uint64_t StackSize = MF.getFrameInfo().getStackSize(); 228*0b57cec5SDimitry Andric if (!needsSP(MF) || !needsSPWriteback(MF)) 229*0b57cec5SDimitry Andric return; 230*0b57cec5SDimitry Andric const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 231*0b57cec5SDimitry Andric auto &MRI = MF.getRegInfo(); 232*0b57cec5SDimitry Andric auto InsertPt = MBB.getFirstTerminator(); 233*0b57cec5SDimitry Andric DebugLoc DL; 234*0b57cec5SDimitry Andric 235*0b57cec5SDimitry Andric if (InsertPt != MBB.end()) 236*0b57cec5SDimitry Andric DL = InsertPt->getDebugLoc(); 237*0b57cec5SDimitry Andric 238*0b57cec5SDimitry Andric // Restore the stack pointer. If we had fixed-size locals, add the offset 239*0b57cec5SDimitry Andric // subtracted in the prolog. 240*0b57cec5SDimitry Andric unsigned SPReg = 0; 241*0b57cec5SDimitry Andric if (hasBP(MF)) { 242*0b57cec5SDimitry Andric auto FI = MF.getInfo<WebAssemblyFunctionInfo>(); 243*0b57cec5SDimitry Andric SPReg = FI->getBasePointerVreg(); 244*0b57cec5SDimitry Andric } else if (StackSize) { 245*0b57cec5SDimitry Andric const TargetRegisterClass *PtrRC = 246*0b57cec5SDimitry Andric MRI.getTargetRegisterInfo()->getPointerRegClass(MF); 247*0b57cec5SDimitry Andric unsigned OffsetReg = MRI.createVirtualRegister(PtrRC); 248*0b57cec5SDimitry Andric BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg) 249*0b57cec5SDimitry Andric .addImm(StackSize); 250*0b57cec5SDimitry Andric // In the epilog we don't need to write the result back to the SP32 physreg 251*0b57cec5SDimitry Andric // because it won't be used again. We can use a stackified register instead. 252*0b57cec5SDimitry Andric SPReg = MRI.createVirtualRegister(PtrRC); 253*0b57cec5SDimitry Andric BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::ADD_I32), SPReg) 254*0b57cec5SDimitry Andric .addReg(hasFP(MF) ? WebAssembly::FP32 : WebAssembly::SP32) 255*0b57cec5SDimitry Andric .addReg(OffsetReg); 256*0b57cec5SDimitry Andric } else { 257*0b57cec5SDimitry Andric SPReg = hasFP(MF) ? WebAssembly::FP32 : WebAssembly::SP32; 258*0b57cec5SDimitry Andric } 259*0b57cec5SDimitry Andric 260*0b57cec5SDimitry Andric writeSPToGlobal(SPReg, MF, MBB, InsertPt, DL); 261*0b57cec5SDimitry Andric } 262