Skip to content

Commit b6eed9a

Browse files
committed
[SimplifyCFG] Check if the return instruction causes undefined behavior
This should fix rust-lang/rust#107681. Return undefined to a noundef return value is undefined. Example: ``` define noundef i32 @test_ret_noundef(i1 %cond) { entry: br i1 %cond, label %bb1, label %bb2 bb1: br label %bb2 bb2: %r = phi i32 [ undef, %entry ], [ 1, %bb1 ] ret i32 %r } ``` Differential Revision: https://reviews.llvm.org/D144319
1 parent 37f5c59 commit b6eed9a

File tree

2 files changed

+136
-0
lines changed

2 files changed

+136
-0
lines changed

llvm/lib/Transforms/Utils/SimplifyCFG.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -7126,6 +7126,20 @@ static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool PtrValu
71267126
return passingValueIsAlwaysUndefined(V, GEP, PtrValueMayBeModified);
71277127
}
71287128

7129+
// Look through return.
7130+
if (ReturnInst *Ret = dyn_cast<ReturnInst>(Use)) {
7131+
bool HasNoUndefAttr =
7132+
Ret->getFunction()->hasRetAttribute(Attribute::NoUndef);
7133+
// Return undefined to a noundef return value is undefined.
7134+
if (isa<UndefValue>(C) && HasNoUndefAttr)
7135+
return true;
7136+
// Return null to a nonnull+noundef return value is undefined.
7137+
if (C->isNullValue() && HasNoUndefAttr &&
7138+
Ret->getFunction()->hasRetAttribute(Attribute::NonNull)) {
7139+
return !PtrValueMayBeModified;
7140+
}
7141+
}
7142+
71297143
// Look through bitcasts.
71307144
if (BitCastInst *BC = dyn_cast<BitCastInst>(Use))
71317145
return passingValueIsAlwaysUndefined(V, BC, PtrValueMayBeModified);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2+
; RUN: opt -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -S < %s | FileCheck %s
3+
4+
define noundef i32 @test_ret_noundef(i1 %cond) {
5+
; CHECK-LABEL: @test_ret_noundef(
6+
; CHECK-NEXT: entry:
7+
; CHECK-NEXT: ret i32 1
8+
;
9+
entry:
10+
br i1 %cond, label %bb1, label %bb2
11+
12+
bb1:
13+
br label %bb2
14+
15+
bb2:
16+
%r = phi i32 [ undef, %entry ], [ 1, %bb1 ]
17+
ret i32 %r
18+
}
19+
20+
define i32 @test_ret(i1 %cond) {
21+
; CHECK-LABEL: @test_ret(
22+
; CHECK-NEXT: entry:
23+
; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[COND:%.*]], i32 1, i32 undef
24+
; CHECK-NEXT: ret i32 [[SPEC_SELECT]]
25+
;
26+
entry:
27+
br i1 %cond, label %bb1, label %bb2
28+
29+
bb1:
30+
br label %bb2
31+
32+
bb2:
33+
%r = phi i32 [ undef, %entry ], [ 1, %bb1 ]
34+
ret i32 %r
35+
}
36+
37+
define nonnull noundef ptr @test_ret_ptr_nonnull_noundef(i1 %cond, ptr %x) {
38+
; CHECK-LABEL: @test_ret_ptr_nonnull_noundef(
39+
; CHECK-NEXT: entry:
40+
; CHECK-NEXT: call void @llvm.assume(i1 [[COND:%.*]])
41+
; CHECK-NEXT: ret ptr [[X:%.*]]
42+
;
43+
entry:
44+
br i1 %cond, label %bb1, label %bb2
45+
46+
bb1:
47+
br label %bb2
48+
49+
bb2:
50+
%r = phi ptr [ null, %entry ], [ %x, %bb1 ]
51+
ret ptr %r
52+
}
53+
54+
define nonnull noundef ptr @test_ret_ptr_nonnull_noundef_gep_nonzero(i1 %cond, ptr %x) {
55+
; CHECK-LABEL: @test_ret_ptr_nonnull_noundef_gep_nonzero(
56+
; CHECK-NEXT: entry:
57+
; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[COND:%.*]], ptr [[X:%.*]], ptr null
58+
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds ptr, ptr [[SPEC_SELECT]], i64 12
59+
; CHECK-NEXT: ret ptr [[GEP]]
60+
;
61+
entry:
62+
br i1 %cond, label %bb1, label %bb2
63+
64+
bb1:
65+
br label %bb2
66+
67+
bb2:
68+
%phi = phi ptr [ null, %entry ], [ %x, %bb1 ]
69+
%gep = getelementptr inbounds ptr, ptr %phi, i64 12
70+
ret ptr %gep
71+
}
72+
73+
define nonnull ptr @test_ret_ptr_nonnull(i1 %cond, ptr %x) {
74+
; CHECK-LABEL: @test_ret_ptr_nonnull(
75+
; CHECK-NEXT: entry:
76+
; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[COND:%.*]], ptr [[X:%.*]], ptr null
77+
; CHECK-NEXT: ret ptr [[SPEC_SELECT]]
78+
;
79+
entry:
80+
br i1 %cond, label %bb1, label %bb2
81+
82+
bb1:
83+
br label %bb2
84+
85+
bb2:
86+
%r = phi ptr [ null, %entry ], [ %x, %bb1 ]
87+
ret ptr %r
88+
}
89+
90+
define noundef ptr @test_ret_ptr_noundef(i1 %cond, ptr %x) {
91+
; CHECK-LABEL: @test_ret_ptr_noundef(
92+
; CHECK-NEXT: entry:
93+
; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[COND:%.*]], ptr [[X:%.*]], ptr null
94+
; CHECK-NEXT: ret ptr [[SPEC_SELECT]]
95+
;
96+
entry:
97+
br i1 %cond, label %bb1, label %bb2
98+
99+
bb1:
100+
br label %bb2
101+
102+
bb2:
103+
%r = phi ptr [ null, %entry ], [ %x, %bb1 ]
104+
ret ptr %r
105+
}
106+
107+
define ptr @test_ret_ptr(i1 %cond, ptr %x) {
108+
; CHECK-LABEL: @test_ret_ptr(
109+
; CHECK-NEXT: entry:
110+
; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[COND:%.*]], ptr [[X:%.*]], ptr null
111+
; CHECK-NEXT: ret ptr [[SPEC_SELECT]]
112+
;
113+
entry:
114+
br i1 %cond, label %bb1, label %bb2
115+
116+
bb1:
117+
br label %bb2
118+
119+
bb2:
120+
%r = phi ptr [ null, %entry ], [ %x, %bb1 ]
121+
ret ptr %r
122+
}

0 commit comments

Comments
 (0)