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" 34 using 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. 45 bool 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. 54 bool 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. 76 bool 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. 85 bool 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. 93 bool 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 100 static 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 134 MachineBasicBlock::iterator 135 WebAssemblyFrameLowering::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 149 void 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 238 void 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