[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