Skip to content

Commit

Permalink
Ensure NoTrapAfterNoreturn is false for the wasm backend (#65876)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
majaha committed Oct 5, 2023
1 parent 6b31b02 commit bd7ca98
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 23 deletions.
2 changes: 1 addition & 1 deletion llvm/include/llvm/CodeGen/MachineFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion llvm/include/llvm/CodeGen/MachineInstr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
// ...
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
104 changes: 84 additions & 20 deletions llvm/test/CodeGen/WebAssembly/unreachable.ll
Original file line number Diff line number Diff line change
@@ -1,33 +1,97 @@
; 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
; Wasm, to generate valid code, always internally sets `--trap-unreachable` to 1
; and `--no-trap-after-noreturn` to 0, and these command lines options, if
; explicitly given, are ignored. Various combinations of these options should
; have no effect and should not generate invalid code.
; 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.

; CHECK-LABEL: f2:
; CHECK: unreachable
define void @f2() {
declare void @llvm.trap() cold noreturn nounwind
declare void @llvm.debugtrap() nounwind

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

; LLVM IR's 'unreachable' is translated to Wasm 'unreachable'.
define i32 @missing_ret_unreachable() {
; CHECK-LABEL: missing_ret_unreachable:
; CHECK: .functype missing_ret_unreachable () -> (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
; 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)
; CHECK-NEXT: # %bb.0:
; CHECK-NEXT: call ext_never_return
; CHECK-NEXT: unreachable
; CHECK-NEXT: end_function
call void @ext_never_return()
unreachable
}

0 comments on commit bd7ca98

Please sign in to comment.