Skip to content

Commit 23db37c

Browse files
authored
[CodeGen] Do not emit TRAP for unreachable after @llvm.trap (#94570)
With `--trap-unreachable`, `clang` can emit double `TRAP` instructions for code that contains a call to `__builtin_trap()`: ``` > cat test.c void test() { __builtin_trap(); } > clang test.c --target=x86_64 -mllvm --trap-unreachable -O1 -S -o - ... test: ... ud2 ud2 ... ``` `SimplifyCFGPass` inserts `unreachable` after a call to a `noreturn` function, and later this instruction causes `TRAP/G_TRAP` to be emitted in `SelectionDAGBuilder::visitUnreachable()` or `IRTranslator::translateUnreachable()` if `TargetOptions.TrapUnreachable` is set. The patch checks the instruction before `unreachable` and avoids inserting an additional trap.
1 parent 60d4a35 commit 23db37c

File tree

7 files changed

+88
-46
lines changed

7 files changed

+88
-46
lines changed

llvm/include/llvm/IR/Instructions.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "llvm/IR/GEPNoWrapFlags.h"
3030
#include "llvm/IR/InstrTypes.h"
3131
#include "llvm/IR/Instruction.h"
32+
#include "llvm/IR/Intrinsics.h"
3233
#include "llvm/IR/OperandTraits.h"
3334
#include "llvm/IR/Use.h"
3435
#include "llvm/IR/User.h"
@@ -1520,6 +1521,17 @@ class CallInst : public CallBase {
15201521
bool canReturnTwice() const { return hasFnAttr(Attribute::ReturnsTwice); }
15211522
void setCanReturnTwice() { addFnAttr(Attribute::ReturnsTwice); }
15221523

1524+
/// Return true if the call is for a noreturn trap intrinsic.
1525+
bool isNonContinuableTrap() const {
1526+
switch (getIntrinsicID()) {
1527+
case Intrinsic::trap:
1528+
case Intrinsic::ubsantrap:
1529+
return !hasFnAttr("trap-func-name");
1530+
default:
1531+
return false;
1532+
}
1533+
}
1534+
15231535
// Methods for support type inquiry through isa, cast, and dyn_cast:
15241536
static bool classof(const Instruction *I) {
15251537
return I->getOpcode() == Instruction::Call;

llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3082,17 +3082,15 @@ bool IRTranslator::translateUnreachable(const User &U, MachineIRBuilder &MIRBuil
30823082
return true;
30833083

30843084
auto &UI = cast<UnreachableInst>(U);
3085+
30853086
// We may be able to ignore unreachable behind a noreturn call.
3086-
if (MF->getTarget().Options.NoTrapAfterNoreturn) {
3087-
const BasicBlock &BB = *UI.getParent();
3088-
if (&UI != &BB.front()) {
3089-
BasicBlock::const_iterator PredI =
3090-
std::prev(BasicBlock::const_iterator(UI));
3091-
if (const CallInst *Call = dyn_cast<CallInst>(&*PredI)) {
3092-
if (Call->doesNotReturn())
3093-
return true;
3094-
}
3095-
}
3087+
if (const CallInst *Call = dyn_cast_or_null<CallInst>(UI.getPrevNode());
3088+
Call && Call->doesNotReturn()) {
3089+
if (MF->getTarget().Options.NoTrapAfterNoreturn)
3090+
return true;
3091+
// Do not emit an additional trap instruction.
3092+
if (Call->isNonContinuableTrap())
3093+
return true;
30963094
}
30973095

30983096
MIRBuilder.buildTrap();

llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3541,11 +3541,13 @@ void SelectionDAGBuilder::visitUnreachable(const UnreachableInst &I) {
35413541
return;
35423542

35433543
// We may be able to ignore unreachable behind a noreturn call.
3544-
if (DAG.getTarget().Options.NoTrapAfterNoreturn) {
3545-
if (const CallInst *Call = dyn_cast_or_null<CallInst>(I.getPrevNode())) {
3546-
if (Call->doesNotReturn())
3547-
return;
3548-
}
3544+
if (const CallInst *Call = dyn_cast_or_null<CallInst>(I.getPrevNode());
3545+
Call && Call->doesNotReturn()) {
3546+
if (DAG.getTarget().Options.NoTrapAfterNoreturn)
3547+
return;
3548+
// Do not emit an additional trap instruction.
3549+
if (Call->isNonContinuableTrap())
3550+
return;
35493551
}
35503552

35513553
DAG.setRoot(DAG.getNode(ISD::TRAP, getCurSDLoc(), MVT::Other, DAG.getRoot()));

llvm/test/CodeGen/X86/trap.ll

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,22 @@
1-
; RUN: llc < %s -mtriple=i686-apple-darwin8 -mcpu=yonah | FileCheck %s -check-prefix=DARWIN
2-
; RUN: llc < %s -mtriple=i686-unknown-linux -mcpu=yonah | FileCheck %s -check-prefix=LINUX
3-
; RUN: llc < %s -mtriple=x86_64-scei-ps4 | FileCheck %s -check-prefix=PS4
4-
; RUN: llc < %s -mtriple=x86_64-sie-ps5 | FileCheck %s -check-prefix=PS4
5-
; RUN: llc < %s -mtriple=x86_64-windows-msvc | FileCheck %s -check-prefix=WIN64
1+
; RUN: llc < %s -mtriple=i686-apple-darwin8 -mcpu=yonah | FileCheck %s -check-prefixes=CHECK,DARWIN
2+
; RUN: llc < %s -mtriple=i686-unknown-linux -mcpu=yonah | FileCheck %s -check-prefixes=CHECK,LINUX
3+
; RUN: llc < %s -mtriple=x86_64-scei-ps4 | FileCheck %s -check-prefixes=CHECK,PS4
4+
; RUN: llc < %s -mtriple=x86_64-sie-ps5 | FileCheck %s -check-prefixes=CHECK,PS4
5+
; RUN: llc < %s -mtriple=x86_64-windows-msvc | FileCheck %s -check-prefixes=CHECK,WIN64
66

7-
; DARWIN-LABEL: test0:
8-
; DARWIN: ud2
9-
; LINUX-LABEL: test0:
10-
; LINUX: ud2
11-
; FIXME: PS4 probably doesn't want two ud2s.
12-
; PS4-LABEL: test0:
13-
; PS4: ud2
14-
; PS4: ud2
15-
; WIN64-LABEL: test0:
16-
; WIN64: ud2
17-
; WIN64-NOT: ud2
7+
; CHECK-LABEL: test0:
8+
; CHECK: ud2
9+
; CHECK-NOT: ud2
1810
define i32 @test0() noreturn nounwind {
1911
entry:
2012
tail call void @llvm.trap( )
2113
unreachable
2214
}
2315

24-
; DARWIN-LABEL: test1:
16+
; CHECK-LABEL: test1:
2517
; DARWIN: int3
26-
; LINUX-LABEL: test1:
2718
; LINUX: int3
28-
; PS4-LABEL: test1:
2919
; PS4: int $65
30-
; WIN64-LABEL: test1:
3120
; WIN64: int3
3221
; WIN64-NOT: ud2
3322
define i32 @test1() noreturn nounwind {
@@ -38,4 +27,3 @@ entry:
3827

3928
declare void @llvm.trap() nounwind
4029
declare void @llvm.debugtrap() nounwind
41-

llvm/test/CodeGen/X86/unreachable-trap.ll

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,51 @@
1-
; RUN: llc -o - %s -mtriple=x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,NORMAL
2-
; RUN: llc -o - %s -mtriple=x86_64-windows-msvc | FileCheck %s --check-prefixes=CHECK,NORMAL
1+
; RUN: llc -o - %s -mtriple=x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK
2+
; RUN: llc -o - %s -mtriple=x86_64-windows-msvc | FileCheck %s --check-prefixes=CHECK
33
; RUN: llc -o - %s -mtriple=x86_64-scei-ps4 | FileCheck %s --check-prefixes=CHECK,TRAP_AFTER_NORETURN
44
; RUN: llc -o - %s -mtriple=x86_64-apple-darwin | FileCheck %s --check-prefixes=CHECK,NO_TRAP_AFTER_NORETURN
5+
; RUN: llc --trap-unreachable -o - %s -mtriple=x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,TRAP_AFTER_NORETURN
6+
; RUN: llc --trap-unreachable -global-isel -o - %s -mtriple=x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,TRAP_AFTER_NORETURN
7+
; RUN: llc --trap-unreachable -fast-isel -o - %s -mtriple=x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,TRAP_AFTER_NORETURN
58

69
; CHECK-LABEL: call_exit:
710
; CHECK: callq {{_?}}exit
811
; TRAP_AFTER_NORETURN: ud2
9-
; NO_TRAP_AFTER_NORETURN-NOT: ud2
10-
; NORMAL-NOT: ud2
12+
; CHECK-NOT: ud2
1113
define i32 @call_exit() noreturn nounwind {
1214
tail call void @exit(i32 0)
1315
unreachable
1416
}
1517

1618
; CHECK-LABEL: trap:
1719
; CHECK: ud2
18-
; TRAP_AFTER_NORETURN: ud2
19-
; NO_TRAP_AFTER_NORETURN-NOT: ud2
20-
; NORMAL-NOT: ud2
20+
; CHECK-NOT: ud2
2121
define i32 @trap() noreturn nounwind {
2222
tail call void @llvm.trap()
2323
unreachable
2424
}
2525

26+
; CHECK-LABEL: trap_fn_attr:
27+
; CHECK: callq {{_?}}trap_func
28+
; TRAP_AFTER_NORETURN: ud2
29+
; CHECK-NOT: ud2
30+
define i32 @trap_fn_attr() noreturn nounwind {
31+
tail call void @llvm.trap() "trap-func-name"="trap_func"
32+
unreachable
33+
}
34+
35+
; CHECK-LABEL: noreturn_indirect:
36+
; CHECK: callq *%r{{.+}}
37+
; TRAP_AFTER_NORETURN: ud2
38+
; CHECK-NOT: ud2
39+
define i32 @noreturn_indirect(ptr %fptr) noreturn nounwind {
40+
tail call void (...) %fptr() noreturn nounwind
41+
unreachable
42+
}
43+
2644
; CHECK-LABEL: unreachable:
2745
; TRAP_AFTER_NORETURN: ud2
2846
; NO_TRAP_AFTER_NORETURN: ud2
29-
; NORMAL-NOT: ud2
30-
; NORMAL: # -- End function
47+
; CHECK-NOT: ud2
48+
; CHECK: # -- End function
3149
define i32 @unreachable() noreturn nounwind {
3250
unreachable
3351
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
; RUN: llc -o - %s -mtriple=x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK
2+
; RUN: llc -global-isel -o - %s -mtriple=x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK
3+
; RUN: llc -fast-isel -o - %s -mtriple=x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK
4+
; RUN: llc --trap-unreachable -o - %s -mtriple=x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,TRAP_UNREACHABLE
5+
; RUN: llc --trap-unreachable -global-isel -o - %s -mtriple=x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,TRAP_UNREACHABLE
6+
; RUN: llc --trap-unreachable -fast-isel -o - %s -mtriple=x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,TRAP_UNREACHABLE
7+
8+
; CHECK-LABEL: ubsantrap:
9+
; CHECK: ud1l 12(%eax), %eax
10+
; CHECK-NOT: ud2
11+
define i32 @ubsantrap() noreturn nounwind {
12+
call void @llvm.ubsantrap(i8 12)
13+
unreachable
14+
}
15+
16+
; CHECK-LABEL: ubsantrap_fn_attr:
17+
; CHECK: callq {{_?}}ubsantrap_func
18+
; TRAP_UNREACHABLE: ud2
19+
; CHECK-NOT: ud2
20+
define i32 @ubsantrap_fn_attr() noreturn nounwind {
21+
call void @llvm.ubsantrap(i8 12) "trap-func-name"="ubsantrap_func"
22+
unreachable
23+
}
24+
25+
declare void @llvm.ubsantrap(i8) cold noreturn nounwind

llvm/test/LTO/X86/unified-cfi.ll

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
; CHECK: jbe
88
; CHECK-NEXT: ud2
9-
; CHECK-NEXT: ud2
109

1110
; ModuleID = 'llvm/test/LTO/X86/unified-cfi.ll'
1211
source_filename = "cfi.cpp"

0 commit comments

Comments
 (0)