1 //===-- WebAssemblyFrameLowering.cpp - WebAssembly Frame Lowering ----------==// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 /// 9 /// \file 10 /// This file contains the WebAssembly implementation of 11 /// TargetFrameLowering class. 12 /// 13 /// On WebAssembly, there aren't a lot of things to do here. There are no 14 /// callee-saved registers to save, and no spill slots. 15 /// 16 /// The stack grows downward. 17 /// 18 //===----------------------------------------------------------------------===// 19 20 #include "WebAssemblyFrameLowering.h" 21 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" 22 #include "WebAssembly.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/MC/MCAsmInfo.h" 34 #include "llvm/Support/Debug.h" 35 using namespace llvm; 36 37 #define DEBUG_TYPE "wasm-frame-info" 38 39 // TODO: wasm64 40 // TODO: Emit TargetOpcode::CFI_INSTRUCTION instructions 41 42 /// We need a base pointer in the case of having items on the stack that 43 /// require stricter alignment than the stack pointer itself. Because we need 44 /// to shift the stack pointer by some unknown amount to force the alignment, 45 /// we need to record the value of the stack pointer on entry to the function. 46 bool WebAssemblyFrameLowering::hasBP(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 // Returns true if this function needs a local user-space stack pointer for its 82 // local frame (not for exception handling). 83 bool WebAssemblyFrameLowering::needsSPForLocalFrame( 84 const MachineFunction &MF) const { 85 auto &MFI = MF.getFrameInfo(); 86 return MFI.getStackSize() || MFI.adjustsStack() || hasFP(MF); 87 } 88 89 // In function with EH pads, we need to make a copy of the value of 90 // __stack_pointer global in SP32/64 register, in order to use it when 91 // restoring __stack_pointer after an exception is caught. 92 bool WebAssemblyFrameLowering::needsPrologForEH( 93 const MachineFunction &MF) const { 94 auto EHType = MF.getTarget().getMCAsmInfo()->getExceptionHandlingType(); 95 return EHType == ExceptionHandling::Wasm && 96 MF.getFunction().hasPersonalityFn() && MF.getFrameInfo().hasCalls(); 97 } 98 99 /// Returns true if this function needs a local user-space stack pointer. 100 /// Unlike a machine stack pointer, the wasm user stack pointer is a global 101 /// variable, so it is loaded into a register in the prolog. 102 bool WebAssemblyFrameLowering::needsSP(const MachineFunction &MF) const { 103 return needsSPForLocalFrame(MF) || needsPrologForEH(MF); 104 } 105 106 /// Returns true if the local user-space stack pointer needs to be written back 107 /// to __stack_pointer global by this function (this is not meaningful if 108 /// needsSP is false). If false, the stack red zone can be used and only a local 109 /// SP is needed. 110 bool WebAssemblyFrameLowering::needsSPWriteback( 111 const MachineFunction &MF) const { 112 auto &MFI = MF.getFrameInfo(); 113 assert(needsSP(MF)); 114 // When we don't need a local stack pointer for its local frame but only to 115 // support EH, we don't need to write SP back in the epilog, because we don't 116 // bump down the stack pointer in the prolog. We need to write SP back in the 117 // epilog only if 118 // 1. We need SP not only for EH support but also because we actually use 119 // stack or we have a frame address taken. 120 // 2. We cannot use the red zone. 121 bool CanUseRedZone = MFI.getStackSize() <= RedZoneSize && !MFI.hasCalls() && 122 !MF.getFunction().hasFnAttribute(Attribute::NoRedZone); 123 return needsSPForLocalFrame(MF) && !CanUseRedZone; 124 } 125 126 unsigned WebAssemblyFrameLowering::getSPReg(const MachineFunction &MF) { 127 return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64() 128 ? WebAssembly::SP64 129 : WebAssembly::SP32; 130 } 131 132 unsigned WebAssemblyFrameLowering::getFPReg(const MachineFunction &MF) { 133 return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64() 134 ? WebAssembly::FP64 135 : WebAssembly::FP32; 136 } 137 138 unsigned 139 WebAssemblyFrameLowering::getOpcConst(const MachineFunction &MF) { 140 return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64() 141 ? WebAssembly::CONST_I64 142 : WebAssembly::CONST_I32; 143 } 144 145 unsigned WebAssemblyFrameLowering::getOpcAdd(const MachineFunction &MF) { 146 return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64() 147 ? WebAssembly::ADD_I64 148 : WebAssembly::ADD_I32; 149 } 150 151 unsigned WebAssemblyFrameLowering::getOpcSub(const MachineFunction &MF) { 152 return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64() 153 ? WebAssembly::SUB_I64 154 : WebAssembly::SUB_I32; 155 } 156 157 unsigned WebAssemblyFrameLowering::getOpcAnd(const MachineFunction &MF) { 158 return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64() 159 ? WebAssembly::AND_I64 160 : WebAssembly::AND_I32; 161 } 162 163 unsigned 164 WebAssemblyFrameLowering::getOpcGlobGet(const MachineFunction &MF) { 165 return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64() 166 ? WebAssembly::GLOBAL_GET_I64 167 : WebAssembly::GLOBAL_GET_I32; 168 } 169 170 unsigned 171 WebAssemblyFrameLowering::getOpcGlobSet(const MachineFunction &MF) { 172 return MF.getSubtarget<WebAssemblySubtarget>().hasAddr64() 173 ? WebAssembly::GLOBAL_SET_I64 174 : WebAssembly::GLOBAL_SET_I32; 175 } 176 177 void WebAssemblyFrameLowering::writeSPToGlobal( 178 unsigned SrcReg, MachineFunction &MF, MachineBasicBlock &MBB, 179 MachineBasicBlock::iterator &InsertStore, const DebugLoc &DL) const { 180 const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 181 182 const char *ES = "__stack_pointer"; 183 auto *SPSymbol = MF.createExternalSymbolName(ES); 184 185 BuildMI(MBB, InsertStore, DL, TII->get(getOpcGlobSet(MF))) 186 .addExternalSymbol(SPSymbol) 187 .addReg(SrcReg); 188 } 189 190 MachineBasicBlock::iterator 191 WebAssemblyFrameLowering::eliminateCallFramePseudoInstr( 192 MachineFunction &MF, MachineBasicBlock &MBB, 193 MachineBasicBlock::iterator I) const { 194 assert(!I->getOperand(0).getImm() && (hasFP(MF) || hasBP(MF)) && 195 "Call frame pseudos should only be used for dynamic stack adjustment"); 196 auto &ST = MF.getSubtarget<WebAssemblySubtarget>(); 197 const auto *TII = ST.getInstrInfo(); 198 if (I->getOpcode() == TII->getCallFrameDestroyOpcode() && 199 needsSPWriteback(MF)) { 200 DebugLoc DL = I->getDebugLoc(); 201 writeSPToGlobal(getSPReg(MF), MF, MBB, I, DL); 202 } 203 return MBB.erase(I); 204 } 205 206 void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF, 207 MachineBasicBlock &MBB) const { 208 // TODO: Do ".setMIFlag(MachineInstr::FrameSetup)" on emitted instructions 209 auto &MFI = MF.getFrameInfo(); 210 assert(MFI.getCalleeSavedInfo().empty() && 211 "WebAssembly should not have callee-saved registers"); 212 213 if (!needsSP(MF)) 214 return; 215 uint64_t StackSize = MFI.getStackSize(); 216 217 auto &ST = MF.getSubtarget<WebAssemblySubtarget>(); 218 const auto *TII = ST.getInstrInfo(); 219 auto &MRI = MF.getRegInfo(); 220 221 auto InsertPt = MBB.begin(); 222 while (InsertPt != MBB.end() && 223 WebAssembly::isArgument(InsertPt->getOpcode())) 224 ++InsertPt; 225 DebugLoc DL; 226 227 const TargetRegisterClass *PtrRC = 228 MRI.getTargetRegisterInfo()->getPointerRegClass(MF); 229 unsigned SPReg = getSPReg(MF); 230 if (StackSize) 231 SPReg = MRI.createVirtualRegister(PtrRC); 232 233 const char *ES = "__stack_pointer"; 234 auto *SPSymbol = MF.createExternalSymbolName(ES); 235 BuildMI(MBB, InsertPt, DL, TII->get(getOpcGlobGet(MF)), SPReg) 236 .addExternalSymbol(SPSymbol); 237 238 bool HasBP = hasBP(MF); 239 if (HasBP) { 240 auto FI = MF.getInfo<WebAssemblyFunctionInfo>(); 241 Register BasePtr = MRI.createVirtualRegister(PtrRC); 242 FI->setBasePointerVreg(BasePtr); 243 BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::COPY), BasePtr) 244 .addReg(SPReg); 245 } 246 if (StackSize) { 247 // Subtract the frame size 248 Register OffsetReg = MRI.createVirtualRegister(PtrRC); 249 BuildMI(MBB, InsertPt, DL, TII->get(getOpcConst(MF)), OffsetReg) 250 .addImm(StackSize); 251 BuildMI(MBB, InsertPt, DL, TII->get(getOpcSub(MF)), getSPReg(MF)) 252 .addReg(SPReg) 253 .addReg(OffsetReg); 254 } 255 if (HasBP) { 256 Register BitmaskReg = MRI.createVirtualRegister(PtrRC); 257 Align Alignment = MFI.getMaxAlign(); 258 BuildMI(MBB, InsertPt, DL, TII->get(getOpcConst(MF)), BitmaskReg) 259 .addImm((int64_t) ~(Alignment.value() - 1)); 260 BuildMI(MBB, InsertPt, DL, TII->get(getOpcAnd(MF)), getSPReg(MF)) 261 .addReg(getSPReg(MF)) 262 .addReg(BitmaskReg); 263 } 264 if (hasFP(MF)) { 265 // Unlike most conventional targets (where FP points to the saved FP), 266 // FP points to the bottom of the fixed-size locals, so we can use positive 267 // offsets in load/store instructions. 268 BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::COPY), getFPReg(MF)) 269 .addReg(getSPReg(MF)); 270 } 271 if (StackSize && needsSPWriteback(MF)) { 272 writeSPToGlobal(getSPReg(MF), MF, MBB, InsertPt, DL); 273 } 274 } 275 276 void WebAssemblyFrameLowering::emitEpilogue(MachineFunction &MF, 277 MachineBasicBlock &MBB) const { 278 uint64_t StackSize = MF.getFrameInfo().getStackSize(); 279 if (!needsSP(MF) || !needsSPWriteback(MF)) 280 return; 281 auto &ST = MF.getSubtarget<WebAssemblySubtarget>(); 282 const auto *TII = ST.getInstrInfo(); 283 auto &MRI = MF.getRegInfo(); 284 auto InsertPt = MBB.getFirstTerminator(); 285 DebugLoc DL; 286 287 if (InsertPt != MBB.end()) 288 DL = InsertPt->getDebugLoc(); 289 290 // Restore the stack pointer. If we had fixed-size locals, add the offset 291 // subtracted in the prolog. 292 unsigned SPReg = 0; 293 unsigned SPFPReg = hasFP(MF) ? getFPReg(MF) : getSPReg(MF); 294 if (hasBP(MF)) { 295 auto FI = MF.getInfo<WebAssemblyFunctionInfo>(); 296 SPReg = FI->getBasePointerVreg(); 297 } else if (StackSize) { 298 const TargetRegisterClass *PtrRC = 299 MRI.getTargetRegisterInfo()->getPointerRegClass(MF); 300 Register OffsetReg = MRI.createVirtualRegister(PtrRC); 301 BuildMI(MBB, InsertPt, DL, TII->get(getOpcConst(MF)), OffsetReg) 302 .addImm(StackSize); 303 // In the epilog we don't need to write the result back to the SP32/64 304 // physreg because it won't be used again. We can use a stackified register 305 // instead. 306 SPReg = MRI.createVirtualRegister(PtrRC); 307 BuildMI(MBB, InsertPt, DL, TII->get(getOpcAdd(MF)), SPReg) 308 .addReg(SPFPReg) 309 .addReg(OffsetReg); 310 } else { 311 SPReg = SPFPReg; 312 } 313 314 writeSPToGlobal(SPReg, MF, MBB, InsertPt, DL); 315 } 316 317 TargetFrameLowering::DwarfFrameBase 318 WebAssemblyFrameLowering::getDwarfFrameBase(const MachineFunction &MF) const { 319 DwarfFrameBase Loc; 320 Loc.Kind = DwarfFrameBase::WasmFrameBase; 321 const WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>(); 322 if (needsSP(MF) && MFI.isFrameBaseVirtual()) { 323 unsigned LocalNum = MFI.getFrameBaseLocal(); 324 Loc.Location.WasmLoc = {WebAssembly::TI_LOCAL, LocalNum}; 325 } else { 326 // TODO: This should work on a breakpoint at a function with no frame, 327 // but probably won't work for traversing up the stack. 328 Loc.Location.WasmLoc = {WebAssembly::TI_GLOBAL_RELOC, 0}; 329 } 330 return Loc; 331 } 332