Index: lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp +++ lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp @@ -44,11 +44,11 @@ /// register. bool WebAssemblyFrameLowering::hasFP(const MachineFunction &MF) const { const MachineFrameInfo *MFI = MF.getFrameInfo(); - assert(!MFI->isFrameAddressTaken()); const auto *RegInfo = MF.getSubtarget().getRegisterInfo(); - return MFI->hasVarSizedObjects() || MFI->hasStackMap() || - MFI->hasPatchPoint() || RegInfo->needsStackRealignment(MF); + return MFI->isFrameAddressTaken() || MFI->hasVarSizedObjects() || + MFI->hasStackMap() || MFI->hasPatchPoint() || + RegInfo->needsStackRealignment(MF); } /// Under normal circumstances, when a frame pointer is not required, we reserve Index: lib/Target/WebAssembly/WebAssemblyISelLowering.h =================================================================== --- lib/Target/WebAssembly/WebAssemblyISelLowering.h +++ lib/Target/WebAssembly/WebAssemblyISelLowering.h @@ -78,6 +78,7 @@ // Custom lowering hooks. SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override; SDValue LowerFrameIndex(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const; SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; SDValue LowerExternalSymbol(SDValue Op, SelectionDAG &DAG) const; SDValue LowerBR_JT(SDValue Op, SelectionDAG &DAG) const; Index: lib/Target/WebAssembly/WebAssemblyISelLowering.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -542,9 +542,8 @@ case ISD::RETURNADDR: // Probably nothing meaningful can be returned here. fail(DL, DAG, "WebAssembly hasn't implemented __builtin_return_address"); return SDValue(); - case ISD::FRAMEADDR: // TODO: Make this return the userspace frame address - fail(DL, DAG, "WebAssembly hasn't implemented __builtin_frame_address"); - return SDValue(); + case ISD::FRAMEADDR: + return LowerFRAMEADDR(Op, DAG); case ISD::CopyToReg: return LowerCopyToReg(Op, DAG); } @@ -579,6 +578,20 @@ return DAG.getTargetFrameIndex(FI, Op.getValueType()); } +SDValue WebAssemblyTargetLowering::LowerFRAMEADDR(SDValue Op, + SelectionDAG &DAG) const { + // Non-zero depths are not supported yet. We currently don't store saved + // frame pointers in the address space. + if (cast(Op.getOperand(0))->getZExtValue() > 0) + return SDValue(); + + DAG.getMachineFunction().getFrameInfo()->setFrameAddressIsTaken(true); + EVT VT = Op.getValueType(); + unsigned FP = + Subtarget->getRegisterInfo()->getFrameRegister(DAG.getMachineFunction()); + return DAG.getCopyFromReg(DAG.getEntryNode(), SDLoc(Op), FP, VT); +} + SDValue WebAssemblyTargetLowering::LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const { SDLoc DL(Op); Index: lib/Target/WebAssembly/WebAssemblyRegNumbering.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyRegNumbering.cpp +++ lib/Target/WebAssembly/WebAssemblyRegNumbering.cpp @@ -108,11 +108,11 @@ } } // Allocate locals for used physical registers - if (FrameInfo.getStackSize() > 0 || FrameInfo.adjustsStack()) { + bool HasFP = MF.getSubtarget().getFrameLowering()->hasFP(MF); + if (FrameInfo.getStackSize() > 0 || FrameInfo.adjustsStack() || HasFP) { DEBUG(dbgs() << "PReg SP " << CurReg << "\n"); MFI.addPReg(WebAssembly::SP32, CurReg++); } - bool HasFP = MF.getSubtarget().getFrameLowering()->hasFP(MF); if (HasFP) { DEBUG(dbgs() << "PReg FP " << CurReg << "\n"); MFI.addPReg(WebAssembly::FP32, CurReg++); Index: test/CodeGen/WebAssembly/userstack.ll =================================================================== --- test/CodeGen/WebAssembly/userstack.ll +++ test/CodeGen/WebAssembly/userstack.ll @@ -181,4 +181,35 @@ ret void } +declare void @use_i8_star(i8*) +declare i8* @llvm.frameaddress(i32) + +; Test __builtin_frame_address(0). +; TODO: When the prolog/epilog sequences are optimized, refine these checks to +; be more specific. + +; CHECK-LABEL: frameaddress_0: +; CHECK: __stack_pointer +; CHECK: load +; CHECK: call use_i8_star +; CHECK: __stack_pointer +; CHECK: store +define void @frameaddress_0() { + %t = call i8* @llvm.frameaddress(i32 0) + call void @use_i8_star(i8* %t) + ret void +} + +; Test __builtin_frame_address(1). + +; CHECK-LABEL: frameaddress_1: +; CHECK-NEXT: i32.const $push0=, 0{{$}} +; CHECK-NEXT: call use_i8_star@FUNCTION, $pop0{{$}} +; CHECK-NEXT: return{{$}} +define void @frameaddress_1() { + %t = call i8* @llvm.frameaddress(i32 1) + call void @use_i8_star(i8* %t) + ret void +} + ; TODO: test over-aligned alloca