[llvm] Add Wasm peephole optimisation to remove dead code around `unreachable`s (PR #66062)
Matt Harding via llvm-commits
llvm-commits at lists.llvm.org
Wed Oct 25 15:05:19 PDT 2023
https://github.com/majaha updated https://github.com/llvm/llvm-project/pull/66062
>From 98be8be18338f94cfa897359f24ecadcf0e9d047 Mon Sep 17 00:00:00 2001
From: Matt Harding <majaharding at gmail.com>
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 72f865842bdcaa5..fc2d8e24390143d 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 <majaharding at gmail.com>
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 14385f042efb7c2..35286bbcccc2f54 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 4a75bab6b95ddcd..0fbfeb3271ad340 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 6e2d566d9b48630..4237a6d39e269d3 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 <iterator>
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<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"
@@ -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 fc2d8e24390143d..9354a0cd14dc1e4 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 bc1be7931349697..f1ec71da1ebb641 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 <majaharding at gmail.com>
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 0fbfeb3271ad340..de3710971048094 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 <majaharding at gmail.com>
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 de3710971048094..e25c832e7e1b143 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;
More information about the llvm-commits
mailing list