Skip to content

Commit e20c0bd

Browse files
committed
implement integration for exception type inference
Integration for JuliaLang/julia#51754. Statement-wise and call-wise information is available only after `v"1.11.0-DEV.1127"`.
1 parent 82f678d commit e20c0bd

File tree

7 files changed

+214
-100
lines changed

7 files changed

+214
-100
lines changed

src/Cthulhu.jl

Lines changed: 77 additions & 40 deletions
Large diffs are not rendered by default.

src/callsite.jl

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,19 @@ struct MICallInfo <: CallInfo
77
mi::MethodInstance
88
rt
99
effects::Effects
10-
function MICallInfo(mi::MethodInstance, @nospecialize(rt), effects)
10+
exct
11+
function MICallInfo(mi::MethodInstance, @nospecialize(rt), effects, @nospecialize(exct=nothing))
1112
if isa(rt, LimitedAccuracy)
12-
return LimitedCallInfo(new(mi, ignorelimited(rt), effects))
13+
return LimitedCallInfo(new(mi, ignorelimited(rt), effects, exct))
1314
else
14-
return new(mi, rt, effects)
15+
return new(mi, rt, effects, exct)
1516
end
1617
end
1718
end
1819
get_mi(ci::MICallInfo) = ci.mi
19-
get_rt(ci::CallInfo) = ci.rt
20+
get_rt(ci::MICallInfo) = ci.rt
2021
get_effects(ci::MICallInfo) = ci.effects
22+
get_exct(ci::MICallInfo) = ci.exct
2123

2224
abstract type WrappedCallInfo <: CallInfo end
2325

@@ -27,6 +29,7 @@ ignorewrappers(ci::WrappedCallInfo) = ignorewrappers(get_wrapped(ci))
2729
get_mi(ci::WrappedCallInfo) = get_mi(ignorewrappers(ci))
2830
get_rt(ci::WrappedCallInfo) = get_rt(ignorewrappers(ci))
2931
get_effects(ci::WrappedCallInfo) = get_effects(ignorewrappers(ci))
32+
get_exct(ci::WrappedCallInfo) = get_exct(ignorewrappers(ci))
3033

3134
# only appears when inspecting pre-optimization states
3235
struct LimitedCallInfo <: WrappedCallInfo
@@ -38,9 +41,12 @@ struct RTCallInfo <: CallInfo
3841
f
3942
argtyps
4043
rt
44+
exct
4145
end
46+
get_rt(ci::RTCallInfo) = ci.rt
4247
get_mi(ci::RTCallInfo) = nothing
4348
get_effects(ci::RTCallInfo) = Effects()
49+
get_exct(ci::RTCallInfo) = ci.exct
4450

4551
# uncached callsite, we can't recurse into this call
4652
struct UncachedCallInfo <: WrappedCallInfo
@@ -56,6 +62,7 @@ end
5662
get_mi(::PureCallInfo) = nothing
5763
get_rt(pci::PureCallInfo) = pci.rt
5864
get_effects(::PureCallInfo) = EFFECTS_TOTAL
65+
get_exct(::PureCallInfo) = Union{}
5966

6067
# Failed
6168
struct FailedCallInfo <: CallInfo
@@ -64,7 +71,8 @@ struct FailedCallInfo <: CallInfo
6471
end
6572
get_mi(ci::FailedCallInfo) = fail(ci)
6673
get_rt(ci::FailedCallInfo) = fail(ci)
67-
get_effects(ci::FailedCallInfo) = Effects()
74+
get_effects(ci::FailedCallInfo) = fail(ci)
75+
get_exct(ci::FailedCallInfo) = fail(ci)
6876
function fail(ci::FailedCallInfo)
6977
@warn "MethodInstance extraction failed." ci.sig ci.rt
7078
return nothing
@@ -77,7 +85,8 @@ struct GeneratedCallInfo <: CallInfo
7785
end
7886
get_mi(genci::GeneratedCallInfo) = fail(genci)
7987
get_rt(genci::GeneratedCallInfo) = fail(genci)
80-
get_effects(genci::GeneratedCallInfo) = Effects()
88+
get_effects(genci::GeneratedCallInfo) = fail(genci)
89+
get_exct(genci::GeneratedCallInfo) = fail(genci)
8190
function fail(genci::GeneratedCallInfo)
8291
@warn "Can't extract MethodInstance from call to generated functions." genci.sig genci.rt
8392
return nothing
@@ -86,18 +95,24 @@ end
8695
struct MultiCallInfo <: CallInfo
8796
sig
8897
rt
98+
exct
8999
callinfos::Vector{CallInfo}
100+
MultiCallInfo(@nospecialize(sig), @nospecialize(rt), callinfos::Vector{CallInfo},
101+
@nospecialize(exct=nothing)) =
102+
new(sig, rt, exct, callinfos)
90103
end
91-
# actual code-error
92104
get_mi(ci::MultiCallInfo) = error("Can't extract MethodInstance from multiple call informations")
105+
get_rt(ci::MultiCallInfo) = ci.rt
93106
get_effects(mci::MultiCallInfo) = mapreduce(get_effects, CC.merge_effects, mci.callinfos)
107+
get_exct(ci::MultiCallInfo) = ci.exct
94108

