Skip to content

Commit dc3d649

Browse files
authored
Merge branch 'master' into reimplement_cosc_for_Float32_Float64
2 parents c2173b3 + fd52126 commit dc3d649

File tree

52 files changed

+475
-188
lines changed

Some content is hidden

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

52 files changed

+475
-188
lines changed

Compiler/src/Compiler.jl

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ using Base: @_foldable_meta, @_gc_preserve_begin, @_gc_preserve_end, @nospeciali
4848
PARTITION_KIND_GLOBAL, PARTITION_KIND_UNDEF_CONST, PARTITION_KIND_BACKDATED_CONST, PARTITION_KIND_DECLARED,
4949
PARTITION_FLAG_DEPWARN,
5050
Base, BitVector, Bottom, Callable, DataTypeFieldDesc,
51-
EffectsOverride, Filter, Generator, IteratorSize, JLOptions, NUM_EFFECTS_OVERRIDES,
52-
OneTo, Ordering, RefValue, SizeUnknown, _NAMEDTUPLE_NAME,
51+
EffectsOverride, Filter, Generator, NUM_EFFECTS_OVERRIDES,
52+
OneTo, Ordering, RefValue, _NAMEDTUPLE_NAME,
5353
_array_for, _bits_findnext, _methods_by_ftype, _uniontypes, all, allocatedinline, any,
5454
argument_datatype, binding_kind, cconvert, copy_exprargs, datatype_arrayelem,
5555
datatype_fieldcount, datatype_fieldtypes, datatype_layoutsize, datatype_nfields,
@@ -64,9 +64,11 @@ using Base: @_foldable_meta, @_gc_preserve_begin, @_gc_preserve_end, @nospeciali
6464
partition_restriction, quoted, rename_unionall, rewrap_unionall, specialize_method,
6565
structdiff, tls_world_age, unconstrain_vararg_length, unionlen, uniontype_layout,
6666
uniontypes, unsafe_convert, unwrap_unionall, unwrapva, vect, widen_diagonal,
67-
_uncompressed_ir, maybe_add_binding_backedge!, datatype_min_ninitialized,
67+
_uncompressed_ir, datatype_min_ninitialized,
6868
partialstruct_init_undefs, fieldcount_noerror, _eval_import, _eval_using,
69-
get_ci_mi
69+
get_ci_mi, get_methodtable, morespecific, specializations, has_image_globalref,
70+
PARTITION_MASK_KIND, PARTITION_KIND_GUARD, PARTITION_FLAG_EXPORTED, PARTITION_FLAG_DEPRECATED,
71+
BINDING_FLAG_ANY_IMPLICIT_EDGES, is_some_implicit, IteratorSize, SizeUnknown, get_require_world, JLOptions
7072

7173
using Base.Order
7274

@@ -188,6 +190,10 @@ include("bootstrap.jl")
188190
include("reflection_interface.jl")
189191
include("opaque_closure.jl")
190192

193+
baremodule ReinferUtils end
194+
include(ReinferUtils, "reinfer.jl")
195+
include(ReinferUtils, "bindinginvalidations.jl")
196+
191197
macro __SOURCE_FILE__()
192198
__source__.file === nothing && return nothing
193199
return QuoteNode(__source__.file::Symbol)

base/invalidation.jl renamed to Compiler/src/bindinginvalidations.jl

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

