Skip to content

Commit 8fa9985

Browse files
authored
Merge branch 'master' into gb/size-t-offset
2 parents d789c23 + b9a8d46 commit 8fa9985

File tree

103 files changed

+1730
-1132
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

103 files changed

+1730
-1132
lines changed

CONTRIBUTING.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ The Julia community uses [GitHub issues](https://github.com/JuliaLang/julia/issu
190190
Issues and pull requests should have self explanatory titles such that they can be understood from the list of PRs and Issues.
191191
i.e. `Add {feature}` and `Fix {bug}` are good, `Fix #12345. Corrects the bug.` is bad.
192192
193-
You can make pull requests for incomplete features to get code review. The convention is to open these a draft PRs and prefix
193+
You can make pull requests for incomplete features to get code review. The convention is to open these as draft PRs and prefix
194194
the pull request title with "WIP:" for Work In Progress, or "RFC:" for Request for Comments when work is completed and ready
195195
for merging. This will prevent accidental merging of work that is in progress.
196196
@@ -209,6 +209,7 @@ Add new code to Julia's base libraries as follows (this is the "basic" approach;
209209
Build as usual, and do `make clean testall` to test your contribution. If your contribution includes changes to Makefiles or external dependencies, make sure you can build Julia from a clean tree using `git clean -fdx` or equivalent (be careful – this command will delete any files lying around that aren't checked into git).
210210
211211
#### Running specific tests
212+
212213
There are `make` targets for running specific tests:
213214
214215
make test-bitarray
@@ -346,8 +347,8 @@ please remove the `backport-X.Y` tag from the originating pull request for the c
346347

347348
### Git Recommendations For Pull Requests
348349

349-
- Avoid working from the `master` branch of your fork, creating a new branch will make it easier if Julia's `master` changes and you need to update your pull request.
350-
- Try to [squash](https://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) together small commits that make repeated changes to the same section of code so your pull request is easier to review. A reasonable number of separate well-factored commits is fine, especially for larger changes.
350+
- Avoid working from the `master` branch of your fork. Create a new branch as it will make it easier to update your pull request if Julia's `master` changes.
351+
- Try to [squash](https://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) together small commits that make repeated changes to the same section of code, so your pull request is easier to review. A reasonable number of separate well-factored commits is fine, especially for larger changes.
351352
- If any conflicts arise due to changes in Julia's `master`, prefer updating your pull request branch with `git rebase` versus `git merge` or `git pull`, since the latter will introduce merge commits that clutter the git history with noise that makes your changes more difficult to review.
352353
- Descriptive commit messages are good.
353354
- Using `git add -p` or `git add -i` can be useful to avoid accidentally committing unrelated changes.

Compiler/extras/CompilerDevTools/src/CompilerDevTools.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ struct SplitCacheInterp <: Compiler.AbstractInterpreter
99
inf_params::Compiler.InferenceParams
1010
opt_params::Compiler.OptimizationParams
1111
inf_cache::Vector{Compiler.InferenceResult}
12+
codegen_cache::IdDict{CodeInstance,CodeInfo}
1213
function SplitCacheInterp(;
1314
world::UInt = Base.get_world_counter(),
1415
inf_params::Compiler.InferenceParams = Compiler.InferenceParams(),
1516
opt_params::Compiler.OptimizationParams = Compiler.OptimizationParams(),
1617
inf_cache::Vector{Compiler.InferenceResult} = Compiler.InferenceResult[])
17-
new(world, inf_params, opt_params, inf_cache)
18+
new(world, inf_params, opt_params, inf_cache, IdDict{CodeInstance,CodeInfo}())
1819
end
1920
end
2021

@@ -23,10 +24,11 @@ Compiler.OptimizationParams(interp::SplitCacheInterp) = interp.opt_params
2324
Compiler.get_inference_world(interp::SplitCacheInterp) = interp.world
2425
Compiler.get_inference_cache(interp::SplitCacheInterp) = interp.inf_cache
2526
Compiler.cache_owner(::SplitCacheInterp) = SplitCacheOwner()
27+
Compiler.codegen_cache(interp::SplitCacheInterp) = interp.codegen_cache
2628

2729
import Core.OptimizedGenerics.CompilerPlugins: typeinf, typeinf_edge
2830
@eval @noinline typeinf(::SplitCacheOwner, mi::MethodInstance, source_mode::UInt8) =
29-
Base.invoke_in_world(which(typeinf, Tuple{SplitCacheOwner, MethodInstance, UInt8}).primary_world, Compiler.typeinf_ext, SplitCacheInterp(; world=Base.tls_world_age()), mi, source_mode)
31+
Base.invoke_in_world(which(typeinf, Tuple{SplitCacheOwner, MethodInstance, UInt8}).primary_world, Compiler.typeinf_ext_toplevel, SplitCacheInterp(; world=Base.tls_world_age()), mi, source_mode)
3032

3133
@eval @noinline function typeinf_edge(::SplitCacheOwner, mi::MethodInstance, parent_frame::Compiler.InferenceState, world::UInt, source_mode::UInt8)
3234
# TODO: This isn't quite right, we're just sketching things for now

Compiler/src/abstractinterpretation.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3421,7 +3421,7 @@ function abstract_eval_foreigncall(interp::AbstractInterpreter, e::Expr, sstate:
34213421
abstract_eval_value(interp, x, sstate, sv)
34223422
end
34233423
cconv = e.args[5]
3424-
if isa(cconv, QuoteNode) && (v = cconv.value; isa(v, Tuple{Symbol, UInt16}))
3424+
if isa(cconv, QuoteNode) && (v = cconv.value; isa(v, Tuple{Symbol, UInt16, Bool}))
34253425
override = decode_effects_override(v[2])
34263426
effects = override_effects(effects, override)
34273427
end

Compiler/src/ssair/verify.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3+
irshow_was_loaded() = invokelatest(isdefined, Compiler.IRShow, :debuginfo_firstline)
34
function maybe_show_ir(ir::IRCode)
4-
if isdefined(Core, :Main) && isdefined(Core.Main, :Base)
5+
if irshow_was_loaded()
56
# ensure we use I/O that does not yield, as this gets called during compilation
67
invokelatest(Core.Main.Base.show, Core.stdout, "text/plain", ir)
78
else
@@ -104,15 +105,16 @@ function count_int(val::Int, arr::Vector{Int})
104105
n
105106
end
106107

108+
_debuginfo_firstline(debuginfo::Union{DebugInfo,DebugInfoStream}) = IRShow.debuginfo_firstline(debuginfo)
107109
function verify_ir(ir::IRCode, print::Bool=true,
108110
allow_frontend_forms::Bool=false,
109111
𝕃ₒ::AbstractLattice = SimpleInferenceLattice.instance,
110112
mi::Union{Nothing,MethodInstance}=nothing)
111113
function raise_error()
112114
error_args = Any["IR verification failed."]
113-
if isdefined(Core, :Main) && isdefined(Core.Main, :Base)
115+
if irshow_was_loaded()
114116
# ensure we use I/O that does not yield, as this gets called during compilation
115-
firstline = invokelatest(IRShow.debuginfo_firstline, ir.debuginfo)
117+
firstline = invokelatest(_debuginfo_firstline, ir.debuginfo)
116118
else
117119
firstline = nothing
118120
end

Compiler/src/typeinfer.jl

Lines changed: 52 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ being used for this purpose alone.
1010
"""
1111
module Timings
1212

13+
using ..Core
1314
using ..Compiler: -, +, :, Vector, length, first, empty!, push!, pop!, @inline,
1415
@inbounds, copy, backtrace
1516

@@ -142,9 +143,10 @@ function finish!(interp::AbstractInterpreter, caller::InferenceState, validation
142143
ci, inferred_result, const_flag, first(result.valid_worlds), last(result.valid_worlds), encode_effects(result.ipo_effects),
143144
result.analysis_results, di, edges)
144145
engine_reject(interp, ci)
145-
if !discard_src && isdefined(interp, :codegen) && uncompressed isa CodeInfo
146+
codegen = codegen_cache(interp)
147+
if !discard_src && codegen !== nothing && uncompressed isa CodeInfo
146148
# record that the caller could use this result to generate code when required, if desired, to avoid repeating n^2 work
147-
interp.codegen[ci] = uncompressed
149+
codegen[ci] = uncompressed
148150
if bootstrapping_compiler && inferred_result == nothing
149151
# This is necessary to get decent bootstrapping performance
150152
# when compiling the compiler to inject everything eagerly
@@ -184,8 +186,9 @@ function finish!(interp::AbstractInterpreter, mi::MethodInstance, ci::CodeInstan
184186
ccall(:jl_update_codeinst, Cvoid, (Any, Any, Int32, UInt, UInt, UInt32, Any, Any, Any),
185187
ci, nothing, const_flag, min_world, max_world, ipo_effects, nothing, di, edges)
186188
code_cache(interp)[mi] = ci
187-
if isdefined(interp, :codegen)
188-
interp.codegen[ci] = src
189+
codegen = codegen_cache(interp)
190+
if codegen !== nothing
191+
codegen[ci] = src
189192
end
190193
engine_reject(interp, ci)
191194
return nothing
@@ -1167,7 +1170,10 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance, source_mod
11671170

11681171
ci = result.ci # reload from result in case it changed
11691172
@assert frame.cache_mode != CACHE_MODE_NULL
1170-
@assert is_result_constabi_eligible(result) || (!isdefined(interp, :codegen) || haskey(interp.codegen, ci))
1173+
@assert is_result_constabi_eligible(result) || begin
1174+
codegen = codegen_cache(interp)
1175+
codegen === nothing || haskey(codegen, ci)
1176+
end
11711177
@assert is_result_constabi_eligible(result) == use_const_api(ci)
11721178
@assert isdefined(ci, :inferred) "interpreter did not fulfill our expectations"
11731179
if !is_cached(frame) && source_mode == SOURCE_MODE_ABI
@@ -1233,44 +1239,55 @@ function collectinvokes!(wq::Vector{CodeInstance}, ci::CodeInfo)
12331239
end
12341240
end
12351241

1236-
# This is a bridge for the C code calling `jl_typeinf_func()` on a single Method match
1237-
function typeinf_ext_toplevel(mi::MethodInstance, world::UInt, source_mode::UInt8)
1238-
interp = NativeInterpreter(world)
1239-
ci = typeinf_ext(interp, mi, source_mode)
1240-
if source_mode == SOURCE_MODE_ABI && ci isa CodeInstance && !ci_has_invoke(ci)
1241-
inspected = IdSet{CodeInstance}()
1242-
tocompile = Vector{CodeInstance}()
1243-
push!(tocompile, ci)
1244-
while !isempty(tocompile)
1245-
# ci_has_real_invoke(ci) && return ci # optimization: cease looping if ci happens to get compiled (not just jl_fptr_wait_for_compiled, but fully jl_is_compiled_codeinst)
1246-
callee = pop!(tocompile)
1247-
ci_has_invoke(callee) && continue
1248-
callee in inspected && continue
1249-
src = get(interp.codegen, callee, nothing)
1242+
function add_codeinsts_to_jit!(interp::AbstractInterpreter, ci, source_mode::UInt8)
1243+
source_mode == SOURCE_MODE_ABI || return ci
1244+
ci isa CodeInstance && !ci_has_invoke(ci) || return ci
1245+
codegen = codegen_cache(interp)
1246+
codegen === nothing && return ci
1247+
inspected = IdSet{CodeInstance}()
1248+
tocompile = Vector{CodeInstance}()
1249+
push!(tocompile, ci)
1250+
while !isempty(tocompile)
1251+
# ci_has_real_invoke(ci) && return ci # optimization: cease looping if ci happens to get compiled (not just jl_fptr_wait_for_compiled, but fully jl_is_compiled_codeinst)
1252+
callee = pop!(tocompile)
1253+
ci_has_invoke(callee) && continue
1254+
callee in inspected && continue
1255+
src = get(codegen, callee, nothing)
1256+
if !isa(src, CodeInfo)
1257+
src = @atomic :monotonic callee.inferred
1258+
if isa(src, String)
1259+
src = _uncompressed_ir(callee, src)
1260+
end
12501261
if !isa(src, CodeInfo)
1251-
src = @atomic :monotonic callee.inferred
1252-
if isa(src, String)
1253-
src = _uncompressed_ir(callee, src)
1254-
end
1255-
if !isa(src, CodeInfo)
1256-
newcallee = typeinf_ext(interp, callee.def, source_mode)
1257-
if newcallee isa CodeInstance
1258-
callee === ci && (ci = newcallee) # ci stopped meeting the requirements after typeinf_ext last checked, try again with newcallee
1259-
push!(tocompile, newcallee)
1260-
#else
1261-
# println("warning: could not get source code for ", callee.def)
1262-
end
1263-
continue
1262+
newcallee = typeinf_ext(interp, callee.def, source_mode)
1263+
if newcallee isa CodeInstance
1264+
callee === ci && (ci = newcallee) # ci stopped meeting the requirements after typeinf_ext last checked, try again with newcallee
1265+
push!(tocompile, newcallee)
1266+
#else
1267+
# println("warning: could not get source code for ", callee.def)
12641268
end
1269+
continue
12651270
end
1266-
push!(inspected, callee)
1267-
collectinvokes!(tocompile, src)
1268-
ccall(:jl_add_codeinst_to_jit, Cvoid, (Any, Any), callee, src)
12691271
end
1272+
push!(inspected, callee)
1273+
collectinvokes!(tocompile, src)
1274+
ccall(:jl_add_codeinst_to_jit, Cvoid, (Any, Any), callee, src)
12701275
end
12711276
return ci
12721277
end
12731278

1279+
function typeinf_ext_toplevel(interp::AbstractInterpreter, mi::MethodInstance, source_mode::UInt8)
1280+
ci = typeinf_ext(interp, mi, source_mode)
1281+
ci = add_codeinsts_to_jit!(interp, ci, source_mode)
1282+
return ci
1283+
end
1284+
1285+
# This is a bridge for the C code calling `jl_typeinf_func()` on a single Method match
1286+
function typeinf_ext_toplevel(mi::MethodInstance, world::UInt, source_mode::UInt8)
1287+
interp = NativeInterpreter(world)
1288+
return typeinf_ext_toplevel(interp, mi, source_mode)
1289+
end
1290+
12741291
# This is a bridge for the C code calling `jl_typeinf_func()` on set of Method matches
12751292
function typeinf_ext_toplevel(methods::Vector{Any}, worlds::Vector{UInt}, trim::Bool)
12761293
inspected = IdSet{CodeInstance}()

Compiler/src/types.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ the following methods to satisfy the `AbstractInterpreter` API requirement:
2323
- `get_inference_world(interp::NewInterpreter)` - return the world age for this interpreter
2424
- `get_inference_cache(interp::NewInterpreter)` - return the local inference cache
2525
- `cache_owner(interp::NewInterpreter)` - return the owner of any new cache entries
26+
27+
If `CodeInstance`s compiled using `interp::NewInterpreter` are meant to be executed with `invoke`,
28+
a method `codegen_cache(interp::NewInterpreter) -> IdDict{CodeInstance, CodeInfo}` must be defined,
29+
and inference must be triggered via `typeinf_ext_toplevel` with source mode `SOURCE_MODE_ABI`.
2630
"""
2731
abstract type AbstractInterpreter end
2832

@@ -430,6 +434,19 @@ to incorporate customized dispatches for the overridden methods.
430434
method_table(interp::AbstractInterpreter) = InternalMethodTable(get_inference_world(interp))
431435
method_table(interp::NativeInterpreter) = interp.method_table
432436

437+
"""
438+
codegen_cache(interp::AbstractInterpreter) -> Union{Nothing, IdDict{CodeInstance, CodeInfo}}
439+
440+
Optionally return a cache associating a `CodeInfo` to a `CodeInstance` that should be added to the JIT
441+
for future execution via `invoke(f, ::CodeInstance, args...)`. This cache is used during `typeinf_ext_toplevel`,
442+
and may be safely discarded between calls to this function.
443+
444+
By default, a value of `nothing` is returned indicating that `CodeInstance`s should not be added to the JIT.
445+
Attempting to execute them via `invoke` will result in an error.
446+
"""
447+
codegen_cache(interp::AbstractInterpreter) = nothing
448+
codegen_cache(interp::NativeInterpreter) = interp.codegen
449+
433450
"""
434451
By default `AbstractInterpreter` implements the following inference bail out logic:
435452
- `bail_out_toplevel_call(::AbstractInterpreter, sig, ::InferenceState)`: bail out from

Compiler/src/typeutils.jl

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,8 @@ function isTypeDataType(@nospecialize t)
3636
isType(t) && return false
3737
# Could be Union{} at runtime
3838
t === Core.TypeofBottom && return false
39-
if t.name === Tuple.name
40-
# If we have a Union parameter, could have been redistributed at runtime,
41-
# e.g. `Tuple{Union{Int, Float64}, Int}` is a DataType, but
42-
# `Union{Tuple{Int, Int}, Tuple{Float64, Int}}` is typeequal to it and
43-
# is not.
44-
return all(isTypeDataType, t.parameters)
45-
end
46-
return true
39+
# Return true if `t` is not covariant
40+
return t.name !== Tuple.name
4741
end
4842

4943
has_extended_info(@nospecialize x) = (!isa(x, Type) && !isvarargtype(x)) || isType(x)

Compiler/src/validation.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const VALID_EXPR_HEADS = IdDict{Symbol,UnitRange{Int}}(
2323
:meta => 0:typemax(Int),
2424
:global => 1:1,
2525
:globaldecl => 1:2,
26-
:foreigncall => 5:typemax(Int), # name, RT, AT, nreq, (cconv, effects), args..., roots...
26+
:foreigncall => 5:typemax(Int), # name, RT, AT, nreq, (cconv, effects, gc_safe), args..., roots...
2727
:cfunction => 5:5,
2828
:isdefined => 1:2,
2929
:code_coverage_effect => 0:0,

Compiler/test/AbstractInterpreter.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,3 +534,17 @@ let interp = DebugInterp()
534534
end
535535
@test found
536536
end
537+
538+
@newinterp InvokeInterp
539+
struct InvokeOwner end
540+
codegen = IdDict{CodeInstance, CodeInfo}()
541+
Compiler.cache_owner(::InvokeInterp) = InvokeOwner()
542+
Compiler.codegen_cache(::InvokeInterp) = codegen
543+
let interp = InvokeInterp()
544+
source_mode = Compiler.SOURCE_MODE_ABI
545+
f = (+)
546+
args = (1, 1)
547+
mi = @ccall jl_method_lookup(Any[f, args...]::Ptr{Any}, (1+length(args))::Csize_t, Base.tls_world_age()::Csize_t)::Ref{Core.MethodInstance}
548+
ci = Compiler.typeinf_ext_toplevel(interp, mi, source_mode)
549+
@test invoke(f, ci, args...) == 2
550+
end

Compiler/test/codegen.jl

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -889,57 +889,6 @@ ex54166 = Union{Missing, Int64}[missing -2; missing -2];
889889
dims54166 = (1,2)
890890
@test (minimum(ex54166; dims=dims54166)[1] === missing)
891891

892-
# #54109 - Excessive LLVM time for egal
893-
struct DefaultOr54109{T}
894-
x::T
895-
default::Bool
896-
end
897-
898-
@eval struct Torture1_54109
899-
$((Expr(:(::), Symbol("x$i"), DefaultOr54109{Float64}) for i = 1:897)...)
900-
end
901-
Torture1_54109() = Torture1_54109((DefaultOr54109(1.0, false) for i = 1:897)...)
902-
903-
@eval struct Torture2_54109
904-
$((Expr(:(::), Symbol("x$i"), DefaultOr54109{Float64}) for i = 1:400)...)
905-
$((Expr(:(::), Symbol("x$(i+400)"), DefaultOr54109{Int16}) for i = 1:400)...)
906-
end
907-
Torture2_54109() = Torture2_54109((DefaultOr54109(1.0, false) for i = 1:400)..., (DefaultOr54109(Int16(1), false) for i = 1:400)...)
908-
909-
@noinline egal_any54109(x, @nospecialize(y::Any)) = x === Base.compilerbarrier(:type, y)
910-
911-
let ir1 = get_llvm(egal_any54109, Tuple{Torture1_54109, Any}),
912-
ir2 = get_llvm(egal_any54109, Tuple{Torture2_54109, Any})
913-
914-
# We can't really do timing on CI, so instead, let's look at the length of
915-
# the optimized IR. The original version had tens of thousands of lines and
916-
# was slower, so just check here that we only have < 500 lines. If somebody,
917-
# implements a better comparison that's larger than that, just re-benchmark
918-
# this and adjust the threshold.
919-
920-
@test count(==('\n'), ir1) < 500
921-
@test count(==('\n'), ir2) < 500
922-
end
923-
924-
## Regression test for egal of a struct of this size without padding, but with
925-
## non-bitsegal, to make sure that it doesn't accidentally go down the accelerated
926-
## path.
927-
@eval struct BigStructAnyInt
928-
$((Expr(:(::), Symbol("x$i"), Pair{Any, Int}) for i = 1:33)...)
929-
end
930-
BigStructAnyInt() = BigStructAnyInt((Union{Base.inferencebarrier(Float64), Int}=>i for i = 1:33)...)
931-
@test egal_any54109(BigStructAnyInt(), BigStructAnyInt())
932-
933-
## For completeness, also test correctness, since we don't have a lot of
934-
## large-struct tests.
935-
936-
# The two allocations of the same struct will likely have different padding,
937-
# we want to make sure we find them egal anyway - a naive memcmp would
938-
# accidentally look at it.
939-
@test egal_any54109(Torture1_54109(), Torture1_54109())
940-
@test egal_any54109(Torture2_54109(), Torture2_54109())
941-
@test !egal_any54109(Torture1_54109(), Torture1_54109((DefaultOr54109(2.0, false) for i = 1:897)...))
942-
943892
bar54599() = Base.inferencebarrier(true) ? (Base.PkgId(Main),1) : nothing
944893

945894
function foo54599()

0 commit comments

Comments
 (0)