Skip to content

Commit 05bda8b

Browse files
Annotate return values of allocation functions with dereferenceable_or_null
Summary: Example define dso_local noalias i8* @_Z6maixxnv() local_unnamed_addr #0 { entry: %call = tail call noalias dereferenceable_or_null(64) i8* @malloc(i64 64) #6 ret i8* %call } Reviewers: jdoerfert Reviewed By: jdoerfert Subscribers: aaron.ballman, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D66651 llvm-svn: 370168
1 parent b9d87b9 commit 05bda8b

File tree

8 files changed

+236
-105
lines changed

8 files changed

+236
-105
lines changed

llvm/include/llvm/Analysis/MemoryBuiltins.h

+5
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ bool isReallocLikeFn(const Value *V, const TargetLibraryInfo *TLI,
9393
/// reallocates memory (e.g., realloc).
9494
bool isReallocLikeFn(const Function *F, const TargetLibraryInfo *TLI);
9595

96+
/// Tests if a value is a call or invoke to a library function that
97+
/// allocates memory and throws if an allocation failed (e.g., new).
98+
bool isOpNewLikeFn(const Value *V, const TargetLibraryInfo *TLI,
99+
bool LookThroughBitCast = false);
100+
96101
//===----------------------------------------------------------------------===//
97102
// malloc Call Utility Functions.
98103
//

llvm/lib/Analysis/MemoryBuiltins.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,13 @@ bool llvm::isReallocLikeFn(const Function *F, const TargetLibraryInfo *TLI) {
276276
return getAllocationDataForFunction(F, ReallocLike, TLI).hasValue();
277277
}
278278

279+
/// Tests if a value is a call or invoke to a library function that
280+
/// allocates memory and throws if an allocation failed (e.g., new).
281+
bool llvm::isOpNewLikeFn(const Value *V, const TargetLibraryInfo *TLI,
282+
bool LookThroughBitCast) {
283+
return getAllocationData(V, OpNewLike, TLI, LookThroughBitCast).hasValue();
284+
}
285+
279286
/// extractMallocCall - Returns the corresponding CallInst if the instruction
280287
/// is a malloc call. Since CallInst::CreateMalloc() only creates calls, we
281288
/// ignore InvokeInst here.

llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp

+33
Original file line numberDiff line numberDiff line change
@@ -4178,8 +4178,41 @@ static IntrinsicInst *findInitTrampoline(Value *Callee) {
41784178
return nullptr;
41794179
}
41804180

4181+
static void annotateAnyAllocSite(CallBase &Call, const TargetLibraryInfo *TLI) {
4182+
ConstantInt *Op0C = dyn_cast<ConstantInt>(Call.getOperand(0));
4183+
ConstantInt *Op1C = (Call.getNumArgOperands() == 1)
4184+
? nullptr
4185+
: dyn_cast<ConstantInt>(Call.getOperand(1));
4186+
if ((Op0C && Op0C->isNullValue()) || (Op1C && Op1C->isNullValue()))
4187+
return;
4188+
if (isMallocLikeFn(&Call, TLI) && Op0C) {
4189+
Call.addAttribute(AttributeList::ReturnIndex,
4190+
Attribute::getWithDereferenceableOrNullBytes(
4191+
Call.getContext(), Op0C->getZExtValue()));
4192+
} else if (isOpNewLikeFn(&Call, TLI) && Op0C) {
4193+
Call.addAttribute(AttributeList::ReturnIndex,
4194+
Attribute::getWithDereferenceableBytes(
4195+
Call.getContext(), Op0C->getZExtValue()));
4196+
} else if (isReallocLikeFn(&Call, TLI) && Op1C) {
4197+
Call.addAttribute(AttributeList::ReturnIndex,
4198+
Attribute::getWithDereferenceableOrNullBytes(
4199+
Call.getContext(), Op1C->getZExtValue()));
4200+
} else if (isCallocLikeFn(&Call, TLI) && Op0C && Op1C) {
4201+
bool Overflow;
4202+
const APInt &N = Op0C->getValue();
4203+
APInt Size = N.umul_ov(Op1C->getValue(), Overflow);
4204+
if (!Overflow)
4205+
Call.addAttribute(AttributeList::ReturnIndex,
4206+
Attribute::getWithDereferenceableOrNullBytes(
4207+
Call.getContext(), Size.getZExtValue()));
4208+
}
4209+
}
4210+
41814211
/// Improvements for call, callbr and invoke instructions.
41824212
Instruction *InstCombiner::visitCallBase(CallBase &Call) {
4213+
if (isAllocationFn(&Call, &TLI))
4214+
annotateAnyAllocSite(Call, &TLI);
4215+
41834216
if (isAllocLikeFn(&Call, &TLI))
41844217
return visitAllocSite(Call);
41854218

Original file line numberDiff line numberDiff line change
@@ -1,27 +1,30 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
12
; RUN: opt -instcombine -S < %s | FileCheck %s
23

34
@gp = global i32* null, align 8
45

56
declare i8* @malloc(i64) #1
67

78
define i1 @compare_global_trivialeq() {
9+
; CHECK-LABEL: @compare_global_trivialeq(
10+
; CHECK-NEXT: ret i1 false
11+
;
812
%m = call i8* @malloc(i64 4)
913
%bc = bitcast i8* %m to i32*
1014
%lgp = load i32*, i32** @gp, align 8
1115
%cmp = icmp eq i32* %bc, %lgp
1216
ret i1 %cmp
13-
; CHECK-LABEL: compare_global_trivialeq
14-
; CHECK: ret i1 false
1517
}
1618

1719
define i1 @compare_global_trivialne() {
20+
; CHECK-LABEL: @compare_global_trivialne(
21+
; CHECK-NEXT: ret i1 true
22+
;
1823
%m = call i8* @malloc(i64 4)
1924
%bc = bitcast i8* %m to i32*
2025
%lgp = load i32*, i32** @gp, align 8
2126
%cmp = icmp ne i32* %bc, %lgp
2227
ret i1 %cmp
23-
; CHECK-LABEL: compare_global_trivialne
24-
; CHECK: ret i1 true
2528
}
2629

2730

@@ -30,102 +33,143 @@ define i1 @compare_global_trivialne() {
3033
; The comparison should fold to false irrespective of whether the call to malloc can be elided or not
3134
declare void @f()
3235
define i1 @compare_and_call_with_deopt() {
33-
; CHECK-LABEL: compare_and_call_with_deopt
36+
; CHECK-LABEL: @compare_and_call_with_deopt(
37+
; CHECK-NEXT: [[M:%.*]] = call dereferenceable_or_null(24) i8* @malloc(i64 24)
38+
; CHECK-NEXT: tail call void @f() [ "deopt"(i8* [[M]]) ]
39+
; CHECK-NEXT: ret i1 false
40+
;
3441
%m = call i8* @malloc(i64 24)
3542
%bc = bitcast i8* %m to i32*
3643
%lgp = load i32*, i32** @gp, align 8, !nonnull !0
3744
%cmp = icmp eq i32* %lgp, %bc
3845
tail call void @f() [ "deopt"(i8* %m) ]
3946
ret i1 %cmp
40-
; CHECK: ret i1 false
4147
}
4248

4349
; Same functon as above with deopt operand in function f, but comparison is NE
4450
define i1 @compare_ne_and_call_with_deopt() {
45-
; CHECK-LABEL: compare_ne_and_call_with_deopt
51+
; CHECK-LABEL: @compare_ne_and_call_with_deopt(
52+
; CHECK-NEXT: [[M:%.*]] = call dereferenceable_or_null(24) i8* @malloc(i64 24)
53+
; CHECK-NEXT: tail call void @f() [ "deopt"(i8* [[M]]) ]
54+
; CHECK-NEXT: ret i1 true
55+
;
4656
%m = call i8* @malloc(i64 24)
4757
%bc = bitcast i8* %m to i32*
4858
%lgp = load i32*, i32** @gp, align 8, !nonnull !0
4959
%cmp = icmp ne i32* %lgp, %bc
5060
tail call void @f() [ "deopt"(i8* %m) ]
5161
ret i1 %cmp
52-
; CHECK: ret i1 true
5362
}
5463

5564
; Same function as above, but global not marked nonnull, and we cannot fold the comparison
5665
define i1 @compare_ne_global_maybe_null() {
57-
; CHECK-LABEL: compare_ne_global_maybe_null
66+
; CHECK-LABEL: @compare_ne_global_maybe_null(
67+
; CHECK-NEXT: [[M:%.*]] = call dereferenceable_or_null(24) i8* @malloc(i64 24)
68+
; CHECK-NEXT: [[BC:%.*]] = bitcast i8* [[M]] to i32*
69+
; CHECK-NEXT: [[LGP:%.*]] = load i32*, i32** @gp, align 8
70+
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32* [[LGP]], [[BC]]
71+
; CHECK-NEXT: tail call void @f() [ "deopt"(i8* [[M]]) ]
72+
; CHECK-NEXT: ret i1 [[CMP]]
73+
;
5874
%m = call i8* @malloc(i64 24)
5975
%bc = bitcast i8* %m to i32*
6076
%lgp = load i32*, i32** @gp
6177
%cmp = icmp ne i32* %lgp, %bc
6278
tail call void @f() [ "deopt"(i8* %m) ]
6379
ret i1 %cmp
64-
; CHECK: ret i1 %cmp
6580
}
6681

6782
; FIXME: The comparison should fold to false since %m escapes (call to function escape)
6883
; after the comparison.
6984
declare void @escape(i8*)
7085
define i1 @compare_and_call_after() {
71-
; CHECK-LABEL: compare_and_call_after
86+
; CHECK-LABEL: @compare_and_call_after(
87+
; CHECK-NEXT: [[M:%.*]] = call dereferenceable_or_null(24) i8* @malloc(i64 24)
88+
; CHECK-NEXT: [[BC:%.*]] = bitcast i8* [[M]] to i32*
89+
; CHECK-NEXT: [[LGP:%.*]] = load i32*, i32** @gp, align 8, !nonnull !0
90+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32* [[LGP]], [[BC]]
91+
; CHECK-NEXT: br i1 [[CMP]], label [[ESCAPE_CALL:%.*]], label [[JUST_RETURN:%.*]]
92+
; CHECK: escape_call:
93+
; CHECK-NEXT: call void @escape(i8* [[M]])
94+
; CHECK-NEXT: ret i1 true
95+
; CHECK: just_return:
96+
; CHECK-NEXT: ret i1 [[CMP]]
97+
;
7298
%m = call i8* @malloc(i64 24)
7399
%bc = bitcast i8* %m to i32*
74100
%lgp = load i32*, i32** @gp, align 8, !nonnull !0
75101
%cmp = icmp eq i32* %bc, %lgp
76102
br i1 %cmp, label %escape_call, label %just_return
77103

78104
escape_call:
79-
call void @escape(i8* %m)
80-
ret i1 true
105+
call void @escape(i8* %m)
106+
ret i1 true
81107

82108
just_return:
83-
ret i1 %cmp
109+
ret i1 %cmp
84110
}
85111

86112
define i1 @compare_distinct_mallocs() {
113+
; CHECK-LABEL: @compare_distinct_mallocs(
114+
; CHECK-NEXT: ret i1 false
115+
;
87116
%m = call i8* @malloc(i64 4)
88117
%n = call i8* @malloc(i64 4)
89118
%cmp = icmp eq i8* %m, %n
90119
ret i1 %cmp
91-
; CHECK-LABEL: compare_distinct_mallocs
92-
; CHECK: ret i1 false
93120
}
94121

95-
; the compare is folded to true since the folding compare looks through bitcasts.
96-
; call to malloc and the bitcast instructions are elided after that since there are no uses of the malloc
122+
; the compare is folded to true since the folding compare looks through bitcasts.
123+
; call to malloc and the bitcast instructions are elided after that since there are no uses of the malloc
97124
define i1 @compare_samepointer_under_bitcast() {
125+
; CHECK-LABEL: @compare_samepointer_under_bitcast(
126+
; CHECK-NEXT: ret i1 true
127+
;
98128
%m = call i8* @malloc(i64 4)
99129
%bc = bitcast i8* %m to i32*
100130
%bcback = bitcast i32* %bc to i8*
101131
%cmp = icmp eq i8* %m, %bcback
102132
ret i1 %cmp
103-
; CHECK-LABEL: compare_samepointer_under_bitcast
104-
; CHECK: ret i1 true
105133
}
106134

107-
; the compare is folded to true since the folding compare looks through bitcasts.
135+
; the compare is folded to true since the folding compare looks through bitcasts.
108136
; The malloc call for %m cannot be elided since it is used in the call to function f.
109137
define i1 @compare_samepointer_escaped() {
138+
; CHECK-LABEL: @compare_samepointer_escaped(
139+
; CHECK-NEXT: [[M:%.*]] = call dereferenceable_or_null(4) i8* @malloc(i64 4)
140+
; CHECK-NEXT: call void @f() [ "deopt"(i8* [[M]]) ]
141+
; CHECK-NEXT: ret i1 true
142+
;
110143
%m = call i8* @malloc(i64 4)
111144
%bc = bitcast i8* %m to i32*
112145
%bcback = bitcast i32* %bc to i8*
113146
%cmp = icmp eq i8* %m, %bcback
114147
call void @f() [ "deopt"(i8* %m) ]
115148
ret i1 %cmp
116-
; CHECK-LABEL: compare_samepointer_escaped
117-
; CHECK-NEXT: %m = call i8* @malloc(i64 4)
118-
; CHECK-NEXT: call void @f() [ "deopt"(i8* %m) ]
119-
; CHECK: ret i1 true
120149
}
121150

122151
; Technically, we can fold the %cmp2 comparison, even though %m escapes through
123152
; the ret statement since `ret` terminates the function and we cannot reach from
124-
; the ret to cmp.
153+
; the ret to cmp.
125154
; FIXME: Folding this %cmp2 when %m escapes through ret could be an issue with
126155
; cross-threading data dependencies since we do not make the distinction between
127156
; atomic and non-atomic loads in capture tracking.
128157
define i8* @compare_ret_escape(i8* %c) {
158+
; CHECK-LABEL: @compare_ret_escape(
159+
; CHECK-NEXT: [[M:%.*]] = call dereferenceable_or_null(4) i8* @malloc(i64 4)
160+
; CHECK-NEXT: [[N:%.*]] = call dereferenceable_or_null(4) i8* @malloc(i64 4)
161+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8* [[N]], [[C:%.*]]
162+
; CHECK-NEXT: br i1 [[CMP]], label [[RETST:%.*]], label [[CHK:%.*]]
163+
; CHECK: retst:
164+
; CHECK-NEXT: ret i8* [[M]]
165+
; CHECK: chk:
166+
; CHECK-NEXT: [[BC:%.*]] = bitcast i8* [[M]] to i32*
167+
; CHECK-NEXT: [[LGP:%.*]] = load i32*, i32** @gp, align 8, !nonnull !0
168+
; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i32* [[LGP]], [[BC]]
169+
; CHECK-NEXT: br i1 [[CMP2]], label [[RETST]], label [[CHK2:%.*]]
170+
; CHECK: chk2:
171+
; CHECK-NEXT: ret i8* [[N]]
172+
;
129173
%m = call i8* @malloc(i64 4)
130174
%n = call i8* @malloc(i64 4)
131175
%cmp = icmp eq i8* %n, %c
@@ -142,23 +186,21 @@ chk:
142186

143187
chk2:
144188
ret i8* %n
145-
; CHECK-LABEL: compare_ret_escape
146-
; CHECK: %cmp = icmp eq i8* %n, %c
147-
; CHECK: %cmp2 = icmp eq i32* %lgp, %bc
148189
}
149190

150191
; The malloc call for %m cannot be elided since it is used in the call to function f.
151192
; However, the cmp can be folded to true as %n doesnt escape and %m, %n are distinct allocations
152193
define i1 @compare_distinct_pointer_escape() {
194+
; CHECK-LABEL: @compare_distinct_pointer_escape(
195+
; CHECK-NEXT: [[M:%.*]] = call dereferenceable_or_null(4) i8* @malloc(i64 4)
196+
; CHECK-NEXT: tail call void @f() [ "deopt"(i8* [[M]]) ]
197+
; CHECK-NEXT: ret i1 true
198+
;
153199
%m = call i8* @malloc(i64 4)
154200
%n = call i8* @malloc(i64 4)
155201
tail call void @f() [ "deopt"(i8* %m) ]
156202
%cmp = icmp ne i8* %m, %n
157203
ret i1 %cmp
158-
; CHECK-LABEL: compare_distinct_pointer_escape
159-
; CHECK-NEXT: %m = call i8* @malloc(i64 4)
160-
; CHECK-NEXT: tail call void @f() [ "deopt"(i8* %m) ]
161-
; CHECK-NEXT: ret i1 true
162204
}
163205

164206
!0 = !{}

llvm/test/Transforms/InstCombine/deref-alloc-fns.ll

+9-10
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ define noalias i8* @malloc_nonconstant_size(i64 %n) {
1717

1818
define noalias i8* @malloc_constant_size() {
1919
; CHECK-LABEL: @malloc_constant_size(
20-
; CHECK-NEXT: [[CALL:%.*]] = tail call noalias i8* @malloc(i64 40)
20+
; CHECK-NEXT: [[CALL:%.*]] = tail call noalias dereferenceable_or_null(40) i8* @malloc(i64 40)
2121
; CHECK-NEXT: ret i8* [[CALL]]
2222
;
2323
%call = tail call noalias i8* @malloc(i64 40)
@@ -35,7 +35,7 @@ define noalias i8* @malloc_constant_size2() {
3535

3636
define noalias i8* @malloc_constant_size3() {
3737
; CHECK-LABEL: @malloc_constant_size3(
38-
; CHECK-NEXT: [[CALL:%.*]] = tail call noalias dereferenceable(80) i8* @malloc(i64 40)
38+
; CHECK-NEXT: [[CALL:%.*]] = tail call noalias dereferenceable(80) dereferenceable_or_null(40) i8* @malloc(i64 40)
3939
; CHECK-NEXT: ret i8* [[CALL]]
4040
;
4141
%call = tail call noalias dereferenceable(80) i8* @malloc(i64 40)
@@ -72,7 +72,7 @@ define noalias i8* @realloc_constant_zero_size(i8* %p) {
7272

7373
define noalias i8* @realloc_constant_size(i8* %p) {
7474
; CHECK-LABEL: @realloc_constant_size(
75-
; CHECK-NEXT: [[CALL:%.*]] = tail call noalias i8* @realloc(i8* [[P:%.*]], i64 40)
75+
; CHECK-NEXT: [[CALL:%.*]] = tail call noalias dereferenceable_or_null(40) i8* @realloc(i8* [[P:%.*]], i64 40)
7676
; CHECK-NEXT: ret i8* [[CALL]]
7777
;
7878
%call = tail call noalias i8* @realloc(i8* %p, i64 40)
@@ -136,7 +136,7 @@ define noalias i8* @calloc_constant_zero_size3(i64 %n) {
136136

137137
define noalias i8* @calloc_constant_size() {
138138
; CHECK-LABEL: @calloc_constant_size(
139-
; CHECK-NEXT: [[CALL:%.*]] = tail call noalias i8* @calloc(i64 16, i64 8)
139+
; CHECK-NEXT: [[CALL:%.*]] = tail call noalias dereferenceable_or_null(128) i8* @calloc(i64 16, i64 8)
140140
; CHECK-NEXT: ret i8* [[CALL]]
141141
;
142142
%call = tail call noalias i8* @calloc(i64 16, i64 8)
@@ -152,7 +152,6 @@ define noalias i8* @calloc_constant_size_overflow() {
152152
ret i8* %call
153153
}
154154

155-
156155
define noalias i8* @op_new_nonconstant_size(i64 %n) {
157156
; CHECK-LABEL: @op_new_nonconstant_size(
158157
; CHECK-NEXT: [[CALL:%.*]] = tail call i8* @_Znam(i64 [[N:%.*]])
@@ -162,17 +161,17 @@ define noalias i8* @op_new_nonconstant_size(i64 %n) {
162161
ret i8* %call
163162
}
164163

165-
define noalias i8* @op_new_constant_zero_size() {
166-
; CHECK-LABEL: @op_new_constant_zero_size(
167-
; CHECK-NEXT: [[CALL:%.*]] = tail call i8* @_Znam(i64 40)
164+
define noalias i8* @op_new_constant_size() {
165+
; CHECK-LABEL: @op_new_constant_size(
166+
; CHECK-NEXT: [[CALL:%.*]] = tail call dereferenceable_or_null(40) i8* @_Znam(i64 40)
168167
; CHECK-NEXT: ret i8* [[CALL]]
169168
;
170169
%call = tail call i8* @_Znam(i64 40)
171170
ret i8* %call
172171
}
173172

174-
define noalias i8* @op_new_constant_size() {
175-
; CHECK-LABEL: @op_new_constant_size(
173+
define noalias i8* @op_new_constant_zero_size() {
174+
; CHECK-LABEL: @op_new_constant_zero_size(
176175
; CHECK-NEXT: [[CALL:%.*]] = tail call i8* @_Znam(i64 0)
177176
; CHECK-NEXT: ret i8* [[CALL]]
178177
;

0 commit comments

Comments
 (0)