95109
struct TaskCallInfo <: CallInfo
96110
ci::CallInfo
97111
end
98112
get_mi(tci::TaskCallInfo) = get_mi(tci.ci)
99113
get_rt(tci::TaskCallInfo) = get_rt(tci.ci)
100114
get_effects(tci::TaskCallInfo) = get_effects(tci.ci)
115+
get_exct(tci::TaskCallInfo) = get_exct(tci.ci)
101116

102117
struct InvokeCallInfo <: CallInfo
103118
ci::CallInfo
@@ -106,6 +121,7 @@ end
106121
get_mi(ici::InvokeCallInfo) = get_mi(ici.ci)
107122
get_rt(ici::InvokeCallInfo) = get_rt(ici.ci)
108123
get_effects(ici::InvokeCallInfo) = get_effects(ici.ci)
124+
get_exct(ici::InvokeCallInfo) = get_exct(ici.ci)
109125

110126
# OpaqueClosure CallInfo
111127
struct OCCallInfo <: CallInfo
@@ -115,6 +131,7 @@ end
115131
get_mi(occi::OCCallInfo) = get_mi(occi.ci)
116132
get_rt(occi::OCCallInfo) = get_rt(occi.ci)
117133
get_effects(occi::OCCallInfo) = get_effects(occi.ci)
134+
get_exct(occi::OCCallInfo) = get_exct(occi.ci)
118135

119136
# Special handling for ReturnTypeCall
120137
struct ReturnTypeCallInfo <: CallInfo
@@ -123,6 +140,7 @@ end
123140
get_mi((; vmi)::ReturnTypeCallInfo) = isa(vmi, FailedCallInfo) ? nothing : get_mi(vmi)
124141
get_rt((; vmi)::ReturnTypeCallInfo) = Type{isa(vmi, FailedCallInfo) ? Union{} : widenconst(get_rt(vmi))}
125142
get_effects(::ReturnTypeCallInfo) = EFFECTS_TOTAL
143+
get_exct(::ReturnTypeCallInfo) = Union{} # FIXME
126144

127145
struct ConstPropCallInfo <: CallInfo
128146
mi::CallInfo
@@ -131,6 +149,7 @@ end
131149
get_mi(cpci::ConstPropCallInfo) = cpci.result.linfo
132150
get_rt(cpci::ConstPropCallInfo) = get_rt(cpci.mi)
133151
get_effects(cpci::ConstPropCallInfo) = get_effects(cpci.result)
152+
get_exct(cpci::ConstPropCallInfo) = get_exct(cpci.mi)
134153

135154
struct ConcreteCallInfo <: CallInfo
136155
mi::CallInfo
@@ -139,6 +158,7 @@ end
139158
get_mi(ceci::ConcreteCallInfo) = get_mi(ceci.mi)
140159
get_rt(ceci::ConcreteCallInfo) = get_rt(ceci.mi)
141160
get_effects(ceci::ConcreteCallInfo) = get_effects(ceci.mi)
161+
get_exct(cici::ConcreteCallInfo) = get_exct(ceci.mi)
142162

