|
| 1 | +struct TrimAnalyzer <: AbstractAnalyzer |
| 2 | + state::AnalyzerState |
| 3 | + analysis_token::AnalysisToken |
| 4 | + method_table::CC.CachedMethodTable{CC.OverlayMethodTable} |
| 5 | + function TrimAnalyzer(state::AnalyzerState, analysis_token::AnalysisToken) |
| 6 | + method_table = CC.CachedMethodTable(CC.OverlayMethodTable(state.world, TRIM_METHOD_TABLE)) |
| 7 | + return new(state, analysis_token, method_table) |
| 8 | + end |
| 9 | +end |
| 10 | +function TrimAnalyzer(state::AnalyzerState) |
| 11 | + analysis_cache_key = compute_hash(state.inf_params) |
| 12 | + analysis_token = get!(AnalysisToken, TRIM_ANALYZER_CACHE, analysis_cache_key) |
| 13 | + return TrimAnalyzer(state, analysis_token) |
| 14 | +end |
| 15 | + |
| 16 | +# AbstractInterpreter API |
| 17 | +# ======================= |
| 18 | + |
| 19 | +# TrimAnalyzer does not need any sources, so discard them always |
| 20 | +CC.method_table(analyzer::TrimAnalyzer) = analyzer.method_table |
| 21 | + |
| 22 | +# AbstractAnalyzer API |
| 23 | +# ==================== |
| 24 | + |
| 25 | +JETInterface.AnalyzerState(analyzer::TrimAnalyzer) = analyzer.state |
| 26 | +function JETInterface.AbstractAnalyzer(analyzer::TrimAnalyzer, state::AnalyzerState) |
| 27 | + return TrimAnalyzer(state, analyzer.analysis_token) |
| 28 | +end |
| 29 | +JETInterface.AnalysisToken(analyzer::TrimAnalyzer) = analyzer.analysis_token |
| 30 | + |
| 31 | +const TRIM_ANALYZER_CACHE = Dict{UInt, AnalysisToken}() |
| 32 | + |
| 33 | +# TRIM_METHOD_TABLE |
| 34 | +# =============== |
| 35 | + |
| 36 | +using Base.Experimental: @overlay |
| 37 | +Base.Experimental.@MethodTable TRIM_METHOD_TABLE |
| 38 | + |
| 39 | +@eval @overlay TRIM_METHOD_TABLE Core.DomainError(@nospecialize(val), @nospecialize(msg::AbstractString)) = (@noinline; $(Expr(:new, :DomainError, :val, :msg))) |
| 40 | + |
| 41 | +@overlay TRIM_METHOD_TABLE (f::Base.RedirectStdStream)(io::Core.CoreSTDOUT) = Base._redirect_io_global(io, f.unix_fd) |
| 42 | + |
| 43 | +@overlay TRIM_METHOD_TABLE Base.depwarn(msg, funcsym; force::Bool=false) = nothing |
| 44 | +@overlay TRIM_METHOD_TABLE Base._assert_tostring(msg) = "" |
| 45 | +@overlay TRIM_METHOD_TABLE Base.reinit_stdio() = nothing |
| 46 | +@overlay TRIM_METHOD_TABLE Base.JuliaSyntax.enable_in_core!() = nothing |
| 47 | +@overlay TRIM_METHOD_TABLE Base.init_active_project() = Base.ACTIVE_PROJECT[] = nothing |
| 48 | +@overlay TRIM_METHOD_TABLE Base.set_active_project(projfile::Union{AbstractString,Nothing}) = Base.ACTIVE_PROJECT[] = projfile |
| 49 | +@overlay TRIM_METHOD_TABLE Base.disable_library_threading() = nothing |
| 50 | +@overlay TRIM_METHOD_TABLE Base.start_profile_listener() = nothing |
| 51 | +@overlay TRIM_METHOD_TABLE Base.invokelatest(f, args...; kwargs...) = f(args...; kwargs...) |
| 52 | +@overlay TRIM_METHOD_TABLE function Base.sprint(f::F, args::Vararg{Any,N}; context=nothing, sizehint::Integer=0) where {F<:Function,N} |
| 53 | + s = IOBuffer(sizehint=sizehint) |
| 54 | + if context isa Tuple |
| 55 | + f(IOContext(s, context...), args...) |
| 56 | + elseif context !== nothing |
| 57 | + f(IOContext(s, context), args...) |
| 58 | + else |
| 59 | + f(s, args...) |
| 60 | + end |
| 61 | + String(Base._unsafe_take!(s)) |
| 62 | +end |
| 63 | +function show_typeish(io::IO, @nospecialize(T)) |
| 64 | + if T isa Type |
| 65 | + show(io, T) |
| 66 | + elseif T isa TypeVar |
| 67 | + print(io, (T::TypeVar).name) |
| 68 | + else |
| 69 | + print(io, "?") |
| 70 | + end |
| 71 | +end |
| 72 | +@overlay TRIM_METHOD_TABLE function Base.show(io::IO, T::Type) |
| 73 | + if T isa DataType |
| 74 | + print(io, T.name.name) |
| 75 | + if T !== T.name.wrapper && length(T.parameters) > 0 |
| 76 | + print(io, "{") |
| 77 | + first = true |
| 78 | + for p in T.parameters |
| 79 | + if !first |
| 80 | + print(io, ", ") |
| 81 | + end |
| 82 | + first = false |
| 83 | + if p isa Int |
| 84 | + show(io, p) |
| 85 | + elseif p isa Type |
| 86 | + show(io, p) |
| 87 | + elseif p isa Symbol |
| 88 | + print(io, ":") |
| 89 | + print(io, p) |
| 90 | + elseif p isa TypeVar |
| 91 | + print(io, p.name) |
| 92 | + else |
| 93 | + print(io, "?") |
| 94 | + end |
| 95 | + end |
| 96 | + print(io, "}") |
| 97 | + end |
| 98 | + elseif T isa Union |
| 99 | + print(io, "Union{") |
| 100 | + show_typeish(io, T.a) |
| 101 | + print(io, ", ") |
| 102 | + show_typeish(io, T.b) |
| 103 | + print(io, "}") |
| 104 | + elseif T isa UnionAll |
| 105 | + print(io, T.body::Type) |
| 106 | + print(io, " where ") |
| 107 | + print(io, T.var.name) |
| 108 | + end |
| 109 | +end |
| 110 | +@overlay TRIM_METHOD_TABLE Base.show_type_name(io::IO, tn::Core.TypeName) = print(io, tn.name) |
| 111 | + |
| 112 | +@overlay TRIM_METHOD_TABLE Base.mapreduce(f::F, op::F2, A::Base.AbstractArrayOrBroadcasted; dims=:, init=Base._InitialValue()) where {F, F2} = |
| 113 | + Base._mapreduce_dim(f, op, init, A, dims) |
| 114 | +@overlay TRIM_METHOD_TABLE Base.mapreduce(f::F, op::F2, A::Base.AbstractArrayOrBroadcasted...; kw...) where {F, F2} = |
| 115 | + reduce(op, map(f, A...); kw...) |
| 116 | + |
| 117 | +@overlay TRIM_METHOD_TABLE Base._mapreduce_dim(f::F, op::F2, nt, A::Base.AbstractArrayOrBroadcasted, ::Colon) where {F, F2} = |
| 118 | + Base.mapfoldl_impl(f, op, nt, A) |
| 119 | + |
| 120 | +@overlay TRIM_METHOD_TABLE Base._mapreduce_dim(f::F, op::F2, ::Base._InitialValue, A::Base.AbstractArrayOrBroadcasted, ::Colon) where {F, F2} = |
| 121 | + Base._mapreduce(f, op, IndexStyle(A), A) |
| 122 | + |
| 123 | +@overlay TRIM_METHOD_TABLE Base._mapreduce_dim(f::F, op::F2, nt, A::Base.AbstractArrayOrBroadcasted, dims) where {F, F2} = |
| 124 | + Base.mapreducedim!(f, op, Base.reducedim_initarray(A, dims, nt), A) |
| 125 | + |
| 126 | +@overlay TRIM_METHOD_TABLE Base._mapreduce_dim(f::F, op::F2, ::Base._InitialValue, A::Base.AbstractArrayOrBroadcasted, dims) where {F,F2} = |
| 127 | + Base.mapreducedim!(f, op, Base.reducedim_init(f, op, A, dims), A) |
| 128 | + |
| 129 | +@overlay TRIM_METHOD_TABLE Base.mapreduce_empty_iter(f::F, op::F2, itr, ItrEltype) where {F, F2} = |
| 130 | + Base.reduce_empty_iter(Base.MappingRF(f, op), itr, ItrEltype) |
| 131 | +@overlay TRIM_METHOD_TABLE Base.mapreduce_first(f::F, op::F2, x) where {F,F2} = Base.reduce_first(op, f(x)) |
| 132 | + |
| 133 | +@overlay TRIM_METHOD_TABLE Base._mapreduce(f::F, op::F2, A::Base.AbstractArrayOrBroadcasted) where {F,F2} = Base._mapreduce(f, op, Base.IndexStyle(A), A) |
| 134 | +@overlay TRIM_METHOD_TABLE Base.mapreduce_empty(::typeof(identity), op::F, T) where {F} = Base.reduce_empty(op, T) |
| 135 | +@overlay TRIM_METHOD_TABLE Base.mapreduce_empty(::typeof(abs), op::F, T) where {F} = abs(Base.reduce_empty(op, T)) |
| 136 | +@overlay TRIM_METHOD_TABLE Base.mapreduce_empty(::typeof(abs2), op::F, T) where {F} = abs2(Base.reduce_empty(op, T)) |
| 137 | + |
| 138 | +@overlay TRIM_METHOD_TABLE Base.Sys.__init_build() = nothing |
| 139 | + |
| 140 | +# function __init__() |
| 141 | +# try |
| 142 | +# ccall((:__gmp_set_memory_functions, libgmp), Cvoid, |
| 143 | +# (Ptr{Cvoid},Ptr{Cvoid},Ptr{Cvoid}), |
| 144 | +# cglobal(:jl_gc_counted_malloc), |
| 145 | +# cglobal(:jl_gc_counted_realloc_with_old_size), |
| 146 | +# cglobal(:jl_gc_counted_free_with_size)) |
| 147 | +# ZERO.alloc, ZERO.size, ZERO.d = 0, 0, C_NULL |
| 148 | +# ONE.alloc, ONE.size, ONE.d = 1, 1, pointer(_ONE) |
| 149 | +# catch ex |
| 150 | +# Base.showerror_nostdio(ex, "WARNING: Error during initialization of module GMP") |
| 151 | +# end |
| 152 | +# # This only works with a patched version of GMP, ignore otherwise |
| 153 | +# try |
| 154 | +# ccall((:__gmp_set_alloc_overflow_function, libgmp), Cvoid, |
| 155 | +# (Ptr{Cvoid},), |
| 156 | +# cglobal(:jl_throw_out_of_memory_error)) |
| 157 | +# ALLOC_OVERFLOW_FUNCTION[] = true |
| 158 | +# catch ex |
| 159 | +# # ErrorException("ccall: could not find function...") |
| 160 | +# if typeof(ex) != ErrorException |
| 161 | +# rethrow() |
| 162 | +# end |
| 163 | +# end |
| 164 | +# end |
| 165 | + |
| 166 | +@overlay TRIM_METHOD_TABLE Base.Sort.issorted(itr; |
| 167 | + lt::T=isless, by::F=identity, rev::Union{Bool,Nothing}=nothing, order::Base.Sort.Ordering=Forward) where {T,F} = |
| 168 | + Base.Sort.issorted(itr, Base.Sort.ord(lt,by,rev,order)) |
| 169 | + |
| 170 | +@overlay TRIM_METHOD_TABLE function Base.TOML.try_return_datetime(p, year, month, day, h, m, s, ms) |
| 171 | + return Base.TOML.DateTime(year, month, day, h, m, s, ms) |
| 172 | +end |
| 173 | +@overlay TRIM_METHOD_TABLE function Base.TOML.try_return_date(p, year, month, day) |
| 174 | + return Base.TOML.Date(year, month, day) |
| 175 | +end |
| 176 | +@overlay TRIM_METHOD_TABLE function Base.TOML.parse_local_time(l::Base.TOML.Parser) |
| 177 | + h = Base.TOML.@try Base.TOML.parse_int(l, false) |
| 178 | + h in 0:23 || return Base.TOML.ParserError(Base.TOML.ErrParsingDateTime) |
| 179 | + _, m, s, ms = Base.TOML.@try Base.TOML._parse_local_time(l, true) |
| 180 | + # TODO: Could potentially parse greater accuracy for the |
| 181 | + # fractional seconds here. |
| 182 | + return Base.TOML.try_return_time(l, h, m, s, ms) |
| 183 | +end |
| 184 | +@overlay TRIM_METHOD_TABLE function Base.TOML.try_return_time(p, h, m, s, ms) |
| 185 | + return Base.TOML.Time(h, m, s, ms) |
| 186 | +end |
| 187 | + |
| 188 | +# analysis injections |
| 189 | +# =================== |
| 190 | + |
| 191 | +function CC.abstract_call_gf_by_type(analyzer::TrimAnalyzer, |
| 192 | + @nospecialize(func), arginfo::CC.ArgInfo, si::CC.StmtInfo, @nospecialize(atype), sv::CC.InferenceState, |
| 193 | + max_methods::Int) |
| 194 | + ret = @invoke CC.abstract_call_gf_by_type(analyzer::AbstractAnalyzer, |
| 195 | + func::Any, arginfo::CC.ArgInfo, si::CC.StmtInfo, atype::Any, sv::CC.InferenceState, max_methods::Int) |
| 196 | + atype′ = Ref{Any}(atype) |
| 197 | + function after_abstract_call_gf_by_type(analyzer′::TrimAnalyzer, sv′::CC.InferenceState) |
| 198 | + ret′ = ret[] |
| 199 | + report_dispatch_error!(analyzer′, sv′, ret′, atype′[]) |
| 200 | + return true |
| 201 | + end |
| 202 | + if isready(ret) |
| 203 | + after_abstract_call_gf_by_type(analyzer, sv) |
| 204 | + else |
| 205 | + push!(sv.tasks, after_abstract_call_gf_by_type) |
| 206 | + end |
| 207 | + return ret |
| 208 | +end |
| 209 | + |
| 210 | +# analysis |
| 211 | +# ======== |
| 212 | + |
| 213 | +# DispatchErrorReport |
| 214 | +# ------------------- |
| 215 | + |
| 216 | +@jetreport struct DispatchErrorReport <: InferenceErrorReport |
| 217 | + @nospecialize t # ::Union{Type, Vector{Type}} |
| 218 | +end |
| 219 | +JETInterface.print_report_message(io::IO, report::DispatchErrorReport) = print(io, "Unresolved call found") |
| 220 | + |
| 221 | +function is_inlineable(analyzer::TrimAnalyzer, match, info) |
| 222 | + mi = CC.specialize_method(match; preexisting=true) |
| 223 | + isnothing(mi) && return false |
| 224 | + ci = get(CC.code_cache(analyzer), mi, nothing) |
| 225 | + isnothing(ci) && return false |
| 226 | + src = @atomic :monotonic ci.inferred |
| 227 | + return CC.src_inlining_policy(analyzer, src, info, zero(UInt32)) |
| 228 | +end |
| 229 | + |
| 230 | +function report_dispatch_error!(analyzer::TrimAnalyzer, sv::CC.InferenceState, call::CC.CallMeta, @nospecialize(atype)) |
| 231 | + info = call.info |
| 232 | + if info === CC.NoCallInfo() |
| 233 | + report = DispatchErrorReport(sv, atype) |
| 234 | + add_new_report!(analyzer, sv.result, report) |
| 235 | + else |
| 236 | + if info isa CC.ConstCallInfo |
| 237 | + info = info.call |
| 238 | + end |
| 239 | + if info isa CC.MethodMatchInfo |
| 240 | + for match in info.results |
| 241 | + if (isnothing(CC.get_compileable_sig(match.method, match.spec_types, match.sparams)) && |
| 242 | + !is_inlineable(analyzer, match, info)) |
| 243 | + report = DispatchErrorReport(sv, atype) |
| 244 | + add_new_report!(analyzer, sv.result, report) |
| 245 | + end |
| 246 | + end |
| 247 | + else |
| 248 | + @assert info isa CC.UnionSplitInfo |
| 249 | + for info in info.split |
| 250 | + for match in info.results |
| 251 | + if (isnothing(CC.get_compileable_sig(match.method, match.spec_types, match.sparams)) && |
| 252 | + !is_inlineable(analyzer, match, info)) |
| 253 | + report = DispatchErrorReport(sv, atype) |
| 254 | + add_new_report!(analyzer, sv.result, report) |
| 255 | + end |
| 256 | + end |
| 257 | + end |
| 258 | + end |
| 259 | + end |
| 260 | + return false |
| 261 | +end |
| 262 | + |
| 263 | +# Constructor |
| 264 | +# =========== |
| 265 | + |
| 266 | +# the entry constructor |
| 267 | +function TrimAnalyzer(world::UInt = Base.get_world_counter(); jetconfigs...) |
| 268 | + jetconfigs = kwargs_dict(jetconfigs) |
| 269 | + jetconfigs[:max_methods] = 3 |
| 270 | + # jetconfigs[:assume_bindings_static] = true # TODO |
| 271 | + state = AnalyzerState(world; jetconfigs...) |
| 272 | + return TrimAnalyzer(state) |
| 273 | +end |
| 274 | + |
| 275 | +JETInterface.valid_configurations(::TrimAnalyzer) = GENERAL_CONFIGURATIONS |
| 276 | + |
| 277 | +# Interactive entry points |
| 278 | +# ======================== |
| 279 | + |
| 280 | +function report_trim(args...; jetconfigs...) |
| 281 | + analyzer = TrimAnalyzer(; jetconfigs...) |
| 282 | + return analyze_and_report_call!(analyzer, args...; jetconfigs...) |
| 283 | +end |
| 284 | +macro report_trim(ex0...) |
| 285 | + return InteractiveUtils.gen_call_with_extracted_types_and_kwargs(__module__, :report_trim, ex0) |
| 286 | +end |
| 287 | + |
| 288 | +# Test.jl integration |
| 289 | +# =================== |
| 290 | + |
| 291 | +macro test_trim(ex0...) |
| 292 | + return call_test_ex(:report_trim, Symbol("@test_trim"), ex0, __module__, __source__) |
| 293 | +end |
| 294 | + |
| 295 | +function test_trim(args...; jetconfigs...) |
| 296 | + return func_test(report_trim, :test_trim, args...; jetconfigs...) |
| 297 | +end |
0 commit comments