[WebAssembly] Restore __stack_pointer after catch instructions

Summary:
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.

Reviewers: jgravelle-google, dschuff

Subscribers: mgorny, sbc100, sunfish, llvm-commits

Differential Revision: https://reviews.llvm.org/D50980

llvm-svn: 340339
This commit is contained in:
Heejin Ahn 2018-08-21 21:23:07 +00:00
parent 1d78503f6a
commit 78d1910891
7 changed files with 114 additions and 6 deletions

View File

@ -20,6 +20,7 @@ add_llvm_target(WebAssemblyCodeGen
WebAssemblyCFGStackify.cpp
WebAssemblyCFGSort.cpp
WebAssemblyLateEHPrepare.cpp
WebAssemblyEHRestoreStackPointer.cpp
WebAssemblyExceptionInfo.cpp
WebAssemblyExplicitLocals.cpp
WebAssemblyFastISel.cpp

View File

@ -39,6 +39,7 @@ FunctionPass *createWebAssemblyArgumentMove();
FunctionPass *createWebAssemblySetP2AlignOperands();
// Late passes.
FunctionPass *createWebAssemblyEHRestoreStackPointer();
FunctionPass *createWebAssemblyReplacePhysRegs();
FunctionPass *createWebAssemblyPrepareForLiveIntervals();
FunctionPass *createWebAssemblyOptimizeLiveIntervals();
@ -63,6 +64,7 @@ void initializeFixFunctionBitcastsPass(PassRegistry &);
void initializeOptimizeReturnedPass(PassRegistry &);
void initializeWebAssemblyArgumentMovePass(PassRegistry &);
void initializeWebAssemblySetP2AlignOperandsPass(PassRegistry &);
void initializeWebAssemblyEHRestoreStackPointerPass(PassRegistry &);
void initializeWebAssemblyReplacePhysRegsPass(PassRegistry &);
void initializeWebAssemblyPrepareForLiveIntervalsPass(PassRegistry &);
void initializeWebAssemblyOptimizeLiveIntervalsPass(PassRegistry &);

View File

@ -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<const WebAssemblyFrameLowering *>(
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;
}

View File

@ -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 @@ bool WebAssemblyFrameLowering::hasReservedCallFrame(
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 @@ bool WebAssemblyFrameLowering::needsSPWriteback(
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<WebAssemblySubtarget>().getInstrInfo();
const char *ES = "__stack_pointer";

View File

@ -45,7 +45,15 @@ class WebAssemblyFrameLowering final : public TargetFrameLowering {
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,

View File

@ -58,6 +58,7 @@ extern "C" void LLVMInitializeWebAssemblyTarget() {
initializeOptimizeReturnedPass(PR);
initializeWebAssemblyArgumentMovePass(PR);
initializeWebAssemblySetP2AlignOperandsPass(PR);
initializeWebAssemblyEHRestoreStackPointerPass(PR);
initializeWebAssemblyReplacePhysRegsPass(PR);
initializeWebAssemblyPrepareForLiveIntervalsPass(PR);
initializeWebAssemblyOptimizeLiveIntervalsPass(PR);
@ -280,6 +281,9 @@ void WebAssemblyPassConfig::addPostRegAlloc() {
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.

View File

@ -19,9 +19,11 @@ define void @test_throw() {
}
; 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 @@ try.cont: ; preds = %entry, %catch
; 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 @@ terminate10: ; preds = %ehcleanup7
; 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