143163
struct SemiConcreteCallInfo <: CallInfo
144164
mi::CallInfo
@@ -147,6 +167,7 @@ end
147167
get_mi(scci::SemiConcreteCallInfo) = get_mi(scci.mi)
148168
get_rt(scci::SemiConcreteCallInfo) = get_rt(scci.mi)
149169
get_effects(scci::SemiConcreteCallInfo) = get_effects(scci.mi)
170+
get_exct(scci::SemiConcreteCallInfo) = get_exct(scci.mi)
150171

151172
# CUDA callsite
152173
struct CuCallInfo <: CallInfo
@@ -187,22 +208,22 @@ function headstring(@nospecialize(T))
187208
end
188209
end
189210

190-
function __show_limited(limiter, name, tt, @nospecialize(rt), effects)
211+
function __show_limited(limiter, name, tt, @nospecialize(rt), effects, @nospecialize(exct=nothing))
191212
vastring(@nospecialize(T)) = (isvarargtype(T) ? headstring(T)*"..." : string(T)::String)
192213

193214
# If effects are explicitly turned on, make sure to print them, even
194215
# if there otherwise isn't space for them, since the effects are the
195216
# most important piece of information if turned on.
196217
with_effects = get(limiter, :with_effects, false)::Bool
218+
exception_type = get(limiter, :exception_type, false)::Bool && exct !== nothing
197219

