Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 020536e

Browse files
committedJan 25, 2022
update EA: disable inlining info printing by default
1 parent a575e70 commit 020536e

File tree

2 files changed

+135
-128
lines changed

2 files changed

+135
-128
lines changed
 

‎base/compiler/ssair/EscapeAnalysis/EAUtils.jl

+62-55
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,16 @@ import InteractiveUtils: gen_call_with_extracted_types_and_kwargs
2525
Evaluates the arguments to the function call, determines its types, and then calls
2626
[`code_escapes`](@ref) on the resulting expression.
2727
As 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
"""
3030
macro code_escapes(ex0...)
3131
return gen_call_with_extracted_types_and_kwargs(__module__, :code_escapes, ex0)
3232
end
3333
end # @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
3939
Runs the escape analysis on optimized IR of a generic function call with the given type signature.
4040
Note 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
109109
The symbols in the side of each call argument and SSA statements represents the following meaning:
@@ -125,11 +125,12 @@ NoEscape′
125125
"""
126126
function 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)
133134
end
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
347352
end
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.
356361
end
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)
393397
end
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)

‎doc/src/devdocs/EscapeAnalysis/README.md

+73-73
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ julia> code_escapes((String,)) do s
5555
return obj
5656
end
5757
#1(↑ _2::String) in Main at REPL[2]:2
58-
2 1%1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String} │╻╷╷ Ref
59-
3 ◌ └── return %1
58+
1%1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}
59+
◌ └── return %1
6060
```
6161

6262
The key observation here is that this backward analysis allows escape information to flow
@@ -73,13 +73,13 @@ julia> code_escapes((Bool, String, String)) do cnd, s, t
7373
end
7474
return obj
7575
end
76-
#3( _2::Bool, ↑ _3::String, ↑ _4::String) in Main at REPL[3]:2
77-
2 1 ─ goto #3 if not _2
78-
3 2%2 = %new(Base.RefValue{String}, _3)::Base.RefValue{String} │╻╷╷ Ref
79-
◌ └── goto #4
80-
5 3%4 = %new(Base.RefValue{String}, _4)::Base.RefValue{String} │╻╷╷ Ref
81-
7 4%5 = φ (#2 => %2, #3 => %4)::Base.RefValue{String}
82-
◌ └── return %5
76+
#3( _2::Bool, ↑ _3::String, ↑ _4::String) in Main at REPL[3]:2
77+
1 ─ goto #3 if not _2
78+
2%2 = %new(Base.RefValue{String}, _3)::Base.RefValue{String}
79+
◌ └── goto #4
80+
3%4 = %new(Base.RefValue{String}, _4)::Base.RefValue{String}
81+
4%5 = φ (#2 => %2, #3 => %4)::Base.RefValue{String}
82+
◌ └── return %5
8383
```
8484
8585
### [Alias Analysis](@id EA-Alias-Analysis)
@@ -107,10 +107,10 @@ julia> code_escapes((String,)) do s
107107
return v
108108
end
109109
#5(↑ _2::String) in Main at REPL[7]:2
110-
2 ✓′ 1%1 = %new(SafeRef{String}, "init")::SafeRef{String} │╻╷ SafeRef
111-
3 ◌ │ Base.setfield!(%1, :x, _2)::String │╻╷ setindex!
112-
4 %3 = Base.getfield(%1, :x)::String │╻╷ getindex
113-
5 ◌ └── return %3
110+
✓′ 1%1 = %new(SafeRef{String}, "init")::SafeRef{String}
111+
◌ │ Base.setfield!(%1, :x, _2)::String
112+
%3 = Base.getfield(%1, :x)::String
113+
◌ └── return %3
114114
```
115115
In the example above, `ReturnEscape` imposed on `%3` (corresponding to `v`) is _not_ directly
116116
propagated to `%1` (corresponding to `obj`) but rather that `ReturnEscape` is only propagated
@@ -139,16 +139,16 @@ julia> code_escapes((Bool, String,)) do cond, x
139139
y = ϕ1[]
140140
return y
141141
end
142-
#7( _2::Bool, ↑ _3::String) in Main at REPL[8]:2
143-
2 1 ─ goto #3 if not _2
144-
3 ✓′ 2%2 = %new(SafeRef{String}, "foo")::SafeRef{String} │╻╷ SafeRef
145-
◌ └── goto #4
146-
5 ✓′ 3%4 = %new(SafeRef{String}, "bar")::SafeRef{String} │╻╷ SafeRef
147-
7 ✓′ 4%5 = φ (#2 => %2, #3 => %4)::SafeRef{String}
148-
✓′ │ %6 = φ (#2 => %2, #3 => %4)::SafeRef{String}
149-
◌ │ Base.setfield!(%5, :x, _3)::String │╻ setindex!
150-
8 %8 = Base.getfield(%6, :x)::String │╻╷ getindex
151-
9 ◌ └── return %8
142+
#7( _2::Bool, ↑ _3::String) in Main at REPL[8]:2
143+
1 ─ goto #3 if not _2
144+
✓′ 2%2 = %new(SafeRef{String}, "foo")::SafeRef{String}
145+
◌ └── goto #4
146+
✓′ 3%4 = %new(SafeRef{String}, "bar")::SafeRef{String}
147+
✓′ 4%5 = φ (#2 => %2, #3 => %4)::SafeRef{String}
148+
✓′ │ %6 = φ (#2 => %2, #3 => %4)::SafeRef{String}
149+
◌ │ Base.setfield!(%5, :x, _3)::String
150+
%8 = Base.getfield(%6, :x)::String
151+
◌ └── return %8
152152
```
153153
`ϕ1 = %5` and `ϕ2 = %6` are aliased and thus `ReturnEscape` imposed on `%8 = Base.getfield(%6, :x)::String` (corresponding to `y = ϕ1[]`)
154154
needs to be propagated to `Base.setfield!(%5, :x, _3)::String` (corresponding to `ϕ2[] = x`).
@@ -176,7 +176,7 @@ julia> code_escapes((String,)) do s
176176
push!(ary, SafeRef(s))
177177
return ary[1], length(ary)
178178
end
179-
#21(↑ _2::String) in Main at none:2
179+
#9(↑ _2::String) in Main at REPL[9]:2
180180
*1 ── %1 = $(Expr(:foreigncall, :(:jl_alloc_array_1d), Vector{Any}, svec(Any, Int64), 0, :(:ccall), Vector{Any}, 0, 0))::Vector{Any}
181181
%2 = %new(SafeRef{String}, _2)::SafeRef{String}
182182
◌ │ %3 = Core.lshr_int(1, 63)::Int64
@@ -218,7 +218,7 @@ julia> code_escapes((String,String)) do s, t
218218
ary[2] = SafeRef(t)
219219
return ary[1], length(ary)
220220
end
221-
#23(↑ _2::String, * _3::String) in Main at none:2
221+
#11(↑ _2::String, * _3::String) in Main at REPL[10]:2
222222
*1%1 = $(Expr(:foreigncall, :(:jl_alloc_array_1d), Vector{Any}, svec(Any, Int64), 0, :(:ccall), Vector{Any}, 2, 2))::Vector{Any}
223223
%2 = %new(SafeRef{String}, _2)::SafeRef{String}
224224
◌ │ Base.arrayset(true, %1, %2, 1)::Vector{Any}
@@ -264,7 +264,7 @@ julia> code_escapes((String,String)) do s, t
264264
ary[2] = SafeRef(t)
265265
return ary[1], length(ary)
266266
end
267-
#27(↑ _2::String, ↑ _3::String) in Main at none:2
267+
#13(↑ _2::String, ↑ _3::String) in Main at REPL[11]:2
268268
1%1 = Main.nothing::Core.Const(nothing)
269269
◌ │ %2 = Main.nothing::Core.Const(nothing)
270270
◌ │ %3 = Core.tuple(%1, %2)::Core.Const((nothing, nothing))
@@ -310,7 +310,7 @@ julia> code_escapes((String,String)) do s, t
310310
push!(ary, SafeRef(t))
311311
ary[1], length(ary)
312312
end
313-
#31(↑ _2::String, ↑ _3::String) in Main at none:2
313+
#15(↑ _2::String, ↑ _3::String) in Main at REPL[12]:2
314314
*1 ── %1 = $(Expr(:foreigncall, :(:jl_alloc_array_1d), Vector{Any}, svec(Any, Int64), 0, :(:ccall), Vector{Any}, 0, 0))::Vector{Any}
315315
%2 = %new(SafeRef{String}, _2)::SafeRef{String}
316316
◌ │ %3 = Core.lshr_int(1, 63)::Int64
@@ -395,24 +395,24 @@ julia> code_escapes() do
395395
end
396396
return t
397397
end
398-
#9() in Main at REPL[12]:2
399-
2 X 1 ── %1 = %new(Base.RefValue{String})::Base.RefValue{String} │╻╷ Ref
400-
4 2 ── %2 = $(Expr(:enter, #8))
401-
5 3 ── %3 = Base.isdefined(%1, :x)::Bool │╻╷ get′
402-
◌ └─── goto #5 if not %3 ││
403-
4 ── %5 = Base.getfield(%1, :x)::String ││╻ getindex
404-
◌ └─── goto #6 ││
405-
5 ── Main.throw(%1)::Union{} ││
406-
◌ └─── unreachable ││
407-
6 ── $(Expr(:leave, 1))
408-
7 ── goto #10
409-
8 ── $(Expr(:leave, 1))
410-
9 ── %12 = $(Expr(:the_exception))::Any
411-
7 %13 = Main.typeof(%12)::DataType
412-
8 ◌ │ invoke Main.rethrow_escape!()::Any
413-
◌ └─── $(Expr(:pop_exception, :(%2)))::Any
414-
10 10%16 = φ (#7 => %5, #9 => %13)::Union{DataType, String}
415-
◌ └─── return %16
398+
#17() in Main at REPL[16]:2
399+
X 1 ── %1 = %new(Base.RefValue{String})::Base.RefValue{String}
400+
2 ── %2 = $(Expr(:enter, #8))
401+
3 ── %3 = Base.isdefined(%1, :x)::Bool
402+
◌ └─── goto #5 if not %3
403+
4 ── %5 = Base.getfield(%1, :x)::String
404+
◌ └─── goto #6
405+
5 ── Main.throw(%1)::Union{}
406+
◌ └─── unreachable
407+
6 ── $(Expr(:leave, 1))
408+
7 ── goto #10
409+
8 ── $(Expr(:leave, 1))
410+
9 ── %12 = $(Expr(:the_exception))::Any
411+
%13 = Main.typeof(%12)::DataType
412+
◌ │ invoke Main.rethrow_escape!()::Any
413+
◌ └─── $(Expr(:pop_exception, :(%2)))::Any
414+
10%16 = φ (#7 => %5, #9 => %13)::Union{DataType, String}
415+
◌ └─── return %16
416416
```
417417
418418
It requires a global analysis in order to correctly reason about all possible escapes via
@@ -439,34 +439,34 @@ julia> result = code_escapes((String,String)) do s1, s2
439439
s2 = get′(r2) # still `r2` doesn't escape fully
440440
return s2
441441
end
442-
#11(X _2::String, ↑ _3::String) in Main at REPL[13]:2
443-
2 X 1 ── %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String} │╻╷╷ Ref
444-
3 *′ └─── %2 = %new(Base.RefValue{String}, _3)::Base.RefValue{String} │╻╷╷ Ref
445-
5 2 ── %3 = $(Expr(:enter, #8))
446-
*′ └─── %4 = ϒ (%2)::Base.RefValue{String}
447-
6 3 ── %5 = Base.isdefined(%1, :x)::Bool │╻╷ get′
448-
◌ └─── goto #5 if not %5 ││
449-
X 4 ── Base.getfield(%1, :x)::String ││╻ getindex
450-
◌ └─── goto #6 ││
451-
5 ── Main.throw(%1)::Union{} ││
452-
◌ └─── unreachable ││
453-
7 6 ── nothing::typeof(Core.sizeof) │╻ sizeof
454-
◌ │ nothing::Int64 ││
455-
◌ └─── $(Expr(:leave, 1))
456-
7 ── goto #10
457-
*8 ── %15 = φᶜ (%4)::Base.RefValue{String}
458-
◌ └─── $(Expr(:leave, 1))
459-
X 9 ── %17 = $(Expr(:the_exception))::Any
460-
9 ◌ │ (Main.g = %17)::Any
461-
◌ └─── $(Expr(:pop_exception, :(%3)))::Any
462-
11 *10%20 = φ (#7 => %2, #9 => %15)::Base.RefValue{String}
463-
◌ │ %21 = Base.isdefined(%20, :x)::Bool ││╻ isassigned
464-
◌ └─── goto #12 if not %21 ││
465-
11%23 = Base.getfield(%20, :x)::String │││╻ getproperty
466-
◌ └─── goto #13 ││
467-
12 ─ Main.throw(%20)::Union{} ││
468-
◌ └─── unreachable ││
469-
12 13return %23
442+
#19(X _2::String, ↑ _3::String) in Main at REPL[17]:2
443+
X 1 ── %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}
444+
*′ └─── %2 = %new(Base.RefValue{String}, _3)::Base.RefValue{String}
445+
2 ── %3 = $(Expr(:enter, #8))
446+
*′ └─── %4 = ϒ (%2)::Base.RefValue{String}
447+
3 ── %5 = Base.isdefined(%1, :x)::Bool
448+
◌ └─── goto #5 if not %5
449+
X 4 ── Base.getfield(%1, :x)::String
450+
◌ └─── goto #6
451+
5 ── Main.throw(%1)::Union{}
452+
◌ └─── unreachable
453+
6 ── nothing::typeof(Core.sizeof)
454+
◌ │ nothing::Int64
455+
◌ └─── $(Expr(:leave, 1))
456+
7 ── goto #10
457+
*8 ── %15 = φᶜ (%4)::Base.RefValue{String}
458+
◌ └─── $(Expr(:leave, 1))
459+
X 9 ── %17 = $(Expr(:the_exception))::Any
460+
◌ │ (Main.g = %17)::Any
461+
◌ └─── $(Expr(:pop_exception, :(%3)))::Any
462+
*10%20 = φ (#7 => %2, #9 => %15)::Base.RefValue{String}
463+
◌ │ %21 = Base.isdefined(%20, :x)::Bool
464+
◌ └─── goto #12 if not %21
465+
11%23 = Base.getfield(%20, :x)::String
466+
◌ └─── goto #13
467+
12 ─ Main.throw(%20)::Union{}
468+
◌ └─── unreachable
469+
13return %23
470470
```
471471
472472
## Analysis Usage

0 commit comments

Comments
 (0)
Please sign in to comment.