Index: lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp +++ lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp @@ -64,6 +64,7 @@ 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 @@ // map DenseMap 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 @@ 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 @@ ScopeTops[Number] = Header; } +void WebAssemblyCFGStackify::removeUnnecessaryInstrs(MachineFunction &MF) { + const auto &TII = *MF.getSubtarget().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 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 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) { + MI->eraseFromParent(); + if (MI->getOpcode() == WebAssembly::BLOCK) + unregisterScope(MI); + } +} + static unsigned getDepth(const SmallVectorImpl &Stack, const MachineBasicBlock *MBB) { @@ -747,6 +833,7 @@ LLVM_DEBUG(dbgs() << "********** CFG Stackifying **********\n" "********** Function: " << MF.getName() << '\n'); + const MCAsmInfo *MCAI = MF.getTarget().getMCAsmInfo(); releaseMemory(); @@ -756,6 +843,14 @@ // Place the BLOCK/LOOP/TRY markers to indicate the beginnings of scopes. placeMarkers(MF); + // Remove unnecessary instructions to reduce code size. + removeUnnecessaryInstrs(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); Index: test/CodeGen/WebAssembly/cfg-stackify-eh.ll =================================================================== --- test/CodeGen/WebAssembly/cfg-stackify-eh.ll +++ test/CodeGen/WebAssembly/cfg-stackify-eh.ll @@ -18,26 +18,23 @@ ; } ; CHECK-LABEL: test0 -; CHECK: block -; CHECK: try -; CHECK: call foo -; CHECK: br 0 -; CHECK: catch -; 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: 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 +; 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: 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 @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { entry: invoke void @foo() Index: test/CodeGen/WebAssembly/exception.ll =================================================================== --- test/CodeGen/WebAssembly/exception.ll +++ test/CodeGen/WebAssembly/exception.ll @@ -37,29 +37,26 @@ ; 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 @@ ; } ; 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 @@ ; } ; 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 @@ ; } ; 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 @@ ; } ; 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*) {