Skip to content

Commit

Permalink
[WebAssembly] Remove unnecessary instructions after TRY marker placement
Browse files Browse the repository at this point in the history
Summary:
This removes unnecessary instructions after TRY marker placement. There
are two cases:
- `end`/`end_block` can be removed if they overlap with `try`/`end_try`
  and they have the same return types.
- `br` right before `catch` that branches to after `end_try` can be
  deleted.

Reviewers: dschuff

Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits

Tags: #llvm

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

llvm-svn: 354939
  • Loading branch information
aheejin committed Feb 27, 2019
1 parent 6d6288a commit cf699b4
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 146 deletions.
96 changes: 94 additions & 2 deletions llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class WebAssemblyCFGStackify final : public MachineFunctionPass {
void placeBlockMarker(MachineBasicBlock &MBB);
void placeLoopMarker(MachineBasicBlock &MBB);
void placeTryMarker(MachineBasicBlock &MBB);
void removeUnnecessaryInstrs(MachineFunction &MF);
void rewriteDepthImmediates(MachineFunction &MF);
void fixEndsAtEndOfFunction(MachineFunction &MF);

Expand All @@ -76,11 +77,12 @@ class WebAssemblyCFGStackify final : public MachineFunctionPass {
// <EH pad, TRY marker> map
DenseMap<const MachineBasicBlock *, MachineInstr *> EHPadToTry;

// Helper functions to register scope information created by marker
// instructions.
// Helper functions to register / unregister scope information created by
// marker instructions.
void registerScope(MachineInstr *Begin, MachineInstr *End);
void registerTryScope(MachineInstr *Begin, MachineInstr *End,
MachineBasicBlock *EHPad);
void unregisterScope(MachineInstr *Begin);

public:
static char ID; // Pass identification, replacement for typeid
Expand Down Expand Up @@ -175,6 +177,20 @@ void WebAssemblyCFGStackify::registerTryScope(MachineInstr *Begin,
EHPadToTry[EHPad] = Begin;
}

void WebAssemblyCFGStackify::unregisterScope(MachineInstr *Begin) {
assert(BeginToEnd.count(Begin));
MachineInstr *End = BeginToEnd[Begin];
assert(EndToBegin.count(End));
BeginToEnd.erase(Begin);
EndToBegin.erase(End);
MachineBasicBlock *EHPad = TryToEHPad.lookup(Begin);
if (EHPad) {
assert(EHPadToTry.count(EHPad));
TryToEHPad.erase(Begin);
EHPadToTry.erase(EHPad);
}
}

/// Insert a BLOCK marker for branches to MBB (if needed).
void WebAssemblyCFGStackify::placeBlockMarker(MachineBasicBlock &MBB) {
// This should have been handled in placeTryMarker.
Expand Down Expand Up @@ -585,6 +601,76 @@ void WebAssemblyCFGStackify::placeTryMarker(MachineBasicBlock &MBB) {
ScopeTops[Number] = Header;
}

void WebAssemblyCFGStackify::removeUnnecessaryInstrs(MachineFunction &MF) {
const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();

// When there is an unconditional branch right before a catch instruction and
// it branches to the end of end_try marker, we don't need the branch, because
// it there is no exception, the control flow transfers to that point anyway.
// bb0:
// try
// ...
// br bb2 <- Not necessary
// bb1:
// catch
// ...
// bb2:
// end
for (auto &MBB : MF) {
if (!MBB.isEHPad())
continue;

MachineBasicBlock *TBB = nullptr, *FBB = nullptr;
SmallVector<MachineOperand, 4> Cond;
MachineBasicBlock *EHPadLayoutPred =
&*std::prev(MachineFunction::iterator(&MBB));
MachineBasicBlock *Cont = BeginToEnd[EHPadToTry[&MBB]]->getParent();
bool Analyzable = !TII.analyzeBranch(*EHPadLayoutPred, TBB, FBB, Cond);
if (Analyzable && ((Cond.empty() && TBB && TBB == Cont) ||
(!Cond.empty() && FBB && FBB == Cont)))
TII.removeBranch(*EHPadLayoutPred);
}

// When there are block / end_block markers that overlap with try / end_try
// markers, and the block and try markers' return types are the same, the
// block /end_block markers are not necessary, because try / end_try markers
// also can serve as boundaries for branches.
// block <- Not necessary
// try
// ...
// catch
// ...
// end
// end <- Not necessary
SmallVector<MachineInstr *, 32> ToDelete;
for (auto &MBB : MF) {
for (auto &MI : MBB) {
if (MI.getOpcode() != WebAssembly::TRY)
continue;

MachineInstr *Try = &MI, *EndTry = BeginToEnd[Try];
MachineBasicBlock *TryBB = Try->getParent();
MachineBasicBlock *Cont = EndTry->getParent();
int64_t RetType = Try->getOperand(0).getImm();
for (auto B = MachineBasicBlock::iterator(Try),
E = std::next(MachineBasicBlock::iterator(EndTry));
B != TryBB->begin() && E != Cont->end() &&
std::prev(B)->getOpcode() == WebAssembly::BLOCK &&
E->getOpcode() == WebAssembly::END_BLOCK &&
std::prev(B)->getOperand(0).getImm() == RetType;
--B, ++E) {
ToDelete.push_back(&*std::prev(B));
ToDelete.push_back(&*E);
}
}
}
for (auto *MI : ToDelete) {
if (MI->getOpcode() == WebAssembly::BLOCK)
unregisterScope(MI);
MI->eraseFromParent();
}
}

static unsigned
getDepth(const SmallVectorImpl<const MachineBasicBlock *> &Stack,
const MachineBasicBlock *MBB) {
Expand Down Expand Up @@ -747,6 +833,7 @@ bool WebAssemblyCFGStackify::runOnMachineFunction(MachineFunction &MF) {
LLVM_DEBUG(dbgs() << "********** CFG Stackifying **********\n"
"********** Function: "
<< MF.getName() << '\n');
const MCAsmInfo *MCAI = MF.getTarget().getMCAsmInfo();

releaseMemory();

Expand All @@ -756,6 +843,11 @@ bool WebAssemblyCFGStackify::runOnMachineFunction(MachineFunction &MF) {
// Place the BLOCK/LOOP/TRY markers to indicate the beginnings of scopes.
placeMarkers(MF);

if (MCAI->getExceptionHandlingType() == ExceptionHandling::Wasm &&
MF.getFunction().hasPersonalityFn())
// Remove unnecessary instructions.
removeUnnecessaryInstrs(MF);

// Convert MBB operands in terminators to relative depth immediates.
rewriteDepthImmediates(MF);

Expand Down
89 changes: 40 additions & 49 deletions llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,23 @@ target triple = "wasm32-unknown-unknown"
; }

; CHECK-LABEL: test0
; CHECK: block
; CHECK: try
; CHECK: call foo
; CHECK: br 0 # 0: down to label1
; CHECK: catch
; CHECK: block
; CHECK: br_if 0, {{.*}} # 0: down to label3
; CHECK: i32.call $drop=, __cxa_begin_catch
; CHECK: call __cxa_end_catch
; CHECK: br 1 # 1: down to label1
; CHECK: end_block # label3:
; CHECK: block
; CHECK: br_if 0, {{.*}} # 0: down to label4
; CHECK: i32.call $drop=, __cxa_begin_catch
; CHECK: call __cxa_end_catch
; CHECK: br 1 # 1: down to label1
; CHECK: end_block # label4:
; CHECK: call __cxa_rethrow
; CHECK: end_try # label1:
; CHECK: end_block
; CHECK: try
; CHECK: call foo
; CHECK: catch
; CHECK: block
; CHECK: br_if 0, {{.*}} # 0: down to label2
; CHECK: i32.call $drop=, __cxa_begin_catch
; CHECK: call __cxa_end_catch
; CHECK: br 1 # 1: down to label0
; CHECK: end_block # label2:
; CHECK: block
; CHECK: br_if 0, {{.*}} # 0: down to label3
; CHECK: i32.call $drop=, __cxa_begin_catch
; CHECK: call __cxa_end_catch
; CHECK: br 1 # 1: down to label0
; CHECK: end_block # label3:
; CHECK: call __cxa_rethrow
; CHECK: end_try # label0:
define void @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
entry:
invoke void @foo()
Expand Down Expand Up @@ -177,39 +174,33 @@ unreachable: ; preds = %rethrow5
; }

; CHECK-LABEL: test2
; CHECK: block
; CHECK: try
; CHECK: call foo
; CHECK: br 0 # 0: down to label17
; CHECK: catch
; CHECK: i32.call $drop=, __cxa_begin_catch
; CHECK: loop # label19:
; CHECK: try
; CHECK: call foo
; CHECK: catch
; CHECK: i32.call $drop=, __cxa_begin_catch
; CHECK: loop # label15:
; CHECK: block
; CHECK: block
; CHECK: block
; CHECK: br_if 0, {{.*}} # 0: down to label21
; CHECK: br_if 0, {{.*}} # 0: down to label17
; CHECK: try
; CHECK: call foo
; CHECK: br 2 # 2: down to label16
; CHECK: catch
; CHECK: try
; CHECK: call foo
; CHECK: br 2 # 2: down to label20
; CHECK: call __cxa_end_catch
; CHECK: catch
; CHECK: block
; CHECK: try
; CHECK: call __cxa_end_catch
; CHECK: br 0 # 0: down to label24
; CHECK: catch
; CHECK: call __clang_call_terminate
; CHECK: unreachable
; CHECK: end_try # label24:
; CHECK: end_block
; CHECK: rethrow # to caller
; CHECK: call __clang_call_terminate
; CHECK: unreachable
; CHECK: end_try
; CHECK: end_block # label21:
; CHECK: call __cxa_end_catch
; CHECK: br 2 # 2: down to label17
; CHECK: end_block # label20:
; CHECK: br 0 # 0: up to label19
; CHECK: end_loop
; CHECK: end_try # label17:
; CHECK: end_block
; CHECK: rethrow # to caller
; CHECK: end_try
; CHECK: end_block # label17:
; CHECK: call __cxa_end_catch
; CHECK: br 2 # 2: down to label13
; CHECK: end_block # label16:
; CHECK: br 0 # 0: up to label15
; CHECK: end_loop
; CHECK: end_try # label13:
define void @test2() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
entry:
invoke void @foo()
Expand Down
Loading

0 comments on commit cf699b4

Please sign in to comment.