From 98be8be18338f94cfa897359f24ecadcf0e9d047 Mon Sep 17 00:00:00 2001 From: Matt Harding Date: Mon, 9 Oct 2023 10:29:50 +0100 Subject: [PATCH 1/4] Add test for peephole optimization --- llvm/test/CodeGen/WebAssembly/unreachable.ll | 25 ++++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/llvm/test/CodeGen/WebAssembly/unreachable.ll b/llvm/test/CodeGen/WebAssembly/unreachable.ll index 72f865842bdca..fc2d8e2439014 100644 --- a/llvm/test/CodeGen/WebAssembly/unreachable.ll +++ b/llvm/test/CodeGen/WebAssembly/unreachable.ll @@ -80,11 +80,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) @@ -95,3 +94,19 @@ 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: drop +; CHECK-NEXT: unreachable +; CHECK-NEXT: end_function + call i32 @ext_func_i32() + unreachable +} From e3eb414aeae582981435b4adc49a91e60b191981 Mon Sep 17 00:00:00 2001 From: Matt Harding Date: Mon, 9 Oct 2023 10:32:13 +0100 Subject: [PATCH 2/4] Add dead code removal peephole optimization --- lld/test/wasm/init-fini.ll | 2 +- .../WebAssembly/WebAssemblyDebugFixup.cpp | 14 +++++ .../WebAssembly/WebAssemblyPeephole.cpp | 51 +++++++++++++++++++ llvm/test/CodeGen/WebAssembly/unreachable.ll | 2 - llvm/test/MC/WebAssembly/global-ctor-dtor.ll | 12 ++--- 5 files changed, 72 insertions(+), 9 deletions(-) diff --git a/lld/test/wasm/init-fini.ll b/lld/test/wasm/init-fini.ll index 14385f042efb7..35286bbcccc2f 100644 --- a/lld/test/wasm/init-fini.ll +++ b/lld/test/wasm/init-fini.ll @@ -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: diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp index 4a75bab6b95dd..0fbfeb3271ad3 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp @@ -122,9 +122,23 @@ bool WebAssemblyDebugFixup::runOnMachineFunction(MachineFunction &MF) { // it will be culled later. } } else { + + // There is a WebAssembly Peephole optimisation can remove instructions before and after + // 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, unreachable can only appear at the end of a basic block, and the stack is empty + // at the end of every basic block. + // So we can assume unreachable has stack-type [t*] -> [], and simply clear the stack. + 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() && diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyPeephole.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyPeephole.cpp index 6e2d566d9b486..4237a6d39e269 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyPeephole.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyPeephole.cpp @@ -20,6 +20,7 @@ #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineRegisterInfo.h" +#include using namespace llvm; #define DEBUG_TYPE "wasm-peephole" @@ -109,6 +110,53 @@ static bool maybeRewriteToFallthrough(MachineInstr &MI, MachineBasicBlock &MBB, return true; } +static bool eraseDeadCodeAroundUnreachable(MachineInstr &UnreachbleMI, + MachineBasicBlock &MBB) { + SmallVector 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" @@ -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; diff --git a/llvm/test/CodeGen/WebAssembly/unreachable.ll b/llvm/test/CodeGen/WebAssembly/unreachable.ll index fc2d8e2439014..9354a0cd14dc1 100644 --- a/llvm/test/CodeGen/WebAssembly/unreachable.ll +++ b/llvm/test/CodeGen/WebAssembly/unreachable.ll @@ -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 @@ -104,7 +103,6 @@ define i64 @drop_unreachable() { ; CHECK: .functype drop_unreachable () -> (i64) ; CHECK-NEXT: # %bb.0: ; CHECK-NEXT: call ext_func_i32 -; CHECK-NEXT: drop ; CHECK-NEXT: unreachable ; CHECK-NEXT: end_function call i32 @ext_func_i32() diff --git a/llvm/test/MC/WebAssembly/global-ctor-dtor.ll b/llvm/test/MC/WebAssembly/global-ctor-dtor.ll index bc1be79313496..f1ec71da1ebb6 100644 --- a/llvm/test/MC/WebAssembly/global-ctor-dtor.ll +++ b/llvm/test/MC/WebAssembly/global-ctor-dtor.ll @@ -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 From 966d466aac74bedd1ade652d3edb2dd99f5f7011 Mon Sep 17 00:00:00 2001 From: Matt Harding Date: Wed, 11 Oct 2023 04:43:04 +0100 Subject: [PATCH 3/4] Run clang-format --- .../lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp index 0fbfeb3271ad3..de37109710480 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp @@ -123,12 +123,14 @@ bool WebAssemblyDebugFixup::runOnMachineFunction(MachineFunction &MF) { } } else { - // There is a WebAssembly Peephole optimisation can remove instructions before and after - // 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, unreachable can only appear at the end of a basic block, and the stack is empty + // There is a WebAssembly Peephole optimisation can remove instructions + // before and after 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, unreachable + // can only appear at the end of a basic block, and the stack is empty // at the end of every basic block. - // So we can assume unreachable has stack-type [t*] -> [], and simply clear the stack. + // So we can assume unreachable has stack-type [t*] -> [], and simply + // clear the stack. if (MI.getOpcode() == WebAssembly::UNREACHABLE) { Stack.clear(); break; From 764b9062677cf15d759cb1f45f1b73f43021a5b3 Mon Sep 17 00:00:00 2001 From: Matt Harding Date: Wed, 25 Oct 2023 21:22:57 +0100 Subject: [PATCH 4/4] Improve the explanatory comment --- .../Target/WebAssembly/WebAssemblyDebugFixup.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp index de37109710480..e25c832e7e1b1 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp @@ -123,14 +123,14 @@ bool WebAssemblyDebugFixup::runOnMachineFunction(MachineFunction &MF) { } } else { - // There is a WebAssembly Peephole optimisation can remove instructions - // before and after 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, unreachable - // can only appear at the end of a basic block, and the stack is empty - // at the end of every basic block. - // So we can assume unreachable has stack-type [t*] -> [], and simply - // clear the stack. + // 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;