1; RUN: opt < %s -instcombine -S | FileCheck %s
2;
3; Test that instcombine folds allocsize function calls properly.
4; Dummy arguments are inserted to verify that allocsize is picking the right
5; args, and to prove that arbitrary unfoldable values don't interfere with
6; allocsize if they're not used by allocsize.
7
8declare i8* @my_malloc(i8*, i32) allocsize(1)
9declare i8* @my_calloc(i8*, i8*, i32, i32) allocsize(2, 3)
10
11; CHECK-LABEL: define void @test_malloc
12define void @test_malloc(i8** %p, i64* %r) {
13  %1 = call i8* @my_malloc(i8* null, i32 100)
14  store i8* %1, i8** %p, align 8 ; To ensure objectsize isn't killed
15
16  %2 = call i64 @llvm.objectsize.i64.p0i8(i8* %1, i1 false)
17  ; CHECK: store i64 100
18  store i64 %2, i64* %r, align 8
19  ret void
20}
21
22; CHECK-LABEL: define void @test_calloc
23define void @test_calloc(i8** %p, i64* %r) {
24  %1 = call i8* @my_calloc(i8* null, i8* null, i32 100, i32 5)
25  store i8* %1, i8** %p, align 8 ; To ensure objectsize isn't killed
26
27  %2 = call i64 @llvm.objectsize.i64.p0i8(i8* %1, i1 false)
28  ; CHECK: store i64 500
29  store i64 %2, i64* %r, align 8
30  ret void
31}
32
33; Failure cases with non-constant values...
34; CHECK-LABEL: define void @test_malloc_fails
35define void @test_malloc_fails(i8** %p, i64* %r, i32 %n) {
36  %1 = call i8* @my_malloc(i8* null, i32 %n)
37  store i8* %1, i8** %p, align 8 ; To ensure objectsize isn't killed
38
39  ; CHECK: @llvm.objectsize.i64.p0i8
40  %2 = call i64 @llvm.objectsize.i64.p0i8(i8* %1, i1 false)
41  store i64 %2, i64* %r, align 8
42  ret void
43}
44
45; CHECK-LABEL: define void @test_calloc_fails
46define void @test_calloc_fails(i8** %p, i64* %r, i32 %n) {
47  %1 = call i8* @my_calloc(i8* null, i8* null, i32 %n, i32 5)
48  store i8* %1, i8** %p, align 8 ; To ensure objectsize isn't killed
49
50  ; CHECK: @llvm.objectsize.i64.p0i8
51  %2 = call i64 @llvm.objectsize.i64.p0i8(i8* %1, i1 false)
52  store i64 %2, i64* %r, align 8
53
54
55  %3 = call i8* @my_calloc(i8* null, i8* null, i32 100, i32 %n)
56  store i8* %3, i8** %p, align 8 ; To ensure objectsize isn't killed
57
58  ; CHECK: @llvm.objectsize.i64.p0i8
59  %4 = call i64 @llvm.objectsize.i64.p0i8(i8* %3, i1 false)
60  store i64 %4, i64* %r, align 8
61  ret void
62}
63
64declare i8* @my_malloc_outofline(i8*, i32) #0
65declare i8* @my_calloc_outofline(i8*, i8*, i32, i32) #1
66
67; Verifying that out of line allocsize is parsed correctly
68; CHECK-LABEL: define void @test_outofline
69define void @test_outofline(i8** %p, i64* %r) {
70  %1 = call i8* @my_malloc_outofline(i8* null, i32 100)
71  store i8* %1, i8** %p, align 8 ; To ensure objectsize isn't killed
72
73  %2 = call i64 @llvm.objectsize.i64.p0i8(i8* %1, i1 false)
74  ; CHECK: store i64 100
75  store i64 %2, i64* %r, align 8
76
77
78  %3 = call i8* @my_calloc_outofline(i8* null, i8* null, i32 100, i32 5)
79  store i8* %3, i8** %p, align 8 ; To ensure objectsize isn't killed
80
81  %4 = call i64 @llvm.objectsize.i64.p0i8(i8* %3, i1 false)
82  ; CHECK: store i64 500
83  store i64 %4, i64* %r, align 8
84  ret void
85}
86
87declare i8* @my_malloc_i64(i8*, i64) #0
88declare i8* @my_tiny_calloc(i8*, i8*, i8, i8) #1
89declare i8* @my_varied_calloc(i8*, i8*, i32, i8) #1
90
91; CHECK-LABEL: define void @test_overflow
92define void @test_overflow(i8** %p, i32* %r) {
93  %r64 = bitcast i32* %r to i64*
94
95  ; (2**31 + 1) * 2 > 2**31. So overflow. Yay.
96  %big_malloc = call i8* @my_calloc(i8* null, i8* null, i32 2147483649, i32 2)
97  store i8* %big_malloc, i8** %p, align 8
98
99  ; CHECK: @llvm.objectsize
100  %1 = call i32 @llvm.objectsize.i32.p0i8(i8* %big_malloc, i1 false)
101  store i32 %1, i32* %r, align 4
102
103
104  %big_little_malloc = call i8* @my_tiny_calloc(i8* null, i8* null, i8 127, i8 4)
105  store i8* %big_little_malloc, i8** %p, align 8
106
107  ; CHECK: store i32 508
108  %2 = call i32 @llvm.objectsize.i32.p0i8(i8* %big_little_malloc, i1 false)
109  store i32 %2, i32* %r, align 4
110
111
112  ; malloc(2**33)
113  %big_malloc_i64 = call i8* @my_malloc_i64(i8* null, i64 8589934592)
114  store i8* %big_malloc_i64, i8** %p, align 8
115
116  ; CHECK: @llvm.objectsize
117  %3 = call i32 @llvm.objectsize.i32.p0i8(i8* %big_malloc_i64, i1 false)
118  store i32 %3, i32* %r, align 4
119
120
121  %4 = call i64 @llvm.objectsize.i64.p0i8(i8* %big_malloc_i64, i1 false)
122  ; CHECK: store i64 8589934592
123  store i64 %4, i64* %r64, align 8
124
125
126  ; Just intended to ensure that we properly handle args of different types...
127  %varied_calloc = call i8* @my_varied_calloc(i8* null, i8* null, i32 1000, i8 5)
128  store i8* %varied_calloc, i8** %p, align 8
129
130  ; CHECK: store i32 5000
131  %5 = call i32 @llvm.objectsize.i32.p0i8(i8* %varied_calloc, i1 false)
132  store i32 %5, i32* %r, align 4
133
134  ret void
135}
136
137; CHECK-LABEL: define void @test_nobuiltin
138; We had a bug where `nobuiltin` would cause `allocsize` to be ignored in
139; @llvm.objectsize calculations.
140define void @test_nobuiltin(i8** %p, i64* %r) {
141  %1 = call i8* @my_malloc(i8* null, i32 100) nobuiltin
142  store i8* %1, i8** %p, align 8
143
144  %2 = call i64 @llvm.objectsize.i64.p0i8(i8* %1, i1 false)
145  ; CHECK: store i64 100
146  store i64 %2, i64* %r, align 8
147  ret void
148}
149
150attributes #0 = { allocsize(1) }
151attributes #1 = { allocsize(2, 3) }
152
153declare i32 @llvm.objectsize.i32.p0i8(i8*, i1)
154declare i64 @llvm.objectsize.i64.p0i8(i8*, i1)
155