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

Heejin Ahn via cfe-commits cfe-commits at lists.llvm.org
Tue Oct 3 23:24:55 PDT 2023


================
@@ -1,33 +1,151 @@
-; 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
+; The assertions in this file were autogenerated by
+; utils/update_llc_test_checks.py, but were hand-edited to add the
+; "end_function" lines to prevent the tests from passing when there
+; are superfluous instructions at the end of a function.
+; You can run update_llc_test_checks.py again, but please keep the
+; "end_function" lines intact when you commit.
 
-; Test that LLVM unreachable instruction and trap intrinsic are lowered to
-; wasm unreachable
+
+; --trap-unreachable and --no-trap-after-noreturn are sensitive and bug-prone
+; options for the WebAssembly back-end as, unlike in many other target
+; architechtures, unreachable code being compiled to a trap instruction (i.e.
+; WebAssembly "unreachable" instruction) is often necessary for the code to
+; pass wasm's validation step. We test that various combinations of
+; these options produce the expected output.
+; 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"
 
-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 @debugtrap_ret_void() {
+; CHECK-LABEL: debugtrap_ret_void:
+; CHECK:         .functype debugtrap_ret_void () -> ()
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    unreachable
+; CHECK-NEXT:    # fallthrough-return
+; CHECK-NEXT:    end_function
   call void @llvm.debugtrap()
   ret void
 }
+
+; LLVM trap followed by LLVM unreachable could become exactly one wasm
+; unreachable, but two are emitted currently.
+define void @trap_unreacheable() {
+; CHECK-LABEL: 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
+}
+
+
+; 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,
+; which is necessary for the function to pass wasm validation.
+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() {
+; 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
+}
+
+; 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 currently, wasm unreachable is emitted for them.
+
+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() {
+; CHECK-LABEL: void_sig_match_noreturn_unreach:
+; CHECK:         .functype void_sig_match_noreturn_unreach () -> ()
----------------
aheejin wrote:

```suggestion
define void @void_sig_match_noreturn_unreachable() {
; CHECK-LABEL: void_sig_match_noreturn_unreachable:
; CHECK:         .functype void_sig_match_noreturn_unreachable () -> ()
```

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


More information about the cfe-commits mailing list