Index: llvm/trunk/lib/Target/WebAssembly/CMakeLists.txt =================================================================== --- llvm/trunk/lib/Target/WebAssembly/CMakeLists.txt +++ llvm/trunk/lib/Target/WebAssembly/CMakeLists.txt @@ -20,6 +20,7 @@ WebAssemblyCFGStackify.cpp WebAssemblyCFGSort.cpp WebAssemblyLateEHPrepare.cpp + WebAssemblyEHRestoreStackPointer.cpp WebAssemblyExceptionInfo.cpp WebAssemblyExplicitLocals.cpp WebAssemblyFastISel.cpp Index: llvm/trunk/lib/Target/WebAssembly/WebAssembly.h =================================================================== --- llvm/trunk/lib/Target/WebAssembly/WebAssembly.h +++ llvm/trunk/lib/Target/WebAssembly/WebAssembly.h @@ -39,6 +39,7 @@ FunctionPass *createWebAssemblySetP2AlignOperands(); // Late passes. +FunctionPass *createWebAssemblyEHRestoreStackPointer(); FunctionPass *createWebAssemblyReplacePhysRegs(); FunctionPass *createWebAssemblyPrepareForLiveIntervals(); FunctionPass *createWebAssemblyOptimizeLiveIntervals(); @@ -63,6 +64,7 @@ void initializeOptimizeReturnedPass(PassRegistry &); void initializeWebAssemblyArgumentMovePass(PassRegistry &); void initializeWebAssemblySetP2AlignOperandsPass(PassRegistry &); +void initializeWebAssemblyEHRestoreStackPointerPass(PassRegistry &); void initializeWebAssemblyReplacePhysRegsPass(PassRegistry &); void initializeWebAssemblyPrepareForLiveIntervalsPass(PassRegistry &); void initializeWebAssemblyOptimizeLiveIntervalsPass(PassRegistry &); Index: llvm/trunk/lib/Target/WebAssembly/WebAssemblyEHRestoreStackPointer.cpp =================================================================== --- llvm/trunk/lib/Target/WebAssembly/WebAssemblyEHRestoreStackPointer.cpp +++ llvm/trunk/lib/Target/WebAssembly/WebAssemblyEHRestoreStackPointer.cpp @@ -0,0 +1,78 @@ +//===-- WebAssemblyEHRestoreStackPointer.cpp - __stack_pointer restoration ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// After the stack is unwound due to a thrown exception, the __stack_pointer +/// global can point to an invalid address. This inserts instructions that +/// restore __stack_pointer global. +/// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" +#include "WebAssembly.h" +#include "WebAssemblySubtarget.h" +#include "WebAssemblyUtilities.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/MC/MCAsmInfo.h" +using namespace llvm; + +#define DEBUG_TYPE "wasm-eh-restore-stack-pointer" + +namespace { +class WebAssemblyEHRestoreStackPointer final : public MachineFunctionPass { +public: + static char ID; // Pass identification, replacement for typeid + WebAssemblyEHRestoreStackPointer() : MachineFunctionPass(ID) {} + + StringRef getPassName() const override { + return "WebAssembly Restore Stack Pointer for Exception Handling"; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + MachineFunctionPass::getAnalysisUsage(AU); + } + + bool runOnMachineFunction(MachineFunction &MF) override; +}; +} // end anonymous namespace + +char WebAssemblyEHRestoreStackPointer::ID = 0; +INITIALIZE_PASS(WebAssemblyEHRestoreStackPointer, DEBUG_TYPE, + "Restore Stack Pointer for Exception Handling", true, false) + +FunctionPass *llvm::createWebAssemblyEHRestoreStackPointer() { + return new WebAssemblyEHRestoreStackPointer(); +} + +bool WebAssemblyEHRestoreStackPointer::runOnMachineFunction( + MachineFunction &MF) { + const auto *FrameLowering = static_cast( + MF.getSubtarget().getFrameLowering()); + if (!FrameLowering->needsPrologForEH(MF)) + return false; + bool Changed = false; + + for (auto &MBB : MF) { + if (!MBB.isEHPad()) + continue; + Changed = true; + + // Insert __stack_pointer restoring instructions at the beginning of each EH + // pad, after the catch instruction. (Catch instructions may have been + // reordered, and catch_all instructions have not been inserted yet, but + // those cases are handled in LateEHPrepare). + auto InsertPos = MBB.begin(); + if (WebAssembly::isCatch(*MBB.begin())) + InsertPos++; + FrameLowering->writeSPToGlobal(WebAssembly::SP32, MF, MBB, InsertPos, + MBB.begin()->getDebugLoc()); + } + return Changed; +} Index: llvm/trunk/lib/Target/WebAssembly/WebAssemblyFrameLowering.h =================================================================== --- llvm/trunk/lib/Target/WebAssembly/WebAssemblyFrameLowering.h +++ llvm/trunk/lib/Target/WebAssembly/WebAssemblyFrameLowering.h @@ -45,7 +45,15 @@ bool hasFP(const MachineFunction &MF) const override; bool hasReservedCallFrame(const MachineFunction &MF) const override; - private: + bool needsPrologForEH(const MachineFunction &MF) const; + + /// Write SP back to __stack_pointer global. + void writeSPToGlobal(unsigned SrcReg, MachineFunction &MF, + MachineBasicBlock &MBB, + MachineBasicBlock::iterator &InsertStore, + const DebugLoc &DL) const; + +private: bool hasBP(const MachineFunction &MF) const; bool needsSP(const MachineFunction &MF, const MachineFrameInfo &MFI) const; bool needsSPWriteback(const MachineFunction &MF, Index: llvm/trunk/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp =================================================================== --- llvm/trunk/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp +++ llvm/trunk/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp @@ -30,6 +30,7 @@ #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineModuleInfoImpls.h" #include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/MC/MCAsmInfo.h" #include "llvm/Support/Debug.h" using namespace llvm; @@ -78,13 +79,23 @@ return !MF.getFrameInfo().hasVarSizedObjects(); } +// In function with EH pads, we need to make a copy of the value of +// __stack_pointer global in SP32 register, in order to use it when restoring +// __stack_pointer after an exception is caught. +bool WebAssemblyFrameLowering::needsPrologForEH( + const MachineFunction &MF) const { + auto EHType = MF.getTarget().getMCAsmInfo()->getExceptionHandlingType(); + return EHType == ExceptionHandling::Wasm && + MF.getFunction().hasPersonalityFn() && MF.getFrameInfo().hasCalls(); +} /// Returns true if this function needs a local user-space stack pointer. /// Unlike a machine stack pointer, the wasm user stack pointer is a global /// variable, so it is loaded into a register in the prolog. bool WebAssemblyFrameLowering::needsSP(const MachineFunction &MF, const MachineFrameInfo &MFI) const { - return MFI.getStackSize() || MFI.adjustsStack() || hasFP(MF); + return MFI.getStackSize() || MFI.adjustsStack() || hasFP(MF) || + needsPrologForEH(MF); } /// Returns true if the local user-space stack pointer needs to be written back @@ -97,10 +108,9 @@ MF.getFunction().hasFnAttribute(Attribute::NoRedZone); } -static void writeSPToGlobal(unsigned SrcReg, MachineFunction &MF, - MachineBasicBlock &MBB, - MachineBasicBlock::iterator &InsertStore, - const DebugLoc &DL) { +void WebAssemblyFrameLowering::writeSPToGlobal( + unsigned SrcReg, MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator &InsertStore, const DebugLoc &DL) const { const auto *TII = MF.getSubtarget().getInstrInfo(); const char *ES = "__stack_pointer"; Index: llvm/trunk/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp =================================================================== --- llvm/trunk/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ llvm/trunk/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -58,6 +58,7 @@ initializeOptimizeReturnedPass(PR); initializeWebAssemblyArgumentMovePass(PR); initializeWebAssemblySetP2AlignOperandsPass(PR); + initializeWebAssemblyEHRestoreStackPointerPass(PR); initializeWebAssemblyReplacePhysRegsPass(PR); initializeWebAssemblyPrepareForLiveIntervalsPass(PR); initializeWebAssemblyOptimizeLiveIntervalsPass(PR); @@ -280,6 +281,9 @@ void WebAssemblyPassConfig::addPreEmitPass() { TargetPassConfig::addPreEmitPass(); + // Restore __stack_pointer global after an exception is thrown. + addPass(createWebAssemblyEHRestoreStackPointer()); + // Now that we have a prologue and epilogue and all frame indices are // rewritten, eliminate SP and FP. This allows them to be stackified, // colored, and numbered with the rest of the registers. Index: llvm/trunk/test/CodeGen/WebAssembly/exception.ll =================================================================== --- llvm/trunk/test/CodeGen/WebAssembly/exception.ll +++ llvm/trunk/test/CodeGen/WebAssembly/exception.ll @@ -19,9 +19,11 @@ } ; CHECK-LABEL: test_catch_rethrow: +; CHECK: get_global $push{{.+}}=, __stack_pointer@GLOBAL ; CHECK: try ; CHECK: call foo@FUNCTION ; CHECK: i32.catch $push{{.+}}=, 0 +; CHECK: set_global __stack_pointer@GLOBAL ; CHECK-DAG: i32.store __wasm_lpad_context ; CHECK-DAG: i32.store __wasm_lpad_context+4 ; CHECK: i32.call $push{{.+}}=, _Unwind_CallPersonality@FUNCTION @@ -63,6 +65,7 @@ ; CHECK: try ; CHECK: call foo@FUNCTION ; CHECK: catch_all +; CHECK: set_global __stack_pointer@GLOBAL ; CHECK: i32.call $push{{.+}}=, _ZN7CleanupD1Ev@FUNCTION ; CHECK: rethrow ; CHECK: end_try @@ -161,10 +164,12 @@ ; CHECK: call foo@FUNCTION ; CHECK: i32.catch ; CHECK-NOT: get_global $push{{.+}}=, __stack_pointer@GLOBAL +; CHECK: set_global __stack_pointer@GLOBAL ; CHECK: try ; CHECK: call foo@FUNCTION ; CHECK: catch_all ; CHECK-NOT: get_global $push{{.+}}=, __stack_pointer@GLOBAL +; CHECK: set_global __stack_pointer@GLOBAL ; CHECK: call __cxa_end_catch@FUNCTION ; CHECK-NOT: set_global __stack_pointer@GLOBAL, $pop{{.+}} ; CHECK: end_try