1 ; RUN: opt -functionattrs -S < %s | FileCheck %s --check-prefix=FNATTR
2 ; RUN: opt -passes=attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR
5 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
7 ; Test cases specifically designed for the "willreturn" function attribute.
8 ; We use FIXME's to indicate problems and missing attributes.
11 ; TEST 1 (positive case)
12 ; FNATTR: Function Attrs: noinline norecurse nounwind readnone uwtable
13 ; FNATTR-NEXT: define void @only_return()
14 ; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable willreturn
15 ; ATTRIBUTOR-NEXT: define void @only_return()
16 define void @only_return() #0 {
21 ; TEST 2 (positive & negative case)
23 ; recursive function which will halt
25 ; return n<=1? n : fib(n-1) + fib(n-2);
28 ; FNATTR: Function Attrs: noinline nounwind readnone uwtable
29 ; FNATTR-NEXT: define i32 @fib(i32 %0)
30 ; FIXME: missing willreturn
31 ; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable
32 ; ATTRIBUTOR-NEXT: define i32 @fib(i32 %0) local_unnamed_addr
33 define i32 @fib(i32 %0) local_unnamed_addr #0 {
34 %2 = icmp slt i32 %0, 2
35 br i1 %2, label %9, label %3
37 ; <label>:3: ; preds = %1
38 %4 = add nsw i32 %0, -1
39 %5 = tail call i32 @fib(i32 %4)
40 %6 = add nsw i32 %0, -2
41 %7 = tail call i32 @fib(i32 %6)
42 %8 = add nsw i32 %7, %5
45 ; <label>:9: ; preds = %1
50 ; recursive function which doesn't stop for some input.
51 ; int fact_maybe_not_halt(int n) {
55 ; return fact_maybe_not_halt( n > 0 ? n-1 : n) * n;
57 ; fact_maybe_not(-1) doesn't stop.
59 ; FNATTR: Function Attrs: noinline norecurse nounwind readnone uwtable
60 ; FNATTR-NOT: willreturn
61 ; FNATTR-NEXT: define i32 @fact_maybe_not_halt(i32 %0) local_unnamed_addr
62 ; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable
63 ; ATTRIBUTOR-NOT: willreturn
64 ; ATTRIBUTOR-NEXT: define i32 @fact_maybe_not_halt(i32 %0) local_unnamed_addr
65 define i32 @fact_maybe_not_halt(i32 %0) local_unnamed_addr #0 {
66 %2 = icmp eq i32 %0, 0
67 br i1 %2, label %11, label %3
69 ; <label>:3: ; preds = %1, %3
70 %4 = phi i32 [ %8, %3 ], [ %0, %1 ]
71 %5 = phi i32 [ %9, %3 ], [ 1, %1 ]
72 %6 = icmp sgt i32 %4, 0
73 %7 = sext i1 %6 to i32
74 %8 = add nsw i32 %4, %7
75 %9 = mul nsw i32 %4, %5
76 %10 = icmp eq i32 %8, 0
77 br i1 %10, label %11, label %3
79 ; <label>:11: ; preds = %3, %1
80 %12 = phi i32 [ 1, %1 ], [ %9, %3 ]
85 ; TEST 3 (positive case)
87 ; int fact_loop(int n ){
89 ; for(int i = 1;i<=n;i++){
95 ; FIXME: missing willreturn
96 ; FNATTR: Function Attrs: noinline norecurse nounwind readnone uwtable
97 ; FNATTR-NEXT: define i32 @fact_loop(i32 %0)
98 ; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable
99 ; ATTRIBUTOR-NEXT: define i32 @fact_loop(i32 %0) local_unnamed_addr
100 define i32 @fact_loop(i32 %0) local_unnamed_addr #0 {
101 %2 = icmp slt i32 %0, 1
102 br i1 %2, label %3, label %5
104 ; <label>:3: ; preds = %5, %1
105 %4 = phi i32 [ 1, %1 ], [ %8, %5 ]
108 ; <label>:5: ; preds = %1, %5
109 %6 = phi i32 [ %9, %5 ], [ 1, %1 ]
110 %7 = phi i32 [ %8, %5 ], [ 1, %1 ]
111 %8 = mul nsw i32 %6, %7
112 %9 = add nuw nsw i32 %6, 1
113 %10 = icmp eq i32 %6, %0
114 br i1 %10, label %3, label %5
117 ; TEST 4 (negative case)
119 ; void mutual_recursion1(){
120 ; mutual_recursion2();
122 ; void mutual_recursion2(){
123 ; mutual_recursion1();
126 ; FNATTR: Function Attrs: noinline nounwind readnone uwtable
127 ; FNATTR-NOT: willreturn
128 ; FNATTR-NEXT: define void @mutual_recursion1(i1 %c)
129 ; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable
130 ; ATTRIBUTOR-NOT: willreturn
131 ; ATTRIBUTOR-NEXT: define void @mutual_recursion1(i1 %c)
132 define void @mutual_recursion1(i1 %c) #0 {
133 br i1 %c, label %rec, label %end
135 call void @mutual_recursion2(i1 %c)
142 ; FNATTR: Function Attrs: noinline nounwind readnone uwtable
143 ; FNATTR-NOT: willreturn
144 ; FNATTR-NEXT: define void @mutual_recursion2(i1 %c)
145 ; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable
146 ; ATTRIBUTOR-NOT: willreturn
147 ; ATTRIBUTOR-NEXT: define void @mutual_recursion2(i1 %c)
148 define void @mutual_recursion2(i1 %c) #0 {
149 call void @mutual_recursion1(i1 %c)
154 ; TEST 5 (negative case)
155 ; call exit/abort (has noreturn attribute)
156 ; FNATTR: Function Attrs: noreturn
157 ; FNATTR-NEXT: declare void @exit(i32) local_unnamed_addr
158 ; ATTRIBUTOR: Function Attrs: noreturn
159 ; ATTRIBUTOR-NEXT: declare void @exit(i32) local_unnamed_add
160 declare void @exit(i32 %0) local_unnamed_addr noreturn
162 ; FNATTR: Function Attrs: noinline nounwind uwtable
163 ; FNATTR-NOT: willreturn
164 ; FNATTR-NEXT: define void @only_exit()
165 ; ATTRIBUTOR: Function Attrs: noinline noreturn nounwind uwtable
166 ; ATTRIBUTOR-NOT: willreturn
167 ; ATTRIBUTOR-NEXT: define void @only_exit() local_unnamed_addr
168 define void @only_exit() local_unnamed_addr #0 {
169 tail call void @exit(i32 0)
174 ; void conditional_exit(int cond, int *p){
183 ; FNATTR: Function Attrs: noinline nounwind uwtable
184 ; FNATTR-NOT: willreturn
185 ; FNATTR-NEXT: define void @conditional_exit(i32 %0, i32* nocapture readonly %1) local_unnamed_addr
186 ; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
187 ; ATTRIBUTOR-NOT: willreturn
188 ; ATTRIBUTOR-NEXT: define void @conditional_exit(i32 %0, i32* nocapture readonly %1) local_unnamed_addr
189 define void @conditional_exit(i32 %0, i32* nocapture readonly %1) local_unnamed_addr #0 {
190 %3 = icmp eq i32 %0, 0
191 br i1 %3, label %5, label %4
193 ; <label>:4: ; preds = %2
194 tail call void @exit(i32 0)
197 ; <label>:5: ; preds = %2
198 %6 = load i32, i32* %1, align 4
199 %7 = icmp eq i32 %6, 0
200 br i1 %7, label %9, label %8
202 ; <label>:8: ; preds = %5
203 tail call void @exit(i32 1)
206 ; <label>:9: ; preds = %5
210 ; TEST 6 (positive case)
211 ; Call intrinsic function
212 ; FIXME: missing willreturn
213 ; FNATTRS: Function Attrs: noinline readnone speculatable
214 ; FNATTRS-NEXT: declare float @llvm.floor.f32(float %0)
215 ; ATTRIBUTOR: Function Attrs: nounwind readnone speculatable
216 ; ATTRIBUTOR-NEXT: declare float @llvm.floor.f32(float)
217 declare float @llvm.floor.f32(float)
219 ; FNATTRS: Function Attrs: noinline nounwind readnone uwtable
220 ; FNATTRS-NEXT: define void @call_floor(float %a)
221 ; FIXME: missing willreturn
222 ; ATTRIBUTOR: Function Attrs: noinline nosync nounwind readnone uwtable
223 ; ATTRIBUTOR-NEXT: define void @call_floor(float %a)
224 define void @call_floor(float %a) #0 {
225 tail call float @llvm.floor.f32(float %a)
230 ; TEST 7 (negative case)
231 ; Call function declaration without willreturn
233 ; FNATTR: Function Attrs: noinline nounwind uwtable
234 ; FNATTR-NOT: willreturn
235 ; FNATTR-NEXT: declare void @maybe_noreturn()
236 ; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
237 ; ATTRIBUTOR-NOT: willreturn
238 ; ATTRIBUTOR-NEXT: declare void @maybe_noreturn()
239 declare void @maybe_noreturn() #0
241 ; FNATTR: Function Attrs: noinline nounwind uwtable
242 ; FNATTR-NOT: willreturn
243 ; FNATTR-NEXT: define void @call_maybe_noreturn()
244 ; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
245 ; ATTRIBUTOR-NOT: willreturn
246 ; ATTRIBUTOR-NEXT: define void @call_maybe_noreturn()
247 define void @call_maybe_noreturn() #0 {
248 tail call void @maybe_noreturn()
253 ; TEST 8 (positive case)
256 ; FNATTR: Function Attrs: norecurse willreturn
257 ; FNATTR-NEXT: declare void @will_return()
258 ; ATTRIBUTOR: Function Attrs: norecurse willreturn
259 ; ATTRIBUTOR-NEXT: declare void @will_return()
260 declare void @will_return() willreturn norecurse
262 ; FNATTR: Function Attrs: noinline norecurse nounwind uwtable
263 ; FNATTR-NEXT: define void @f1()
264 ; ATTRIBUTOR: Function Attrs: noinline norecurse nounwind uwtable willreturn
265 ; ATTRIBUTOR-NEXT: define void @f1()
266 define void @f1() #0 {
267 tail call void @will_return()
271 ; FNATTR: Function Attrs: noinline norecurse nounwind uwtable
272 ; FNATTR-NEXT: define void @f2()
273 ; ATTRIBUTOR: Function Attrs: noinline norecurse nounwind uwtable willreturn
274 ; ATTRIBUTOR-NEXT: define void @f2()
275 define void @f2() #0 {
281 ; TEST 9 (negative case)
282 ; call willreturn function in endless loop.
284 ; FNATTR: Function Attrs: noinline norecurse nounwind uwtable
285 ; FNATTR-NOT: willreturn
286 ; FNATTR-NEXT: define void @call_will_return_but_has_loop()
287 ; ATTRIBUTOR: Function Attrs: noinline norecurse noreturn nounwind uwtable
288 ; ATTRIBUTOR-NOT: willreturn
289 ; ATTRIBUTOR-NEXT: define void @call_will_return_but_has_loop()
290 define void @call_will_return_but_has_loop() #0 {
293 tail call void @will_return()
300 ; TEST 10 (positive case)
301 ; invoke a function with willreturn
303 ; FNATTR: Function Attrs: noinline uwtable willreturn
304 ; FNATTR-NEXT: declare i1 @maybe_raise_exception()
305 ; ATTRIBUTOR: Function Attrs: noinline uwtable willreturn
306 ; ATTRIBUTOR-NEXT: declare i1 @maybe_raise_exception()
307 declare i1 @maybe_raise_exception() #1 willreturn
309 ; FNATTR: Function Attrs: nounwind
310 ; FNATTR-NEXT: define void @invoke_test()
311 ; ATTRIBUTOR: Function Attrs: nounwind willreturn
312 ; ATTRIBUTOR-NEXT: define void @invoke_test()
313 define void @invoke_test() personality i32 (...)* @__gxx_personality_v0 {
314 invoke i1 @maybe_raise_exception()
315 to label %N unwind label %F
319 %val = landingpad { i8*, i32 }
324 declare i32 @__gxx_personality_v0(...)
327 ; TEST 11 (positive case)
328 ; counstant trip count
329 ; int loop_constant_trip_count(int*p){
331 ; for(int i = 0;i<10;i++){
337 ; FIXME: missing willreturn
338 ; FNATTR: Function Attrs: noinline norecurse nounwind readonly uwtable
339 ; FNATTR-NEXT: define i32 @loop_constant_trip_count(i32* nocapture readonly %0)
340 ; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readonly uwtable
341 ; ATTRIBUTOR-NEXT: define i32 @loop_constant_trip_count(i32* nocapture readonly %0)
342 define i32 @loop_constant_trip_count(i32* nocapture readonly %0) #0 {
345 ; <label>:2: ; preds = %3
348 ; <label>:3: ; preds = %3, %1
349 %4 = phi i64 [ 0, %1 ], [ %9, %3 ]
350 %5 = phi i32 [ 0, %1 ], [ %8, %3 ]
351 %6 = getelementptr inbounds i32, i32* %0, i64 %4
352 %7 = load i32, i32* %6, align 4
353 %8 = add nsw i32 %7, %5
354 %9 = add nuw nsw i64 %4, 1
355 %10 = icmp eq i64 %9, 10
356 br i1 %10, label %2, label %3
360 ; TEST 12 (negative case)
361 ; unbounded trip count
363 ; int loop_trip_count_unbound(unsigned s,unsigned e, int *p, int offset){
365 ; for(unsigned i = s;i != e;i+=offset){
370 ; FNATTR: Function Attrs: noinline norecurse nounwind readonly uwtable
371 ; FNATTR-NOT: willreturn
372 ; FNATTR-NEXT: define i32 @loop_trip_count_unbound(i32 %0, i32 %1, i32* nocapture readonly %2, i32 %3) local_unnamed_addr
373 ; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readonly uwtable
374 ; ATTRIBUTOR-NOT: willreturn
375 ; ATTRIBUTOR-NEXT: define i32 @loop_trip_count_unbound(i32 %0, i32 %1, i32* nocapture readonly %2, i32 %3) local_unnamed_addr
376 define i32 @loop_trip_count_unbound(i32 %0, i32 %1, i32* nocapture readonly %2, i32 %3) local_unnamed_addr #0 {
377 %5 = icmp eq i32 %0, %1
378 br i1 %5, label %6, label %8
380 ; <label>:6: ; preds = %8, %4
381 %7 = phi i32 [ 0, %4 ], [ %14, %8 ]
384 ; <label>:8: ; preds = %4, %8
385 %9 = phi i32 [ %15, %8 ], [ %0, %4 ]
386 %10 = phi i32 [ %14, %8 ], [ 0, %4 ]
387 %11 = zext i32 %9 to i64
388 %12 = getelementptr inbounds i32, i32* %2, i64 %11
389 %13 = load i32, i32* %12, align 4
390 %14 = add nsw i32 %13, %10
392 %16 = icmp eq i32 %15, %1
393 br i1 %16, label %6, label %8
397 ; TEST 13 (positive case)
398 ; Function Attrs: norecurse nounwind readonly uwtable
399 ; int loop_trip_dec(int n, int *p){
408 ; FIXME: missing willreturn
409 ; FNATTR: Function Attrs: noinline norecurse nounwind readonly uwtable
410 ; FNATTR-NEXT: define i32 @loop_trip_dec(i32 %0, i32* nocapture readonly %1)
411 ; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readonly uwtable
412 ; ATTRIBUTOR-NEXT: define i32 @loop_trip_dec(i32 %0, i32* nocapture readonly %1) local_unnamed_addr
414 define i32 @loop_trip_dec(i32 %0, i32* nocapture readonly %1) local_unnamed_addr #0 {
415 %3 = icmp sgt i32 %0, -1
416 br i1 %3, label %4, label %14
418 ; <label>:4: ; preds = %2
419 %5 = sext i32 %0 to i64
422 ; <label>:6: ; preds = %4, %6
423 %7 = phi i64 [ %5, %4 ], [ %12, %6 ]
424 %8 = phi i32 [ 0, %4 ], [ %11, %6 ]
425 %9 = getelementptr inbounds i32, i32* %1, i64 %7
426 %10 = load i32, i32* %9, align 4
427 %11 = add nsw i32 %10, %8
428 %12 = add nsw i64 %7, -1
429 %13 = icmp sgt i64 %7, 0
430 br i1 %13, label %6, label %14
432 ; <label>:14: ; preds = %6, %2
433 %15 = phi i32 [ 0, %2 ], [ %11, %6 ]
437 ; TEST 14 (positive case)
440 ; FNATTR: Function Attrs: noinline norecurse nounwind readnone uwtable
441 ; FNATTR-NEXT: define i32 @multiple_return(i32 %a)
442 ; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable willreturn
443 ; ATTRIBUTOR-NEXT: define i32 @multiple_return(i32 %a)
444 define i32 @multiple_return(i32 %a) #0 {
445 %b = icmp eq i32 %a, 0
446 br i1 %b, label %t, label %f
454 ; TEST 15 (positive & negative case)
457 ; 15.1 (positive case)
458 ; FNATTR: Function Attrs: noinline nounwind uwtable
459 ; FNATTR-NEXT: define void @unreachable_exit_positive1()
460 ; ATTRIBUTOR: Function Attrs: noinline norecurse nounwind uwtable willreturn
461 ; ATTRIBUTOR-NEXT: define void @unreachable_exit_positive1()
462 define void @unreachable_exit_positive1() #0 {
463 tail call void @will_return()
467 tail call void @exit(i32 0)
471 ; FIXME: missing willreturn
472 ; FNATTR: Function Attrs: noinline nounwind uwtable
473 ; FNATTR-NEXT: define i32 @unreachable_exit_positive2(i32 %0)
474 ; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable
475 ; ATTRIBUTOR-NEXT: define i32 @unreachable_exit_positive2(i32 %0)
476 define i32 @unreachable_exit_positive2(i32) local_unnamed_addr #0 {
477 %2 = icmp slt i32 %0, 1
478 br i1 %2, label %3, label %5
480 ; <label>:3: ; preds = %5, %1
481 %4 = phi i32 [ 1, %1 ], [ %8, %5 ]
484 ; <label>:5: ; preds = %1, %5
485 %6 = phi i32 [ %9, %5 ], [ 1, %1 ]
486 %7 = phi i32 [ %8, %5 ], [ 1, %1 ]
487 %8 = mul nsw i32 %6, %7
488 %9 = add nuw nsw i32 %6, 1
489 %10 = icmp eq i32 %6, %0
490 br i1 %10, label %3, label %5
493 tail call void @exit(i32 0)
500 ; FNATTR: Function Attrs: noinline nounwind uwtable
501 ; FNATTR-NOT: willreturn
502 ; FNATTR-NEXT: define void @unreachable_exit_negative1()
503 ; ATTRIBUTOR: Function Attrs: noinline noreturn nounwind uwtable
504 ; ATTRIBUTOR-NOT: willreturn
505 ; ATTRIBUTOR-NEXT: define void @unreachable_exit_negative1()
506 define void @unreachable_exit_negative1() #0 {
507 tail call void @exit(i32 0)
511 tail call void @exit(i32 0)
515 ; FNATTR: Function Attrs: noinline nounwind uwtable
516 ; FNATTR-NOT: willreturn
517 ; FNATTR-NEXT: define void @unreachable_exit_negative2()
518 ; ATTRIBUTOR: Function Attrs: nofree noinline norecurse noreturn nosync nounwind readnone uwtable
519 ; ATTRIBUTOR-NOT: willreturn
520 ; ATTRIBUTOR-NEXT: define void @unreachable_exit_negative2()
521 define void @unreachable_exit_negative2() #0 {
530 tail call void @exit(i32 0)
534 ; FNATTR: Function Attrs: noreturn nounwind
535 ; FNATTR-NEXT: declare void @llvm.eh.sjlj.longjmp(i8*)
536 ; ATTRIBUTOR: Function Attrs: noreturn nounwind
537 ; ATTRIBUTOR-NEXT: declare void @llvm.eh.sjlj.longjmp(i8*)
538 declare void @llvm.eh.sjlj.longjmp(i8*)
540 ; FNATTR: Function Attrs: noinline nounwind uwtable
541 ; FNATTR-NOT: willreturn
542 ; FNATTR-NEXT: define void @call_longjmp(i8* nocapture readnone %0) local_unnamed_addr #3 {
543 ; ATTRIBUTOR: Function Attrs: noinline noreturn nounwind uwtable
544 ; ATTRIBUTOR-NOT: willreturn
545 ; ATTRIBUTOR-NEXT: define void @call_longjmp(i8* nocapture readnone %0) local_unnamed_addr
546 define void @call_longjmp(i8* nocapture readnone %0) local_unnamed_addr #0 {
547 tail call void @llvm.eh.sjlj.longjmp(i8* %0)
552 attributes #0 = { nounwind uwtable noinline }
553 attributes #1 = { uwtable noinline }