[llvm] Ensure NoTrapAfterNoreturn is false for the wasm backend (PR #65876)

Matt Harding via llvm-commits llvm-commits at lists.llvm.org
Sat Sep 9 23:52:23 PDT 2023


https://github.com/majaha created https://github.com/llvm/llvm-project/pull/65876:

In the WebAssembly back end, the TrapUnreachable option is currently load-bearing for correctness, inserting wasm `unreachable` instructions where needed to create valid wasm. There is another option, NoTrapAfterNoreturn, that removes some of those traps and causes incorrect wasm to be emitted.

The first commit adds a command line flag for NoTrapAfterNoreturn so that it can be tested, and adds new tests (and fixes some typos).
The second commit turns off NoTrapAfterNoreturn for the Wasm backend.
The third commit adds a new peephole optimisation, to remove some common cases of unnecessary instructions around `unreachable`. Properly modelling how `unreachable` can be a sink and a source for operands would be better, but this will do for now.

>From 7c43c803764bf9e0256d4e3e9f497d2622bb8f69 Mon Sep 17 00:00:00 2001
From: Matt Harding <majaharding at gmail.com>
Date: Fri, 25 Aug 2023 06:19:14 +0100
Subject: [PATCH 1/3] Add no-trap-after-noreturn flag and wasm tests

Add the command line flag --no-trap-after-noreturn.
Add and improve tests related to WebAssembly's unreachable instruction.
Also fix various typos.
---
 llvm/include/llvm/CodeGen/MachineFunction.h   |   2 +-
 llvm/include/llvm/CodeGen/MachineInstr.h      |   2 +-
 llvm/lib/CodeGen/LLVMTargetMachine.cpp        |   7 +
 .../WebAssembly/WebAssemblyCFGStackify.cpp    |   2 +-
 llvm/test/CodeGen/WebAssembly/unreachable.ll  | 178 +++++++++++++++---
 5 files changed, 167 insertions(+), 24 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/MachineFunction.h b/llvm/include/llvm/CodeGen/MachineFunction.h
index 6c2da626ea54b4d..8f1651c2958e591 100644
--- a/llvm/include/llvm/CodeGen/MachineFunction.h
+++ b/llvm/include/llvm/CodeGen/MachineFunction.h
@@ -266,7 +266,7 @@ class LLVM_EXTERNAL_VISIBILITY MachineFunction {
   // RegInfo - Information about each register in use in the function.
   MachineRegisterInfo *RegInfo;
 
-  // Used to keep track of target-specific per-machine function information for
+  // Used to keep track of target-specific per-machine-function information for
   // the target implementation.
   MachineFunctionInfo *MFInfo;
 
diff --git a/llvm/include/llvm/CodeGen/MachineInstr.h b/llvm/include/llvm/CodeGen/MachineInstr.h
index 03fb15f77c65cbb..8367f999fcc76d3 100644
--- a/llvm/include/llvm/CodeGen/MachineInstr.h
+++ b/llvm/include/llvm/CodeGen/MachineInstr.h
@@ -1276,7 +1276,7 @@ class MachineInstr
   /// eraseFromBundle() to erase individual bundled instructions.
   void eraseFromParent();
 
-  /// Unlink 'this' form its basic block and delete it.
+  /// Unlink 'this' from its basic block and delete it.
   ///
   /// If the instruction is part of a bundle, the other instructions in the
   /// bundle remain bundled.
diff --git a/llvm/lib/CodeGen/LLVMTargetMachine.cpp b/llvm/lib/CodeGen/LLVMTargetMachine.cpp
index d02ec1db1165d41..aadc3709b85bfb7 100644
--- a/llvm/lib/CodeGen/LLVMTargetMachine.cpp
+++ b/llvm/lib/CodeGen/LLVMTargetMachine.cpp
@@ -37,6 +37,11 @@ static cl::opt<bool>
     EnableTrapUnreachable("trap-unreachable", cl::Hidden,
                           cl::desc("Enable generating trap for unreachable"));
 
+static cl::opt<bool>
+    EnableNoTrapAfterNoreturn("no-trap-after-noreturn", cl::Hidden,
+                              cl::desc("Do not emit a trap instruction for 'unreachable' IR instructions "
+                              "after noreturn calls, even if --trap-unreachable is set."));
+
 void LLVMTargetMachine::initAsmInfo() {
   MRI.reset(TheTarget.createMCRegInfo(getTargetTriple().str()));
   assert(MRI && "Unable to create reg info");
@@ -95,6 +100,8 @@ LLVMTargetMachine::LLVMTargetMachine(const Target &T,
 
   if (EnableTrapUnreachable)
     this->Options.TrapUnreachable = true;
+  if (EnableNoTrapAfterNoreturn)
+    this->Options.NoTrapAfterNoreturn = true;
 }
 
 TargetTransformInfo
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp
index 131e99c66fa2e5a..d8cbddf74545da6 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp
@@ -667,7 +667,7 @@ void WebAssemblyCFGStackify::removeUnnecessaryInstrs(MachineFunction &MF) {
 
   // 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.
+  // if there is no exception, the control flow transfers to that point anyway.
   // bb0:
   //   try
   //     ...
diff --git a/llvm/test/CodeGen/WebAssembly/unreachable.ll b/llvm/test/CodeGen/WebAssembly/unreachable.ll
index ad1c90090ac58bf..1bac30b842e1e05 100644
--- a/llvm/test/CodeGen/WebAssembly/unreachable.ll
+++ b/llvm/test/CodeGen/WebAssembly/unreachable.ll
@@ -1,33 +1,169 @@
-; RUN: llc < %s -asm-verbose=false -verify-machineinstrs | FileCheck %s
-; RUN: llc < %s -asm-verbose=false -fast-isel -fast-isel-abort=1 -verify-machineinstrs | FileCheck %s
-
-; Test that LLVM unreachable instruction and trap intrinsic are lowered to
-; wasm unreachable
+; RUN: llc < %s -verify-machineinstrs | FileCheck %s --check-prefixes CHECK,NORMAL
+; RUN: llc < %s -fast-isel -fast-isel-abort=1 -verify-machineinstrs | FileCheck %s --check-prefixes CHECK,NORMAL
+; RUN: llc < %s -verify-machineinstrs --trap-unreachable | FileCheck %s --check-prefixes CHECK,NORMAL
+; RUN: llc < %s -fast-isel -fast-isel-abort=1 -verify-machineinstrs --trap-unreachable | FileCheck %s --check-prefixes CHECK,NORMAL
+; RUN: llc < %s -verify-machineinstrs --trap-unreachable --no-trap-after-noreturn | FileCheck %s --check-prefixes CHECK,NTANR
+; RUN: llc < %s -fast-isel -fast-isel-abort=1 -verify-machineinstrs --trap-unreachable --no-trap-after-noreturn | FileCheck %s --check-prefixes CHECK,FNTANR
 
 target triple = "wasm32-unknown-unknown"
 
-declare void @llvm.trap()
-declare void @llvm.debugtrap()
-declare void @abort()
 
-; CHECK-LABEL: f1:
-; CHECK: call abort{{$}}
-; CHECK: unreachable
-define i32 @f1() {
-  call void @abort()
-  unreachable
-}
+; Test that the LLVM trap and debug trap intrinsics are lowered to wasm unreachable.
+
+declare void @llvm.trap() cold noreturn nounwind
+declare void @llvm.debugtrap() nounwind
 
-; CHECK-LABEL: f2:
-; CHECK: unreachable
-define void @f2() {
+define void @trap_ret_void() {
+; CHECK-LABEL: trap_ret_void:
+; CHECK:         .functype trap_ret_void () -> ()
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    unreachable
+; CHECK-NEXT:    # fallthrough-return
+; CHECK-NEXT:    end_function
   call void @llvm.trap()
   ret void
 }
 
-; CHECK-LABEL: f3:
-; CHECK: unreachable
-define void @f3() {
+define void @dtrap_ret_void() {
+; CHECK-LABEL: dtrap_ret_void:
+; CHECK:         .functype dtrap_ret_void () -> ()
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    unreachable
+; CHECK-NEXT:    # fallthrough-return
+; CHECK-NEXT:    end_function
   call void @llvm.debugtrap()
   ret void
 }
+
+; Test that LLVM trap followed by LLVM unreachable becomes exactly one wasm unreachable.
+define void @trap_unreach() {
+; NORMAL-LABEL: trap_unreach:
+; NORMAL:         .functype trap_unreach () -> ()
+; NORMAL-NEXT:  # %bb.0:
+; NORMAL-NEXT:    unreachable
+; NORMAL-NEXT:    unreachable
+; NORMAL-NEXT:    end_function
+;
+; NTANR-LABEL: trap_unreach:
+; NTANR:         .functype trap_unreach () -> ()
+; NTANR-NEXT:  # %bb.0:
+; NTANR-NEXT:    unreachable
+; NTANR-NEXT:    end_function
+;
+; FNTANR-LABEL: trap_unreach:
+; FNTANR:         .functype trap_unreach () -> ()
+; FNTANR-NEXT:  # %bb.0:
+; FNTANR-NEXT:    unreachable
+; FNTANR-NEXT:    unreachable
+; FNTANR-NEXT:    end_function
+  call void @llvm.trap()
+  unreachable
+}
+
+
+; Test that LLVM unreachable instruction is lowered to wasm unreachable when necessary
+; to fulfill the wasm operand stack requirements.
+
+declare void @ext_func()
+declare i32 @ext_func_i32()
+declare void @ext_never_return() noreturn
+
+; This test emits wasm unreachable to fill in for the missing i32 return value.
+define i32 @missing_ret_unreach() {
+; CHECK-LABEL: missing_ret_unreach:
+; CHECK:         .functype missing_ret_unreach () -> (i32)
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call ext_func
+; CHECK-NEXT:    unreachable
+; CHECK-NEXT:    end_function
+  call void @ext_func()
+  unreachable
+}
+
+; This is similar to the above test, but ensures wasm unreachable is emitted even
+; after a noreturn call.
+define i32 @missing_ret_noreturn_unreach() {
+; NORMAL-LABEL: missing_ret_noreturn_unreach:
+; NORMAL:         .functype missing_ret_noreturn_unreach () -> (i32)
+; NORMAL-NEXT:  # %bb.0:
+; NORMAL-NEXT:    call ext_never_return
+; NORMAL-NEXT:    unreachable
+; NORMAL-NEXT:    end_function
+;
+; NTANR-LABEL: missing_ret_noreturn_unreach:
+; NTANR:         .functype missing_ret_noreturn_unreach () -> (i32)
+; NTANR-NEXT:  # %bb.0:
+; NTANR-NEXT:    call ext_never_return
+; NTANR-NEXT:    end_function
+;
+; FNTANR-LABEL: missing_ret_noreturn_unreach:
+; FNTANR:         .functype missing_ret_noreturn_unreach () -> (i32)
+; FNTANR-NEXT:  # %bb.0:
+; FNTANR-NEXT:    call ext_never_return
+; FNTANR-NEXT:    unreachable
+; FNTANR-NEXT:    end_function
+  call void @ext_never_return()
+  unreachable
+}
+
+; We could emit no instructions at all for the llvm unreachables in these next three tests, as the signatures match
+; and reaching llvm unreachable is undefined behaviour. But wasm unreachable is emitted for the time being.
+
+define void @void_sig_match_unreach() {
+; CHECK-LABEL: void_sig_match_unreach:
+; CHECK:         .functype void_sig_match_unreach () -> ()
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call ext_func
+; CHECK-NEXT:    unreachable
+; CHECK-NEXT:    end_function
+  call void @ext_func()
+  unreachable
+}
+
+define i32 @i32_sig_match_unreach() {
+; CHECK-LABEL: i32_sig_match_unreach:
+; CHECK:         .functype i32_sig_match_unreach () -> (i32)
+; 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
+}
+
+define void @void_sig_match_noreturn_unreach() {
+; NORMAL-LABEL: void_sig_match_noreturn_unreach:
+; NORMAL:         .functype void_sig_match_noreturn_unreach () -> ()
+; NORMAL-NEXT:  # %bb.0:
+; NORMAL-NEXT:    call ext_never_return
+; NORMAL-NEXT:    unreachable
+; NORMAL-NEXT:    end_function
+;
+; NTANR-LABEL: void_sig_match_noreturn_unreach:
+; NTANR:         .functype void_sig_match_noreturn_unreach () -> ()
+; NTANR-NEXT:  # %bb.0:
+; NTANR-NEXT:    call ext_never_return
+; NTANR-NEXT:    end_function
+;
+; FNTANR-LABEL: void_sig_match_noreturn_unreach:
+; FNTANR:         .functype void_sig_match_noreturn_unreach () -> ()
+; FNTANR-NEXT:  # %bb.0:
+; FNTANR-NEXT:    call ext_never_return
+; FNTANR-NEXT:    unreachable
+; FNTANR-NEXT:    end_function
+  call void @ext_never_return()
+  unreachable
+}
+
+; This function currently doesn't emit unreachable.
+define void @void_sig_match_noreturn_ret() {
+; CHECK-LABEL: void_sig_match_noreturn_ret:
+; CHECK:         .functype void_sig_match_noreturn_ret () -> ()
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call ext_never_return
+; CHECK-NEXT:    # fallthrough-return
+; CHECK-NEXT:    end_function
+  call void @ext_never_return()
+  ret void
+}

>From 7e834694b09e6a7787674879442b5f0d0c92eb22 Mon Sep 17 00:00:00 2001
From: Matt Harding <majaharding at gmail.com>
Date: Mon, 28 Aug 2023 18:47:44 +0100
Subject: [PATCH 2/3] Ensure NoTrapAfterNoreturn is false for wasm

---
 .../WebAssembly/WebAssemblyTargetMachine.cpp  |  1 +
 llvm/test/CodeGen/WebAssembly/unreachable.ll  | 87 +++++--------------
 2 files changed, 25 insertions(+), 63 deletions(-)

diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
index f8a4b95a95515e4..184910ae68f15e6 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -127,6 +127,7 @@ WebAssemblyTargetMachine::WebAssemblyTargetMachine(
   // LLVM 'unreachable' to ISD::TRAP and then lower that to WebAssembly's
   // 'unreachable' instructions which is meant for that case.
   this->Options.TrapUnreachable = true;
+  this->Options.NoTrapAfterNoreturn = false;
 
   // WebAssembly treats each function as an independent unit. Force
   // -ffunction-sections, effectively, so that we can emit them independently.
diff --git a/llvm/test/CodeGen/WebAssembly/unreachable.ll b/llvm/test/CodeGen/WebAssembly/unreachable.ll
index 1bac30b842e1e05..2f17746965fde3f 100644
--- a/llvm/test/CodeGen/WebAssembly/unreachable.ll
+++ b/llvm/test/CodeGen/WebAssembly/unreachable.ll
@@ -1,9 +1,9 @@
-; RUN: llc < %s -verify-machineinstrs | FileCheck %s --check-prefixes CHECK,NORMAL
-; RUN: llc < %s -fast-isel -fast-isel-abort=1 -verify-machineinstrs | FileCheck %s --check-prefixes CHECK,NORMAL
-; RUN: llc < %s -verify-machineinstrs --trap-unreachable | FileCheck %s --check-prefixes CHECK,NORMAL
-; RUN: llc < %s -fast-isel -fast-isel-abort=1 -verify-machineinstrs --trap-unreachable | FileCheck %s --check-prefixes CHECK,NORMAL
-; RUN: llc < %s -verify-machineinstrs --trap-unreachable --no-trap-after-noreturn | FileCheck %s --check-prefixes CHECK,NTANR
-; RUN: llc < %s -fast-isel -fast-isel-abort=1 -verify-machineinstrs --trap-unreachable --no-trap-after-noreturn | FileCheck %s --check-prefixes CHECK,FNTANR
+; RUN: llc < %s -verify-machineinstrs | FileCheck %s
+; RUN: llc < %s -fast-isel -fast-isel-abort=1 -verify-machineinstrs | FileCheck %s
+; RUN: llc < %s -verify-machineinstrs --trap-unreachable | FileCheck %s
+; RUN: llc < %s -fast-isel -fast-isel-abort=1 -verify-machineinstrs --trap-unreachable | FileCheck %s
+; RUN: llc < %s -verify-machineinstrs --trap-unreachable --no-trap-after-noreturn | FileCheck %s
+; RUN: llc < %s -fast-isel -fast-isel-abort=1 -verify-machineinstrs --trap-unreachable --no-trap-after-noreturn | FileCheck %s
 
 target triple = "wasm32-unknown-unknown"
 
@@ -37,25 +37,12 @@ define void @dtrap_ret_void() {
 
 ; Test that LLVM trap followed by LLVM unreachable becomes exactly one wasm unreachable.
 define void @trap_unreach() {
-; NORMAL-LABEL: trap_unreach:
-; NORMAL:         .functype trap_unreach () -> ()
-; NORMAL-NEXT:  # %bb.0:
-; NORMAL-NEXT:    unreachable
-; NORMAL-NEXT:    unreachable
-; NORMAL-NEXT:    end_function
-;
-; NTANR-LABEL: trap_unreach:
-; NTANR:         .functype trap_unreach () -> ()
-; NTANR-NEXT:  # %bb.0:
-; NTANR-NEXT:    unreachable
-; NTANR-NEXT:    end_function
-;
-; FNTANR-LABEL: trap_unreach:
-; FNTANR:         .functype trap_unreach () -> ()
-; FNTANR-NEXT:  # %bb.0:
-; FNTANR-NEXT:    unreachable
-; FNTANR-NEXT:    unreachable
-; FNTANR-NEXT:    end_function
+; CHECK-LABEL: trap_unreach:
+; CHECK:         .functype trap_unreach () -> ()
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    unreachable
+; CHECK-NEXT:    unreachable
+; CHECK-NEXT:    end_function
   call void @llvm.trap()
   unreachable
 }
@@ -83,25 +70,12 @@ define i32 @missing_ret_unreach() {
 ; This is similar to the above test, but ensures wasm unreachable is emitted even
 ; after a noreturn call.
 define i32 @missing_ret_noreturn_unreach() {
-; NORMAL-LABEL: missing_ret_noreturn_unreach:
-; NORMAL:         .functype missing_ret_noreturn_unreach () -> (i32)
-; NORMAL-NEXT:  # %bb.0:
-; NORMAL-NEXT:    call ext_never_return
-; NORMAL-NEXT:    unreachable
-; NORMAL-NEXT:    end_function
-;
-; NTANR-LABEL: missing_ret_noreturn_unreach:
-; NTANR:         .functype missing_ret_noreturn_unreach () -> (i32)
-; NTANR-NEXT:  # %bb.0:
-; NTANR-NEXT:    call ext_never_return
-; NTANR-NEXT:    end_function
-;
-; FNTANR-LABEL: missing_ret_noreturn_unreach:
-; FNTANR:         .functype missing_ret_noreturn_unreach () -> (i32)
-; FNTANR-NEXT:  # %bb.0:
-; FNTANR-NEXT:    call ext_never_return
-; FNTANR-NEXT:    unreachable
-; FNTANR-NEXT:    end_function
+; CHECK-LABEL: missing_ret_noreturn_unreach:
+; CHECK:         .functype missing_ret_noreturn_unreach () -> (i32)
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call ext_never_return
+; CHECK-NEXT:    unreachable
+; CHECK-NEXT:    end_function
   call void @ext_never_return()
   unreachable
 }
@@ -133,25 +107,12 @@ define i32 @i32_sig_match_unreach() {
 }
 
 define void @void_sig_match_noreturn_unreach() {
-; NORMAL-LABEL: void_sig_match_noreturn_unreach:
-; NORMAL:         .functype void_sig_match_noreturn_unreach () -> ()
-; NORMAL-NEXT:  # %bb.0:
-; NORMAL-NEXT:    call ext_never_return
-; NORMAL-NEXT:    unreachable
-; NORMAL-NEXT:    end_function
-;
-; NTANR-LABEL: void_sig_match_noreturn_unreach:
-; NTANR:         .functype void_sig_match_noreturn_unreach () -> ()
-; NTANR-NEXT:  # %bb.0:
-; NTANR-NEXT:    call ext_never_return
-; NTANR-NEXT:    end_function
-;
-; FNTANR-LABEL: void_sig_match_noreturn_unreach:
-; FNTANR:         .functype void_sig_match_noreturn_unreach () -> ()
-; FNTANR-NEXT:  # %bb.0:
-; FNTANR-NEXT:    call ext_never_return
-; FNTANR-NEXT:    unreachable
-; FNTANR-NEXT:    end_function
+; CHECK-LABEL: void_sig_match_noreturn_unreach:
+; CHECK:         .functype void_sig_match_noreturn_unreach () -> ()
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    call ext_never_return
+; CHECK-NEXT:    unreachable
+; CHECK-NEXT:    end_function
   call void @ext_never_return()
   unreachable
 }

>From c8ad813e9d5164ace9aa2178f912272762824e91 Mon Sep 17 00:00:00 2001
From: Matt Harding <majaharding at gmail.com>
Date: Thu, 31 Aug 2023 22:55:50 +0100
Subject: [PATCH 3/3] Add peephole optimisation

---
 .../WebAssembly/WebAssemblyDebugFixup.cpp     | 11 ++++
 .../WebAssembly/WebAssemblyPeephole.cpp       | 51 +++++++++++++++++++
 llvm/test/CodeGen/WebAssembly/unreachable.ll  |  2 -
 llvm/test/MC/WebAssembly/global-ctor-dtor.ll  | 12 ++---
 4 files changed, 68 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp
index 4a75bab6b95ddcd..eb7d5b2cfd2a4b3 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyDebugFixup.cpp
@@ -122,9 +122,20 @@ bool WebAssemblyDebugFixup::runOnMachineFunction(MachineFunction &MF) {
           // it will be culled later.
         }
       } else {
+        
+        // WebAssembly Peephole optimisation can remove instructions around wasm unreachable.
+        // This is valid for wasm, as unreachable is operand stack polymorphic. But this is not modeled
+        // in llvm at the moment, and so the stack may not seem to pop all that it pushes.
+        // Clear the stack so we don't violate the assert(Stack.empty()) later on.
+        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.size() != 0 && "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..a573f0d86436e58 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:
+        return;
+      }
+    }
+  }();
+
+  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 2f17746965fde3f..38b8f543dffd972 100644
--- a/llvm/test/CodeGen/WebAssembly/unreachable.ll
+++ b/llvm/test/CodeGen/WebAssembly/unreachable.ll
@@ -41,7 +41,6 @@ define void @trap_unreach() {
 ; CHECK:         .functype trap_unreach () -> ()
 ; CHECK-NEXT:  # %bb.0:
 ; CHECK-NEXT:    unreachable
-; CHECK-NEXT:    unreachable
 ; CHECK-NEXT:    end_function
   call void @llvm.trap()
   unreachable
@@ -99,7 +98,6 @@ define i32 @i32_sig_match_unreach() {
 ; CHECK:         .functype i32_sig_match_unreach () -> (i32)
 ; 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



More information about the llvm-commits mailing list