@@ -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
154160end
@@ -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)
164171end
165172JETInterface. ReportPass(analyzer:: OptAnalyzer ) = analyzer. report_pass
166173JETInterface. AnalysisCache(analyzer:: OptAnalyzer ) = analyzer. analysis_cache
@@ -313,24 +320,61 @@ end
313320function (:: 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)
355406end
356407
357408const 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
360412let valid_keys = GENERAL_CONFIGURATIONS ∪ OPT_ANALYZER_CONFIGURATIONS
361413 @eval JETInterface. valid_configurations(:: OptAnalyzer ) = $ valid_keys
0 commit comments