198-
if with_effects
199-
limiter.width += textwidth(repr(effects)) + 1
200-
end
201-
220+
with_effects && (limiter.width += textwidth(repr(effects)) + 1)
221+
exception_type && (limiter.width += textwidth(string(exct)) + 1)
202222
if !has_space(limiter, name)
203223
print(limiter, '')
204224
@goto print_effects
205225
end
226+
206227
print(limiter, string(name))
207228
pstrings = String[vastring(T) for T in tt]
208229
headstrings = String[
@@ -234,15 +255,24 @@ function __show_limited(limiter, name, tt, @nospecialize(rt), effects)
234255
print(limiter, "::…")
235256
end
236257

237-
@label print_effects
258+
@label print_effects
238259
if with_effects
239260
# Print effects unlimited
240261
print(limiter.io, " ", effects)
241262
end
263+
if exception_type
264+
print(limiter.io, ' ')
265+
print_exct(limiter.io, exct)
266+
end
242267

243268
return nothing
244269
end
245270

271+
function print_exct(io::IO, @nospecialize(exct))
272+
color = exct === Union{} ? :green : :yellow
273+
printstyled(io, "(↑::", exct, ")"; color)
274+
end
275+
246276
function show_callinfo(limiter, mici::MICallInfo)
247277
mi = mici.mi
248278
tt = (Base.unwrap_unionall(mi.specTypes)::DataType).parameters[2:end]
@@ -252,7 +282,8 @@ function show_callinfo(limiter, mici::MICallInfo)
252282
name = mi.def.name
253283
end
254284
rt = get_rt(mici)
255-
__show_limited(limiter, name, tt, rt, get_effects(mici))
285+
exct = get_exct(mici)
286+
__show_limited(limiter, name, tt, rt, get_effects(mici), exct)
256287
end
257288

258289
function show_callinfo(limiter, ci::Union{MultiCallInfo, FailedCallInfo, GeneratedCallInfo})

src/codeview.jl

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,12 @@ end
121121
cthulhu_typed(io::IO, debuginfo::DebugInfo, args...; kwargs...) =
122122
cthulhu_typed(io, Symbol(debuginfo), args...; kwargs...)
123123
function cthulhu_typed(io::IO, debuginfo::Symbol,
124-
src::Union{CodeInfo,IRCode}, @nospecialize(rt), effects::Effects, mi::Union{Nothing,MethodInstance};
124+
src::Union{CodeInfo,IRCode}, @nospecialize(rt), @nospecialize(exct),
125+
effects::Effects, mi::Union{Nothing,MethodInstance};
125126
iswarn::Bool=false, hide_type_stable::Bool=false, optimize::Bool=true,
126-
pc2remarks::Union{Nothing,PC2Remarks}=nothing, pc2effects::Union{Nothing,PC2Effects}=nothing,
127+
pc2remarks::Union{Nothing,PC2Remarks}=nothing,
128+
pc2effects::Union{Nothing,PC2Effects}=nothing,
129+
pc2excts::Union{Nothing,PC2Excts}=nothing,
127130
inline_cost::Bool=false, type_annotations::Bool=true, annotate_source::Bool=false,
128131
inlay_types_vscode::Bool=false, diagnostics_vscode::Bool=false, jump_always::Bool=false,
129132
interp::AbstractInterpreter=CthulhuInterpreter())
@@ -248,18 +251,29 @@ function cthulhu_typed(io::IO, debuginfo::Symbol,
248251
end
249252
end
250253
# postprinter configuration
251-
__postprinter = if type_annotations
254+
___postprinter = if type_annotations
252255
iswarn ? InteractiveUtils.warntype_type_printer : IRShow.default_expr_type_printer
253256
else
254257
Returns(nothing)
255258
end
256-
_postprinter = if isa(src, CodeInfo) && !isnothing(pc2effects)
259+
__postprinter = if isa(src, CodeInfo) && !isnothing(pc2effects)
257260
function (io::IO; idx::Int, @nospecialize(kws...))
258-
__postprinter(io; idx, kws...)
261+
___postprinter(io; idx, kws...)
259262
local effects = get(pc2effects, idx, nothing)
260263
effects === nothing && return
261264
print(io, ' ', effects)
262265
end
266+
else
267+
___postprinter
268+
end
269+
_postprinter = if isa(src, CodeInfo) && !isnothing(pc2excts)
270+
function (io::IO; idx::Int, @nospecialize(kws...))
271+
__postprinter(io; idx, kws...)
272+
local exct = get(pc2excts, idx, nothing)
273+
exct === nothing && return
274+
print(io, ' ')
275+
print_exct(io, exct)
276+
end
263277
else
264278
__postprinter
265279
end
@@ -293,7 +307,7 @@ function cthulhu_typed(io::IO, debuginfo::Symbol,
293307
cfg = src isa IRCode ? src.cfg : Core.Compiler.compute_basic_blocks(src.code)
294308
max_bb_idx_size = length(string(length(cfg.blocks)))
295309
str = irshow_config.line_info_preprinter(lambda_io, " "^(max_bb_idx_size + 2), -1)
296-
callsite = Callsite(0, MICallInfo(mi, rettype, effects), :invoke)
310+
callsite = Callsite(0, MICallInfo(mi, rettype, effects, exct), :invoke)
297311
println(lambda_io, "", ""^(max_bb_idx_size), str, " ", callsite)
298312
end
299313

src/interface.jl

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,14 @@ missing `$AbstractCursor` API:
6868
""")
6969
navigate(curs::CthulhuCursor, callsite::Callsite) = CthulhuCursor(get_mi(callsite))
7070

71-
get_remarks(::AbstractInterpreter, ::Union{MethodInstance,InferenceResult}) = nothing
72-
get_remarks(interp::CthulhuInterpreter, key::Union{MethodInstance,InferenceResult}) = get(interp.remarks, key, nothing)
73-
get_remarks(::AbstractInterpreter, ::SemiConcreteCallInfo) = PC2Remarks()
71+
get_remarks(::AbstractInterpreter, ::InferenceKey) = nothing
72+
get_remarks(interp::CthulhuInterpreter, key::InferenceKey) = get(interp.remarks, key, nothing)
7473

75-
get_effects(::AbstractInterpreter, ::Union{MethodInstance,InferenceResult}) = nothing
76-
get_effects(interp::CthulhuInterpreter, key::Union{MethodInstance,InferenceResult}) = get(interp.effects, key, nothing)
77-
get_effects(::AbstractInterpreter, ::SemiConcreteCallInfo) = PC2Effects()
74+
get_effects(::AbstractInterpreter, ::InferenceKey) = nothing
75+
get_effects(interp::CthulhuInterpreter, key::InferenceKey) = get(interp.effects, key, nothing)
76+
77+
get_excts(::AbstractInterpreter, ::InferenceKey) = nothing
78+
get_excts(interp::CthulhuInterpreter, key::InferenceKey) = get(interp.exception_types, key, nothing)
7879

7980
# This method is optional, but should be implemented if there is
8081
# a sensible default cursor for a MethodInstance

src/interpreter.jl

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ struct InferredSource
99
stmt_info::Vector{CCCallInfo}
1010
effects::Effects
1111
rt::Any
12-
InferredSource(src::CodeInfo, stmt_info::Vector{CCCallInfo}, effects, @nospecialize(rt)) =
13-
new(src, stmt_info, effects, rt)
12+
exct::Any
13+
InferredSource(src::CodeInfo, stmt_info::Vector{CCCallInfo}, effects, @nospecialize(rt),
14+
@nospecialize(exct)) =
15+
new(src, stmt_info, effects, rt, exct)
1416
end
1517

1618
struct OptimizedSource
@@ -20,26 +22,31 @@ struct OptimizedSource
2022
effects::Effects
2123
end
2224

25+
const InferenceKey = Union{MethodInstance,InferenceResult}
26+
const InferenceDict{T} = Dict{InferenceKey, T}
2327
const PC2Remarks = Vector{Pair{Int, String}}
2428
const PC2Effects = Dict{Int, Effects}
29+
const PC2Excts = Dict{Int, Any}
2530

2631
struct CthulhuInterpreter <: AbstractInterpreter
2732
native::AbstractInterpreter
2833

29-
unopt::Dict{Union{MethodInstance,InferenceResult}, InferredSource}
34+
unopt::InferenceDict{InferredSource}
3035
opt::Dict{MethodInstance, CodeInstance}
3136

32-
remarks::Dict{Union{MethodInstance,InferenceResult}, PC2Remarks}
33-
effects::Dict{Union{MethodInstance,InferenceResult}, PC2Effects}
37+
remarks::InferenceDict{PC2Remarks}
38+
effects::InferenceDict{PC2Effects}
39+
exception_types::InferenceDict{PC2Excts}
3440
end
3541

3642
function CthulhuInterpreter(interp::AbstractInterpreter=NativeInterpreter())
3743
return CthulhuInterpreter(
3844
interp,
39-
Dict{Union{MethodInstance,InferenceResult}, InferredSource}(),
45+
InferenceDict{InferredSource}(),
4046
Dict{MethodInstance, CodeInstance}(),
41-
Dict{Union{MethodInstance,InferenceResult}, PC2Remarks}(),
42-
Dict{Union{MethodInstance,InferenceResult}, PC2Effects}())
47+
InferenceDict{PC2Remarks}(),
48+
InferenceDict{PC2Effects}(),
49+
InferenceDict{PC2Excts}())
4350
end
4451

4552
import .CC: InferenceParams, OptimizationParams, get_world_counter,
@@ -138,11 +145,13 @@ function InferredSource(state::InferenceState)
138145
slottypes === nothing ? nothing : copy(slottypes)
139146
end
140147
end
148+
exct = @static VERSION v"1.11.0-DEV.207" ? state.result.exc_result : nothing
141149
return InferredSource(
142150
unoptsrc,
143151
copy(state.stmt_info),
144152
isdefined(CC, :Effects) ? state.ipo_effects : nothing,
145-
state.result.result)
153+
state.result.result,
154+
exct)
146155
end
147156

148157
function CC.finish(state::InferenceState, interp::CthulhuInterpreter)
@@ -236,3 +245,14 @@ function CC.finish!(interp::CthulhuInterpreter, caller::InferenceResult)
236245
caller.src = create_cthulhu_source(caller.src, caller.ipo_effects)
237246
end
238247
end
248+
249+
@static if VERSION v"1.11.0-DEV.1127"
250+
function CC.update_exc_bestguess!(interp::CthulhuInterpreter, @nospecialize(exct),
251+
frame::InferenceState)
252+
key = CC.any(frame.result.overridden_by_const) ? frame.result : frame.linfo
253+
pc2excts = get!(PC2Excts, interp.exception_types, key)
254+
pc2excts[frame.currpc] = CC.tmerge(CC.typeinf_lattice(interp), exct, get(pc2excts, frame.currpc, Union{}))
255+
return @invoke CC.update_exc_bestguess!(interp::AbstractInterpreter, exct::Any,
256+
frame::InferenceState)
257+
end
258+
end

0 commit comments

Comments
 (0)