3-
struct GlobalRefIterator
4-
mod::Module
5-
end
6-
IteratorSize(::Type{GlobalRefIterator}) = SizeUnknown()
7-
globalrefs(mod::Module) = GlobalRefIterator(mod)
8-
9-
function iterate(gri::GlobalRefIterator, i = 1)
10-
m = gri.mod
11-
table = ccall(:jl_module_get_bindings, Ref{SimpleVector}, (Any,), m)
12-
i > length(table) && return nothing
13-
b = table[i]
14-
b === nothing && return iterate(gri, i+1)
15-
return ((b::Core.Binding).globalref, i+1)
16-
end
3+
using ..Compiler: _uncompressed_ir, specializations, get_ci_mi, convert, unsafe_load, cglobal, generating_output, has_image_globalref,
4+
PARTITION_MASK_KIND, PARTITION_KIND_GUARD, PARTITION_FLAG_EXPORTED, PARTITION_FLAG_DEPRECATED,
5+
BINDING_FLAG_ANY_IMPLICIT_EDGES, binding_kind, partition_restriction, is_some_imported,
6+
is_some_binding_imported, is_some_implicit, SizeUnknown, maybe_add_binding_backedge!, walk_binding_partition, abstract_eval_partition_load, userefs
7+
using .Core: SimpleVector, CodeInfo
178

