@@ -4,7 +4,34 @@ using Test
44using Base. Meta
55using Core: PhiNode, SSAValue, GotoNode, PiNode, QuoteNode, ReturnNode, GotoIfNot
66
7- # Tests for domsort
7+ # utilities
8+ # =========
9+
10+ import Core. Compiler: argextype, singleton_type
11+
12+ argextype (@nospecialize args... ) = argextype (args... , Any[])
13+ code_typed1 (args... ; kwargs... ) = first (only (code_typed (args... ; kwargs... ))):: Core.CodeInfo
14+ get_code (args... ; kwargs... ) = code_typed1 (args... ; kwargs... ). code
15+
16+ # check if `x` is a statement with a given `head`
17+ isnew (@nospecialize x) = Meta. isexpr (x, :new )
18+
19+ # check if `x` is a dynamic call of a given function
20+ iscall (y) = @nospecialize (x) -> iscall (y, x)
21+ function iscall ((src, f):: Tuple{Core.CodeInfo,Base.Callable} , @nospecialize (x))
22+ return iscall (x) do @nospecialize x
23+ singleton_type (argextype (x, src)) === f
24+ end
25+ end
26+ iscall (pred:: Base.Callable , @nospecialize (x)) = Meta. isexpr (x, :call ) && pred (x. args[1 ])
27+
28+ # check if `x` is a statically-resolved call of a function whose name is `sym`
29+ isinvoke (y) = @nospecialize (x) -> isinvoke (y, x)
30+ isinvoke (sym:: Symbol , @nospecialize (x)) = isinvoke (mi-> mi. def. name=== sym, x)
31+ isinvoke (pred:: Function , @nospecialize (x)) = Meta. isexpr (x, :invoke ) && pred (x. args[1 ]:: Core.MethodInstance )
32+
33+ # domsort
34+ # =======
835
936# # Test that domsort doesn't mangle single-argument phis (#29262)
1037let m = Meta. @lower 1 + 1
@@ -67,25 +94,8 @@ let m = Meta.@lower 1 + 1
6794 Core. Compiler. verify_ir (ir)
6895end
6996
70- # Tests for SROA
71-
72- import Core. Compiler: argextype, singleton_type
73- const EMPTY_SPTYPES = Any[]
74-
75- code_typed1 (args... ; kwargs... ) = first (only (code_typed (args... ; kwargs... ))):: Core.CodeInfo
76- get_code (args... ; kwargs... ) = code_typed1 (args... ; kwargs... ). code
77-
78- # check if `x` is a statement with a given `head`
79- isnew (@nospecialize x) = Meta. isexpr (x, :new )
80-
81- # check if `x` is a dynamic call of a given function
82- iscall (y) = @nospecialize (x) -> iscall (y, x)
83- function iscall ((src, f):: Tuple{Core.CodeInfo,Function} , @nospecialize (x))
84- return iscall (x) do @nospecialize x
85- singleton_type (argextype (x, src, EMPTY_SPTYPES)) === f
86- end
87- end
88- iscall (pred:: Function , @nospecialize (x)) = Meta. isexpr (x, :call ) && pred (x. args[1 ])
97+ # SROA
98+ # ====
8999
90100struct ImmutableXYZ; x; y; z; end
91101mutable struct MutableXYZ; x; y; z; end
@@ -277,6 +287,38 @@ let src = code_typed1((Any,Any,Any)) do x, y, z
277287 @test_broken ! any (isnew, src. code)
278288end
279289
290+ let # should work with constant globals
291+ # immutable case
292+ # --------------
293+ src = @eval Module () begin
294+ const REF_FLD = :x
295+ struct ImmutableRef{T}
296+ x:: T
297+ end
298+
299+ code_typed ((Int,)) do x
300+ r = ImmutableRef {Int} (x) # should be eliminated
301+ x = getfield (r, REF_FLD) # should be eliminated
302+ return sin (x)
303+ end |> only |> first
304+ end
305+ @test count (iscall ((src, getfield)), src. code) == 0
306+ @test count (isnew, src. code) == 0
307+
308+ # mutable case
309+ # ------------
310+ src = @eval Module () begin
311+ const REF_FLD = :x
312+ code_typed () do
313+ r = Ref {Int} (42 ) # should be eliminated
314+ x = getfield (r, REF_FLD) # should be eliminated
315+ return sin (x)
316+ end |> only |> first
317+ end
318+ @test count (iscall ((src, getfield)), src. code) == 0
319+ @test count (isnew, src. code) == 0
320+ end
321+
280322# should work nicely with inlining to optimize away a complicated case
281323# adapted from http://wiki.luajit.org/Allocation-Sinking-Optimization#implementation%5B
282324struct Point
@@ -296,6 +338,75 @@ let src = code_typed1(compute_points)
296338 @test ! any (isnew, src. code)
297339end
298340
341+ # comparison lifting
342+ # ==================
343+
344+ let # lifting `===`
345+ src = code_typed1 ((Bool,Int,)) do c, x
346+ y = c ? x : nothing
347+ y === nothing # => ϕ(false, true)
348+ end
349+ @test count (iscall ((src, === )), src. code) == 0
350+
351+ # should optimize away the iteration protocol
352+ src = code_typed1 ((Int,)) do n
353+ s = 0
354+ for i in 1 : n
355+ s += i
356+ end
357+ s
358+ end
359+ @test ! any (src. code) do @nospecialize x
360+ iscall ((src, === ), x) && argextype (x. args[2 ], src) isa Union
361+ end
362+ end
363+
364+ let # lifting `isa`
365+ src = code_typed1 ((Bool,Int,)) do c, x
366+ y = c ? x : nothing
367+ isa (y, Int) # => ϕ(true, false)
368+ end
369+ @test count (iscall ((src, isa)), src. code) == 0
370+
371+ src = code_typed1 ((Int,)) do n
372+ s = 0
373+ itr = 1 : n
374+ st = iterate (itr)
375+ while ! isa (st, Nothing)
376+ i, st = itr
377+ s += i
378+ st = iterate (itr, st)
379+ end
380+ s
381+ end
382+ @test ! any (src. code) do @nospecialize x
383+ iscall ((src, isa), x) && argextype (x. args[2 ], src) isa Union
384+ end
385+ end
386+
387+ let # lifting `isdefined`
388+ src = code_typed1 ((Bool,Some{Int},)) do c, x
389+ y = c ? x : nothing
390+ isdefined (y, 1 ) # => ϕ(true, false)
391+ end
392+ @test count (iscall ((src, isdefined)), src. code) == 0
393+
394+ src = code_typed1 ((Int,)) do n
395+ s = 0
396+ itr = 1 : n
397+ st = iterate (itr)
398+ while isdefined (st, 2 )
399+ i, st = itr
400+ s += i
401+ st = iterate (itr, st)
402+ end
403+ s
404+ end
405+ @test ! any (src. code) do @nospecialize x
406+ iscall ((src, isdefined), x) && argextype (x. args[2 ], src) isa Union
407+ end
408+ end
409+
299410mutable struct Foo30594; x:: Float64 ; end
300411Base. copy (x:: Foo30594 ) = Foo30594 (x. x)
301412function add! (p:: Foo30594 , off:: Foo30594 )
@@ -611,48 +722,6 @@ exc39508 = ErrorException("expected")
611722end
612723@test test39508 () === exc39508
613724
614- let # `sroa_pass!` should work with constant globals
615- # immutable pass
616- src = @eval Module () begin
617- const REF_FLD = :x
618- struct ImmutableRef{T}
619- x:: T
620- end
621-
622- code_typed ((Int,)) do x
623- r = ImmutableRef {Int} (x) # should be eliminated
624- x = getfield (r, REF_FLD) # should be eliminated
625- return sin (x)
626- end |> only |> first
627- end
628- @test ! any (src. code) do @nospecialize (stmt)
629- Meta. isexpr (stmt, :call ) || return false
630- ft = Core. Compiler. argextype (stmt. args[1 ], src, EMPTY_SPTYPES)
631- return Core. Compiler. widenconst (ft) == typeof (getfield)
632- end
633- @test ! any (src. code) do @nospecialize (stmt)
634- return Meta. isexpr (stmt, :new )
635- end
636-
637- # mutable pass
638- src = @eval Module () begin
639- const REF_FLD = :x
640- code_typed () do
641- r = Ref {Int} (42 ) # should be eliminated
642- x = getfield (r, REF_FLD) # should be eliminated
643- return sin (x)
644- end |> only |> first
645- end
646- @test ! any (src. code) do @nospecialize (stmt)
647- Meta. isexpr (stmt, :call ) || return false
648- ft = Core. Compiler. argextype (stmt. args[1 ], src, EMPTY_SPTYPES)
649- return Core. Compiler. widenconst (ft) == typeof (getfield)
650- end
651- @test ! any (src. code) do @nospecialize (stmt)
652- return Meta. isexpr (stmt, :new )
653- end
654- end
655-
656725let
657726 # `typeassert` elimination after SROA
658727 # NOTE we can remove this optimization once inference is able to reason about memory-effects
666735 end |> only |> first
667736 end
668737 # eliminate `typeassert(x2.x, Foo)`
669- @test all (src. code) do @nospecialize stmt
670- Meta. isexpr (stmt, :call ) || return true
671- ft = Core. Compiler. argextype (stmt. args[1 ], src, EMPTY_SPTYPES)
672- return Core. Compiler. widenconst (ft) != = typeof (typeassert)
673- end
738+ @test count (iscall ((src, typeassert)), src. code) == 0
674739end
675740
676741let
0 commit comments