@@ -25,16 +25,16 @@ import InteractiveUtils: gen_call_with_extracted_types_and_kwargs
2525Evaluates the arguments to the function call, determines its types, and then calls
2626[`code_escapes`](@ref) on the resulting expression.
2727As with `@code_typed` and its family, any of `code_escapes` keyword arguments can be given
28- as the optional arguments like `@code_escapes interp=myinterp myfunc(myargs...)`.
28+ as the optional arguments like `@code_escapes debuginfo=:source myfunc(myargs...)`.
2929"""
3030macro code_escapes (ex0... )
3131 return gen_call_with_extracted_types_and_kwargs (__module__, :code_escapes , ex0)
3232end
3333end # @static if EA_AS_PKG
3434
3535"""
36- code_escapes(f, argtypes=Tuple{}; [world], [interp]) -> result::EscapeResult
37- code_escapes(tt::Type{<:Tuple}; [world], [interp]) -> result::EscapeResult
36+ code_escapes(f, argtypes=Tuple{}; [world], [interp], [debuginfo] ) -> result::EscapeResult
37+ code_escapes(tt::Type{<:Tuple}; [world], [interp], [debuginfo] ) -> result::EscapeResult
3838
3939Runs the escape analysis on optimized IR of a generic function call with the given type signature.
4040Note that the escape analysis runs after inlining, but before any other optimizations.
@@ -66,44 +66,44 @@ julia> result = code_escapes((String,String,String,String)) do s1, s2, s3, s4
6666 return s2, s3, s4
6767 end
6868#1(X _2::String, ↑ _3::String, ↑ _4::String, ✓ _5::String) in Main at REPL[6]:2
69- 2 X 1 ── %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String} │╻╷╷ Ref
70- 3 *′ │ %2 = %new(Base.RefValue{String}, _3)::Base.RefValue{String} │╻╷╷ Ref
71- 4 ✓′ └─── %3 = %new(SafeRef{String}, _4)::SafeRef{String} │╻╷ SafeRef
72- 5 ◌ 2 ── %4 = \$ (Expr(:enter, #8)) │
73- ✓′ │ %5 = ϒ (%3)::SafeRef{String} │
74- *′ │ %6 = ϒ (%2)::Base.RefValue{String} │
75- ✓ └─── %7 = ϒ (_5)::String │
76- 6 ◌ 3 ── %8 = Base.isdefined(%1, :x)::Bool │╻╷ get′
77- ◌ └─── goto #5 if not %8 ││
78- X 4 ── Base.getfield(%1, :x)::String ││╻ getindex
79- ◌ └─── goto #6 ││
80- ◌ 5 ── Main.throw(%1)::Union{} ││
81- ◌ └─── unreachable ││
82- 7 ◌ 6 ── nothing::typeof(Core.sizeof) │╻ sizeof
83- ◌ │ nothing::Int64 ││
84- ◌ └─── \$ (Expr(:leave, 1)) │
85- ◌ 7 ── goto #10 │
86- ✓′ 8 ── %18 = φᶜ (%5)::SafeRef{String} │
87- *′ │ %19 = φᶜ (%6)::Base.RefValue{String} │
88- ✓ │ %20 = φᶜ (%7)::String │
89- ◌ └─── \$ (Expr(:leave, 1)) │
90- X 9 ── %22 = \$ (Expr(:the_exception))::Any │
91- 9 ◌ │ (Main.g = %22)::Any │
92- ◌ └─── \$ (Expr(:pop_exception, :(%4)))::Any │
93- 11 ✓′ 10 ┄ %25 = φ (#7 => %3, #9 => %18)::SafeRef{String} │
94- *′ │ %26 = φ (#7 => %2, #9 => %19)::Base.RefValue{String} │
95- ✓ │ %27 = φ (#7 => _5, #9 => %20)::String │
96- ◌ │ %28 = Base.isdefined(%26, :x)::Bool ││╻ isassigned
97- ◌ └─── goto #12 if not %28 ││
98- ↑ 11 ─ %30 = Base.getfield(%26, :x)::String │││╻ getproperty
99- ◌ └─── goto #13 ││
100- ◌ 12 ─ Main.throw(%26)::Union{} ││
101- ◌ └─── unreachable ││
102- 12 ↑ 13 ─ %34 = Base.getfield(%25, :x)::String │╻╷╷ get′
103- 13 ◌ │ %35 = Core.sizeof::typeof(Core.sizeof) │╻ sizeof
104- ◌ │ %36 = (%35)(%27)::Int64 ││
105- 14 ↑ │ %37 = Core.tuple(%30, %34, %36)::Tuple{String, String, Int64} │
106- ◌ └─── return %37 │
69+ X 1 ── %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}
70+ *′ │ %2 = %new(Base.RefValue{String}, _3)::Base.RefValue{String}
71+ ✓′ └─── %3 = %new(SafeRef{String}, _4)::SafeRef{String}
72+ ◌ 2 ── %4 = \$ (Expr(:enter, #8))
73+ ✓′ │ %5 = ϒ (%3)::SafeRef{String}
74+ *′ │ %6 = ϒ (%2)::Base.RefValue{String}
75+ ✓ └─── %7 = ϒ (_5)::String
76+ ◌ 3 ── %8 = Base.isdefined(%1, :x)::Bool
77+ ◌ └─── goto #5 if not %8
78+ X 4 ── Base.getfield(%1, :x)::String
79+ ◌ └─── goto #6
80+ ◌ 5 ── Main.throw(%1)::Union{}
81+ ◌ └─── unreachable
82+ ◌ 6 ── nothing::typeof(Core.sizeof)
83+ ◌ │ nothing::Int64
84+ ◌ └─── \$ (Expr(:leave, 1))
85+ ◌ 7 ── goto #10
86+ ✓′ 8 ── %18 = φᶜ (%5)::SafeRef{String}
87+ *′ │ %19 = φᶜ (%6)::Base.RefValue{String}
88+ ✓ │ %20 = φᶜ (%7)::String
89+ ◌ └─── \$ (Expr(:leave, 1))
90+ X 9 ── %22 = \$ (Expr(:the_exception))::Any
91+ ◌ │ (Main.g = %22)::Any
92+ ◌ └─── \$ (Expr(:pop_exception, :(%4)))::Any
93+ ✓′ 10 ┄ %25 = φ (#7 => %3, #9 => %18)::SafeRef{String}
94+ *′ │ %26 = φ (#7 => %2, #9 => %19)::Base.RefValue{String}
95+ ✓ │ %27 = φ (#7 => _5, #9 => %20)::String
96+ ◌ │ %28 = Base.isdefined(%26, :x)::Bool
97+ ◌ └─── goto #12 if not %28
98+ ↑ 11 ─ %30 = Base.getfield(%26, :x)::String
99+ ◌ └─── goto #13
100+ ◌ 12 ─ Main.throw(%26)::Union{}
101+ ◌ └─── unreachable
102+ ↑ 13 ─ %34 = Base.getfield(%25, :x)::String
103+ ◌ │ %35 = Core.sizeof::typeof(Core.sizeof)
104+ ◌ │ %36 = (%35)(%27)::Int64
105+ ↑ │ %37 = Core.tuple(%30, %34, %36)::Tuple{String, String, Int64}
106+ ◌ └─── return %37
107107```
108108
109109The symbols in the side of each call argument and SSA statements represents the following meaning:
@@ -125,11 +125,12 @@ NoEscape′
125125"""
126126function code_escapes (@nospecialize (args... );
127127 world = get_world_counter (),
128- interp = Core. Compiler. NativeInterpreter (world))
128+ interp = Core. Compiler. NativeInterpreter (world),
129+ debuginfo = :none )
129130 interp = EscapeAnalyzer (interp)
130131 results = code_typed (args... ; optimize= true , world, interp)
131132 isone (length (results)) || throw (ArgumentError (" `code_escapes` only supports single analysis result" ))
132- return EscapeResult (interp. ir, interp. state, interp. linfo)
133+ return EscapeResult (interp. ir, interp. state, interp. linfo, debuginfo === :source )
133134end
134135
135136# AbstractInterpreter
@@ -342,10 +343,14 @@ struct EscapeResult
342343 ir:: IRCode
343344 state:: EscapeState
344345 linfo:: Union{Nothing,MethodInstance}
345- EscapeResult (ir:: IRCode , state:: EscapeState , linfo:: Union{Nothing,MethodInstance} = nothing ) =
346- new (ir, state, linfo)
346+ source:: Bool
347+ function EscapeResult (ir:: IRCode , state:: EscapeState ,
348+ linfo:: Union{Nothing,MethodInstance} = nothing ,
349+ source:: Bool = false )
350+ return new (ir, state, linfo, source)
351+ end
347352end
348- Base. show (io:: IO , result:: EscapeResult ) = print_with_info (io, result. ir, result . state, result . linfo )
353+ Base. show (io:: IO , result:: EscapeResult ) = print_with_info (io, result)
349354@eval Base. iterate (res:: EscapeResult , state= 1 ) =
350355 return state > $ (fieldcount (EscapeResult)) ? nothing : (getfield (res, state), state+ 1 )
351356
@@ -356,8 +361,7 @@ Base.show(io::IO, result::EscapeResult) = print_with_info(io, result.ir, result.
356361end
357362
358363# adapted from https://github.com/JuliaDebug/LoweredCodeUtils.jl/blob/4612349432447e868cf9285f647108f43bd0a11c/src/codeedges.jl#L881-L897
359- function print_with_info (io:: IO ,
360- ir:: IRCode , state:: EscapeState , linfo:: Union{Nothing,MethodInstance} )
364+ function print_with_info (io:: IO , (; ir, state, linfo, source):: EscapeResult )
361365 # print escape information on SSA values
362366 function preprint (io:: IO )
363367 ft = ir. argtypes[1 ]
@@ -389,17 +393,20 @@ function print_with_info(io::IO,
389393 printstyled (io, rpad (c, 2 ), ' ' ; color)
390394 end
391395
392- print_with_info (preprint, (args... )-> nothing , io, ir)
396+ print_with_info (preprint, (args... )-> nothing , io, ir, source )
393397end
394398
395- function print_with_info (preprint, postprint, io:: IO , ir:: IRCode )
399+ function print_with_info (preprint, postprint, io:: IO , ir:: IRCode , source :: Bool )
396400 io = IOContext (io, :displaysize => displaysize (io))
397401 used = Base. IRShow. stmts_used (io, ir)
398- # line_info_preprinter = Base.IRShow.lineinfo_disabled
399- line_info_preprinter = function (io:: IO , indent:: String , idx:: Int )
400- r = Base. IRShow. inline_linfo_printer (ir)(io, indent, idx)
401- idx ≠ 0 && preprint (io, idx)
402- return r
402+ if source
403+ line_info_preprinter = function (io:: IO , indent:: String , idx:: Int )
404+ r = Base. IRShow. inline_linfo_printer (ir)(io, indent, idx)
405+ idx ≠ 0 && preprint (io, idx)
406+ return r
407+ end
408+ else
409+ line_info_preprinter = Base. IRShow. lineinfo_disabled
403410 end
404411 line_info_postprinter = Base. IRShow. default_expr_type_printer
405412 preprint (io)
0 commit comments