Skip to content

Commit 7be55b0

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 } ``` Reviewed By: nikic Differential Revision: https://reviews.llvm.org/D144319
1 parent 4d5fd9b commit 7be55b0

File tree

2 files changed

+100
-0
lines changed

2 files changed

+100
-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 true;
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,86 @@
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_nonnull_noundef(i1 %cond, ptr %x) {
38+
; CHECK-LABEL: @test_ret_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 ptr @test_ret_nonnull(i1 %cond, ptr %x) {
55+
; CHECK-LABEL: @test_ret_nonnull(
56+
; CHECK-NEXT: entry:
57+
; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[COND:%.*]], ptr [[X:%.*]], ptr null
58+
; CHECK-NEXT: ret ptr [[SPEC_SELECT]]
59+
;
60+
entry:
61+
br i1 %cond, label %bb1, label %bb2
62+
63+
bb1:
64+
br label %bb2
65+
66+
bb2:
67+
%r = phi ptr [ null, %entry ], [ %x, %bb1 ]
68+
ret ptr %r
69+
}
70+
71+
define ptr @test_ret_ptr(i1 %cond, ptr %x) {
72+
; CHECK-LABEL: @test_ret_ptr(
73+
; CHECK-NEXT: entry:
74+
; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[COND:%.*]], ptr [[X:%.*]], ptr null
75+
; CHECK-NEXT: ret ptr [[SPEC_SELECT]]
76+
;
77+
entry:
78+
br i1 %cond, label %bb1, label %bb2
79+
80+
bb1:
81+
br label %bb2
82+
83+
bb2:
84+
%r = phi ptr [ null, %entry ], [ %x, %bb1 ]
85+
ret ptr %r
86+
}

0 commit comments

Comments
 (0)