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