Skip to content

Commit bd7ca98

Browse files
authored
Ensure NoTrapAfterNoreturn is false for the wasm backend (#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. This turns off `NoTrapAfterNoreturn` for the Wasm backend and adds new tests.
1 parent 6b31b02 commit bd7ca98

File tree

5 files changed

+88
-23
lines changed

5 files changed

+88
-23
lines changed

llvm/include/llvm/CodeGen/MachineFunction.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ class LLVM_EXTERNAL_VISIBILITY MachineFunction {
266266
// RegInfo - Information about each register in use in the function.
267267
MachineRegisterInfo *RegInfo;
268268

269-
// Used to keep track of target-specific per-machine function information for
269+
// Used to keep track of target-specific per-machine-function information for
270270
// the target implementation.
271271
MachineFunctionInfo *MFInfo;
272272

llvm/include/llvm/CodeGen/MachineInstr.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1276,7 +1276,7 @@ class MachineInstr
12761276
/// eraseFromBundle() to erase individual bundled instructions.
12771277
void eraseFromParent();
12781278

1279-
/// Unlink 'this' form its basic block and delete it.
1279+
/// Unlink 'this' from its basic block and delete it.
12801280
///
12811281
/// If the instruction is part of a bundle, the other instructions in the
12821282
/// bundle remain bundled.

llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,7 @@ void WebAssemblyCFGStackify::removeUnnecessaryInstrs(MachineFunction &MF) {
667667

668668
// When there is an unconditional branch right before a catch instruction and
669669
// it branches to the end of end_try marker, we don't need the branch, because
670-
// it there is no exception, the control flow transfers to that point anyway.
670+
// if there is no exception, the control flow transfers to that point anyway.
671671
// bb0:
672672
// try
673673
// ...

llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ WebAssemblyTargetMachine::WebAssemblyTargetMachine(
127127
// LLVM 'unreachable' to ISD::TRAP and then lower that to WebAssembly's
128128
// 'unreachable' instructions which is meant for that case.
129129
this->Options.TrapUnreachable = true;
130+
this->Options.NoTrapAfterNoreturn = false;
130131

131132
// WebAssembly treats each function as an independent unit. Force
132133
// -ffunction-sections, effectively, so that we can emit them independently.
Lines changed: 84 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,97 @@
1-
; RUN: llc < %s -asm-verbose=false -verify-machineinstrs | FileCheck %s
2-
; RUN: llc < %s -asm-verbose=false -fast-isel -fast-isel-abort=1 -verify-machineinstrs | FileCheck %s
1+
; The assertions in this file were autogenerated by
2+
; utils/update_llc_test_checks.py, but were hand-edited to add the
3+
; "end_function" lines to prevent the tests from passing when there are
4+
; superfluous instructions at the end of a function. You can run
5+
; update_llc_test_checks.py again, but please keep the "end_function" lines
6+
; intact when you commit.
37

4-
; Test that LLVM unreachable instruction and trap intrinsic are lowered to
5-
; wasm unreachable
8+
; Wasm, to generate valid code, always internally sets `--trap-unreachable` to 1
9+
; and `--no-trap-after-noreturn` to 0, and these command lines options, if
10+
; explicitly given, are ignored. Various combinations of these options should
11+
; have no effect and should not generate invalid code.
12+
; RUN: llc < %s -verify-machineinstrs | FileCheck %s
13+
; RUN: llc < %s -fast-isel -fast-isel-abort=1 -verify-machineinstrs | FileCheck %s
14+
; RUN: llc < %s -verify-machineinstrs --trap-unreachable | FileCheck %s
15+
; RUN: llc < %s -fast-isel -fast-isel-abort=1 -verify-machineinstrs --trap-unreachable | FileCheck %s
16+
; RUN: llc < %s -verify-machineinstrs --trap-unreachable --no-trap-after-noreturn | FileCheck %s
17+
; RUN: llc < %s -fast-isel -fast-isel-abort=1 -verify-machineinstrs --trap-unreachable --no-trap-after-noreturn | FileCheck %s
618

719
target triple = "wasm32-unknown-unknown"
820

9-
declare void @llvm.trap()
10-
declare void @llvm.debugtrap()
11-
declare void @abort()
1221

13-
; CHECK-LABEL: f1:
14-
; CHECK: call abort{{$}}
15-
; CHECK: unreachable
16-
define i32 @f1() {
17-
call void @abort()
18-
unreachable
19-
}
22+
; Test that the LLVM trap and debug trap intrinsics are lowered to wasm
23+
; unreachable.
2024

21-
; CHECK-LABEL: f2:
22-
; CHECK: unreachable
23-
define void @f2() {
25+
declare void @llvm.trap() cold noreturn nounwind
26+
declare void @llvm.debugtrap() nounwind
27+
28+
define void @trap_ret_void() {
29+
; CHECK-LABEL: trap_ret_void:
30+
; CHECK: .functype trap_ret_void () -> ()
31+
; CHECK-NEXT: # %bb.0:
32+
; CHECK-NEXT: unreachable
33+
; CHECK-NEXT: # fallthrough-return
34+
; CHECK-NEXT: end_function
2435
call void @llvm.trap()
2536
ret void
2637
}
2738

28-
; CHECK-LABEL: f3:
29-
; CHECK: unreachable
30-
define void @f3() {
39+
define void @debugtrap_ret_void() {
40+
; CHECK-LABEL: debugtrap_ret_void:
41+
; CHECK: .functype debugtrap_ret_void () -> ()
42+
; CHECK-NEXT: # %bb.0:
43+
; CHECK-NEXT: unreachable
44+
; CHECK-NEXT: # fallthrough-return
45+
; CHECK-NEXT: end_function
3146
call void @llvm.debugtrap()
3247
ret void
3348
}
49+
50+
; LLVM trap followed by LLVM unreachable could become exactly one wasm
51+
; unreachable, but two are emitted currently.
52+
define void @trap_unreacheable() {
53+
; CHECK-LABEL: trap_unreacheable:
54+
; CHECK: .functype trap_unreacheable () -> ()
55+
; CHECK-NEXT: # %bb.0:
56+
; CHECK-NEXT: unreachable
57+
; CHECK-NEXT: unreachable
58+
; CHECK-NEXT: end_function
59+
call void @llvm.trap()
60+
unreachable
61+
}
62+
63+
64+
; Test that LLVM unreachable instruction is lowered to wasm unreachable when
65+
; necessary to fulfill the wasm operand stack requirements.
66+
67+
declare void @ext_func()
68+
declare i32 @ext_func_i32()
69+
declare void @ext_never_return() noreturn
70+
71+
; LLVM IR's 'unreachable' is translated to Wasm 'unreachable'.
72+
define i32 @missing_ret_unreachable() {
73+
; CHECK-LABEL: missing_ret_unreachable:
74+
; CHECK: .functype missing_ret_unreachable () -> (i32)
75+
; CHECK-NEXT: # %bb.0:
76+
; CHECK-NEXT: call ext_func
77+
; CHECK-NEXT: unreachable
78+
; CHECK-NEXT: end_function
79+
call void @ext_func()
80+
unreachable
81+
}
82+
83+
; This is similar to the above test, but ensures wasm unreachable is emitted
84+
; This is similar to the above test, but the callee has a 'noreturn' attribute.
85+
; There is an optimization that removes an 'unreachable' after a noreturn call,
86+
; but Wasm backend doesn't use it and ignore `--no-trap-after-noreturn`, if
87+
; given, to generate valid code.
88+
define i32 @missing_ret_noreturn_unreachable() {
89+
; CHECK-LABEL: missing_ret_noreturn_unreachable:
90+
; CHECK: .functype missing_ret_noreturn_unreachable () -> (i32)
91+
; CHECK-NEXT: # %bb.0:
92+
; CHECK-NEXT: call ext_never_return
93+
; CHECK-NEXT: unreachable
94+
; CHECK-NEXT: end_function
95+
call void @ext_never_return()
96+
unreachable
97+
}

0 commit comments

Comments
 (0)