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