@@ -380,3 +380,120 @@ end
380
380
using Base. Experimental: @opaque
381
381
f_oc_getfield (x) = (@opaque ()-> x)()
382
382
@test fully_eliminated (f_oc_getfield, Tuple{Int})
383
+
384
+ # check if `x` is a statically-resolved call of a function whose name is `sym`
385
+ isinvoke (@nospecialize (x), sym:: Symbol ) = isinvoke (x, mi-> mi. def. name=== sym)
386
+ function isinvoke (@nospecialize (x), pred)
387
+ if Meta. isexpr (x, :invoke )
388
+ return pred (x. args[1 ]:: Core.MethodInstance )
389
+ end
390
+ return false
391
+ end
392
+ code_typed1 (args... ; kwargs... ) = (first∘ first)(code_typed (args... ; kwargs... )):: Core.CodeInfo
393
+
394
+ @testset " @inline/@noinline annotation before definition" begin
395
+ m = Module ()
396
+ @eval m begin
397
+ @inline function _def_inline (x)
398
+ # this call won't be resolved and thus will prevent inlining to happen if we don't
399
+ # annotate `@inline` at the top of this function body
400
+ return unresolved_call (x)
401
+ end
402
+ def_inline (x) = _def_inline (x)
403
+ @noinline _def_noinline (x) = x # obviously will be inlined otherwise
404
+ def_noinline (x) = _def_noinline (x)
405
+
406
+ # test that they don't conflict with other "before-definition" macros
407
+ @inline Base. @aggressive_constprop function _def_inline_noconflict (x)
408
+ # this call won't be resolved and thus will prevent inlining to happen if we don't
409
+ # annotate `@inline` at the top of this function body
410
+ return unresolved_call (x)
411
+ end
412
+ def_inline_noconflict (x) = _def_inline_noconflict (x)
413
+ @noinline Base. @aggressive_constprop _def_noinline_noconflict (x) = x # obviously will be inlined otherwise
414
+ def_noinline_noconflict (x) = _def_noinline_noconflict (x)
415
+ end
416
+
417
+ let ci = code_typed1 (m. def_inline, (Int,))
418
+ @test all (ci. code) do x
419
+ ! isinvoke (x, :_def_inline )
420
+ end
421
+ end
422
+ let ci = code_typed1 (m. def_noinline, (Int,))
423
+ @test any (ci. code) do x
424
+ isinvoke (x, :_def_noinline )
425
+ end
426
+ end
427
+ # test that they don't conflict with other "before-definition" macros
428
+ let ci = code_typed1 (m. def_inline_noconflict, (Int,))
429
+ @test all (ci. code) do x
430
+ ! isinvoke (x, :_def_inline_noconflict )
431
+ end
432
+ end
433
+ let ci = code_typed1 (m. def_noinline_noconflict, (Int,))
434
+ @test any (ci. code) do x
435
+ isinvoke (x, :_def_noinline_noconflict )
436
+ end
437
+ end
438
+ end
439
+
440
+ @testset " @inline/@noinline annotation within a function body" begin
441
+ m = Module ()
442
+ @eval m begin
443
+ function _body_inline (x)
444
+ @inline
445
+ # this call won't be resolved and thus will prevent inlining to happen if we don't
446
+ # annotate `@inline` at the top of this function body
447
+ return unresolved_call (x)
448
+ end
449
+ body_inline (x) = _body_inline (x)
450
+ function _body_noinline (x)
451
+ @noinline
452
+ return x # obviously will be inlined otherwise
453
+ end
454
+ body_noinline (x) = _body_noinline (x)
455
+
456
+ # test annotations for `do` blocks
457
+ @inline simple_caller (a) = a ()
458
+ function do_inline (x)
459
+ simple_caller () do
460
+ @inline
461
+ # this call won't be resolved and thus will prevent inlining to happen if we don't
462
+ # annotate `@inline` at the top of this anonymous function body
463
+ return unresolved_call (x)
464
+ end
465
+ end
466
+ function do_noinline (x)
467
+ simple_caller () do
468
+ @noinline
469
+ return x # obviously will be inlined otherwise
470
+ end
471
+ end
472
+ end
473
+
474
+ let ci = code_typed1 (m. body_inline, (Int,))
475
+ @test all (ci. code) do x
476
+ ! isinvoke (x, :_body_inline )
477
+ end
478
+ end
479
+ let ci = code_typed1 (m. body_noinline, (Int,))
480
+ @test any (ci. code) do x
481
+ isinvoke (x, :_body_noinline )
482
+ end
483
+ end
484
+ # test annotations for `do` blocks
485
+ let ci = code_typed1 (m. do_inline, (Int,))
486
+ # what we test here is that both `simple_caller` and the anonymous function that the
487
+ # `do` block creates should inlined away, and as a result there is only the unresolved call
488
+ @test all (ci. code) do x
489
+ ! isinvoke (x, :simple_caller ) &&
490
+ ! isinvoke (x, mi-> startswith (string (mi. def. name), ' #' ))
491
+ end
492
+ end
493
+ let ci = code_typed1 (m. do_noinline, (Int,))
494
+ # the anonymous function that the `do` block created shouldn't be inlined here
495
+ @test any (ci. code) do x
496
+ isinvoke (x, mi-> startswith (string (mi. def. name), ' #' ))
497
+ end
498
+ end
499
+ end
0 commit comments