[lld] Ensure NoTrapAfterNoreturn is false for the wasm backend (PR #65876)
Matt Harding via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 27 16:26:29 PDT 2023
================
@@ -1,33 +1,133 @@
-; 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
+; 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 @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
}
+
+; LLVM trap followed by LLVM unreachable could become exactly one
+; wasm unreachable, but two are emitted currently.
+define void @trap_unreach() {
+; 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
+}
+
+
+; 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() {
+; 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 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() {
+; 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
+}
+
+; This function currently doesn't emit unreachable.
----------------
majaha wrote:
I intended the last four test functions to be in more of a declarative style, describing the current state of affairs for a few examples of interest, and showing where there's room for improvement. They can be updated as the codegen changes, but they make sure that changes are intentional.
The last function, `@void_sig_match_noreturn_ret()`, is an interesting example because the `ret void` is unreachable code, but doesn't get replaced with a wasm unreachable. It answers a question you might have, adding another data point to when it is and is not emitted.
[There's an optimisation pass in llvm called SimplifyCFGPass](https://godbolt.org/z/9Y1hcqYEK) that can replace dead code with llvm `unreachable`, but it isn't enabled for the wasm backend. If a dead code elimination pass like that was added, it would be useful for the developer to be made aware that this example gained an unnecessary wasm unreachable.
https://github.com/llvm/llvm-project/pull/65876
More information about the llvm-commits
mailing list