189
function foreachgr(visit, src::CodeInfo)
1910
stmts = src.code
2011
for i = 1:length(stmts)
2112
stmt = stmts[i]
2213
isa(stmt, GlobalRef) && visit(stmt)
23-
for ur in Compiler.userefs(stmt)
14+
for ur in userefs(stmt)
2415
arg = ur[]
2516
isa(arg, GlobalRef) && visit(arg)
2617
end
@@ -35,7 +26,7 @@ function anygr(visit, src::CodeInfo)
3526
visit(stmt) && return true
3627
continue
3728
end
38-
for ur in Compiler.userefs(stmt)
29+
for ur in userefs(stmt)
3930
arg = ur[]
4031
isa(arg, GlobalRef) && visit(arg) && return true
4132
end
@@ -69,7 +60,7 @@ function invalidate_method_for_globalref!(gr::GlobalRef, method::Method, invalid
6960
src = _uncompressed_ir(method)
7061
invalidate_all = should_invalidate_code_for_globalref(gr, src)
7162
end
72-
if invalidate_all && !Base.generating_output()
63+
if invalidate_all && !generating_output()
7364
@atomic method.did_scan_source |= 0x4
7465
end
7566
invalidated_any = false
@@ -99,15 +90,15 @@ export_affecting_partition_flags(bpart::Core.BindingPartition) =
9990
function invalidate_code_for_globalref!(b::Core.Binding, invalidated_bpart::Core.BindingPartition, new_bpart::Core.BindingPartition, new_max_world::UInt)
10091
gr = b.globalref
10192

102-
(_, (ib, ibpart)) = Compiler.walk_binding_partition(b, invalidated_bpart, new_max_world)
103-
(_, (nb, nbpart)) = Compiler.walk_binding_partition(b, new_bpart, new_max_world+1)
93+
(_, (ib, ibpart)) = walk_binding_partition(b, invalidated_bpart, new_max_world)
94+
(_, (nb, nbpart)) = walk_binding_partition(b, new_bpart, new_max_world+1)
10495

10596
# `abstract_eval_partition_load` is the maximum amount of information that inference
10697
# reads from a binding partition. If this information does not change - we do not need to
10798
# invalidate any code that inference created, because we know that the result will not change.
10899
need_to_invalidate_code =
109-
Compiler.abstract_eval_partition_load(nothing, ib, ibpart) !==
110-
Compiler.abstract_eval_partition_load(nothing, nb, nbpart)
100+
abstract_eval_partition_load(nothing, ib, ibpart) !==
101+
abstract_eval_partition_load(nothing, nb, nbpart)
111102

112103
need_to_invalidate_export = export_affecting_partition_flags(invalidated_bpart) !==
113104
export_affecting_partition_flags(new_bpart)
@@ -173,12 +164,6 @@ end
173164
invalidate_code_for_globalref!(gr::GlobalRef, invalidated_bpart::Core.BindingPartition, new_bpart::Core.BindingPartition, new_max_world::UInt) =
174165
invalidate_code_for_globalref!(convert(Core.Binding, gr), invalidated_bpart, new_bpart, new_max_world)
175166

176-
function maybe_add_binding_backedge!(b::Core.Binding, edge::Union{Method, CodeInstance})
177-
meth = isa(edge, Method) ? edge : get_ci_mi(edge).def
178-
ccall(:jl_maybe_add_binding_backedge, Cint, (Any, Any, Any), b, edge, meth)
179-
return nothing
180-
end
181-
182167
function binding_was_invalidated(b::Core.Binding)
183168
# At least one partition is required for invalidation
184169
!isdefined(b, :partitions) && return false
@@ -206,7 +191,7 @@ function scan_new_method!(method::Method, image_backedges_only::Bool)
206191
end
207192

208193
function scan_new_methods!(extext_methods::Vector{Any}, internal_methods::Vector{Any}, image_backedges_only::Bool)
209-
if image_backedges_only && Base.generating_output(true)
194+
if image_backedges_only && generating_output(true)
210195
# Replacing image bindings is forbidden during incremental precompilation - skip backedge insertion
211196
return
212197
end

base/staticdata.jl renamed to Compiler/src/reinfer.jl

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

3+
using ..Compiler.Base
4+
using ..Compiler: _findsup, store_backedges, JLOptions, get_world_counter,
5+
_methods_by_ftype, get_methodtable, get_ci_mi, should_instrument,
6+
morespecific, RefValue, get_require_world, Vector, IdDict
37
using .Core: CodeInstance, MethodInstance
4-
using .Base: JLOptions, Compiler, get_world_counter, _methods_by_ftype, get_methodtable, get_ci_mi, morespecific
58

69
const WORLD_AGE_REVALIDATION_SENTINEL::UInt = 1
7-
const _jl_debug_method_invalidation = Ref{Union{Nothing,Vector{Any}}}(nothing)
10+
const _jl_debug_method_invalidation = RefValue{Union{Nothing,Vector{Any}}}(nothing)
811
debug_method_invalidation(onoff::Bool) =
912
_jl_debug_method_invalidation[] = onoff ? Any[] : nothing
1013

@@ -70,7 +73,7 @@ function insert_backedges(edges::Vector{Any}, ext_ci_list::Union{Nothing,Vector{
7073
# determine which CodeInstance objects are still valid in our image
7174
# to enable any applicable new codes
7275
backedges_only = unsafe_load(cglobal(:jl_first_image_replacement_world, UInt)) == typemax(UInt)
73-
Base.scan_new_methods!(extext_methods, internal_methods, backedges_only)
76+
scan_new_methods!(extext_methods, internal_methods, backedges_only)
7477
workspace = VerifyMethodWorkspace()
7578
_insert_backedges(edges, workspace)
7679
if ext_ci_list !== nothing
@@ -135,23 +138,23 @@ function needs_instrumentation(codeinst::CodeInstance, mi::MethodInstance, def::
135138
if JLOptions().code_coverage != 0 || JLOptions().malloc_log != 0
136139
# test if the code needs to run with instrumentation, in which case we cannot use existing generated code
137140
if isdefined(def, :debuginfo) ? # generated_only functions do not have debuginfo, so fall back to considering their codeinst debuginfo though this may be slower and less reliable
138-
Compiler.should_instrument(def.module, def.debuginfo) :
139-
isdefined(codeinst, :debuginfo) && Compiler.should_instrument(def.module, codeinst.debuginfo)
141+
should_instrument(def.module, def.debuginfo) :
142+
isdefined(codeinst, :debuginfo) && should_instrument(def.module, codeinst.debuginfo)
140143
return true
141144
end
142145
gensig = gen_staged_sig(def, mi)
143146
if gensig !== nothing
144147
# if this is defined by a generator, try to consider forcing re-running the generators too, to add coverage for them
145-
minworld = Ref{UInt}(1)
146-
maxworld = Ref{UInt}(typemax(UInt))
147-
has_ambig = Ref{Int32}(0)
148+
minworld = RefValue{UInt}(1)
149+
maxworld = RefValue{UInt}(typemax(UInt))
150+
has_ambig = RefValue{Int32}(0)
148151
result = _methods_by_ftype(gensig, nothing, -1, validation_world, #=ambig=#false, minworld, maxworld, has_ambig)
149152
if result !== nothing
150153
for k = 1:length(result)
151154
match = result[k]::Core.MethodMatch
152155
genmethod = match.method
153156
# no, I refuse to refuse to recurse into your cursed generated function generators and will only test one level deep here
154-
if isdefined(genmethod, :debuginfo) && Compiler.should_instrument(genmethod.module, genmethod.debuginfo)
157+
if isdefined(genmethod, :debuginfo) && should_instrument(genmethod.module, genmethod.debuginfo)
155158
return true
156159
end
157160
end
@@ -194,7 +197,7 @@ function verify_method(codeinst::CodeInstance, validation_world::UInt, workspace
194197
continue
195198
end
196199

197-
minworld, maxworld = Base.get_require_world(), validation_world
200+
minworld, maxworld = get_require_world(), validation_world
198201

199202
if haskey(workspace.visiting, initial.codeinst)
200203
workspace.result_states[current_depth] = VerifyMethodResultState(workspace.visiting[initial.codeinst], minworld, maxworld)
@@ -209,7 +212,7 @@ function verify_method(codeinst::CodeInstance, validation_world::UInt, workspace
209212
# Check for invalidation of GlobalRef edges
210213
if (initial.def.did_scan_source & 0x1) == 0x0
211214
backedges_only = unsafe_load(cglobal(:jl_first_image_replacement_world, UInt)) == typemax(UInt)
212-
Base.scan_new_method!(initial.def, backedges_only)
215+
scan_new_method!(initial.def, backedges_only)
213216
end
214217
if (initial.def.did_scan_source & 0x4) != 0x0
215218
maxworld = 0
@@ -220,7 +223,7 @@ function verify_method(codeinst::CodeInstance, validation_world::UInt, workspace
220223
end
221224

222225
# Process all non-CodeInstance edges
223-
if !isempty(initial.callees) && maxworld != Base.get_require_world()
226+
if !isempty(initial.callees) && maxworld != get_require_world()
224227
matches = []
225228
j = 1
226229
while j <= length(initial.callees)
@@ -248,7 +251,7 @@ function verify_method(codeinst::CodeInstance, validation_world::UInt, workspace
248251
j += 1
249252
min_valid2 = minworld
250253
max_valid2 = maxworld
251-
if !Base.binding_was_invalidated(edge)
254+
if !binding_was_invalidated(edge)
252255
if isdefined(edge, :partitions)
253256
min_valid2 = edge.partitions.min_world
254257
max_valid2 = edge.partitions.max_world
@@ -333,7 +336,7 @@ function verify_method(codeinst::CodeInstance, validation_world::UInt, workspace
333336
end
334337
@atomic :monotonic child.max_world = result.result_maxworld
335338
if result.result_maxworld == validation_world && validation_world == get_world_counter()
336-
Compiler.store_backedges(child, child.edges)
339+
store_backedges(child, child.edges)
337340
end
338341
@assert workspace.visiting[child] == length(workspace.stack) + 1
339342
delete!(workspace.visiting, child)
@@ -559,9 +562,9 @@ function verify_call(@nospecialize(sig), expecteds::Core.SimpleVector, i::Int, n
559562
end
560563
# next, compare the current result of ml_matches to the old result
561564
lim = _jl_debug_method_invalidation[] !== nothing ? Int(typemax(Int32)) : n
562-
minworld = Ref{UInt}(1)
563-
maxworld = Ref{UInt}(typemax(UInt))
564-
has_ambig = Ref{Int32}(0)
565+
minworld = RefValue{UInt}(1)
566+
maxworld = RefValue{UInt}(typemax(UInt))
567+
has_ambig = RefValue{Int32}(0)
565568
result = _methods_by_ftype(sig, nothing, lim, world, #=ambig=#false, minworld, maxworld, has_ambig)
566569
if result === nothing
567570
empty!(matches)
@@ -626,7 +629,7 @@ function verify_invokesig(@nospecialize(invokesig), expected::Method, world::UIn
626629
minworld = 1
627630
maxworld = 0
628631
else
629-
matched, valid_worlds = Compiler._findsup(invokesig, mt, world)
632+
matched, valid_worlds = _findsup(invokesig, mt, world)
630633
minworld, maxworld = valid_worlds.min_world, valid_worlds.max_world
631634
if matched === nothing
632635
maxworld = 0
@@ -641,3 +644,9 @@ function verify_invokesig(@nospecialize(invokesig), expected::Method, world::UIn
641644
end
642645
return minworld, maxworld
643646
end
647+
648+
# Wrapper to call insert_backedges in typeinf_world for external calls
649+
function insert_backedges_typeinf(edges::Vector{Any}, ext_ci_list::Union{Nothing,Vector{Any}}, extext_methods::Vector{Any}, internal_methods::Vector{Any})
650+
args = Any[insert_backedges, edges, ext_ci_list, extext_methods, internal_methods]
651+
return ccall(:jl_call_in_typeinf_world, Any, (Ptr{Any}, Cint), args, length(args))
652+
end

Compiler/src/typeinfer.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,13 @@ function Base.iterate(it::ForwardToBackedgeIterator, i::Int = 1)
741741
end
742742

743743
# record the backedges
744+
745+
function maybe_add_binding_backedge!(b::Core.Binding, edge::Union{Method, CodeInstance})
746+
meth = isa(edge, Method) ? edge : get_ci_mi(edge).def
747+
ccall(:jl_maybe_add_binding_backedge, Cint, (Any, Any, Any), b, edge, meth)
748+
return nothing
749+
end
750+
744751
function store_backedges(caller::CodeInstance, edges::SimpleVector)
745752
isa(get_ci_mi(caller).def, Method) || return # don't add backedges to toplevel method instance
746753

Compiler/test/invalidation.jl

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,3 +325,39 @@ end
325325
# This test checks for invalidation of recursive backedges. However, unfortunately, the original failure
326326
# manifestation was an unreliable segfault or an assertion failure, so we don't have a more compact test.
327327
@test success(`$(Base.julia_cmd()) -e 'Base.typejoin(x, ::Type) = 0; exit()'`)
328+
329+
# Test drop_all_caches functionality
330+
@testset "drop_all_caches" begin
331+
# Run in subprocess to avoid disrupting the main test process
332+
script = """
333+
# Define test functions
334+
drop_cache_test_f(x) = x + 1
335+
drop_cache_test_g(x) = drop_cache_test_f(x) * 2
336+
337+
# Compile the functions and capture stderr
338+
drop_cache_test_g(5) == 12 || error("failure")
339+
340+
println(stderr, "==DROPPING ALL CACHES==")
341+
342+
# Drop all caches
343+
Base.drop_all_caches()
344+
345+
# Functions should still work (but will be recompiled on next call)
346+
drop_cache_test_g(5) == 12 || error("failure")
347+
348+
println(stderr, "SUCCESS: drop_all_caches test passed")
349+
exit(0)
350+
"""
351+
352+
io = Pipe()
353+
# Run the test in a subprocess because Base.drop_all_caches() is extreme
354+
result = run(pipeline(`$(Base.julia_cmd()[1]) --startup-file=no --trace-compile=stderr -e "$script"`, stderr=io))
355+
close(io.in)
356+
err = read(io, String)
357+
# println(err)
358+
@test success(result)
359+
err_before, err_after = split(err, "==DROPPING ALL CACHES==")
360+
@test occursin("SUCCESS: drop_all_caches test passed", err_after)
361+
@test occursin("precompile(Tuple{typeof(Main.drop_cache_test_g), $Int})", err_before)
362+
@test occursin("precompile(Tuple{typeof(Main.drop_cache_test_g), $Int}) # recompile", err_after)
363+
end

HISTORY.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,6 @@ New language features
3333
Language changes
3434
----------------
3535

36-
* Julia now defaults to 1 "interactive" thread, in addition to the 1 default "worker" thread. i.e. `-t1,1`.
37-
This means in default configuration the main task and repl (when in interactive mode), which both run on
38-
thread 1, now run within the `interactive` threadpool. The libuv IO loop also runs on thread 1,
39-
helping efficient utilization of the worker threadpool used by `Threads.@spawn`. Asking for specifically 1 thread
40-
(`-t1`/`JULIA_NUM_THREADS=1`) or passing `0` will disable the interactive thread i.e. `-t1,0` or `JULIA_NUM_THREADS=1,0`
41-
, or `-tauto,0` etc. Asking for more than 1 thread will enable the interactive thread so
42-
`-t2` will set the equivalent of `-t2,1` ([#57087]).
4336
* When a method is replaced with an exactly equivalent one, the old method is not deleted. Instead, the
4437
new method takes priority and becomes more specific than the old method. Thus if the new method is deleted
4538
later, the old method will resume operating. This can be useful in mocking frameworks (as in SparseArrays,
@@ -87,6 +80,14 @@ Command-line option changes
8780
Multi-threading changes
8881
-----------------------
8982

83+
* Julia now defaults to 1 "interactive" thread, in addition to the 1 default "worker" thread. i.e. `-t1,1`.
84+
This means in default configuration the main task and repl (when in interactive mode), which both run on
85+
thread 1, now run within the `interactive` threadpool. The libuv IO loop also runs on thread 1,
86+
helping efficient utilization of the worker threadpool used by `Threads.@spawn`. Asking for specifically 1 thread
87+
(`-t1`/`JULIA_NUM_THREADS=1`) or `0` interactive threads will disable the interactive thread i.e. `-t1,0` or `JULIA_NUM_THREADS=1,0`
88+
, or `-tauto,0` etc. Asking for more than 1 thread will enable the interactive thread so
89+
`-t2` will set the equivalent of `-t2,1`. As a reminder, buffers
90+
[should not be managed based on `threadid()`](https://docs.julialang.org/en/v1/manual/multi-threading/#Using-@threads-without-data-races) ([#57087]).
9091
* New types are defined to handle the pattern of code that must run once per process, called
9192
a `OncePerProcess{T}` type, which allows defining a function that should be run exactly once
9293
the first time it is called, and then always return the same result value of type `T`

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Command-line option changes
2828

2929
* The option `--sysimage-native-code=no` has been deprecated.
3030
* The `JULIA_CPU_TARGET` environment variable now supports a `sysimage` keyword to match (or extend) the CPU target used to build the current system image ([#58970]).
31+
* The `--code-coverage=all` option now automatically throws away sysimage caches so that code coverage can be accurately measured on methods within the sysimage. It is thrown away after startup (and after startup.jl), before any user code is executed ([#59234])
3132

3233
Multi-threading changes
3334
-----------------------

base/Base.jl

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -267,9 +267,6 @@ include("uuid.jl")
267267
include("pkgid.jl")
268268
include("toml_parser.jl")
269269
include("linking.jl")
270-
module StaticData
271-
include("staticdata.jl")
272-
end
273270
include("loading.jl")
274271

275272
# BinaryPlatforms, used by Artifacts. Needs `Sort`.

base/Base_compiler.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,6 @@ using .Order
348348

349349
include("coreir.jl")
350350
include("module.jl")
351-
include("invalidation.jl")
352351

353352
BUILDROOT::String = ""
354353
DATAROOT::String = ""
@@ -377,6 +376,7 @@ process_sysimg_args!()
377376
function isready end
378377

379378
include(strcat(DATAROOT, "julia/Compiler/src/Compiler.jl"))
379+
using .Compiler.ReinferUtils: ReinferUtils, invalidate_code_for_globalref!
380380

381381
const _return_type = Compiler.return_type
382382

0 commit comments

Comments
 (0)