mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-23 03:02:36 +01:00
[WebAssembly] Remove unnecessary instructions after TRY marker placement
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
This commit is contained in:
parent
50ed82a69f
commit
090e7d58b6
@ -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);
|
||||
|
||||
@ -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
|
||||
@ -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.
|
||||
@ -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) {
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
|
||||
|
@ -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()
|
||||
@ -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()
|
||||
|
@ -37,29 +37,26 @@ define void @test_rethrow(i8* %p) {
|
||||
|
||||
; CHECK-LABEL: test_catch:
|
||||
; CHECK: global.get ${{.+}}=, __stack_pointer
|
||||
; CHECK: block
|
||||
; CHECK: try
|
||||
; CHECK: call foo
|
||||
; CHECK: br 0
|
||||
; CHECK: catch $[[EXCEPT_REF:[0-9]+]]=
|
||||
; CHECK: global.set __stack_pointer
|
||||
; CHECK: block i32
|
||||
; CHECK: br_on_exn 0, __cpp_exception, $[[EXCEPT_REF]]
|
||||
; CHECK: rethrow
|
||||
; CHECK: end_block
|
||||
; CHECK: extract_exception $[[EXN:[0-9]+]]=
|
||||
; CHECK-DAG: i32.store __wasm_lpad_context
|
||||
; CHECK-DAG: i32.store __wasm_lpad_context+4
|
||||
; CHECK: i32.call $drop=, _Unwind_CallPersonality, $[[EXN]]
|
||||
; CHECK: block
|
||||
; CHECK: br_if 0
|
||||
; CHECK: i32.call $drop=, __cxa_begin_catch
|
||||
; CHECK: call __cxa_end_catch
|
||||
; CHECK: br 1
|
||||
; CHECK: end_block
|
||||
; CHECK: call __cxa_rethrow
|
||||
; CHECK: end_try
|
||||
; CHECK: end_block
|
||||
; CHECK: try
|
||||
; CHECK: call foo
|
||||
; CHECK: catch $[[EXCEPT_REF:[0-9]+]]=
|
||||
; CHECK: global.set __stack_pointer
|
||||
; CHECK: block i32
|
||||
; CHECK: br_on_exn 0, __cpp_exception, $[[EXCEPT_REF]]
|
||||
; CHECK: rethrow
|
||||
; CHECK: end_block
|
||||
; CHECK: extract_exception $[[EXN:[0-9]+]]=
|
||||
; CHECK-DAG: i32.store __wasm_lpad_context
|
||||
; CHECK-DAG: i32.store __wasm_lpad_context+4
|
||||
; CHECK: i32.call $drop=, _Unwind_CallPersonality, $[[EXN]]
|
||||
; CHECK: block
|
||||
; CHECK: br_if 0
|
||||
; CHECK: i32.call $drop=, __cxa_begin_catch
|
||||
; CHECK: call __cxa_end_catch
|
||||
; CHECK: br 1
|
||||
; CHECK: end_block
|
||||
; CHECK: call __cxa_rethrow
|
||||
; CHECK: end_try
|
||||
define void @test_catch() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
|
||||
entry:
|
||||
invoke void @foo()
|
||||
@ -101,16 +98,13 @@ try.cont: ; preds = %entry, %catch
|
||||
; }
|
||||
|
||||
; CHECK-LABEL: test_cleanup:
|
||||
; CHECK: block
|
||||
; CHECK: try
|
||||
; CHECK: call foo
|
||||
; CHECK: br 0
|
||||
; CHECK: catch
|
||||
; CHECK: global.set __stack_pointer
|
||||
; CHECK: i32.call $drop=, _ZN4TempD2Ev
|
||||
; CHECK: rethrow
|
||||
; CHECK: end_try
|
||||
; CHECK: end_block
|
||||
; CHECK: try
|
||||
; CHECK: call foo
|
||||
; CHECK: catch
|
||||
; CHECK: global.set __stack_pointer
|
||||
; CHECK: i32.call $drop=, _ZN4TempD2Ev
|
||||
; CHECK: rethrow
|
||||
; CHECK: end_try
|
||||
define void @test_cleanup() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
|
||||
entry:
|
||||
%t = alloca %struct.Temp, align 1
|
||||
@ -140,37 +134,28 @@ ehcleanup: ; preds = %entry
|
||||
; }
|
||||
|
||||
; CHECK-LABEL: test_terminatepad
|
||||
; CHECK: block
|
||||
; CHECK: try
|
||||
; CHECK: call foo
|
||||
; CHECK: catch
|
||||
; CHECK: i32.call $drop=, __cxa_begin_catch
|
||||
; CHECK: try
|
||||
; CHECK: call foo
|
||||
; CHECK: br 0
|
||||
; CHECK: catch
|
||||
; CHECK: i32.call $drop=, __cxa_begin_catch
|
||||
; CHECK: block
|
||||
; CHECK: try
|
||||
; CHECK: call foo
|
||||
; CHECK: br 0
|
||||
; CHECK: catch
|
||||
; CHECK: block
|
||||
; CHECK: try
|
||||
; CHECK: call __cxa_end_catch
|
||||
; CHECK: br 0
|
||||
; CHECK: catch
|
||||
; CHECK: block i32
|
||||
; CHECK: br_on_exn 0, __cpp_exception
|
||||
; CHECK: call __clang_call_terminate, 0
|
||||
; CHECK: unreachable
|
||||
; CHECK: end_block
|
||||
; CHECK: call __clang_call_terminate
|
||||
; CHECK: unreachable
|
||||
; CHECK: end_try
|
||||
; CHECK: end_block
|
||||
; CHECK: rethrow
|
||||
; CHECK: end_try
|
||||
; CHECK: end_block
|
||||
; CHECK: call __cxa_end_catch
|
||||
; CHECK: try
|
||||
; CHECK: call __cxa_end_catch
|
||||
; CHECK: catch
|
||||
; CHECK: block i32
|
||||
; CHECK: br_on_exn 0, __cpp_exception
|
||||
; CHECK: call __clang_call_terminate, 0
|
||||
; CHECK: unreachable
|
||||
; CHECK: end_block
|
||||
; CHECK: call __clang_call_terminate
|
||||
; CHECK: unreachable
|
||||
; CHECK: end_try
|
||||
; CHECK: rethrow
|
||||
; CHECK: end_try
|
||||
; CHECK: end_block
|
||||
; CHECK: call __cxa_end_catch
|
||||
; CHECK: end_try
|
||||
define void @test_terminatepad() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
|
||||
entry:
|
||||
invoke void @foo()
|
||||
@ -227,34 +212,30 @@ terminate: ; preds = %ehcleanup
|
||||
; }
|
||||
|
||||
; CHECK-LABEL: test_no_prolog_epilog_in_ehpad
|
||||
; CHECK: block
|
||||
; CHECK: try
|
||||
; CHECK: call foo
|
||||
; CHECK: br 0
|
||||
; CHECK: catch
|
||||
; CHECK-NOT: global.get $push{{.+}}=, __stack_pointer
|
||||
; CHECK: global.set __stack_pointer
|
||||
; CHECK: try
|
||||
; CHECK: call foo
|
||||
; CHECK: catch
|
||||
; CHECK-NOT: global.get $push{{.+}}=, __stack_pointer
|
||||
; CHECK: global.set __stack_pointer
|
||||
; CHECK: block
|
||||
; CHECK: block
|
||||
; CHECK: block
|
||||
; CHECK: br_if 0
|
||||
; CHECK: i32.call $drop=, __cxa_begin_catch
|
||||
; CHECK: try
|
||||
; CHECK: call foo
|
||||
; CHECK: br 2
|
||||
; CHECK: catch
|
||||
; CHECK-NOT: global.get $push{{.+}}=, __stack_pointer
|
||||
; CHECK: global.set __stack_pointer
|
||||
; CHECK: call __cxa_end_catch
|
||||
; CHECK: rethrow
|
||||
; CHECK-NOT: global.set __stack_pointer, $pop{{.+}}
|
||||
; CHECK: end_try
|
||||
; CHECK: end_block
|
||||
; CHECK: call __cxa_rethrow
|
||||
; CHECK: br_if 0
|
||||
; CHECK: i32.call $drop=, __cxa_begin_catch
|
||||
; CHECK: try
|
||||
; CHECK: call foo
|
||||
; CHECK: catch
|
||||
; CHECK-NOT: global.get $push{{.+}}=, __stack_pointer
|
||||
; CHECK: global.set __stack_pointer
|
||||
; CHECK: call __cxa_end_catch
|
||||
; CHECK: rethrow
|
||||
; CHECK-NOT: global.set __stack_pointer, $pop{{.+}}
|
||||
; CHECK: end_try
|
||||
; CHECK: end_block
|
||||
; CHECK-NOT: global.set __stack_pointer, $pop{{.+}}
|
||||
; CHECK: call __cxa_end_catch
|
||||
; CHECK: end_try
|
||||
; CHECK: end_block
|
||||
; CHECK: call __cxa_rethrow
|
||||
; CHECK: end_block
|
||||
; CHECK-NOT: global.set __stack_pointer, $pop{{.+}}
|
||||
; CHECK: call __cxa_end_catch
|
||||
; CHECK: end_try
|
||||
define void @test_no_prolog_epilog_in_ehpad() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
|
||||
entry:
|
||||
%stack_var = alloca i32, align 4
|
||||
@ -309,15 +290,12 @@ ehcleanup: ; preds = %catch
|
||||
; }
|
||||
|
||||
; CHECK-LABEL: test_no_sp_writeback
|
||||
; CHECK: block
|
||||
; CHECK: try
|
||||
; CHECK: call foo
|
||||
; CHECK: br 0
|
||||
; CHECK: catch
|
||||
; CHECK: i32.call $drop=, __cxa_begin_catch
|
||||
; CHECK: call __cxa_end_catch
|
||||
; CHECK: end_try
|
||||
; CHECK: end_block
|
||||
; CHECK: try
|
||||
; CHECK: call foo
|
||||
; CHECK: catch
|
||||
; CHECK: i32.call $drop=, __cxa_begin_catch
|
||||
; CHECK: call __cxa_end_catch
|
||||
; CHECK: end_try
|
||||
; CHECK-NOT: global.set __stack_pointer
|
||||
; CHECK: return
|
||||
define void @test_no_sp_writeback() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
|
||||
|
Loading…
Reference in New Issue
Block a user