Skip to content

Add Wasm peephole optimisation to remove dead code around unreachables #66062

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lld/test/wasm/init-fini.ll
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ entry:
; CHECK-NEXT: Body: 10031004100A100F1012100F10141003100C100F10161001100E0B
; CHECK: - Index: 22
; CHECK-NEXT: Locals:
; CHECK-NEXT: Body: 02404186808080004100418088808000108780808000450D0000000B0B
; CHECK-NEXT: Body: 02404186808080004100418088808000108780808000450D00000B0B
; CHECK-NEXT: - Type: CUSTOM
; CHECK-NEXT: Name: name
; CHECK-NEXT: FunctionNames:
Expand Down
16 changes: 16 additions & 0 deletions llvm/lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,25 @@ bool WebAssemblyDebugFixup::runOnMachineFunction(MachineFunction &MF) {
// it will be culled later.
}
} else {

// There is a WebAssembly peephole optimisation can remove drop
// instructions before a wasm unreachable. This is a valid
// transformation because unreachable is "stack polymorphic", but stack
// polymorphism is not modeled in the llvm wasm backend.
// In current codegen, virtual registers can only be stackified within
// a basic block, and cannot be stackified across an UNREACHABLE
// because it is marked as having unmodeled side effects.
// So we can assume the operand stack is empty after an UNREACHABLE.
if (MI.getOpcode() == WebAssembly::UNREACHABLE) {
Stack.clear();
break;
}

// Track stack depth.
for (MachineOperand &MO : reverse(MI.explicit_uses())) {
if (MO.isReg() && MFI.isVRegStackified(MO.getReg())) {
assert(!Stack.empty() &&
"WebAssemblyDebugFixup: Pop: Operand stack empty!");
auto Prev = Stack.back();
Stack.pop_back();
assert(Prev.Reg == MO.getReg() &&
Expand Down
51 changes: 51 additions & 0 deletions llvm/lib/Target/WebAssembly/WebAssemblyPeephole.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include <iterator>
using namespace llvm;

#define DEBUG_TYPE "wasm-peephole"
Expand Down Expand Up @@ -109,6 +110,53 @@ static bool maybeRewriteToFallthrough(MachineInstr &MI, MachineBasicBlock &MBB,
return true;
}

static bool eraseDeadCodeAroundUnreachable(MachineInstr &UnreachbleMI,
MachineBasicBlock &MBB) {
SmallVector<MachineInstr *, 16> ToDelete;

// Because wasm unreachable is stack polymorphic and unconditionally ends
// control, all instructions after it can be removed until the end of this
// block. We remove the common case of double unreachable.
auto ForwardsIterator = UnreachbleMI.getIterator();
for (ForwardsIterator++; !ForwardsIterator.isEnd(); ForwardsIterator++) {
MachineInstr &MI = *ForwardsIterator;
if (MI.getOpcode() == WebAssembly::UNREACHABLE) {
ToDelete.push_back(&MI);
} else {
break;
}
}

// For the same reasons as above, previous instructions that only affect
// local function state can be removed (e.g. local.set, drop, various reads).
// We remove the common case of "drop unreachable".
auto BackwardsIterator = UnreachbleMI.getReverseIterator();
for (BackwardsIterator++; !BackwardsIterator.isEnd(); BackwardsIterator++) {
MachineInstr &MI = *BackwardsIterator;
switch (MI.getOpcode()) {
case WebAssembly::DROP_I32:
case WebAssembly::DROP_I64:
case WebAssembly::DROP_F32:
case WebAssembly::DROP_F64:
case WebAssembly::DROP_EXTERNREF:
case WebAssembly::DROP_FUNCREF:
case WebAssembly::DROP_V128:
ToDelete.push_back(&MI);
continue;
default:
goto exit_loop;
}
}
exit_loop:;

bool Changed = false;
for (MachineInstr *MI : ToDelete) {
MI->eraseFromParent();
Changed = true;
}
return Changed;
}

bool WebAssemblyPeephole::runOnMachineFunction(MachineFunction &MF) {
LLVM_DEBUG({
dbgs() << "********** Peephole **********\n"
Expand Down Expand Up @@ -159,6 +207,9 @@ bool WebAssemblyPeephole::runOnMachineFunction(MachineFunction &MF) {
case WebAssembly::RETURN:
Changed |= maybeRewriteToFallthrough(MI, MBB, MF, MFI, MRI, TII);
break;
case WebAssembly::UNREACHABLE:
Changed |= eraseDeadCodeAroundUnreachable(MI, MBB);
break;
}

return Changed;
Expand Down
25 changes: 19 additions & 6 deletions llvm/test/CodeGen/WebAssembly/unreachable.ll
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ define void @trap_unreacheable() {
; CHECK: .functype trap_unreacheable () -> ()
; CHECK-NEXT: # %bb.0:
; CHECK-NEXT: unreachable
; CHECK-NEXT: unreachable
; CHECK-NEXT: end_function
call void @llvm.trap()
unreachable
Expand All @@ -80,11 +79,10 @@ define i32 @missing_ret_unreachable() {
unreachable
}

; This is similar to the above test, but ensures wasm unreachable is emitted
; This is similar to the above test, but the callee has a 'noreturn' attribute.
; There is an optimization that removes an 'unreachable' after a noreturn call,
; but Wasm backend doesn't use it and ignore `--no-trap-after-noreturn`, if
; given, to generate valid code.
; This is similar to the above test, but the callee has a 'noreturn' attribute.
; There is an optimization that removes an 'unreachable' after a noreturn call,
; but Wasm backend doesn't use it and ignore `--no-trap-after-noreturn`, if
; given, to generate valid code.
define i32 @missing_ret_noreturn_unreachable() {
; CHECK-LABEL: missing_ret_noreturn_unreachable:
; CHECK: .functype missing_ret_noreturn_unreachable () -> (i32)
Expand All @@ -95,3 +93,18 @@ define i32 @missing_ret_noreturn_unreachable() {
call void @ext_never_return()
unreachable
}

; This is a test for the wasm peephole optimization that erases unnecessary
; code before and after a wasm unreachable instruction.
; It creates an unused stack variable by calling ext_func_i32() that would
; otherwise require a drop instruction before the wasm unreachable instruction.
define i64 @drop_unreachable() {
; CHECK-LABEL: drop_unreachable:
; CHECK: .functype drop_unreachable () -> (i64)
; CHECK-NEXT: # %bb.0:
; CHECK-NEXT: call ext_func_i32
; CHECK-NEXT: unreachable
; CHECK-NEXT: end_function
call i32 @ext_func_i32()
unreachable
}
12 changes: 6 additions & 6 deletions llvm/test/MC/WebAssembly/global-ctor-dtor.ll
Original file line number Diff line number Diff line change
Expand Up @@ -80,29 +80,29 @@ declare void @func3()
; CHECK-NEXT: Offset: 0x1D
; CHECK-NEXT: - Type: R_WASM_FUNCTION_INDEX_LEB
; CHECK-NEXT: Index: 6
; CHECK-NEXT: Offset: 0x2C
; CHECK-NEXT: Offset: 0x2B
; CHECK-NEXT: - Type: R_WASM_TABLE_INDEX_SLEB
; CHECK-NEXT: Index: 5
; CHECK-NEXT: Offset: 0x37
; CHECK-NEXT: Offset: 0x36
; CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_SLEB
; CHECK-NEXT: Index: 3
; CHECK-NEXT: Offset: 0x3F
; CHECK-NEXT: Offset: 0x3E
; CHECK-NEXT: - Type: R_WASM_FUNCTION_INDEX_LEB
; CHECK-NEXT: Index: 4
; CHECK-NEXT: Offset: 0x45
; CHECK-NEXT: Offset: 0x44
; CHECK-NEXT: Functions:
; CHECK-NEXT: - Index: 5
; CHECK-NEXT: Locals:
; CHECK-NEXT: Body: 1080808080000B
; CHECK-NEXT: - Index: 6
; CHECK-NEXT: Locals:
; CHECK-NEXT: Body: 02404181808080004100418080808000108180808000450D0000000B0B
; CHECK-NEXT: Body: 02404181808080004100418080808000108180808000450D00000B0B
; CHECK-NEXT: - Index: 7
; CHECK-NEXT: Locals:
; CHECK-NEXT: Body: 1082808080000B
; CHECK-NEXT: - Index: 8
; CHECK-NEXT: Locals:
; CHECK-NEXT: Body: 02404182808080004100418080808000108180808000450D0000000B0B
; CHECK-NEXT: Body: 02404182808080004100418080808000108180808000450D00000B0B
; CHECK-NEXT: - Type: DATA
; CHECK-NEXT: Segments:
; CHECK-NEXT: - SectionOffset: 6
Expand Down