llvm.org GIT mirror llvm / 8a0fc5b
[Attr] Add "willreturn" function attribute This patch introduces a new function attribute, willreturn, to indicate that a call of this function will either exhibit undefined behavior or comes back and continues execution at a point in the existing call stack that includes the current invocation. This attribute guarantees that the function does not have any endless loops, endless recursion, or terminating functions like abort or exit. Patch by Hideto Ueno (@uenoku) Reviewers: jdoerfert Subscribers: mehdi_amini, hiraditya, steven_wu, dexonsmith, lebedev.ri, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D62801 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@364555 91177308-0d34-0410-b5e6-96231b3b80d8 Johannes Doerfert a month ago
13 changed file(s) with 503 addition(s) and 3 deletion(s). Raw diff Collapse all Expand all
14741474 This function attribute indicates that the function does not call itself
14751475 either directly or indirectly down any possible call path. This produces
14761476 undefined behavior at runtime if the function ever does recurse.
1477 ``willreturn``
1478 This function attribute indicates that a call of this function will
1479 either exhibit undefined behavior or comes back and continues execution
1480 at a point in the existing call stack that includes the current invocation.
1481 Annotated functions may still raise an exception, i.a., ``nounwind`` is not implied.
1482 If an invocation of an annotated function does not return control back
1483 to a point in the call stack, the behavior is undefined.
14771484 ``nounwind``
14781485 This function attribute indicates that the function never raises an
14791486 exception. If the function does raise an exception, its runtime
605605 ATTR_KIND_OPT_FOR_FUZZING = 57,
606606 ATTR_KIND_SHADOWCALLSTACK = 58,
607607 ATTR_KIND_SPECULATIVE_LOAD_HARDENING = 59,
608 ATTR_KIND_IMMARG = 60
608 ATTR_KIND_IMMARG = 60,
609 ATTR_KIND_WILLRETURN = 61,
609610 };
610611
611612 enum ComdatSelectionKindCodes {
194194
195195 /// Function must be in a unwind table.
196196 def UWTable : EnumAttr<"uwtable">;
197
198 /// Function always comes back to callsite.
199 def WillReturn : EnumAttr<"willreturn">;
197200
198201 /// Function only writes to memory.
199202 def WriteOnly : EnumAttr<"writeonly">;
682682 KEYWORD(swifterror);
683683 KEYWORD(swiftself);
684684 KEYWORD(uwtable);
685 KEYWORD(willreturn);
685686 KEYWORD(writeonly);
686687 KEYWORD(zeroext);
687688 KEYWORD(immarg);
13141314 break;
13151315 case lltok::kw_strictfp: B.addAttribute(Attribute::StrictFP); break;
13161316 case lltok::kw_uwtable: B.addAttribute(Attribute::UWTable); break;
1317 case lltok::kw_willreturn: B.addAttribute(Attribute::WillReturn); break;
13171318 case lltok::kw_writeonly: B.addAttribute(Attribute::WriteOnly); break;
13181319
13191320 // Error handling.
224224 kw_swifterror,
225225 kw_swiftself,
226226 kw_uwtable,
227 kw_willreturn,
227228 kw_writeonly,
228229 kw_zeroext,
229230 kw_immarg,
12711271 return 1ULL << 60;
12721272 case Attribute::ImmArg:
12731273 return 1ULL << 61;
1274 case Attribute::WillReturn:
1275 return 1ULL << 62;
12741276 case Attribute::Dereferenceable:
12751277 llvm_unreachable("dereferenceable attribute not supported in raw format");
12761278 break;
15091511 return Attribute::SwiftSelf;
15101512 case bitc::ATTR_KIND_UW_TABLE:
15111513 return Attribute::UWTable;
1514 case bitc::ATTR_KIND_WILLRETURN:
1515 return Attribute::WillReturn;
15121516 case bitc::ATTR_KIND_WRITEONLY:
15131517 return Attribute::WriteOnly;
15141518 case bitc::ATTR_KIND_Z_EXT:
709709 return bitc::ATTR_KIND_SWIFT_SELF;
710710 case Attribute::UWTable:
711711 return bitc::ATTR_KIND_UW_TABLE;
712 case Attribute::WillReturn:
713 return bitc::ATTR_KIND_WILLRETURN;
712714 case Attribute::WriteOnly:
713715 return bitc::ATTR_KIND_WRITEONLY;
714716 case Attribute::ZExt:
332332 return "noredzone";
333333 if (hasAttribute(Attribute::NoReturn))
334334 return "noreturn";
335 if (hasAttribute(Attribute::WillReturn))
336 return "willreturn";
335337 if (hasAttribute(Attribute::NoCfCheck))
336338 return "nocf_check";
337339 if (hasAttribute(Attribute::NoRecurse))
14851485 static bool isFuncOnlyAttr(Attribute::AttrKind Kind) {
14861486 switch (Kind) {
14871487 case Attribute::NoReturn:
1488 case Attribute::WillReturn:
14881489 case Attribute::NoCfCheck:
14891490 case Attribute::NoUnwind:
14901491 case Attribute::NoInline:
800800 case Attribute::StructRet:
801801 case Attribute::SwiftError:
802802 case Attribute::SwiftSelf:
803 case Attribute::WillReturn:
803804 case Attribute::WriteOnly:
804805 case Attribute::ZExt:
805806 case Attribute::ImmArg:
203203 ; CHECK: define void @f34()
204204 {
205205 call void @nobuiltin() nobuiltin
206 ; CHECK: call void @nobuiltin() #36
206 ; CHECK: call void @nobuiltin() #37
207207 ret void;
208208 }
209209
346346
347347 ; CHECK: define void @f59() #35
348348 define void @f59() shadowcallstack
349 {
350 ret void
351 }
352
353 ; CHECK: define void @f60() #36
354 define void @f60() willreturn
349355 {
350356 ret void
351357 }
386392 ; CHECK: attributes #33 = { speculatable }
387393 ; CHECK: attributes #34 = { sanitize_hwaddress }
388394 ; CHECK: attributes #35 = { shadowcallstack }
389 ; CHECK: attributes #36 = { nobuiltin }
395 ; CHECK: attributes #36 = { willreturn }
396 ; CHECK: attributes #37 = { nobuiltin }
0 ; RUN: opt -functionattrs -S < %s | FileCheck %s --check-prefix=FNATTR
1
2 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
3
4 ; Test cases specifically designed for the "willreturn" function attribute.
5 ; We use FIXME's to indicate problems and missing attributes.
6
7
8 ; TEST 1 (positive case)
9 ; FIXME: missing willreturn
10 ; FNATTR: Function Attrs: noinline norecurse nounwind readnone uwtable
11 ; FNATTR-NEXT: define void @only_return()
12 define void @only_return() #0 {
13 ret void
14 }
15
16
17 ; TEST 2 (positive & negative case)
18 ; 2.1 (positive case)
19 ; recursive function which will halt
20 ; int fib(int n){
21 ; return n<=1? n : fib(n-1) + fib(n-2);
22 ; }
23
24 ; FIXME: missing willreturn
25 ; FNATTR: Function Attrs: noinline nounwind readnone uwtable
26 ; FNATTR-NEXT: define i32 @fib(i32)
27 define i32 @fib(i32) local_unnamed_addr #0 {
28 %2 = icmp slt i32 %0, 2
29 br i1 %2, label %9, label %3
30
31 ;
32 %4 = add nsw i32 %0, -1
33 %5 = tail call i32 @fib(i32 %4)
34 %6 = add nsw i32 %0, -2
35 %7 = tail call i32 @fib(i32 %6)
36 %8 = add nsw i32 %7, %5
37 ret i32 %8
38
39 ;
40 ret i32 %0
41 }
42
43 ; 2.2 (negative case)
44 ; recursive function which doesn't stop for some input.
45 ; int fact_maybe_not_halt(int n) {
46 ; if (n==0) {
47 ; return 1;
48 ; }
49 ; return fact_maybe_not_halt( n > 0 ? n-1 : n) * n;
50 ; }
51 ; fact_maybe_not(-1) doesn't stop.
52
53 ; FNATTR: Function Attrs: noinline norecurse nounwind readnone uwtable
54 ; FNATTR-NOT: willreturn
55 ; FNATTR-NEXT: define i32 @fact_maybe_not_halt(i32) local_unnamed_addr
56 define i32 @fact_maybe_not_halt(i32) local_unnamed_addr #0 {
57 %2 = icmp eq i32 %0, 0
58 br i1 %2, label %11, label %3
59
60 ;
61 %4 = phi i32 [ %8, %3 ], [ %0, %1 ]
62 %5 = phi i32 [ %9, %3 ], [ 1, %1 ]
63 %6 = icmp sgt i32 %4, 0
64 %7 = sext i1 %6 to i32
65 %8 = add nsw i32 %4, %7
66 %9 = mul nsw i32 %4, %5
67 %10 = icmp eq i32 %8, 0
68 br i1 %10, label %11, label %3
69
70 ;
71 %12 = phi i32 [ 1, %1 ], [ %9, %3 ]
72 ret i32 %12
73 }
74
75
76 ; TEST 3 (positive case)
77 ; loop
78 ; int fact_loop(int n ){
79 ; int ans = 1;
80 ; for(int i = 1;i<=n;i++){
81 ; ans *= i;
82 ; }
83 ; return ans;
84 ; }
85
86 ; FIXME: missing willreturn
87 ; FNATTR: Function Attrs: noinline norecurse nounwind readnone uwtable
88 ; FNATTR-NEXT: define i32 @fact_loop(i32)
89 define i32 @fact_loop(i32) local_unnamed_addr #0 {
90 %2 = icmp slt i32 %0, 1
91 br i1 %2, label %3, label %5
92
93 ;
94 %4 = phi i32 [ 1, %1 ], [ %8, %5 ]
95 ret i32 %4
96
97 ;
98 %6 = phi i32 [ %9, %5 ], [ 1, %1 ]
99 %7 = phi i32 [ %8, %5 ], [ 1, %1 ]
100 %8 = mul nsw i32 %6, %7
101 %9 = add nuw nsw i32 %6, 1
102 %10 = icmp eq i32 %6, %0
103 br i1 %10, label %3, label %5
104 }
105
106 ; TEST 4 (negative case)
107 ; mutual recursion
108 ; void mutual_recursion1(){
109 ; mutual_recursion2();
110 ; }
111 ; void mutual_recursion2(){
112 ; mutual_recursion1();
113 ; }
114
115 ; FNATTR: Function Attrs: noinline nounwind readnone uwtable
116 ; FNATTR-NOT: willreturn
117 ; FNATTR-NEXT: define void @mutual_recursion1()
118 define void @mutual_recursion1() #0 {
119 call void @mutual_recursion2()
120 ret void
121 }
122
123
124 ; FNATTR: Function Attrs: noinline nounwind readnone uwtable
125 ; FNATTR-NOT: willreturn
126 ; FNATTR-NEXT: define void @mutual_recursion2()
127 define void @mutual_recursion2() #0 {
128 call void @mutual_recursion1()
129 ret void
130 }
131
132
133 ; TEST 5 (negative case)
134 ; call exit/abort (has noreturn attribute)
135 ; FNATTR: Function Attrs: noreturn
136 ; FNATTR-NEXT: declare void @exit(i32) local_unnamed_addr
137 declare void @exit(i32) local_unnamed_addr noreturn
138
139 ; FNATTR: Function Attrs: noinline nounwind uwtable
140 ; FNATTR-NOT: willreturn
141 ; FNATTR-NEXT: define void @only_exit()
142 define void @only_exit() local_unnamed_addr #0 {
143 tail call void @exit(i32 0)
144 unreachable
145 }
146
147 ; conditional exit
148 ; void conditional_exit(int cond, int *p){
149 ; if(cond){
150 ; exit(0);
151 ; }
152 ; if(*p){
153 ; exit(1);
154 ; }
155 ; return;
156 ; }
157 ; FNATTR: Function Attrs: noinline nounwind uwtable
158 ; FNATTR-NOT: willreturn
159 ; FNATTR-NEXT: define void @conditional_exit(i32, i32* nocapture readonly) local_unnamed_addr
160 define void @conditional_exit(i32, i32* nocapture readonly) local_unnamed_addr #0 {
161 %3 = icmp eq i32 %0, 0
162 br i1 %3, label %5, label %4
163
164 ;
165 tail call void @exit(i32 0)
166 unreachable
167
168 ;
169 %6 = load i32, i32* %1, align 4
170 %7 = icmp eq i32 %6, 0
171 br i1 %7, label %9, label %8
172
173 ;
174 tail call void @exit(i32 1)
175 unreachable
176
177 ;
178 ret void
179 }
180
181 ; TEST 6 (positive case)
182 ; Call intrinsic function
183 ; FNATTRS: Function Attrs: noinline readnone speculatable
184 ; FNATTRS-NEXT: declare float @llvm.floor.f32(float)
185 declare float @llvm.floor.f32(float)
186
187 ; FIXME: missing willreturn
188 ; FNATTRS: Function Attrs: noinline nounwind uwtable
189 ; FNATTRS-NEXT: define void @call_floor(float %a)
190 define void @call_floor(float %a) #0 {
191 tail call float @llvm.floor.f32(float %a)
192 ret void
193 }
194
195
196 ; TEST 7 (negative case)
197 ; Call function declaration without willreturn
198
199 ; FNATTR: Function Attrs: noinline nounwind uwtable
200 ; FNATTR-NOT: willreturn
201 ; FNATTR-NEXT: declare void @maybe_noreturn()
202 declare void @maybe_noreturn() #0
203
204 ; FNATTR: Function Attrs: noinline nounwind uwtable
205 ; FNATTR-NOT: willreturn
206 ; FNATTR-NEXT: define void @call_maybe_noreturn()
207 define void @call_maybe_noreturn() #0 {
208 tail call void @maybe_noreturn()
209 ret void
210 }
211
212
213 ; TEST 8 (positive case)
214 ; Check propagation.
215
216 ; FNATTR: Function Attrs: willreturn
217 ; FNATTR-NEXT: declare void @will_return()
218 declare void @will_return() willreturn
219
220 ; FIXME: missing willreturn
221 ; FNATTR: Function Attrs: noinline nounwind uwtable
222 ; FNATTR-NEXT: define void @f1()
223 define void @f1() #0 {
224 tail call void @will_return()
225 ret void
226 }
227
228 ; FIXME: missing willreturn
229 ; FNATTR: Function Attrs: noinline nounwind uwtable
230 ; FNATTR-NEXT: define void @f2()
231 define void @f2() #0 {
232 tail call void @f1()
233 ret void
234 }
235
236
237 ; TEST 9 (negative case)
238 ; call willreturn function in endless loop.
239
240 ; FNATTR: Function Attrs: noinline nounwind uwtable
241 ; FNATTR-NOT: willreturn
242 ; FNATTR-NEXT: define void @call_will_return_but_has_loop()
243 define void @call_will_return_but_has_loop() #0 {
244 br label %label1
245 label1:
246 tail call void @will_return()
247 br label %label2
248 label2:
249 br label %label1
250 }
251
252
253 ; TEST 10 (positive case)
254 ; invoke a function with willreturn
255
256 ; FNATTR: Function Attrs: noinline uwtable willreturn
257 ; FNATTR-NEXT: declare i1 @maybe_raise_exception()
258 declare i1 @maybe_raise_exception() #1 willreturn
259
260 ; FIXME: missing willreturn
261 ; FNATTR: Function Attrs: nounwind
262 ; FNATTR-NEXT: define void @invoke_test()
263 define void @invoke_test() personality i32 (...)* @__gxx_personality_v0 {
264 invoke i1 @maybe_raise_exception()
265 to label %N unwind label %F
266 N:
267 ret void
268 F:
269 %val = landingpad { i8*, i32 }
270 catch i8* null
271 ret void
272 }
273
274 declare i32 @__gxx_personality_v0(...)
275
276
277 ; TEST 11 (positive case)
278 ; counstant trip count
279 ; int loop_constant_trip_count(int*p){
280 ; int ans = 0;
281 ; for(int i = 0;i<10;i++){
282 ; ans += p[i];
283 ; }
284 ; return ans;
285 ; }
286
287 ; FIXME: missing willreturn
288 ; FNATTR: Function Attrs: noinline norecurse nounwind readonly uwtable
289 ; FNATTR-NEXT: define i32 @loop_constant_trip_count(i32* nocapture readonly)
290 define i32 @loop_constant_trip_count(i32* nocapture readonly) #0 {
291 br label %3
292
293 ;
294 ret i32 %8
295
296 ;
297 %4 = phi i64 [ 0, %1 ], [ %9, %3 ]
298 %5 = phi i32 [ 0, %1 ], [ %8, %3 ]
299 %6 = getelementptr inbounds i32, i32* %0, i64 %4
300 %7 = load i32, i32* %6, align 4
301 %8 = add nsw i32 %7, %5
302 %9 = add nuw nsw i64 %4, 1
303 %10 = icmp eq i64 %9, 10
304 br i1 %10, label %2, label %3
305 }
306
307
308 ; TEST 12 (negative case)
309 ; unbounded trip count
310
311 ; int loop_trip_count_unbound(unsigned s,unsigned e, int *p, int offset){
312 ; int ans = 0;
313 ; for(unsigned i = s;i != e;i+=offset){
314 ; ans += p[i];
315 ; }
316 ; return ans;
317 ; }
318 ; FNATTR: Function Attrs: noinline norecurse nounwind readonly uwtable
319 ; FNATTR-NOT: willreturn
320 ; FNATTR-NEXT: define i32 @loop_trip_count_unbound(i32, i32, i32* nocapture readonly, i32) local_unnamed_addr
321 define i32 @loop_trip_count_unbound(i32, i32, i32* nocapture readonly, i32) local_unnamed_addr #0 {
322 %5 = icmp eq i32 %0, %1
323 br i1 %5, label %6, label %8
324
325 ;
326 %7 = phi i32 [ 0, %4 ], [ %14, %8 ]
327 ret i32 %7
328
329 ;
330 %9 = phi i32 [ %15, %8 ], [ %0, %4 ]
331 %10 = phi i32 [ %14, %8 ], [ 0, %4 ]
332 %11 = zext i32 %9 to i64
333 %12 = getelementptr inbounds i32, i32* %2, i64 %11
334 %13 = load i32, i32* %12, align 4
335 %14 = add nsw i32 %13, %10
336 %15 = add i32 %9, %3
337 %16 = icmp eq i32 %15, %1
338 br i1 %16, label %6, label %8
339 }
340
341
342 ; TEST 13 (positive case)
343 ; Function Attrs: norecurse nounwind readonly uwtable
344 ; int loop_trip_dec(int n, int *p){
345 ; int ans = 0;
346 ; for(;n >= 0;n--){
347 ; ans += p[n];
348 ; }
349 ; return ans;
350 ; }
351
352
353 ; FIXME: missing willreturn
354 ; FNATTR: Function Attrs: noinline norecurse nounwind readonly uwtable
355 ; FNATTR-NEXT: define i32 @loop_trip_dec(i32, i32* nocapture readonly)
356
357 define i32 @loop_trip_dec(i32, i32* nocapture readonly) local_unnamed_addr #0 {
358 %3 = icmp sgt i32 %0, -1
359 br i1 %3, label %4, label %14
360
361 ;
362 %5 = sext i32 %0 to i64
363 br label %6
364
365 ;
366 %7 = phi i64 [ %5, %4 ], [ %12, %6 ]
367 %8 = phi i32 [ 0, %4 ], [ %11, %6 ]
368 %9 = getelementptr inbounds i32, i32* %1, i64 %7
369 %10 = load i32, i32* %9, align 4
370 %11 = add nsw i32 %10, %8
371 %12 = add nsw i64 %7, -1
372 %13 = icmp sgt i64 %7, 0
373 br i1 %13, label %6, label %14
374
375 ;
376 %15 = phi i32 [ 0, %2 ], [ %11, %6 ]
377 ret i32 %15
378 }
379
380 ; TEST 14 (positive case)
381 ; multiple return
382
383 ; FIXME: missing willreturn
384 ; FNATTR: Function Attrs: noinline norecurse nounwind readnone uwtable
385 ; FNATTR-NEXT: define i32 @multiple_return(i32 %a)
386 define i32 @multiple_return(i32 %a) #0 {
387 %b = icmp eq i32 %a, 0
388 br i1 %b, label %t, label %f
389
390 t:
391 ret i32 1
392 f:
393 ret i32 0
394 }
395
396 ; TEST 15 (positive & negative case)
397 ; unreachable exit
398
399 ; 15.1 (positive case)
400 ; FIXME: missing willreturn
401 ; FNATTR: Function Attrs: noinline nounwind uwtable
402 ; FNATTR-NEXT: define void @unreachable_exit_positive1()
403 define void @unreachable_exit_positive1() #0 {
404 tail call void @will_return()
405 ret void
406
407 unreachable_label:
408 tail call void @exit(i32 0)
409 unreachable
410 }
411
412 ; FIXME: missing willreturn
413 ; FNATTR: Function Attrs: noinline nounwind uwtable
414 ; FNATTR-NEXT: define i32 @unreachable_exit_positive2(i32)
415 define i32 @unreachable_exit_positive2(i32) local_unnamed_addr #0 {
416 %2 = icmp slt i32 %0, 1
417 br i1 %2, label %3, label %5
418
419 ;
420 %4 = phi i32 [ 1, %1 ], [ %8, %5 ]
421 ret i32 %4
422
423 ;
424 %6 = phi i32 [ %9, %5 ], [ 1, %1 ]
425 %7 = phi i32 [ %8, %5 ], [ 1, %1 ]
426 %8 = mul nsw i32 %6, %7
427 %9 = add nuw nsw i32 %6, 1
428 %10 = icmp eq i32 %6, %0
429 br i1 %10, label %3, label %5
430
431 unreachable_label:
432 tail call void @exit(i32 0)
433 unreachable
434 }
435
436 ;15.2
437
438 ; FNATTR: Function Attrs: noinline nounwind uwtable
439 ; FNATTR-NOT: willreturn
440 ; FNATTR-NEXT: define void @unreachable_exit_negative1()
441 define void @unreachable_exit_negative1() #0 {
442 tail call void @exit(i32 0)
443 ret void
444
445 unreachable_label:
446 tail call void @exit(i32 0)
447 unreachable
448 }
449
450 ; FNATTR: Function Attrs: noinline nounwind uwtable
451 ; FNATTR-NOT: willreturn
452 ; FNATTR-NEXT: define void @unreachable_exit_negative2()
453 define void @unreachable_exit_negative2() #0 {
454
455 br label %L1
456 L1:
457 br label %L2
458 L2:
459 br label %L1
460
461 unreachable_label:
462 tail call void @exit(i32 0)
463 unreachable
464 }
465
466
467 attributes #0 = { nounwind uwtable noinline }
468 attributes #1 = { uwtable noinline }