Skip to content

Commit c5afbf0

Browse files
committed
OptAnalyzer: ignore dynamic dispatches in "throw blocks"
1 parent 39b4380 commit c5afbf0

File tree

2 files changed

+95
-15
lines changed

2 files changed

+95
-15
lines changed

src/analyzers/optanalyzer.jl

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ that are specific to the optimization analysis.
113113
│└────────────────────
114114
```
115115
116+
---
117+
- `skip_throw_blocks = true`:\\
118+
116119
---
117120
- `function_filter = @nospecialize(f)->true`:\\
118121
A predicate which takes a function object and returns `false` to skip runtime dispatch
@@ -134,21 +137,24 @@ struct OptAnalyzer{RP<:ReportPass,FF} <: AbstractAnalyzer
134137
report_pass::RP
135138
function_filter::FF
136139
skip_noncompileable_calls::Bool
140+
skip_throw_blocks::Bool
137141
__analyze_frame::BitVector # temporary stash to keep per-frame analysis-skip configuration
138142

139143
function OptAnalyzer(state::AnalyzerState,
140144
report_pass::RP,
141145
function_filter::FF,
142-
skip_noncompileable_calls::Bool) where {RP<:ReportPass,FF}
146+
skip_noncompileable_calls::Bool,
147+
skip_throw_blocks::Bool) where {RP<:ReportPass,FF}
143148
cache_key = compute_hash(state.inf_params, state.opt_params, report_pass,
144-
skip_noncompileable_calls)
149+
skip_noncompileable_calls, skip_throw_blocks)
145150
cache_key = @invoke hash(function_filter::Any, cache_key::UInt) # HACK avoid dynamic dispatch
146151
analysis_cache = get!(AnalysisCache, OPT_ANALYZER_CACHE, cache_key)
147152
return new{RP,FF}(state,
148153
analysis_cache,
149154
report_pass,
150155
function_filter,
151156
skip_noncompileable_calls,
157+
skip_throw_blocks,
152158
#=__analyze_frame=# BitVector())
153159
end
154160
end
@@ -160,7 +166,8 @@ function JETInterface.AbstractAnalyzer(analyzer::OptAnalyzer, state::AnalyzerSta
160166
state,
161167
analyzer.report_pass,
162168
analyzer.function_filter,
163-
analyzer.skip_noncompileable_calls)
169+
analyzer.skip_noncompileable_calls,
170+
analyzer.skip_throw_blocks)
164171
end
165172
JETInterface.ReportPass(analyzer::OptAnalyzer) = analyzer.report_pass
166173
JETInterface.AnalysisCache(analyzer::OptAnalyzer) = analyzer.analysis_cache
@@ -313,24 +320,61 @@ end
313320
function (::OptAnalysisPass)(::Type{RuntimeDispatchReport}, analyzer::OptAnalyzer, caller::InferenceResult, opt::OptimizationState)
314321
(; src, sptypes, slottypes) = opt
315322

316-
# TODO better to work on `opt.ir::IRCode` (with some updates on `handle_sig!`)
323+
# TODO better to work on `opt.ir::IRCode` (with some updates on `handle_sig!`),
324+
# without creating duplicated `CFG`s
325+
cfg = CC.compute_basic_blocks(src.code)
326+
327+
unreachables = nothing
328+
if analyzer.skip_throw_blocks
329+
for i = 1:length(src.code)
330+
x = src.code[i]
331+
if x isa ReturnNode && !isdefined(x, :val)
332+
if unreachables === nothing
333+
unreachables = Int[]
334+
end
335+
push!(unreachables, CC.block_for_inst(cfg, i))
336+
end
337+
end
338+
end
339+
is_in_throw_block = nothing
340+
if unreachables !== nothing
341+
postdomtree = CC.construct_postdomtree(cfg)
342+
is_in_throw_block = function (pc::Int)
343+
bb = CC.block_for_inst(cfg, pc)
344+
for unreachable = unreachables
345+
if CC.postdominates(postdomtree, unreachable, bb)
346+
return true
347+
end
348+
end
349+
return false
350+
end
351+
end
352+
317353
local reported = false
318-
for (pc, x) in enumerate(src.code)
354+
for i = 1:length(src.code)
355+
x = src.code[i]
319356
if isexpr(x, :call)
320357
ft = argextype(first(x.args), src, sptypes, slottypes)
321358
f = singleton_type(ft)
322359
if f !== nothing
323360
f isa Builtin && continue # ignore `:call`s of language intrinsics
324361
analyzer.function_filter(f) || continue # ignore user-specified functions
325362
end
326-
lins = get_lins((opt, pc))
363+
lins = get_lins((opt, i))
327364
isempty(lins) && continue # dead statement, just ignore it
328365
if length(lins) > 1
329366
# this statement has been inlined, so ignore it as any problems within
330367
# that callee should already have been reported
331368
continue
332369
end
333-
add_new_report!(analyzer, caller, RuntimeDispatchReport((opt, pc)))
370+
if is_in_throw_block !== nothing && is_in_throw_block(i)
371+
# if this statement is in "throw block", ignore it
372+
# unless this statement itself is causing the throw
373+
if src.ssavaluetypes[i] !== Union{}
374+
continue
375+
end
376+
end
377+
add_new_report!(analyzer, caller, RuntimeDispatchReport((opt, i)))
334378
reported |= true
335379
end
336380
end
@@ -345,17 +389,25 @@ function OptAnalyzer(world::UInt = Base.get_world_counter();
345389
report_pass::ReportPass = OptAnalysisPass(),
346390
function_filter = optanalyzer_function_filter,
347391
skip_noncompileable_calls::Bool = true,
392+
skip_throw_blocks::Bool = true,
393+
unoptimize_throw_blocks::Union{Nothing,Bool} = nothing,
348394
jetconfigs...)
395+
if unoptimize_throw_blocks !== nothing
396+
@warn "The `unoptimize_throw_blocks` configuration is deprecated, use `skip_throw_blocks` instead."
397+
skip_throw_blocks = unoptimize_throw_blocks
398+
end
349399
state = AnalyzerState(world; jetconfigs...)
350400
return OptAnalyzer(
351401
state,
352402
report_pass,
353403
function_filter,
354-
skip_noncompileable_calls)
404+
skip_noncompileable_calls,
405+
skip_throw_blocks)
355406
end
356407

357408
const OPT_ANALYZER_CONFIGURATIONS = Set{Symbol}((
358-
:report_pass, :function_filter, :skip_noncompileable_calls))
409+
:report_pass, :function_filter, :skip_noncompileable_calls, :skip_throw_blocks,
410+
:unoptimize_throw_blocks))
359411

360412
let valid_keys = GENERAL_CONFIGURATIONS OPT_ANALYZER_CONFIGURATIONS
361413
@eval JETInterface.valid_configurations(::OptAnalyzer) = $valid_keys

test/analyzers/test_optanalyzer.jl

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -325,13 +325,41 @@ const issue560μ = zeros(Issue560Vec3, 2, 3, 4, 5)
325325
issue560f(μ) = reinterpret(reshape, Float64, μ)
326326
@test_opt issue560f(issue560μ)
327327

328-
f_issue643_1(x) = throw("$x <- dynamic dispatches from this interpolation should be ignored")
329-
@noinline _f_issue643_2(x) = "$x <- dynamic dispatches from this interpolation should be ignored"
328+
f_issue643_1(x) = throw("$x <- dynamic dispatches from this interpolation should be IGNORED")
329+
@noinline _f_issue643_2(x) = "$x <- dynamic dispatches from this interpolation should be IGNORED"
330330
f_issue643_2(x) = throw(_f_issue643_2(x))
331-
test_opt(f_issue643_1, (Any,); broken=true)
332-
test_opt(f_issue643_1, (Any,); broken=true) # check cached case
333-
test_opt(f_issue643_2, (Any,); broken=true)
334-
test_opt(f_issue643_2, (Any,); broken=true) # check cached case
331+
function f_issue643_3(x)
332+
x isa Float64 ||
333+
throw("$x <- dynamic dispatches from this interpolation should be IGNORED")
334+
return sin(x)
335+
end
336+
function f_issue643_4(x, c)
337+
if c
338+
throw("$x <- dynamic dispatches from this interpolation should be IGNORED")
339+
end
340+
"$x <- dynamic dispatches from this interpolation should be REPORTED"
341+
end
342+
test_opt(f_issue643_1, (Any,))
343+
test_opt(f_issue643_1, (Any,)) # check cached case
344+
test_opt(f_issue643_2, (Any,))
345+
test_opt(f_issue643_2, (Any,)) # check cached case
346+
test_opt(f_issue643_3, (Any,))
347+
test_opt(f_issue643_3, (Any,)) # check cached case
348+
let res = report_opt(f_issue643_4, (Any,Bool))
349+
@test count(@nospecialize(r)->r isa RuntimeDispatchReport, get_reports(res)) == 1
350+
end
351+
let res = report_opt(f_issue643_1, (Any,); skip_throw_blocks=false)
352+
@test any(@nospecialize(r)->r isa RuntimeDispatchReport, get_reports(res))
353+
end
354+
let res = report_opt(f_issue643_2, (Any,); skip_throw_blocks=false)
355+
@test any(@nospecialize(r)->r isa RuntimeDispatchReport, get_reports(res))
356+
end
357+
let res = report_opt(f_issue643_3, (Any,); skip_throw_blocks=false)
358+
@test any(@nospecialize(r)->r isa RuntimeDispatchReport, get_reports(res))
359+
end
360+
let res = report_opt(f_issue643_4, (Any,Bool); skip_throw_blocks=false)
361+
@test count(@nospecialize(r)->r isa RuntimeDispatchReport, get_reports(res)) == 2
362+
end
335363
# the dynamic dispatch should be reported if the method is analyzed standalone
336364
@test !isempty(get_reports(report_opt(_f_issue643_2, (Any,))))
337365

0 commit comments

Comments
 (0)