From cd7250da83837d3e72f8b82aff73418b01bd425a Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 31 Oct 2024 23:56:04 +0000 Subject: [PATCH] Make Compiler an independent package This is a further extension to #56128 to make the compiler into a proper independent, useable outside of `Base` as `using Compiler` in the same way that `JuliaSyntax` works already. InteractiveUtils gains a new `@activate` macro that can be used to activate an outside Compiler package, either for reflection only or for codegen also. --- Compiler/Project.toml | 3 + Compiler/src/Compiler.jl | 194 ++++++++++++++++++ .../src}/abstractinterpretation.jl | 8 +- .../src}/abstractlattice.jl | 0 Compiler/src/bootstrap.jl | 66 ++++++ {base/compiler => Compiler/src}/cicache.jl | 0 {base/compiler => Compiler/src}/effects.jl | 0 .../src}/inferenceresult.jl | 0 .../src}/inferencestate.jl | 0 .../compiler => Compiler/src}/methodtable.jl | 0 {base/compiler => Compiler/src}/optimize.jl | 16 +- Compiler/src/reflection_interface.jl | 58 ++++++ {base/compiler => Compiler/src}/sort.jl | 0 .../ssair/EscapeAnalysis/EscapeAnalysis.jl | 13 +- .../src}/ssair/EscapeAnalysis/disjoint_set.jl | 0 .../src}/ssair/basicblock.jl | 0 .../src}/ssair/domtree.jl | 0 {base/compiler => Compiler/src}/ssair/heap.jl | 0 .../src}/ssair/inlining.jl | 0 {base/compiler => Compiler/src}/ssair/ir.jl | 2 - .../src}/ssair/irinterp.jl | 0 .../compiler => Compiler/src}/ssair/legacy.jl | 0 .../compiler => Compiler/src}/ssair/passes.jl | 0 {base/compiler => Compiler/src}/ssair/show.jl | 128 ++++++------ .../src}/ssair/slot2ssa.jl | 0 .../compiler => Compiler/src}/ssair/tarjan.jl | 0 .../compiler => Compiler/src}/ssair/verify.jl | 0 {base/compiler => Compiler/src}/stmtinfo.jl | 0 {base/compiler => Compiler/src}/tfuncs.jl | 4 +- {base/compiler => Compiler/src}/typeinfer.jl | 2 +- .../compiler => Compiler/src}/typelattice.jl | 80 ++------ {base/compiler => Compiler/src}/typelimits.jl | 2 +- {base/compiler => Compiler/src}/types.jl | 0 {base/compiler => Compiler/src}/typeutils.jl | 0 {base/compiler => Compiler/src}/utilities.jl | 6 - {base/compiler => Compiler/src}/validation.jl | 0 Makefile | 11 +- base/Base.jl | 94 ++------- base/Base_compiler.jl | 50 ++++- base/boot.jl | 1 - base/compiler/bootstrap.jl | 52 ----- base/compiler/compiler.jl | 123 ----------- base/compilerimg.jl | 4 - base/coreir.jl | 54 +++++ base/essentials.jl | 3 +- base/{compiler/parsing.jl => flparse.jl} | 0 base/loading.jl | 129 +++++++----- base/opaque_closure.jl | 11 +- base/reflection.jl | 193 +++++++++-------- base/runtime_internals.jl | 8 +- base/show.jl | 105 +++++----- base/stacktraces.jl | 2 +- base/sysimg.jl | 12 +- contrib/generate_precompile.jl | 1 + doc/src/devdocs/EscapeAnalysis.md | 12 +- src/module.c | 12 +- src/precompile_utils.c | 58 +++--- src/toplevel.c | 15 ++ .../InteractiveUtils/src/InteractiveUtils.jl | 3 +- stdlib/InteractiveUtils/src/codeview.jl | 4 +- stdlib/InteractiveUtils/src/macros.jl | 61 ++++++ stdlib/Makefile | 2 - stdlib/REPL/src/REPLCompletions.jl | 3 + sysimage.mk | 5 +- test/compiler/inference.jl | 63 +++--- 65 files changed, 966 insertions(+), 707 deletions(-) create mode 100644 Compiler/Project.toml create mode 100644 Compiler/src/Compiler.jl rename {base/compiler => Compiler/src}/abstractinterpretation.jl (99%) rename {base/compiler => Compiler/src}/abstractlattice.jl (100%) create mode 100644 Compiler/src/bootstrap.jl rename {base/compiler => Compiler/src}/cicache.jl (100%) rename {base/compiler => Compiler/src}/effects.jl (100%) rename {base/compiler => Compiler/src}/inferenceresult.jl (100%) rename {base/compiler => Compiler/src}/inferencestate.jl (100%) rename {base/compiler => Compiler/src}/methodtable.jl (100%) rename {base/compiler => Compiler/src}/optimize.jl (99%) create mode 100644 Compiler/src/reflection_interface.jl rename {base/compiler => Compiler/src}/sort.jl (100%) rename {base/compiler => Compiler/src}/ssair/EscapeAnalysis/EscapeAnalysis.jl (99%) rename {base/compiler => Compiler/src}/ssair/EscapeAnalysis/disjoint_set.jl (100%) rename {base/compiler => Compiler/src}/ssair/basicblock.jl (100%) rename {base/compiler => Compiler/src}/ssair/domtree.jl (100%) rename {base/compiler => Compiler/src}/ssair/heap.jl (100%) rename {base/compiler => Compiler/src}/ssair/inlining.jl (100%) rename {base/compiler => Compiler/src}/ssair/ir.jl (99%) rename {base/compiler => Compiler/src}/ssair/irinterp.jl (100%) rename {base/compiler => Compiler/src}/ssair/legacy.jl (100%) rename {base/compiler => Compiler/src}/ssair/passes.jl (100%) rename {base/compiler => Compiler/src}/ssair/show.jl (95%) rename {base/compiler => Compiler/src}/ssair/slot2ssa.jl (100%) rename {base/compiler => Compiler/src}/ssair/tarjan.jl (100%) rename {base/compiler => Compiler/src}/ssair/verify.jl (100%) rename {base/compiler => Compiler/src}/stmtinfo.jl (100%) rename {base/compiler => Compiler/src}/tfuncs.jl (99%) rename {base/compiler => Compiler/src}/typeinfer.jl (99%) rename {base/compiler => Compiler/src}/typelattice.jl (92%) rename {base/compiler => Compiler/src}/typelimits.jl (99%) rename {base/compiler => Compiler/src}/types.jl (100%) rename {base/compiler => Compiler/src}/typeutils.jl (100%) rename {base/compiler => Compiler/src}/utilities.jl (99%) rename {base/compiler => Compiler/src}/validation.jl (100%) delete mode 100644 base/compiler/bootstrap.jl delete mode 100644 base/compiler/compiler.jl delete mode 100644 base/compilerimg.jl create mode 100644 base/coreir.jl rename base/{compiler/parsing.jl => flparse.jl} (100%) diff --git a/Compiler/Project.toml b/Compiler/Project.toml new file mode 100644 index 0000000000000..b933d08db5205 --- /dev/null +++ b/Compiler/Project.toml @@ -0,0 +1,3 @@ +name = "Compiler" +uuid = "807dbc54-b67e-4c79-8afb-eafe4df6f2e1" +version = "0.0.1" diff --git a/Compiler/src/Compiler.jl b/Compiler/src/Compiler.jl new file mode 100644 index 0000000000000..c2c074dc92bbc --- /dev/null +++ b/Compiler/src/Compiler.jl @@ -0,0 +1,194 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# When generating an incremental precompile file, we first check whether we +# already have a copy of this *exact* code in the system image. If so, we +# simply generates a pkgimage that has the dependency edges we recorded in +# the system image and simply returns that copy of the compiler. If not, +# we proceed to load/precompile this as an ordinary package. +if isdefined(Base, :generating_output) && Base.generating_output(true) && + Base.samefile(Base._compiler_require_dependencies[1][2], @eval @__FILE__) && + !Base.any_includes_stale( + map(Base.CacheHeaderIncludes, Base._compiler_require_dependencies), + "sysimg", nothing) + + Base.prepare_compiler_stub_image!() + append!(Base._require_dependencies, Base._compiler_require_dependencies) + # There isn't much point in precompiling native code - downstream users will + # specialize their own versions of the compiler code and we don't activate + # the compiler by default anyway, so let's save ourselves some disk space. + ccall(:jl_suppress_precompile, Cvoid, (Cint,), 1) + +else + +@eval baremodule Compiler + +# Needs to match UUID defined in Project.toml +ccall(:jl_set_module_uuid, Cvoid, (Any, NTuple{2, UInt64}), Compiler, + (0x807dbc54_b67e_4c79, 0x8afb_eafe4df6f2e1)) + +using Core.Intrinsics, Core.IR + +import Core: print, println, show, write, unsafe_write, + _apply_iterate, svec, apply_type, Builtin, IntrinsicFunction, + MethodInstance, CodeInstance, MethodTable, MethodMatch, PartialOpaque, + TypeofVararg, Core, SimpleVector, donotdelete, compilerbarrier, + memoryref_isassigned, memoryrefnew, memoryrefoffset, memoryrefget, + memoryrefset!, typename + +using Base +using Base: Ordering, vect, EffectsOverride, BitVector, @_gc_preserve_begin, @_gc_preserve_end, RefValue, + @nospecializeinfer, @_foldable_meta, fieldindex, is_function_def, indexed_iterate, isexpr, methods, + get_world_counter, JLOptions, _methods_by_ftype, unwrap_unionall, cconvert, unsafe_convert, + issingletontype, isType, rewrap_unionall, has_free_typevars, isvarargtype, hasgenerator, + IteratorSize, SizeUnknown, _array_for, Bottom, generating_output, diff_names, + ismutationfree, NUM_EFFECTS_OVERRIDES, _NAMEDTUPLE_NAME, datatype_fieldtypes, + argument_datatype, isfieldatomic, unwrapva, iskindtype, _bits_findnext, copy_exprargs, + Generator, Filter, ismutabletypename, isvatuple, datatype_fieldcount, + isconcretedispatch, isdispatchelem, datatype_layoutsize, + datatype_arrayelem, unionlen, isidentityfree, _uniontypes, uniontypes, OneTo, Callable, + DataTypeFieldDesc, datatype_nfields, datatype_pointerfree, midpoint, is_valid_intrinsic_elptr, + allocatedinline, isbitsunion, widen_diagonal, unconstrain_vararg_length, + rename_unionall, may_invoke_generator, is_meta_expr_head, is_meta_expr, quoted, + specialize_method, hasintersect, is_nospecializeinfer, is_nospecialized, + get_nospecializeinfer_sig, tls_world_age, uniontype_layout, kwerr, + moduleroot, is_file_tracked, decode_effects_override, lookup_binding_partition, + is_some_imported, binding_kind, is_some_guard, is_some_const_binding, partition_restriction, + BINDING_KIND_GLOBAL, structdiff +using Base.Order +import Base: getindex, setindex!, length, iterate, push!, isempty, first, convert, ==, + copy, popfirst!, in, haskey, resize!, copy!, append!, last, get!, size, + get, iterate, findall, min_world, max_world, _topmod + +const getproperty = Core.getfield +const setproperty! = Core.setfield! +const swapproperty! = Core.swapfield! +const modifyproperty! = Core.modifyfield! +const replaceproperty! = Core.replacefield! +const _DOCS_ALIASING_WARNING = "" + +ccall(:jl_set_istopmod, Cvoid, (Any, Bool), Compiler, false) + +eval(x) = Core.eval(Compiler, x) +eval(m, x) = Core.eval(m, x) + +function include(x::String) + if !isdefined(Base, :end_base_include) + # During bootstrap, all includes are relative to `base/` + x = Base.strcat(Base.strcat(Base.BUILDROOT, "../usr/share/julia/Compiler/src/"), x) + end + Base.include(Compiler, x) +end + +function include(mod::Module, x::String) + if !isdefined(Base, :end_base_include) + x = Base.strcat(Base.strcat(Base.BUILDROOT, "../usr/share/julia/Compiler/src/"), x) + end + Base.include(mod, x) +end + + +macro _boundscheck() Expr(:boundscheck) end + +# These types are used by reflection.jl and expr.jl too, so declare them here. +# Note that `@assume_effects` is available only after loading namedtuple.jl. +abstract type MethodTableView end +abstract type AbstractInterpreter end + +function return_type end +function is_return_type(Core.@nospecialize(f)) + f === return_type && return true + if isdefined(Base, :Compiler) && Compiler !== Base.Compiler + # Also model the return_type function of the builtin Compiler the same. + # This isn't completely sound. We don't actually have any idea what the + # base compiler will do at runtime. In the fullness of time, we should + # re-work the semantics to make the cache primary and thus avoid having + # to reason about what the compiler may do at runtime, but we're not + # fully there yet. + return f === Base.Compiler.return_type + end + return false +end + +include("sort.jl") + +# We don't include some.jl, but this definition is still useful. +something(x::Nothing, y...) = something(y...) +something(x::Any, y...) = x + +############ +# compiler # +############ + +baremodule BuildSettings +using Core: ARGS, include +using ..Compiler: >, getindex, length + +global MAX_METHODS::Int = 3 + +if length(ARGS) > 2 && ARGS[2] === "--buildsettings" + include(BuildSettings, ARGS[3]) +end +end + +if false + import Base: Base, @show +else + macro show(ex...) + blk = Expr(:block) + for s in ex + push!(blk.args, :(println(stdout, $(QuoteNode(s)), " = ", + begin local value = $(esc(s)) end))) + end + isempty(ex) || push!(blk.args, :value) + blk + end +end + +include("cicache.jl") +include("methodtable.jl") +include("effects.jl") +include("types.jl") +include("utilities.jl") +include("validation.jl") + +include("ssair/basicblock.jl") +include("ssair/domtree.jl") +include("ssair/ir.jl") +include("ssair/tarjan.jl") + +include("abstractlattice.jl") +include("stmtinfo.jl") +include("inferenceresult.jl") +include("inferencestate.jl") + +include("typeutils.jl") +include("typelimits.jl") +include("typelattice.jl") +include("tfuncs.jl") + +include("abstractinterpretation.jl") +include("typeinfer.jl") +include("optimize.jl") + +include("bootstrap.jl") +include("reflection_interface.jl") + +if isdefined(Base, :IRShow) + @eval module IRShow + import ..Compiler + using Core.IR + using ..Base + import .Compiler: IRCode, CFG, scan_ssa_use!, + isexpr, compute_basic_blocks, block_for_inst, IncrementalCompact, + Effects, ALWAYS_TRUE, ALWAYS_FALSE, DebugInfoStream, getdebugidx, + VarState, InvalidIRError, argextype, widenconst, singleton_type, + sptypes_from_meth_instance, EMPTY_SPTYPES, InferenceState, + NativeInterpreter, CachedMethodTable, LimitedAccuracy, Timings + # During bootstrap, Base will later include this into its own "IRShow module" + Compiler.include(IRShow, "ssair/show.jl") + end +end + +end + +end diff --git a/base/compiler/abstractinterpretation.jl b/Compiler/src/abstractinterpretation.jl similarity index 99% rename from base/compiler/abstractinterpretation.jl rename to Compiler/src/abstractinterpretation.jl index f3ffd6495ce50..d9319c02b110a 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/Compiler/src/abstractinterpretation.jl @@ -2119,7 +2119,7 @@ function form_partially_defined_struct(@nospecialize(obj), @nospecialize(name)) else fldidx > nminfld || return nothing end - return PartialStruct(objt0, Any[obj isa PartialStruct && i≀length(obj.fields) ? + return PartialStruct(fallback_lattice, objt0, Any[obj isa PartialStruct && i≀length(obj.fields) ? obj.fields[i] : fieldtype(objt0,i) for i = 1:fldidx]) end @@ -2955,7 +2955,7 @@ function abstract_eval_new(interp::AbstractInterpreter, e::Expr, vtypes::Union{V # - any refinement information is available (`anyrefine`), or when # - `nargs` is greater than `n_initialized` derived from the struct type # information alone - rt = PartialStruct(rt, ats) + rt = PartialStruct(𝕃ᡒ, rt, ats) end else rt = refine_partial_type(rt) @@ -2990,7 +2990,7 @@ function abstract_eval_splatnew(interp::AbstractInterpreter, e::Expr, vtypes::Un all(i::Int -> βŠ‘(𝕃ᡒ, (at.fields::Vector{Any})[i], fieldtype(t, i)), 1:n) end)) nothrow = isexact - rt = PartialStruct(rt, at.fields::Vector{Any}) + rt = PartialStruct(𝕃ᡒ, rt, at.fields::Vector{Any}) end else rt = refine_partial_type(rt) @@ -3524,7 +3524,7 @@ end end fields[i] = a end - anyrefine && return PartialStruct(rt.typ, fields) + anyrefine && return PartialStruct(𝕃ᡒ, rt.typ, fields) end if isa(rt, PartialOpaque) return rt # XXX: this case was missed in #39512 diff --git a/base/compiler/abstractlattice.jl b/Compiler/src/abstractlattice.jl similarity index 100% rename from base/compiler/abstractlattice.jl rename to Compiler/src/abstractlattice.jl diff --git a/Compiler/src/bootstrap.jl b/Compiler/src/bootstrap.jl new file mode 100644 index 0000000000000..4205b072d232f --- /dev/null +++ b/Compiler/src/bootstrap.jl @@ -0,0 +1,66 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# make sure that typeinf is executed before turning on typeinf_ext +# this ensures that typeinf_ext doesn't recurse before it can add the item to the workq +# especially try to make sure any recursive and leaf functions have concrete signatures, +# since we won't be able to specialize & infer them at runtime + +activate_codegen!() = ccall(:jl_set_typeinf_func, Cvoid, (Any,), typeinf_ext_toplevel) + +function bootstrap!() + let time() = ccall(:jl_clock_now, Float64, ()) + println("Compiling the compiler. This may take several minutes ...") + interp = NativeInterpreter() + + # analyze_escapes_tt = Tuple{typeof(analyze_escapes), IRCode, Int, TODO} + optimize_tt = Tuple{typeof(optimize), NativeInterpreter, OptimizationState{NativeInterpreter}, InferenceResult} + fs = Any[ + # we first create caches for the optimizer, because they contain many loop constructions + # and they're better to not run in interpreter even during bootstrapping + #=analyze_escapes_tt,=# optimize_tt, + # then we create caches for inference entries + typeinf_ext, typeinf, typeinf_edge, + ] + # tfuncs can't be inferred from the inference entries above, so here we infer them manually + for x in T_FFUNC_VAL + push!(fs, x[3]) + end + for i = 1:length(T_IFUNC) + if isassigned(T_IFUNC, i) + x = T_IFUNC[i] + push!(fs, x[3]) + else + println(stderr, "WARNING: tfunc missing for ", reinterpret(IntrinsicFunction, Int32(i))) + end + end + starttime = time() + for f in fs + if isa(f, DataType) && f.name === typename(Tuple) + tt = f + else + tt = Tuple{typeof(f), Vararg{Any}} + end + for m in _methods_by_ftype(tt, 10, get_world_counter())::Vector + # remove any TypeVars from the intersection + m = m::MethodMatch + typ = Any[m.spec_types.parameters...] + for i = 1:length(typ) + typ[i] = unwraptv(typ[i]) + end + typeinf_type(interp, m.method, Tuple{typ...}, m.sparams) + end + end + endtime = time() + println("Base.Compiler ──── ", sub_float(endtime,starttime), " seconds") + end + activate_codegen!() +end + +function activate!(; reflection=true, codegen=false) + if reflection + Base.REFLECTION_COMPILER[] = Compiler + end + if codegen + activate_codegen!() + end +end diff --git a/base/compiler/cicache.jl b/Compiler/src/cicache.jl similarity index 100% rename from base/compiler/cicache.jl rename to Compiler/src/cicache.jl diff --git a/base/compiler/effects.jl b/Compiler/src/effects.jl similarity index 100% rename from base/compiler/effects.jl rename to Compiler/src/effects.jl diff --git a/base/compiler/inferenceresult.jl b/Compiler/src/inferenceresult.jl similarity index 100% rename from base/compiler/inferenceresult.jl rename to Compiler/src/inferenceresult.jl diff --git a/base/compiler/inferencestate.jl b/Compiler/src/inferencestate.jl similarity index 100% rename from base/compiler/inferencestate.jl rename to Compiler/src/inferencestate.jl diff --git a/base/compiler/methodtable.jl b/Compiler/src/methodtable.jl similarity index 100% rename from base/compiler/methodtable.jl rename to Compiler/src/methodtable.jl diff --git a/base/compiler/optimize.jl b/Compiler/src/optimize.jl similarity index 99% rename from base/compiler/optimize.jl rename to Compiler/src/optimize.jl index edc374f675c5f..8cdd56f4c1a76 100644 --- a/base/compiler/optimize.jl +++ b/Compiler/src/optimize.jl @@ -212,14 +212,14 @@ end function argextype end # imported by EscapeAnalysis function try_compute_field end # imported by EscapeAnalysis -include("compiler/ssair/heap.jl") -include("compiler/ssair/slot2ssa.jl") -include("compiler/ssair/inlining.jl") -include("compiler/ssair/verify.jl") -include("compiler/ssair/legacy.jl") -include("compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl") -include("compiler/ssair/passes.jl") -include("compiler/ssair/irinterp.jl") +include("ssair/heap.jl") +include("ssair/slot2ssa.jl") +include("ssair/inlining.jl") +include("ssair/verify.jl") +include("ssair/legacy.jl") +include("ssair/EscapeAnalysis/EscapeAnalysis.jl") +include("ssair/passes.jl") +include("ssair/irinterp.jl") function ir_to_codeinf!(opt::OptimizationState) (; linfo, src) = opt diff --git a/Compiler/src/reflection_interface.jl b/Compiler/src/reflection_interface.jl new file mode 100644 index 0000000000000..3fc182685e598 --- /dev/null +++ b/Compiler/src/reflection_interface.jl @@ -0,0 +1,58 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +_findall_matches(interp::AbstractInterpreter, @nospecialize(tt)) = findall(tt, method_table(interp)) +_default_interp(world::UInt) = NativeInterpreter(world) + +_may_throw_methoderror(matches::MethodLookupResult) = + matches.ambig || !any(match::Core.MethodMatch->match.fully_covers, matches.matches) + +function _infer_exception_type(interp::AbstractInterpreter, @nospecialize(tt), optimize::Bool) + matches = _findall_matches(interp, tt) + matches === nothing && return nothing + exct = Union{} + if _may_throw_methoderror(matches) + # account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature. + exct = MethodError + end + for match in matches.matches + match = match::Core.MethodMatch + frame = typeinf_frame(interp, match, #=run_optimizer=#optimize) + frame === nothing && return Any + exct = tmerge(exct, widenconst(frame.result.exc_result)) + end + return exct +end + +function _infer_effects(interp::AbstractInterpreter, @nospecialize(tt), optimize::Bool) + matches = _findall_matches(interp, tt) + matches === nothing && return nothing + effects = EFFECTS_TOTAL + if _may_throw_methoderror(matches) + # account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature. + effects = Effects(effects; nothrow=false) + end + for match in matches.matches + match = match::Core.MethodMatch + frame = typeinf_frame(interp, match, #=run_optimizer=#optimize) + frame === nothing && return Effects() + effects = merge_effects(effects, frame.result.ipo_effects) + end + return effects +end + +function statement_costs!(interp::AbstractInterpreter, cost::Vector{Int}, body::Vector{Any}, src::Union{CodeInfo, IRCode}, match::Core.MethodMatch) + params = OptimizationParams(interp) + sptypes = VarState[VarState(sp, false) for sp in match.sparams] + return statement_costs!(cost, body, src, sptypes, params) +end + +function findsup_mt(@nospecialize(tt), world, method_table) + if method_table === nothing + table = InternalMethodTable(world) + elseif method_table isa Core.MethodTable + table = OverlayMethodTable(world, method_table) + else + table = method_table + end + return findsup(tt, table) +end diff --git a/base/compiler/sort.jl b/Compiler/src/sort.jl similarity index 100% rename from base/compiler/sort.jl rename to Compiler/src/sort.jl diff --git a/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl b/Compiler/src/ssair/EscapeAnalysis/EscapeAnalysis.jl similarity index 99% rename from base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl rename to Compiler/src/ssair/EscapeAnalysis/EscapeAnalysis.jl index e8de2e40c4880..c3b1a8b641af4 100644 --- a/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl +++ b/Compiler/src/ssair/EscapeAnalysis/EscapeAnalysis.jl @@ -30,13 +30,16 @@ using ..Compiler: # Core.Compiler specific definitions is_meta_expr_head, is_identity_free_argtype, isexpr, println, setfield!_nothrow, singleton_type, try_compute_field, try_compute_fieldidx, widenconst, βŠ‘, Compiler -include(x) = _TOP_MOD.include(@__MODULE__, x) -if _TOP_MOD === Compiler - include("compiler/ssair/EscapeAnalysis/disjoint_set.jl") -else - include("disjoint_set.jl") +function include(x) + if !isdefined(_TOP_MOD.Base, :end_base_include) + # During bootstrap, all includes are relative to `base/` + x = ccall(:jl_prepend_string, Ref{String}, (Any, Any), "ssair/EscapeAnalysis/", x) + end + _TOP_MOD.include(@__MODULE__, x) end +include("disjoint_set.jl") + const AInfo = IdSet{Any} """ diff --git a/base/compiler/ssair/EscapeAnalysis/disjoint_set.jl b/Compiler/src/ssair/EscapeAnalysis/disjoint_set.jl similarity index 100% rename from base/compiler/ssair/EscapeAnalysis/disjoint_set.jl rename to Compiler/src/ssair/EscapeAnalysis/disjoint_set.jl diff --git a/base/compiler/ssair/basicblock.jl b/Compiler/src/ssair/basicblock.jl similarity index 100% rename from base/compiler/ssair/basicblock.jl rename to Compiler/src/ssair/basicblock.jl diff --git a/base/compiler/ssair/domtree.jl b/Compiler/src/ssair/domtree.jl similarity index 100% rename from base/compiler/ssair/domtree.jl rename to Compiler/src/ssair/domtree.jl diff --git a/base/compiler/ssair/heap.jl b/Compiler/src/ssair/heap.jl similarity index 100% rename from base/compiler/ssair/heap.jl rename to Compiler/src/ssair/heap.jl diff --git a/base/compiler/ssair/inlining.jl b/Compiler/src/ssair/inlining.jl similarity index 100% rename from base/compiler/ssair/inlining.jl rename to Compiler/src/ssair/inlining.jl diff --git a/base/compiler/ssair/ir.jl b/Compiler/src/ssair/ir.jl similarity index 99% rename from base/compiler/ssair/ir.jl rename to Compiler/src/ssair/ir.jl index 1efa10f2437ad..9a76c7370c68d 100644 --- a/base/compiler/ssair/ir.jl +++ b/Compiler/src/ssair/ir.jl @@ -1,7 +1,5 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -Core.PhiNode() = Core.PhiNode(Int32[], Any[]) - isterminator(@nospecialize(stmt)) = isa(stmt, GotoNode) || isa(stmt, GotoIfNot) || isa(stmt, ReturnNode) || isa(stmt, EnterNode) || isexpr(stmt, :leave) diff --git a/base/compiler/ssair/irinterp.jl b/Compiler/src/ssair/irinterp.jl similarity index 100% rename from base/compiler/ssair/irinterp.jl rename to Compiler/src/ssair/irinterp.jl diff --git a/base/compiler/ssair/legacy.jl b/Compiler/src/ssair/legacy.jl similarity index 100% rename from base/compiler/ssair/legacy.jl rename to Compiler/src/ssair/legacy.jl diff --git a/base/compiler/ssair/passes.jl b/Compiler/src/ssair/passes.jl similarity index 100% rename from base/compiler/ssair/passes.jl rename to Compiler/src/ssair/passes.jl diff --git a/base/compiler/ssair/show.jl b/Compiler/src/ssair/show.jl similarity index 95% rename from base/compiler/ssair/show.jl rename to Compiler/src/ssair/show.jl index 2ad14c5c5b565..a2212272ce3fc 100644 --- a/base/compiler/ssair/show.jl +++ b/Compiler/src/ssair/show.jl @@ -5,13 +5,6 @@ @nospecialize -if Pair != Base.Pair -import Base: Base, IOContext, string, join, sprint -IOContext(io::IO, KV::Pair) = IOContext(io, Base.Pair(KV[1], KV[2])) -length(s::String) = Base.length(s) -^(s::String, i::Int) = Base.:^(s, i) -end - import Base: show_unquoted using Base: printstyled, with_output_color, prec_decl, @invoke @@ -141,9 +134,9 @@ function print_stmt(io::IO, idx::Int, @nospecialize(stmt), code::Union{IRCode,Co elseif stmt isa GotoNode print(io, "goto #", stmt.label) elseif stmt isa PhiNode - show_unquoted_phinode(io, stmt, indent, "#") + Base.show_unquoted_phinode(io, stmt, indent, "#") elseif stmt isa GotoIfNot - show_unquoted_gotoifnot(io, stmt, indent, "#") + Base.show_unquoted_gotoifnot(io, stmt, indent, "#") # everything else in the IR, defer to the generic AST printer else show_unquoted(io, stmt, indent, show_type ? prec_decl : 0) @@ -151,65 +144,6 @@ function print_stmt(io::IO, idx::Int, @nospecialize(stmt), code::Union{IRCode,Co nothing end -show_unquoted(io::IO, val::Argument, indent::Int, prec::Int) = show_unquoted(io, Core.SlotNumber(val.n), indent, prec) - -show_unquoted(io::IO, stmt::PhiNode, indent::Int, ::Int) = show_unquoted_phinode(io, stmt, indent, "%") -function show_unquoted_phinode(io::IO, stmt::PhiNode, indent::Int, prefix::String) - args = String[let - e = stmt.edges[i] - v = !isassigned(stmt.values, i) ? "#undef" : - sprint(; context=io) do ioβ€² - show_unquoted(ioβ€², stmt.values[i], indent) - end - "$prefix$e => $v" - end for i in 1:length(stmt.edges) - ] - print(io, "Ο† ", '(') - join(io, args, ", ") - print(io, ')') -end - -function show_unquoted(io::IO, stmt::PhiCNode, indent::Int, ::Int) - print(io, "Ο†αΆœ (") - first = true - for v in stmt.values - first ? (first = false) : print(io, ", ") - show_unquoted(io, v, indent) - end - print(io, ")") -end - -function show_unquoted(io::IO, stmt::PiNode, indent::Int, ::Int) - print(io, "Ο€ (") - show_unquoted(io, stmt.val, indent) - print(io, ", ") - printstyled(io, stmt.typ, color=:cyan) - print(io, ")") -end - -function show_unquoted(io::IO, stmt::UpsilonNode, indent::Int, ::Int) - print(io, "Ο’ (") - isdefined(stmt, :val) ? - show_unquoted(io, stmt.val, indent) : - print(io, "#undef") - print(io, ")") -end - -function show_unquoted(io::IO, stmt::ReturnNode, indent::Int, ::Int) - if !isdefined(stmt, :val) - print(io, "unreachable") - else - print(io, "return ") - show_unquoted(io, stmt.val, indent) - end -end - -show_unquoted(io::IO, stmt::GotoIfNot, indent::Int, ::Int) = show_unquoted_gotoifnot(io, stmt, indent, "%") -function show_unquoted_gotoifnot(io::IO, stmt::GotoIfNot, indent::Int, prefix::String) - print(io, "goto ", prefix, stmt.dest, " if not ") - show_unquoted(io, stmt.cond, indent) -end - function should_print_ssa_type(@nospecialize node) if isa(node, Expr) return !(node.head in (:gc_preserve_begin, :gc_preserve_end, :meta, :leave)) @@ -1137,4 +1071,62 @@ function Base.show(io::IO, e::Effects) print(io, ')') end + +function show(io::IO, inferred::Compiler.InferenceResult) + mi = inferred.linfo + tt = mi.specTypes.parameters[2:end] + tts = join(["::$(t)" for t in tt], ", ") + rettype = inferred.result + if isa(rettype, Compiler.InferenceState) + rettype = rettype.bestguess + end + if isa(mi.def, Method) + print(io, mi.def.name, "(", tts, " => ", rettype, ")") + else + print(io, "Toplevel MethodInstance thunk from ", mi.def, " => ", rettype) + end +end + +Base.show(io::IO, sv::InferenceState) = + (print(io, "InferenceState for "); show(io, sv.linfo)) + +Base.show(io::IO, ::NativeInterpreter) = + print(io, "Compiler.NativeInterpreter(...)") + +Base.show(io::IO, cache::CachedMethodTable) = + print(io, typeof(cache), "(", length(cache.cache), " entries)") + +function Base.show(io::IO, limited::LimitedAccuracy) + print(io, "LimitedAccuracy(") + show(io, limited.typ) + print(io, ", #= ", length(limited.causes), " cause(s) =#)") +end + + +# These sometimes show up as Const-values in InferenceFrameInfo signatures +function show(io::IO, mi_info::Timings.InferenceFrameInfo) + mi = mi_info.mi + def = mi.def + if isa(def, Method) + if isdefined(def, :generator) && mi === def.generator + print(io, "InferenceFrameInfo generator for ") + show(io, def) + else + print(io, "InferenceFrameInfo for ") + argnames = [isa(a, Core.Const) ? (isa(a.val, Type) ? "" : a.val) : "" for a in mi_info.slottypes[1:mi_info.nargs]] + show_tuple_as_call(io, def.name, mi.specTypes; argnames, qualified=true) + end + else + di = mi.cache.inferred.debuginfo + file, line = debuginfo_firstline(di) + file = string(file) + line = isempty(file) || line < 0 ? "" : "$file:$line" + print(io, "Toplevel InferenceFrameInfo thunk from ", def, " starting at ", line) + end +end + +function show(io::IO, tinf::Timings.Timing) + print(io, "Compiler.Timings.Timing(", tinf.mi_info, ") with ", length(tinf.children), " children") +end + @specialize diff --git a/base/compiler/ssair/slot2ssa.jl b/Compiler/src/ssair/slot2ssa.jl similarity index 100% rename from base/compiler/ssair/slot2ssa.jl rename to Compiler/src/ssair/slot2ssa.jl diff --git a/base/compiler/ssair/tarjan.jl b/Compiler/src/ssair/tarjan.jl similarity index 100% rename from base/compiler/ssair/tarjan.jl rename to Compiler/src/ssair/tarjan.jl diff --git a/base/compiler/ssair/verify.jl b/Compiler/src/ssair/verify.jl similarity index 100% rename from base/compiler/ssair/verify.jl rename to Compiler/src/ssair/verify.jl diff --git a/base/compiler/stmtinfo.jl b/Compiler/src/stmtinfo.jl similarity index 100% rename from base/compiler/stmtinfo.jl rename to Compiler/src/stmtinfo.jl diff --git a/base/compiler/tfuncs.jl b/Compiler/src/tfuncs.jl similarity index 99% rename from base/compiler/tfuncs.jl rename to Compiler/src/tfuncs.jl index f0212f1082331..3b524742b1609 100644 --- a/base/compiler/tfuncs.jl +++ b/Compiler/src/tfuncs.jl @@ -1436,7 +1436,7 @@ end if TF2 === Bottom RT = Bottom elseif isconcretetype(RT) && has_nontrivial_extended_info(𝕃ᡒ, TF2) # isconcrete condition required to form a PartialStruct - RT = PartialStruct(RT, Any[TF, TF2]) + RT = PartialStruct(fallback_lattice, RT, Any[TF, TF2]) end info = ModifyOpInfo(callinfo.info) return CallMeta(RT, Any, Effects(), info) @@ -1996,7 +1996,7 @@ function tuple_tfunc(𝕃::AbstractLattice, argtypes::Vector{Any}) typ = Tuple{params...} # replace a singleton type with its equivalent Const object issingletontype(typ) && return Const(typ.instance) - return anyinfo ? PartialStruct(typ, argtypes) : typ + return anyinfo ? PartialStruct(𝕃, typ, argtypes) : typ end @nospecs function memoryrefget_tfunc(𝕃::AbstractLattice, mem, order, boundscheck) diff --git a/base/compiler/typeinfer.jl b/Compiler/src/typeinfer.jl similarity index 99% rename from base/compiler/typeinfer.jl rename to Compiler/src/typeinfer.jl index c1b7db82bff3f..94c65684e672c 100644 --- a/base/compiler/typeinfer.jl +++ b/Compiler/src/typeinfer.jl @@ -861,7 +861,7 @@ function cached_return_type(code::CodeInstance) # the second subtyping/egal conditions are necessary to distinguish usual cases # from rare cases when `Const` wrapped those extended lattice type objects if isa(rettype_const, Vector{Any}) && !(Vector{Any} <: rettype) - return PartialStruct(rettype, rettype_const) + return PartialStruct(fallback_lattice, rettype, rettype_const) elseif isa(rettype_const, PartialOpaque) && rettype <: Core.OpaqueClosure return rettype_const elseif isa(rettype_const, InterConditional) && rettype !== InterConditional diff --git a/base/compiler/typelattice.jl b/Compiler/src/typelattice.jl similarity index 92% rename from base/compiler/typelattice.jl rename to Compiler/src/typelattice.jl index 86fa8af21615f..2832edc9219ff 100644 --- a/base/compiler/typelattice.jl +++ b/Compiler/src/typelattice.jl @@ -6,48 +6,7 @@ # N.B.: Const/PartialStruct/InterConditional are defined in Core, to allow them to be used # inside the global code cache. - -import Core: Const, PartialStruct - -""" - struct Const - val - end - -The type representing a constant value. -""" -:(Const) - -""" - struct PartialStruct - typ - fields::Vector{Any} # elements are other type lattice members - end - -This extended lattice element is introduced when we have information about an object's -fields beyond what can be obtained from the object type. E.g. it represents a tuple where -some elements are known to be constants or a struct whose `Any`-typed field is initialized -with `Int` values. - -- `typ` indicates the type of the object -- `fields` holds the lattice elements corresponding to each field of the object - -If `typ` is a struct, `fields` represents the fields of the struct that are guaranteed to be -initialized. For instance, if the length of `fields` of `PartialStruct` representing a -struct with 4 fields is 3, the 4th field may not be initialized. If the length is 4, all -fields are guaranteed to be initialized. - -If `typ` is a tuple, the last element of `fields` may be `Vararg`. In this case, it is -guaranteed that the number of elements in the tuple is at least `length(fields)-1`, but the -exact number of elements is unknown. -""" -:(PartialStruct) -function PartialStruct(@nospecialize(typ), fields::Vector{Any}) - for i = 1:length(fields) - assert_nested_slotwrapper(fields[i]) - end - return Core._PartialStruct(typ, fields) -end +import Core: Const, InterConditional, PartialStruct """ cnd::Conditional @@ -87,23 +46,6 @@ end Conditional(var::SlotNumber, @nospecialize(thentype), @nospecialize(elsetype); isdefined::Bool=false) = Conditional(slot_id(var), thentype, elsetype; isdefined) -import Core: InterConditional -""" - struct InterConditional - slot::Int - thentype - elsetype - end - -Similar to `Conditional`, but conveys inter-procedural constraints imposed on call arguments. -This is separate from `Conditional` to catch logic errors: the lattice element name is `InterConditional` -while processing a call, then `Conditional` everywhere else. Thus `InterConditional` does not appear in -`CompilerTypes`β€”these type's usages are disjointβ€”though we define the lattice for `InterConditional`. -""" -:(InterConditional) -InterConditional(var::SlotNumber, @nospecialize(thentype), @nospecialize(elsetype)) = - InterConditional(slot_id(var), thentype, elsetype) - const AnyConditional = Union{Conditional,InterConditional} Conditional(cnd::InterConditional) = Conditional(cnd.slot, cnd.thentype, cnd.elsetype) InterConditional(cnd::Conditional) = InterConditional(cnd.slot, cnd.thentype, cnd.elsetype) @@ -388,8 +330,8 @@ end end end return Conditional(slot, - thenfields === nothing ? Bottom : PartialStruct(vartyp.typ, thenfields), - elsefields === nothing ? Bottom : PartialStruct(vartyp.typ, elsefields)) + thenfields === nothing ? Bottom : PartialStruct(fallback_lattice, vartyp.typ, thenfields), + elsefields === nothing ? Bottom : PartialStruct(fallback_lattice, vartyp.typ, elsefields)) else vartyp_widened = widenconst(vartyp) thenfields = thentype === Bottom ? nothing : Any[] @@ -405,8 +347,8 @@ end end end return Conditional(slot, - thenfields === nothing ? Bottom : PartialStruct(vartyp_widened, thenfields), - elsefields === nothing ? Bottom : PartialStruct(vartyp_widened, elsefields)) + thenfields === nothing ? Bottom : PartialStruct(fallback_lattice, vartyp_widened, thenfields), + elsefields === nothing ? Bottom : PartialStruct(fallback_lattice, vartyp_widened, elsefields)) end end @@ -734,7 +676,7 @@ widenconst(::AnyConditional) = Bool widenconst(a::AnyMustAlias) = widenconst(widenmustalias(a)) widenconst(c::Const) = (v = c.val; isa(v, Type) ? Type{v} : typeof(v)) widenconst(::PartialTypeVar) = TypeVar -widenconst(t::PartialStruct) = t.typ +widenconst(t::Core.PartialStruct) = t.typ widenconst(t::PartialOpaque) = t.typ @nospecializeinfer widenconst(@nospecialize t::Type) = t widenconst(::TypeVar) = error("unhandled TypeVar") @@ -799,3 +741,13 @@ function stoverwrite1!(state::VarTable, change::StateUpdate) state[changeid] = newtype return state end + +# The ::AbstractLattice argument is unused and simply serves to disambiguate +# different instances of the compiler that may share the `Core.PartialStruct` +# type. +function Core.PartialStruct(::AbstractLattice, @nospecialize(typ), fields::Vector{Any}) + for i = 1:length(fields) + assert_nested_slotwrapper(fields[i]) + end + return Core._PartialStruct(typ, fields) +end diff --git a/base/compiler/typelimits.jl b/Compiler/src/typelimits.jl similarity index 99% rename from base/compiler/typelimits.jl rename to Compiler/src/typelimits.jl index 3d0e5f3d0877d..e420db030715b 100644 --- a/base/compiler/typelimits.jl +++ b/Compiler/src/typelimits.jl @@ -641,7 +641,7 @@ end β‹€(𝕃, tyi, ft) # just a type-level information, but more precise than the declared type end end - anyrefine && return PartialStruct(aty, fields) + anyrefine && return PartialStruct(𝕃, aty, fields) end return nothing end diff --git a/base/compiler/types.jl b/Compiler/src/types.jl similarity index 100% rename from base/compiler/types.jl rename to Compiler/src/types.jl diff --git a/base/compiler/typeutils.jl b/Compiler/src/typeutils.jl similarity index 100% rename from base/compiler/typeutils.jl rename to Compiler/src/typeutils.jl diff --git a/base/compiler/utilities.jl b/Compiler/src/utilities.jl similarity index 99% rename from base/compiler/utilities.jl rename to Compiler/src/utilities.jl index 5361ff26f997c..0f1e2988bd669 100644 --- a/base/compiler/utilities.jl +++ b/Compiler/src/utilities.jl @@ -42,12 +42,6 @@ end anymap(f::Function, a::Array{Any,1}) = Any[ f(a[i]) for i in 1:length(a) ] -########### -# scoping # -########### - -_topmod(m::Module) = ccall(:jl_base_relative_to, Any, (Any,), m)::Module - ############ # inlining # ############ diff --git a/base/compiler/validation.jl b/Compiler/src/validation.jl similarity index 100% rename from base/compiler/validation.jl rename to Compiler/src/validation.jl diff --git a/Makefile b/Makefile index 2cac2f8818324..f0b1dfaa708e4 100644 --- a/Makefile +++ b/Makefile @@ -75,6 +75,13 @@ ifndef JULIA_VAGRANT_BUILD endif endif +TOP_LEVEL_PKGS := Compiler + +TOP_LEVEL_PKG_LINK_TARGETS := $(addprefix $(build_datarootdir)/julia/,$(TOP_LEVEL_PKGS)) + +# Generate symlinks for top level pkgs in usr/share/julia/ +$(foreach module, $(TOP_LEVEL_PKGS), $(eval $(call symlink_target,$$(JULIAHOME)/$(module),$$(build_datarootdir)/julia,$(module)))) + julia-deps: | $(DIRS) $(build_datarootdir)/julia/base $(build_datarootdir)/julia/test @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/deps @@ -103,10 +110,10 @@ julia-src-release julia-src-debug : julia-src-% : julia-deps julia_flisp.boot.in julia-cli-release julia-cli-debug: julia-cli-% : julia-deps @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/cli $* -julia-sysimg-ji : julia-stdlib julia-base julia-cli-$(JULIA_BUILD_MODE) julia-src-$(JULIA_BUILD_MODE) | $(build_private_libdir) +julia-sysimg-ji : $(TOP_LEVEL_PKG_LINK_TARGETS) julia-stdlib julia-base julia-cli-$(JULIA_BUILD_MODE) julia-src-$(JULIA_BUILD_MODE) | $(build_private_libdir) @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT) -f sysimage.mk sysimg-ji JULIA_EXECUTABLE='$(JULIA_EXECUTABLE)' -julia-sysimg-bc : julia-stdlib julia-base julia-cli-$(JULIA_BUILD_MODE) julia-src-$(JULIA_BUILD_MODE) | $(build_private_libdir) +julia-sysimg-bc : $(TOP_LEVEL_PKG_LINK_TARGETS) julia-stdlib julia-base julia-cli-$(JULIA_BUILD_MODE) julia-src-$(JULIA_BUILD_MODE) | $(build_private_libdir) @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT) -f sysimage.mk sysimg-bc JULIA_EXECUTABLE='$(JULIA_EXECUTABLE)' julia-sysimg-release julia-sysimg-debug : julia-sysimg-% : julia-sysimg-ji julia-src-% diff --git a/base/Base.jl b/base/Base.jl index 57b5142604d21..39507b625660d 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -1,54 +1,8 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -had_compiler = isdefined(Base, :Compiler) -if had_compiler; else -include("Base_compiler.jl") -end - const start_base_include = time_ns() include("reflection.jl") - -# define invoke(f, T, args...; kwargs...), without kwargs wrapping -# to forward to invoke -function Core.kwcall(kwargs::NamedTuple, ::typeof(invoke), f, T, args...) - @inline - # prepend kwargs and f to the invoked from the user - T = rewrap_unionall(Tuple{Core.Typeof(kwargs), Core.Typeof(f), (unwrap_unionall(T)::DataType).parameters...}, T) - return invoke(Core.kwcall, T, kwargs, f, args...) -end -# invoke does not have its own call cache, but kwcall for invoke does -setfield!(typeof(invoke).name.mt, :max_args, 3, :monotonic) # invoke, f, T, args... - -# define applicable(f, T, args...; kwargs...), without kwargs wrapping -# to forward to applicable -function Core.kwcall(kwargs::NamedTuple, ::typeof(applicable), @nospecialize(args...)) - @inline - return applicable(Core.kwcall, kwargs, args...) -end -function Core._hasmethod(@nospecialize(f), @nospecialize(t)) # this function has a special tfunc (TODO: make this a Builtin instead like applicable) - tt = rewrap_unionall(Tuple{Core.Typeof(f), (unwrap_unionall(t)::DataType).parameters...}, t) - return Core._hasmethod(tt) -end - -# core operations & types -include("promotion.jl") -include("tuple.jl") -include("expr.jl") -include("pair.jl") -include("traits.jl") -include("range.jl") -include("error.jl") - -# core numeric operations & types -==(x, y) = x === y -include("bool.jl") -include("number.jl") -include("int.jl") -include("operators.jl") -include("pointer.jl") -include("refvalue.jl") -include("cmem.jl") include("refpointer.jl") # now replace the Pair constructor (relevant for NamedTuples) with one that calls our Base.convert @@ -60,37 +14,7 @@ end # The REPL stdlib hooks into Base using this Ref const REPL_MODULE_REF = Ref{Module}(Base) - - -# For OS specific stuff -# We need to strcat things here, before strings are really defined -function strcat(x::String, y::String) - out = ccall(:jl_alloc_string, Ref{String}, (Csize_t,), Core.sizeof(x) + Core.sizeof(y)) - GC.@preserve x y out begin - out_ptr = unsafe_convert(Ptr{UInt8}, out) - unsafe_copyto!(out_ptr, unsafe_convert(Ptr{UInt8}, x), Core.sizeof(x)) - unsafe_copyto!(out_ptr + Core.sizeof(x), unsafe_convert(Ptr{UInt8}, y), Core.sizeof(y)) - end - return out -end - -BUILDROOT::String = "" - -baremodule BuildSettings -end - -let i = 1 - global BUILDROOT - while i <= length(Core.ARGS) - if Core.ARGS[i] == "--buildsettings" - include(BuildSettings, ARGS[i+1]) - i += 1 - else - BUILDROOT = Core.ARGS[i] - end - i += 1 - end -end +process_sysimg_args!() include(strcat(BUILDROOT, "build_h.jl")) # include($BUILDROOT/base/build_h.jl) include(strcat(BUILDROOT, "version_git.jl")) # include($BUILDROOT/base/version_git.jl) @@ -380,6 +304,7 @@ a_method_to_overwrite_in_test() = inferencebarrier(1) # Compatibility with when Compiler was in Core @eval Core const Compiler = Main.Base.Compiler +@eval Compiler const fl_parse = Core.Main.Base.fl_parse # External libraries vendored into Base Core.println("JuliaSyntax/src/JuliaSyntax.jl") @@ -471,10 +396,21 @@ end # enable threads support @eval PCRE PCRE_COMPILE_LOCK = Threads.SpinLock() +# Record dependency information for files belonging to the Compiler, so that +# we know whether the .ji can just give the Base copy or not. +# TODO: We may want to do this earlier to avoid TOCTOU issues. +const _compiler_require_dependencies = Any[] +for i = 1:length(_included_files) + isassigned(_included_files, i) || continue + (mod, file) = _included_files[i] + if mod === Compiler || parentmodule(mod) === Compiler || endswith(file, "/Compiler.jl") + _include_dependency!(_compiler_require_dependencies, true, mod, file, true, false) + end +end +@assert length(_compiler_require_dependencies) >= 15 + end # Ensure this file is also tracked @assert !isassigned(_included_files, 1) _included_files[1] = (parentmodule(Base), abspath(@__FILE__)) - -had_compiler && ccall(:jl_init_restored_module, Cvoid, (Any,), Base) diff --git a/base/Base_compiler.jl b/base/Base_compiler.jl index 3578b8f070db3..691e2c574acd6 100644 --- a/base/Base_compiler.jl +++ b/base/Base_compiler.jl @@ -1,5 +1,6 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +baremodule Base using Core.Intrinsics, Core.IR # to start, we're going to use a very simple definition of `include` @@ -198,7 +199,6 @@ function Core._hasmethod(@nospecialize(f), @nospecialize(t)) # this function has return Core._hasmethod(tt) end - # core operations & types include("promotion.jl") include("tuple.jl") @@ -252,15 +252,51 @@ include("namedtuple.jl") include("ordering.jl") using .Order -include("compiler/compiler.jl") +include("coreir.jl") + + +# For OS specific stuff +# We need to strcat things here, before strings are really defined +function strcat(x::String, y::String) + out = ccall(:jl_alloc_string, Ref{String}, (Csize_t,), Core.sizeof(x) + Core.sizeof(y)) + GC.@preserve x y out begin + out_ptr = unsafe_convert(Ptr{UInt8}, out) + unsafe_copyto!(out_ptr, unsafe_convert(Ptr{UInt8}, x), Core.sizeof(x)) + unsafe_copyto!(out_ptr + Core.sizeof(x), unsafe_convert(Ptr{UInt8}, y), Core.sizeof(y)) + end + return out +end + +BUILDROOT::String = "" + +baremodule BuildSettings +end + +function process_sysimg_args!() + let i = 1 + global BUILDROOT + while i <= length(Core.ARGS) + if Core.ARGS[i] == "--buildsettings" + include(BuildSettings, ARGS[i+1]) + i += 1 + else + BUILDROOT = Core.ARGS[i] + end + i += 1 + end + end +end +process_sysimg_args!() + +include(strcat(BUILDROOT, "../usr/share/julia/Compiler/src/Compiler.jl")) const _return_type = Compiler.return_type # Enable compiler -Core.eval(Compiler, quote -include("compiler/bootstrap.jl") -ccall(:jl_set_typeinf_func, Cvoid, (Any,), typeinf_ext_toplevel) +Compiler.bootstrap!() -include("compiler/parsing.jl") +include("flparse.jl") Core._setparser!(fl_parse) -end) + +# Further definition of Base will happen in Base.jl if loaded. +end diff --git a/base/boot.jl b/base/boot.jl index 5d40191ecab21..612efc0b50c8a 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -512,7 +512,6 @@ eval(Core, quote UpsilonNode(@nospecialize(val)) = $(Expr(:new, :UpsilonNode, :val)) UpsilonNode() = $(Expr(:new, :UpsilonNode)) Const(@nospecialize(v)) = $(Expr(:new, :Const, :v)) - # NOTE the main constructor is defined within `Core.Compiler` _PartialStruct(@nospecialize(typ), fields::Array{Any, 1}) = $(Expr(:new, :PartialStruct, :typ, :fields)) PartialOpaque(@nospecialize(typ), @nospecialize(env), parent::MethodInstance, source) = $(Expr(:new, :PartialOpaque, :typ, :env, :parent, :source)) InterConditional(slot::Int, @nospecialize(thentype), @nospecialize(elsetype)) = $(Expr(:new, :InterConditional, :slot, :thentype, :elsetype)) diff --git a/base/compiler/bootstrap.jl b/base/compiler/bootstrap.jl deleted file mode 100644 index 3162bccbdb4b9..0000000000000 --- a/base/compiler/bootstrap.jl +++ /dev/null @@ -1,52 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# make sure that typeinf is executed before turning on typeinf_ext -# this ensures that typeinf_ext doesn't recurse before it can add the item to the workq -# especially try to make sure any recursive and leaf functions have concrete signatures, -# since we won't be able to specialize & infer them at runtime - -let time() = ccall(:jl_clock_now, Float64, ()) - println("Compiling the compiler. This may take several minutes ...") - interp = NativeInterpreter() - - # analyze_escapes_tt = Tuple{typeof(analyze_escapes), IRCode, Int, TODO} - optimize_tt = Tuple{typeof(optimize), NativeInterpreter, OptimizationState{NativeInterpreter}, InferenceResult} - fs = Any[ - # we first create caches for the optimizer, because they contain many loop constructions - # and they're better to not run in interpreter even during bootstrapping - #=analyze_escapes_tt,=# optimize_tt, - # then we create caches for inference entries - typeinf_ext, typeinf, typeinf_edge, - ] - # tfuncs can't be inferred from the inference entries above, so here we infer them manually - for x in T_FFUNC_VAL - push!(fs, x[3]) - end - for i = 1:length(T_IFUNC) - if isassigned(T_IFUNC, i) - x = T_IFUNC[i] - push!(fs, x[3]) - else - println(stderr, "WARNING: tfunc missing for ", reinterpret(IntrinsicFunction, Int32(i))) - end - end - starttime = time() - for f in fs - if isa(f, DataType) && f.name === typename(Tuple) - tt = f - else - tt = Tuple{typeof(f), Vararg{Any}} - end - for m in _methods_by_ftype(tt, 10, get_world_counter())::Vector - # remove any TypeVars from the intersection - m = m::MethodMatch - typ = Any[m.spec_types.parameters...] - for i = 1:length(typ) - typ[i] = unwraptv(typ[i]) - end - typeinf_type(interp, m.method, Tuple{typ...}, m.sparams) - end - end - endtime = time() - println("Base.Compiler ──── ", sub_float(endtime,starttime), " seconds") -end diff --git a/base/compiler/compiler.jl b/base/compiler/compiler.jl deleted file mode 100644 index f4b7b73f1bf76..0000000000000 --- a/base/compiler/compiler.jl +++ /dev/null @@ -1,123 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - - -baremodule Compiler - -using Core.Intrinsics, Core.IR - -import Core: print, println, show, write, unsafe_write, - _apply_iterate, svec, apply_type, Builtin, IntrinsicFunction, - MethodInstance, CodeInstance, MethodTable, MethodMatch, PartialOpaque, - TypeofVararg, Core, SimpleVector, donotdelete, compilerbarrier, - memoryref_isassigned, memoryrefnew, memoryrefoffset, memoryrefget, - memoryrefset!, typename - -using ..Base -using ..Base: Ordering, vect, EffectsOverride, BitVector, @_gc_preserve_begin, @_gc_preserve_end, RefValue, - @nospecializeinfer, @_foldable_meta, fieldindex, is_function_def, indexed_iterate, isexpr, methods, - get_world_counter, JLOptions, _methods_by_ftype, unwrap_unionall, cconvert, unsafe_convert, - issingletontype, isType, rewrap_unionall, has_free_typevars, isvarargtype, hasgenerator, - IteratorSize, SizeUnknown, _array_for, Bottom, generating_output, diff_names, - ismutationfree, NUM_EFFECTS_OVERRIDES, _NAMEDTUPLE_NAME, datatype_fieldtypes, - argument_datatype, isfieldatomic, unwrapva, iskindtype, _bits_findnext, copy_exprargs, - Generator, Filter, ismutabletypename, isvatuple, datatype_fieldcount, - isconcretedispatch, isdispatchelem, min_world, max_world, datatype_layoutsize, - datatype_arrayelem, unionlen, isidentityfree, _uniontypes, uniontypes, OneTo, Callable, - DataTypeFieldDesc, datatype_nfields, datatype_pointerfree, midpoint, is_valid_intrinsic_elptr, - allocatedinline, isbitsunion, widen_diagonal, unconstrain_vararg_length, - rename_unionall, may_invoke_generator, is_meta_expr_head, is_meta_expr, quoted, - specialize_method, hasintersect, is_nospecializeinfer, is_nospecialized, - get_nospecializeinfer_sig, tls_world_age, uniontype_layout, kwerr, - moduleroot, is_file_tracked, decode_effects_override -using ..Base.Order -import ..Base: getindex, setindex!, length, iterate, push!, isempty, first, convert, ==, - copy, popfirst!, in, haskey, resize!, copy!, append!, last, get!, size, - get, iterate, findall - -const getproperty = Core.getfield -const setproperty! = Core.setfield! -const swapproperty! = Core.swapfield! -const modifyproperty! = Core.modifyfield! -const replaceproperty! = Core.replacefield! -const _DOCS_ALIASING_WARNING = "" - -ccall(:jl_set_istopmod, Cvoid, (Any, Bool), Compiler, false) - -eval(x) = Core.eval(Compiler, x) -eval(m, x) = Core.eval(m, x) - -include(x) = Base.include(Compiler, x) -include(mod, x) = Base.include(mod, x) - -macro _boundscheck() Expr(:boundscheck) end - -# These types are used by reflection.jl and expr.jl too, so declare them here. -# Note that `@assume_effects` is available only after loading namedtuple.jl. -abstract type MethodTableView end -abstract type AbstractInterpreter end - -function return_type end # promotion.jl expects this to exist -is_return_type(Core.@nospecialize(f)) = f === return_type - -include("compiler/sort.jl") - -# We don't include some.jl, but this definition is still useful. -something(x::Nothing, y...) = something(y...) -something(x::Any, y...) = x - -############ -# compiler # -############ - -baremodule BuildSettings -using Core: ARGS, include -using ..Compiler: >, getindex, length - -global MAX_METHODS::Int = 3 - -if length(ARGS) > 2 && ARGS[2] === "--buildsettings" - include(BuildSettings, ARGS[3]) -end -end - -if false - import Base: Base, @show -else - macro show(ex...) - blk = Expr(:block) - for s in ex - push!(blk.args, :(println(stdout, $(QuoteNode(s)), " = ", - begin local value = $(esc(s)) end))) - end - isempty(ex) || push!(blk.args, :value) - blk - end -end - -include("compiler/cicache.jl") -include("compiler/methodtable.jl") -include("compiler/effects.jl") -include("compiler/types.jl") -include("compiler/utilities.jl") -include("compiler/validation.jl") - -include("compiler/ssair/basicblock.jl") -include("compiler/ssair/domtree.jl") -include("compiler/ssair/ir.jl") -include("compiler/ssair/tarjan.jl") - -include("compiler/abstractlattice.jl") -include("compiler/stmtinfo.jl") -include("compiler/inferenceresult.jl") -include("compiler/inferencestate.jl") - -include("compiler/typeutils.jl") -include("compiler/typelimits.jl") -include("compiler/typelattice.jl") -include("compiler/tfuncs.jl") - -include("compiler/abstractinterpretation.jl") -include("compiler/typeinfer.jl") -include("compiler/optimize.jl") - -end diff --git a/base/compilerimg.jl b/base/compilerimg.jl deleted file mode 100644 index c353ee614924b..0000000000000 --- a/base/compilerimg.jl +++ /dev/null @@ -1,4 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -baremodule Base; end -Core.include(Base, "Base_compiler.jl") diff --git a/base/coreir.jl b/base/coreir.jl new file mode 100644 index 0000000000000..a21eeceffe4c5 --- /dev/null +++ b/base/coreir.jl @@ -0,0 +1,54 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +Core.PhiNode() = Core.PhiNode(Int32[], Any[]) + +""" + struct Const + val + end + +The type representing a constant value. +""" +Core.Const + +""" + struct PartialStruct + typ + fields::Vector{Any} # elements are other type lattice members + end + +This extended lattice element is introduced when we have information about an object's +fields beyond what can be obtained from the object type. E.g. it represents a tuple where +some elements are known to be constants or a struct whose `Any`-typed field is initialized +with `Int` values. + +- `typ` indicates the type of the object +- `fields` holds the lattice elements corresponding to each field of the object + +If `typ` is a struct, `fields` represents the fields of the struct that are guaranteed to be +initialized. For instance, if the length of `fields` of `PartialStruct` representing a +struct with 4 fields is 3, the 4th field may not be initialized. If the length is 4, all +fields are guaranteed to be initialized. + +If `typ` is a tuple, the last element of `fields` may be `Vararg`. In this case, it is +guaranteed that the number of elements in the tuple is at least `length(fields)-1`, but the +exact number of elements is unknown. +""" +Core.PartialStruct + +""" + struct InterConditional + slot::Int + thentype + elsetype + end + +Similar to `Conditional`, but conveys inter-procedural constraints imposed on call arguments. +This is separate from `Conditional` to catch logic errors: the lattice element name is `InterConditional` +while processing a call, then `Conditional` everywhere else. Thus `InterConditional` does not appear in +`CompilerTypes`β€”these type's usages are disjointβ€”though we define the lattice for `InterConditional`. +""" +Core.InterConditional + +InterConditional(var::SlotNumber, @nospecialize(thentype), @nospecialize(elsetype)) = + InterConditional(slot_id(var), thentype, elsetype) diff --git a/base/essentials.jl b/base/essentials.jl index 89b891e216d5a..efae59b82b5f9 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -184,7 +184,8 @@ end _nameof(m::Module) = ccall(:jl_module_name, Ref{Symbol}, (Any,), m) function _is_internal(__module__) - return true + return _nameof(__module__) === :Base || + _nameof(ccall(:jl_base_relative_to, Any, (Any,), __module__)::Module) === :Compiler end # can be used in place of `@assume_effects :total` (supposed to be used for bootstrapping) diff --git a/base/compiler/parsing.jl b/base/flparse.jl similarity index 100% rename from base/compiler/parsing.jl rename to base/flparse.jl diff --git a/base/loading.jl b/base/loading.jl index 7b45348f47009..2765c6ea3ed1f 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1296,7 +1296,7 @@ function _include_from_serialized(pkg::PkgId, path::String, ocachepath::Union{No for M in restored M = M::Module - if parentmodule(M) === M && PkgId(M) == pkg + if is_root_module(M) && PkgId(M) == pkg register && register_root_module(M) if timing_imports elapsed_time = time_ns() - t_before @@ -2239,15 +2239,22 @@ const include_callbacks = Any[] const _concrete_dependencies = Pair{PkgId,UInt128}[] # these dependency versions are "set in stone", because they are explicitly loaded, and the process should try to avoid invalidating them const _require_dependencies = Any[] # a list of (mod, abspath, fsize, hash, mtime) tuples that are the file dependencies of the module currently being precompiled const _track_dependencies = Ref(false) # set this to true to track the list of file dependencies -function _include_dependency(mod::Module, _path::AbstractString; track_content=true, - path_may_be_dir=false) + +function _include_dependency(mod::Module, _path::AbstractString; track_content::Bool=true, + path_may_be_dir::Bool=false) + _include_dependency!(_require_dependencies, _track_dependencies[], mod, _path, track_content, path_may_be_dir) +end + +function _include_dependency!(dep_list::Vector{Any}, track_dependencies::Bool, + mod::Module, _path::AbstractString, + track_content::Bool, path_may_be_dir::Bool) prev = source_path(nothing) if prev === nothing path = abspath(_path) else path = normpath(joinpath(dirname(prev), _path)) end - if !_track_dependencies[] + if !track_dependencies[] if !path_may_be_dir && !isfile(path) throw(SystemError("opening file $(repr(path))", Libc.ENOENT)) elseif path_may_be_dir && !Filesystem.isreadable(path) @@ -2258,9 +2265,9 @@ function _include_dependency(mod::Module, _path::AbstractString; track_content=t if track_content hash = isdir(path) ? _crc32c(join(readdir(path))) : open(_crc32c, path, "r") # use mtime=-1.0 here so that fsize==0 && mtime==0.0 corresponds to a missing include_dependency - push!(_require_dependencies, (mod, path, filesize(path), hash, -1.0)) + push!(dep_list, (mod, path, filesize(path), hash, -1.0)) else - push!(_require_dependencies, (mod, path, UInt64(0), UInt32(0), mtime(path))) + push!(dep_list, (mod, path, UInt64(0), UInt32(0), mtime(path))) end end end @@ -2797,9 +2804,6 @@ function require_stdlib(package_uuidkey::PkgId, ext::Union{Nothing, String}=noth end end - - - # relative-path load """ @@ -3326,6 +3330,10 @@ mutable struct CacheHeaderIncludes const modpath::Vector{String} # seemingly not needed in Base, but used by Revise end +function CacheHeaderIncludes(dep_tuple::Tuple{Module, String, Int64, UInt32, Float64}) + return CacheHeaderIncludes(PkgId(dep_tuple[1]), dep_tuple[2:end]..., String[]) +end + function replace_depot_path(path::AbstractString, depots::Vector{String}=normalize_depots_for_relocation()) for depot in depots if startswith(path, string(depot, Filesystem.pathsep())) || path == depot @@ -3865,6 +3873,56 @@ function list_reasons(reasons::Dict{String,Int}) end list_reasons(::Nothing) = "" +function any_includes_stale(includes::Vector{CacheHeaderIncludes}, cachefile::String, reasons::Union{Dict{String,Int},Nothing}=nothing) + for chi in includes + f, fsize_req, hash_req, ftime_req = chi.filename, chi.fsize, chi.hash, chi.mtime + if startswith(f, string("@depot", Filesystem.pathsep())) + @debug("Rejecting stale cache file $cachefile because its depot could not be resolved") + record_reason(reasons, "nonresolveable depot") + return true + end + if !ispath(f) + _f = fixup_stdlib_path(f) + if _f != f && isfile(_f) && startswith(_f, Sys.STDLIB) + continue + end + @debug "Rejecting stale cache file $cachefile because file $f does not exist" + record_reason(reasons, "missing sourcefile") + return true + end + if ftime_req >= 0.0 + # this is an include_dependency for which we only recorded the mtime + ftime = mtime(f) + is_stale = ( ftime != ftime_req ) && + ( ftime != floor(ftime_req) ) && # Issue #13606, PR #13613: compensate for Docker images rounding mtimes + ( ftime != ceil(ftime_req) ) && # PR: #47433 Compensate for CirceCI's truncating of timestamps in its caching + ( ftime != trunc(ftime_req, digits=6) ) && # Issue #20837, PR #20840: compensate for GlusterFS truncating mtimes to microseconds + ( ftime != 1.0 ) && # PR #43090: provide compatibility with Nix mtime. + !( 0 < (ftime_req - ftime) < 1e-6 ) # PR #45552: Compensate for Windows tar giving mtimes that may be incorrect by up to one microsecond + if is_stale + @debug "Rejecting stale cache file $cachefile because mtime of include_dependency $f has changed (mtime $ftime, before $ftime_req)" + record_reason(reasons, "include_dependency mtime change") + return true + end + else + fstat = stat(f) + fsize = filesize(fstat) + if fsize != fsize_req + @debug "Rejecting stale cache file $cachefile because file size of $f has changed (file size $fsize, before $fsize_req)" + record_reason(reasons, "include_dependency fsize change") + return true + end + hash = isdir(fstat) ? _crc32c(join(readdir(f))) : open(_crc32c, f, "r") + if hash != hash_req + @debug "Rejecting stale cache file $cachefile because hash of $f has changed (hash $hash, before $hash_req)" + record_reason(reasons, "include_dependency fhash change") + return true + end + end + end + return false +end + # returns true if it "cachefile.ji" is stale relative to "modpath.jl" and build_id for modkey # otherwise returns the list of dependencies to also check @constprop :none function stale_cachefile(modpath::String, cachefile::String; ignore_loaded::Bool = false, requested_flags::CacheFlags=CacheFlags(), reasons=nothing) @@ -4024,51 +4082,8 @@ end return true end end - for chi in includes - f, fsize_req, hash_req, ftime_req = chi.filename, chi.fsize, chi.hash, chi.mtime - if startswith(f, string("@depot", Filesystem.pathsep())) - @debug("Rejecting stale cache file $cachefile because its depot could not be resolved") - record_reason(reasons, "nonresolveable depot") - return true - end - if !ispath(f) - _f = fixup_stdlib_path(f) - if _f != f && isfile(_f) && startswith(_f, Sys.STDLIB) - continue - end - @debug "Rejecting stale cache file $cachefile because file $f does not exist" - record_reason(reasons, "missing sourcefile") - return true - end - if ftime_req >= 0.0 - # this is an include_dependency for which we only recorded the mtime - ftime = mtime(f) - is_stale = ( ftime != ftime_req ) && - ( ftime != floor(ftime_req) ) && # Issue #13606, PR #13613: compensate for Docker images rounding mtimes - ( ftime != ceil(ftime_req) ) && # PR: #47433 Compensate for CirceCI's truncating of timestamps in its caching - ( ftime != trunc(ftime_req, digits=6) ) && # Issue #20837, PR #20840: compensate for GlusterFS truncating mtimes to microseconds - ( ftime != 1.0 ) && # PR #43090: provide compatibility with Nix mtime. - !( 0 < (ftime_req - ftime) < 1e-6 ) # PR #45552: Compensate for Windows tar giving mtimes that may be incorrect by up to one microsecond - if is_stale - @debug "Rejecting stale cache file $cachefile because mtime of include_dependency $f has changed (mtime $ftime, before $ftime_req)" - record_reason(reasons, "include_dependency mtime change") - return true - end - else - fstat = stat(f) - fsize = filesize(fstat) - if fsize != fsize_req - @debug "Rejecting stale cache file $cachefile because file size of $f has changed (file size $fsize, before $fsize_req)" - record_reason(reasons, "include_dependency fsize change") - return true - end - hash = isdir(fstat) ? _crc32c(join(readdir(f))) : open(_crc32c, f, "r") - if hash != hash_req - @debug "Rejecting stale cache file $cachefile because hash of $f has changed (hash $hash, before $hash_req)" - record_reason(reasons, "include_dependency fhash change") - return true - end - end + if any_includes_stale(includes, cachefile, reasons) + return true end end @@ -4148,6 +4163,12 @@ macro __DIR__() return isempty(_dirname) ? pwd() : abspath(_dirname) end +function prepare_compiler_stub_image!() + ccall(:jl_add_to_module_init_list, Cvoid, (Any,), Compiler) + register_root_module(Compiler) + filter!(mod->mod !== Compiler, loaded_modules_order) +end + """ precompile(f, argtypes::Tuple{Vararg{Any}}) diff --git a/base/opaque_closure.jl b/base/opaque_closure.jl index 26b39879ca852..d7a91cff7d602 100644 --- a/base/opaque_closure.jl +++ b/base/opaque_closure.jl @@ -39,23 +39,24 @@ end # OpaqueClosure construction from pre-inferred CodeInfo/IRCode using Core: CodeInfo, SSAValue -using Base.Compiler: IRCode +using Base: Compiler +using .Compiler: IRCode function compute_ir_rettype(ir::IRCode) rt = Union{} for i = 1:length(ir.stmts) stmt = ir[SSAValue(i)][:stmt] - if isa(stmt, Core.Compiler.ReturnNode) && isdefined(stmt, :val) - rt = Core.Compiler.tmerge(Core.Compiler.argextype(stmt.val, ir), rt) + if isa(stmt, Core.ReturnNode) && isdefined(stmt, :val) + rt = Compiler.tmerge(Compiler.argextype(stmt.val, ir), rt) end end - return Core.Compiler.widenconst(rt) + return Compiler.widenconst(rt) end function compute_oc_signature(ir::IRCode, nargs::Int, isva::Bool) argtypes = Vector{Any}(undef, nargs) for i = 1:nargs - argtypes[i] = Core.Compiler.widenconst(ir.argtypes[i+1]) + argtypes[i] = Compiler.widenconst(ir.argtypes[i+1]) end if isva lastarg = pop!(argtypes) diff --git a/base/reflection.jl b/base/reflection.jl index 834325dd41583..1b8ed9413a35b 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -346,6 +346,36 @@ function raise_match_failure(name::Symbol, @nospecialize(tt)) error("$name: unanalyzable call given $sig_str") end +const REFLECTION_COMPILER = RefValue{Union{Nothing, Module}}(nothing) + +function invoke_in_typeinf_world(args...) + vargs = Any[args...] + return ccall(:jl_call_in_typeinf_world, Any, (Ptr{Any}, Cint), vargs, length(vargs)) +end + +function invoke_default_compiler(fname::Symbol, args...) + if REFLECTION_COMPILER[] === nothing + return invoke_in_typeinf_world(getglobal(Compiler, fname), args...) + else + return getglobal(REFLECTION_COMPILER[], fname)(args...) + end +end + +function invoke_interp_compiler(interp, fname::Symbol, args...) + if interp === nothing + return invoke_default_compiler(fname, args...) + else + T = typeof(interp) + while true + Tname = typename(T).name + Tname === :Any && error("Expected Interpreter") + Tname === :AbstractInterpreter && break + T = supertype(T) + end + return getglobal(typename(T).module, fname)(args...) + end +end + """ code_typed_by_type(types::Type{<:Tuple}; ...) @@ -356,7 +386,9 @@ function code_typed_by_type(@nospecialize(tt::Type); optimize::Bool=true, debuginfo::Symbol=:default, world::UInt=get_world_counter(), - interp::Compiler.AbstractInterpreter=Compiler.NativeInterpreter(world)) + interp=nothing) + passed_interp = interp + interp = passed_interp === nothing ? invoke_default_compiler(:_default_interp, world) : interp (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && error("code reflection cannot be used from generated functions") if @isdefined(IRShow) @@ -368,12 +400,12 @@ function code_typed_by_type(@nospecialize(tt::Type); throw(ArgumentError("'debuginfo' must be either :source or :none")) end tt = to_tuple_type(tt) - matches = Compiler.findall(tt, Compiler.method_table(interp)) + matches = invoke_interp_compiler(passed_interp, :_findall_matches, interp, tt) matches === nothing && raise_match_failure(:code_typed, tt) asts = [] for match in matches.matches match = match::Core.MethodMatch - code = Compiler.typeinf_code(interp, match, optimize) + code = invoke_interp_compiler(passed_interp, :typeinf_code, interp, match, optimize) if code === nothing push!(asts, match.method => Any) else @@ -384,7 +416,7 @@ function code_typed_by_type(@nospecialize(tt::Type); return asts end -function get_oc_code_rt(oc::Core.OpaqueClosure, types, optimize::Bool) +function get_oc_code_rt(passed_interp, oc::Core.OpaqueClosure, types, optimize::Bool) @nospecialize oc types ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") @@ -393,9 +425,9 @@ function get_oc_code_rt(oc::Core.OpaqueClosure, types, optimize::Bool) if isdefined(m, :source) if optimize tt = Tuple{typeof(oc.captures), to_tuple_type(types).parameters...} - mi = Compiler.specialize_method(m, tt, Core.svec()) - interp = Compiler.NativeInterpreter(m.primary_world) - code = Compiler.typeinf_code(interp, mi, optimize) + mi = specialize_method(m, tt, Core.svec()) + interp = invoke_interp_compiler(passed_interp, :_default_interp, m.primary_world) + code = invoke_interp_compiler(passed_interp, :typeinf_code, interp, mi, optimize) if code isa CodeInfo return Pair{CodeInfo, Any}(code, code.rettype) end @@ -418,9 +450,10 @@ end function code_typed_opaque_closure(oc::Core.OpaqueClosure, types; debuginfo::Symbol=:default, optimize::Bool=true, + interp=nothing, _...) @nospecialize oc types - (code, rt) = get_oc_code_rt(oc, types, optimize) + (code, rt) = get_oc_code_rt(interp, oc, types, optimize) debuginfo === :none && remove_linenums!(code) return Any[Pair{CodeInfo,Any}(code, rt)] end @@ -485,18 +518,20 @@ a full signature to query. function code_ircode_by_type( @nospecialize(tt::Type); world::UInt=get_world_counter(), - interp::Compiler.AbstractInterpreter=Compiler.NativeInterpreter(world), + interp=nothing, optimize_until::Union{Integer,AbstractString,Nothing}=nothing, ) + passed_interp = interp + interp = passed_interp === nothing ? invoke_default_compiler(:_default_interp, world) : interp (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && error("code reflection cannot be used from generated functions") tt = to_tuple_type(tt) - matches = Compiler.findall(tt, Compiler.method_table(interp)) + matches = invoke_interp_compiler(passed_interp, :_findall_matches, interp, tt) matches === nothing && raise_match_failure(:code_ircode, tt) asts = [] for match in matches.matches match = match::Core.MethodMatch - (code, ty) = Compiler.typeinf_ircode(interp, match, optimize_until) + (code, ty) = invoke_interp_compiler(passed_interp, :typeinf_ircode, interp, match, optimize_until) if code === nothing push!(asts, match.method => Any) else @@ -506,24 +541,26 @@ function code_ircode_by_type( return asts end -function _builtin_return_type(interp::Compiler.AbstractInterpreter, +function _builtin_return_type(passed_interp, interp, @nospecialize(f::Core.Builtin), @nospecialize(types)) argtypes = Any[to_tuple_type(types).parameters...] - rt = Compiler.builtin_tfunction(interp, f, argtypes, nothing) - return Compiler.widenconst(rt) + rt = invoke_interp_compiler(passed_interp, :builtin_tfunction, interp, f, argtypes, nothing) + return invoke_interp_compiler(passed_interp, :widenconst, rt) end -function _builtin_effects(interp::Compiler.AbstractInterpreter, +function _builtin_effects(passed_interp, interp, @nospecialize(f::Core.Builtin), @nospecialize(types)) argtypes = Any[to_tuple_type(types).parameters...] - rt = Compiler.builtin_tfunction(interp, f, argtypes, nothing) - return Compiler.builtin_effects(Compiler.typeinf_lattice(interp), f, argtypes, rt) + rt = invoke_interp_compiler(passed_interp, :builtin_tfunction, interp, f, argtypes, nothing) + return invoke_interp_compiler(passed_interp, :builtin_effects, + invoke_interp_compiler(passed_interp, :typeinf_lattice, interp), + f, argtypes, rt) end -function _builtin_exception_type(interp::Compiler.AbstractInterpreter, +function _builtin_exception_type(passed_interp, interp, @nospecialize(f::Core.Builtin), @nospecialize(types)) - effects = _builtin_effects(interp, f, types) - return Compiler.is_nothrow(effects) ? Union{} : Any + effects = _builtin_effects(passed_interp, interp, f, types) + return invoke_interp_compiler(passed_interp, :is_nothrow, effects) ? Union{} : Any end check_generated_context(world::UInt) = @@ -579,20 +616,22 @@ julia> Base.return_types(sum, (Union{Vector{Int},UnitRange{Int}},)) """ function return_types(@nospecialize(f), @nospecialize(types=default_tt(f)); world::UInt=get_world_counter(), - interp::Compiler.AbstractInterpreter=Compiler.NativeInterpreter(world)) + interp=nothing) + passed_interp = interp + interp = passed_interp === nothing ? invoke_default_compiler(:_default_interp, world) : interp check_generated_context(world) if isa(f, Core.OpaqueClosure) - _, rt = only(code_typed_opaque_closure(f, types)) + _, rt = only(code_typed_opaque_closure(f, types; Compiler)) return Any[rt] elseif isa(f, Core.Builtin) - return Any[_builtin_return_type(interp, f, types)] + return Any[_builtin_return_type(passed_interp, interp, f, types)] end tt = signature_type(f, types) - matches = Compiler.findall(tt, Compiler.method_table(interp)) + matches = invoke_interp_compiler(passed_interp, :_findall_matches, interp, tt) matches === nothing && raise_match_failure(:return_types, tt) rts = Any[] for match in matches.matches - ty = Compiler.typeinf_type(interp, match::Core.MethodMatch) + ty = invoke_interp_compiler(passed_interp, :typeinf_type, interp, match::Core.MethodMatch) push!(rts, something(ty, Any)) end return rts @@ -647,20 +686,22 @@ On the other hand `Base.infer_return_type` returns one collective result that su """ function infer_return_type(@nospecialize(f), @nospecialize(types=default_tt(f)); world::UInt=get_world_counter(), - interp::Compiler.AbstractInterpreter=Compiler.NativeInterpreter(world)) + interp=nothing) + passed_interp = interp + interp = passed_interp === nothing ? invoke_default_compiler(:_default_interp, world) : interp check_generated_context(world) if isa(f, Core.OpaqueClosure) - return last(only(code_typed_opaque_closure(f, types))) + return last(only(code_typed_opaque_closure(f, types; interp=passed_interp))) elseif isa(f, Core.Builtin) - return _builtin_return_type(interp, f, types) + return _builtin_return_type(passed_interp, interp, f, types) end tt = signature_type(f, types) - matches = Compiler.findall(tt, Compiler.method_table(interp)) + matches = invoke_interp_compiler(passed_interp, :_findall_matches, interp, tt) matches === nothing && raise_match_failure(:infer_return_type, tt) rt = Union{} for match in matches.matches - ty = Compiler.typeinf_type(interp, match::Core.MethodMatch) - rt = Compiler.tmerge(rt, something(ty, Any)) + ty = invoke_interp_compiler(passed_interp, :typeinf_type, interp, match::Core.MethodMatch) + rt = invoke_interp_compiler(passed_interp, :tmerge, rt, something(ty, Any)) end return rt end @@ -717,32 +758,31 @@ julia> Base.infer_exception_types(throw_if_number, (Any,)) """ function infer_exception_types(@nospecialize(f), @nospecialize(types=default_tt(f)); world::UInt=get_world_counter(), - interp::Compiler.AbstractInterpreter=Compiler.NativeInterpreter(world)) + interp=nothing) + passed_interp = interp + interp = passed_interp === nothing ? invoke_default_compiler(:_default_interp, world) : interp check_generated_context(world) if isa(f, Core.OpaqueClosure) return Any[Any] # TODO elseif isa(f, Core.Builtin) - return Any[_builtin_exception_type(interp, f, types)] + return Any[_builtin_exception_type(passed_interp, interp, f, types)] end tt = signature_type(f, types) - matches = Compiler.findall(tt, Compiler.method_table(interp)) + matches = invoke_interp_compiler(passed_interp, :_findall_matches, interp, tt) matches === nothing && raise_match_failure(:infer_exception_types, tt) excts = Any[] for match in matches.matches - frame = Compiler.typeinf_frame(interp, match::Core.MethodMatch, #=run_optimizer=#false) + frame = invoke_interp_compiler(passed_interp, :typeinf_frame, interp, match::Core.MethodMatch, #=run_optimizer=#false) if frame === nothing exct = Any else - exct = Compiler.widenconst(frame.result.exc_result) + exct = invoke_interp_compiler(passed_interp, :widenconst, frame.result.exc_result) end push!(excts, exct) end return excts end -_may_throw_methoderror(matches#=::Core.Compiler.MethodLookupResult=#) = - matches.ambig || !any(match::Core.MethodMatch->match.fully_covers, matches.matches) - """ Base.infer_exception_type( f, types=default_tt(f); @@ -796,27 +836,18 @@ signature, the exception type is widened to `MethodError`. """ function infer_exception_type(@nospecialize(f), @nospecialize(types=default_tt(f)); world::UInt=get_world_counter(), - interp::Compiler.AbstractInterpreter=Compiler.NativeInterpreter(world)) + interp=nothing) + passed_interp = interp + interp = passed_interp === nothing ? invoke_default_compiler(:_default_interp, world) : interp check_generated_context(world) if isa(f, Core.OpaqueClosure) return Any # TODO elseif isa(f, Core.Builtin) - return _builtin_exception_type(interp, f, types) + return _builtin_exception_type(passed_interp, interp, f, types) end tt = signature_type(f, types) - matches = Compiler.findall(tt, Compiler.method_table(interp)) - matches === nothing && raise_match_failure(:infer_exception_type, tt) - exct = Union{} - if _may_throw_methoderror(matches) - # account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature. - exct = Compiler.tmerge(exct, MethodError) - end - for match in matches.matches - match = match::Core.MethodMatch - frame = Compiler.typeinf_frame(interp, match, #=run_optimizer=#false) - frame === nothing && return Any - exct = Compiler.tmerge(exct, Compiler.widenconst(frame.result.exc_result)) - end + exct = invoke_interp_compiler(passed_interp, :_infer_exception_type, interp, tt, false) + exct === nothing && raise_match_failure(:infer_exception_type, tt) return exct end @@ -875,34 +906,25 @@ signature, the `:nothrow` bit gets tainted. The `Base.infer_effects` function should not be used from generated functions; doing so will result in an error. -$(Core.Compiler.effects_key_string) +$(Compiler.effects_key_string) # See Also -- [`Core.Compiler.Effects`](@ref): A type representing the computational effects of a method call. +- [`Compiler.Effects`](@ref): A type representing the computational effects of a method call. - [`Base.@assume_effects`](@ref): A macro for making assumptions about the effects of a method. """ function infer_effects(@nospecialize(f), @nospecialize(types=default_tt(f)); optimize::Bool=true, world::UInt=get_world_counter(), - interp::Compiler.AbstractInterpreter=Compiler.NativeInterpreter(world)) + interp=nothing) + passed_interp = interp + interp = passed_interp === nothing ? invoke_default_compiler(:_default_interp, world) : interp check_generated_context(world) if isa(f, Core.Builtin) - return _builtin_effects(interp, f, types) + return _builtin_effects(passed_interp, interp, f, types) end tt = signature_type(f, types) - matches = Compiler.findall(tt, Compiler.method_table(interp)) - matches === nothing && raise_match_failure(:infer_effects, tt) - effects = Compiler.EFFECTS_TOTAL - if _may_throw_methoderror(matches) - # account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature. - effects = Compiler.Effects(effects; nothrow=false) - end - for match in matches.matches - match = match::Core.MethodMatch - frame = Compiler.typeinf_frame(interp, match, #=run_optimizer=#optimize) - frame === nothing && return Compiler.Effects() - effects = Compiler.merge_effects(effects, frame.result.ipo_effects) - end + effects = invoke_interp_compiler(passed_interp, :_infer_effects, interp, tt, optimize) + effects === nothing && raise_match_failure(:infer_effects, tt) return effects end @@ -919,24 +941,24 @@ end function print_statement_costs(io::IO, @nospecialize(tt::Type); world::UInt=get_world_counter(), - interp::Compiler.AbstractInterpreter=Compiler.NativeInterpreter(world)) + interp=nothing) + passed_interp = interp + interp = passed_interp === nothing ? invoke_default_compiler(:_default_interp, world) : interp tt = to_tuple_type(tt) world == typemax(UInt) && error("code reflection cannot be used from generated functions") - matches = Compiler.findall(tt, Compiler.method_table(interp)) + matches = invoke_interp_compiler(passed_interp, :_findall_matches, interp, tt) matches === nothing && raise_match_failure(:print_statement_costs, tt) - params = Compiler.OptimizationParams(interp) cst = Int[] for match in matches.matches match = match::Core.MethodMatch println(io, match.method) - code = Compiler.typeinf_code(interp, match, true) + code = invoke_interp_compiler(passed_interp, :typeinf_code, interp, match, true) if code === nothing println(io, " inference not successful") else empty!(cst) resize!(cst, length(code.code)) - sptypes = Compiler.VarState[Compiler.VarState(sp, false) for sp in match.sparams] - maxcost = Compiler.statement_costs!(cst, code.code, code, sptypes, params) + maxcost = invoke_interp_compiler(passed_interp, :statement_costs!, interp, cst, code.code, code, match) nd = ndigits(maxcost) irshow_config = IRShow.IRShowConfig() do io, linestart, idx print(io, idx > 0 ? lpad(cst[idx], nd+1) : " "^(nd+1), " ") @@ -951,18 +973,11 @@ end print_statement_costs(args...; kwargs...) = print_statement_costs(stdout, args...; kwargs...) function _which(@nospecialize(tt::Type); - method_table::Union{Nothing,Core.MethodTable,Compiler.MethodTableView}=nothing, + method_table #=::Union{Nothing,Core.MethodTable,Compiler.MethodTableView}=# =nothing, world::UInt=get_world_counter(), raise::Bool=true) world == typemax(UInt) && error("code reflection cannot be used from generated functions") - if method_table === nothing - table = Compiler.InternalMethodTable(world) - elseif method_table isa Core.MethodTable - table = Compiler.OverlayMethodTable(world, method_table) - else - table = method_table - end - match, = Compiler.findsup(tt, table) + match, = invoke_default_compiler(:findsup_mt, tt, world, method_table) if match === nothing raise && error("no unique matching method found for the specified argument types") return nothing @@ -982,7 +997,7 @@ See also: [`parentmodule`](@ref), [`@which`](@ref Main.InteractiveUtils.@which), function which(@nospecialize(f), @nospecialize(t)) tt = signature_type(f, t) world = get_world_counter() - match, _ = Compiler._findsup(tt, nothing, world) + match, _ = invoke_default_compiler(:_findsup, tt, nothing, world) if match === nothing me = MethodError(f, t, world) ee = ErrorException(sprint(io -> begin @@ -1348,7 +1363,7 @@ julia> @macroexpand @invoke (xs::Xs)[i::I] = v::V The additional syntax is supported as of Julia 1.10. """ macro invoke(ex) - topmod = Compiler._topmod(__module__) # well, except, do not get it via CC but define it locally + topmod = _topmod(__module__) f, args, kwargs = destructure_callex(topmod, ex) types = Expr(:curly, :Tuple) out = Expr(:call, GlobalRef(Core, :invoke)) @@ -1407,7 +1422,7 @@ julia> @macroexpand @invokelatest xs[i] = v The additional `x.f` and `xs[i]` syntax requires Julia 1.10. """ macro invokelatest(ex) - topmod = Compiler._topmod(__module__) # well, except, do not get it via CC but define it locally + topmod = _topmod(__module__) f, args, kwargs = destructure_callex(topmod, ex) out = Expr(:call, GlobalRef(Base, :invokelatest)) isempty(kwargs) || push!(out.args, Expr(:parameters, kwargs...)) diff --git a/base/runtime_internals.jl b/base/runtime_internals.jl index 4a04d406550b7..cbbde7d9535a2 100644 --- a/base/runtime_internals.jl +++ b/base/runtime_internals.jl @@ -20,7 +20,7 @@ Base """ parentmodule(m::Module) = (@_total_meta; ccall(:jl_module_parent, Ref{Module}, (Any,), m)) -is_root_module(m::Module) = parentmodule(m) === m || (isdefined(Main, :Base) && m === Main.Base) +is_root_module(m::Module) = parentmodule(m) === m || m === Compiler || (isdefined(Main, :Base) && m === Main.Base) """ moduleroot(m::Module) -> Module @@ -1556,3 +1556,9 @@ function specialize_method(match::Core.MethodMatch; kwargs...) end hasintersect(@nospecialize(a), @nospecialize(b)) = typeintersect(a, b) !== Bottom + +########### +# scoping # +########### + +_topmod(m::Module) = ccall(:jl_base_relative_to, Any, (Any,), m)::Module diff --git a/base/show.jl b/base/show.jl index 26efd0a93f716..8f305107d10f5 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1385,32 +1385,6 @@ function show_mi(io::IO, mi::Core.MethodInstance, from_stackframe::Bool=false) end end -# These sometimes show up as Const-values in InferenceFrameInfo signatures -function show(io::IO, mi_info::Compiler.Timings.InferenceFrameInfo) - mi = mi_info.mi - def = mi.def - if isa(def, Method) - if isdefined(def, :generator) && mi === def.generator - print(io, "InferenceFrameInfo generator for ") - show(io, def) - else - print(io, "InferenceFrameInfo for ") - argnames = [isa(a, Core.Const) ? (isa(a.val, Type) ? "" : a.val) : "" for a in mi_info.slottypes[1:mi_info.nargs]] - show_tuple_as_call(io, def.name, mi.specTypes; argnames, qualified=true) - end - else - di = mi.cache.inferred.debuginfo - file, line = IRShow.debuginfo_firstline(di) - file = string(file) - line = isempty(file) || line < 0 ? "" : "$file:$line" - print(io, "Toplevel InferenceFrameInfo thunk from ", def, " starting at ", line) - end -end - -function show(io::IO, tinf::Compiler.Timings.Timing) - print(io, "Compiler.Timings.Timing(", tinf.mi_info, ") with ", length(tinf.children), " children") -end - function show_delim_array(io::IO, itr::Union{AbstractArray,SimpleVector}, op, delim, cl, delim_one, i1=first(LinearIndices(itr)), l=last(LinearIndices(itr))) print(io, op) @@ -2855,8 +2829,10 @@ module IRShow isexpr, compute_basic_blocks, block_for_inst, IncrementalCompact, Effects, ALWAYS_TRUE, ALWAYS_FALSE, DebugInfoStream, getdebugidx, VarState, InvalidIRError, argextype, widenconst, singleton_type, - sptypes_from_meth_instance, EMPTY_SPTYPES - include("compiler/ssair/show.jl") + sptypes_from_meth_instance, EMPTY_SPTYPES, InferenceState, + NativeInterpreter, CachedMethodTable, LimitedAccuracy, Timings + + Base.include(IRShow, Base.strcat(Base.BUILDROOT, "../usr/share/julia/Compiler/src/ssair/show.jl")) const __debuginfo = Dict{Symbol, Any}( # :full => src -> statementidx_lineinfo_printer(src), # and add variable slot information @@ -2883,34 +2859,63 @@ function show(io::IO, src::CodeInfo; debuginfo::Symbol=:source) print(io, ")") end -function show(io::IO, inferred::Compiler.InferenceResult) - mi = inferred.linfo - tt = mi.specTypes.parameters[2:end] - tts = join(["::$(t)" for t in tt], ", ") - rettype = inferred.result - if isa(rettype, Compiler.InferenceState) - rettype = rettype.bestguess - end - if isa(mi.def, Method) - print(io, mi.def.name, "(", tts, " => ", rettype, ")") - else - print(io, "Toplevel MethodInstance thunk from ", mi.def, " => ", rettype) +show_unquoted(io::IO, val::Argument, indent::Int, prec::Int) = show_unquoted(io, Core.SlotNumber(val.n), indent, prec) + +show_unquoted(io::IO, stmt::PhiNode, indent::Int, ::Int) = show_unquoted_phinode(io, stmt, indent, "%") +function show_unquoted_phinode(io::IO, stmt::PhiNode, indent::Int, prefix::String) + args = String[let + e = stmt.edges[i] + v = !isassigned(stmt.values, i) ? "#undef" : + sprint(; context=io) do ioβ€² + show_unquoted(ioβ€², stmt.values[i], indent) + end + "$prefix$e => $v" + end for i in 1:length(stmt.edges) + ] + print(io, "Ο† ", '(') + join(io, args, ", ") + print(io, ')') +end + +function show_unquoted(io::IO, stmt::PhiCNode, indent::Int, ::Int) + print(io, "Ο†αΆœ (") + first = true + for v in stmt.values + first ? (first = false) : print(io, ", ") + show_unquoted(io, v, indent) end + print(io, ")") end -show(io::IO, sv::Compiler.InferenceState) = - (print(io, "InferenceState for "); show(io, sv.linfo)) +function show_unquoted(io::IO, stmt::PiNode, indent::Int, ::Int) + print(io, "Ο€ (") + show_unquoted(io, stmt.val, indent) + print(io, ", ") + printstyled(io, stmt.typ, color=:cyan) + print(io, ")") +end -show(io::IO, ::Compiler.NativeInterpreter) = - print(io, "Core.Compiler.NativeInterpreter(...)") +function show_unquoted(io::IO, stmt::UpsilonNode, indent::Int, ::Int) + print(io, "Ο’ (") + isdefined(stmt, :val) ? + show_unquoted(io, stmt.val, indent) : + print(io, "#undef") + print(io, ")") +end -show(io::IO, cache::Compiler.CachedMethodTable) = - print(io, typeof(cache), "(", Compiler.length(cache.cache), " entries)") +function show_unquoted(io::IO, stmt::ReturnNode, indent::Int, ::Int) + if !isdefined(stmt, :val) + print(io, "unreachable") + else + print(io, "return ") + show_unquoted(io, stmt.val, indent) + end +end -function show(io::IO, limited::Compiler.LimitedAccuracy) - print(io, "Compiler.LimitedAccuracy(") - show(io, limited.typ) - print(io, ", #= ", Compiler.length(limited.causes), " cause(s) =#)") +show_unquoted(io::IO, stmt::GotoIfNot, indent::Int, ::Int) = show_unquoted_gotoifnot(io, stmt, indent, "%") +function show_unquoted_gotoifnot(io::IO, stmt::GotoIfNot, indent::Int, prefix::String) + print(io, "goto ", prefix, stmt.dest, " if not ") + show_unquoted(io, stmt.cond, indent) end function dump(io::IOContext, x::SimpleVector, n::Int, indent) diff --git a/base/stacktraces.jl b/base/stacktraces.jl index c3d86fc8f5151..e16757541cfc9 100644 --- a/base/stacktraces.jl +++ b/base/stacktraces.jl @@ -124,7 +124,7 @@ end const top_level_scope_sym = Symbol("top-level scope") -function lookup(ip::Union{Base.InterpreterIP}) +function lookup(ip::Base.InterpreterIP) code = ip.code if code === nothing # interpreted top-level expression with no CodeInfo diff --git a/base/sysimg.jl b/base/sysimg.jl index 8347d63d5b740..476b9715b7e11 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -1,13 +1,17 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -# Can be built either a monolith or with a minimal Base image that just has the -# compiler. -if isdefined(Main, :Base); else -Core.eval(Main, :(baremodule Base; end)) +# Can be be loaded on top of either an existing system image built from +# `Base_compiler.jl` or standalone, in which case we will build it now. +let had_compiler = isdefined(Main, :Base) +if had_compiler; else +include("Base_compiler.jl") end Core.include(Base, "Base.jl") +had_compiler && ccall(:jl_init_restored_module, Cvoid, (Any,), Base) +end + using .Base # Set up Main module diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index 55de3492e9447..ffbbfd620997f 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -359,6 +359,7 @@ generate_precompile_statements() = try # Make sure `ansi_enablecursor` is printe eval(PrecompileStagingArea, :(const $(Symbol(_mod)) = $_mod)) end end + eval(PrecompileStagingArea, :(const Compiler = Base.Compiler)) n_succeeded = 0 # Make statements unique diff --git a/doc/src/devdocs/EscapeAnalysis.md b/doc/src/devdocs/EscapeAnalysis.md index 1bd7868790f7f..ea874bf7371b0 100644 --- a/doc/src/devdocs/EscapeAnalysis.md +++ b/doc/src/devdocs/EscapeAnalysis.md @@ -1,6 +1,6 @@ # `EscapeAnalysis` -`Core.Compiler.EscapeAnalysis` is a compiler utility module that aims to analyze +`Compiler.EscapeAnalysis` is a compiler utility module that aims to analyze escape information of [Julia's SSA-form IR](@ref Julia-SSA-form-IR) a.k.a. `IRCode`. This escape analysis aims to: @@ -59,7 +59,7 @@ The symbols on the side of each call argument and SSA statements represent the f - `βœ“` (green or cyan): this value never escapes (`has_no_escape(result.state[x])` holds), colored blue if it has arg escape also (`has_arg_escape(result.state[x])` holds) - `↑` (blue or yellow): this value can escape to the caller via return (`has_return_escape(result.state[x])` holds), colored yellow if it has unhandled thrown escape also (`has_thrown_escape(result.state[x])` holds) - `X` (red): this value can escape to somewhere the escape analysis can't reason about like escapes to a global memory (`has_all_escape(result.state[x])` holds) -- `*` (bold): this value's escape state is between the `ReturnEscape` and `AllEscape` in the partial order of [`EscapeInfo`](@ref Core.Compiler.EscapeAnalysis.EscapeInfo), colored yellow if it has unhandled thrown escape also (`has_thrown_escape(result.state[x])` holds) +- `*` (bold): this value's escape state is between the `ReturnEscape` and `AllEscape` in the partial order of [`EscapeInfo`](@ref Base.Compiler.EscapeAnalysis.EscapeInfo), colored yellow if it has unhandled thrown escape also (`has_thrown_escape(result.state[x])` holds) - `β€²`: this value has additional object field / array element information in its `AliasInfo` property Escape information of each call argument and SSA value can be inspected programmatically as like: @@ -74,7 +74,7 @@ result.state[Core.SSAValue(3)] # get EscapeInfo of `r3` ### Lattice Design `EscapeAnalysis` is implemented as a [data-flow analysis](https://en.wikipedia.org/wiki/Data-flow_analysis) -that works on a lattice of [`x::EscapeInfo`](@ref Core.Compiler.EscapeAnalysis.EscapeInfo), +that works on a lattice of [`x::EscapeInfo`](@ref Base.Compiler.EscapeAnalysis.EscapeInfo), which is composed of the following properties: - `x.Analyzed::Bool`: not formally part of the lattice, only indicates `x` has not been analyzed or not - `x.ReturnEscape::BitSet`: records SSA statements where `x` can escape to the caller via return @@ -366,9 +366,9 @@ More interestingly, it is also valid to use `IPO EA` escape information for type e.g., inference accuracy can be improved by forming `Const`/`PartialStruct`/`MustAlias` of mutable object. ```@docs -Core.Compiler.EscapeAnalysis.analyze_escapes -Core.Compiler.EscapeAnalysis.EscapeState -Core.Compiler.EscapeAnalysis.EscapeInfo +Base.Compiler.EscapeAnalysis.analyze_escapes +Base.Compiler.EscapeAnalysis.EscapeState +Base.Compiler.EscapeAnalysis.EscapeInfo ``` -------------------------------------------------------------------------------------------- diff --git a/src/module.c b/src/module.c index 08ad0d64dbf55..85813af6adc6f 100644 --- a/src/module.c +++ b/src/module.c @@ -1276,6 +1276,7 @@ JL_DLLEXPORT jl_uuid_t jl_module_uuid(jl_module_t* m) { return m->uuid; } // TODO: make this part of the module constructor and read-only? JL_DLLEXPORT void jl_set_module_uuid(jl_module_t *m, jl_uuid_t uuid) { m->uuid = uuid; } +JL_DLLEXPORT void jl_set_module_parent(jl_module_t *m, jl_module_t *parent) { m->parent = parent; } int jl_is_submodule(jl_module_t *child, jl_module_t *parent) JL_NOTSAFEPOINT { @@ -1308,15 +1309,20 @@ JL_DLLEXPORT void jl_clear_implicit_imports(jl_module_t *m) JL_UNLOCK(&m->lock); } +JL_DLLEXPORT void jl_add_to_module_init_list(jl_value_t *mod) +{ + if (jl_module_init_order == NULL) + jl_module_init_order = jl_alloc_vec_any(0); + jl_array_ptr_1d_push(jl_module_init_order, mod); +} + JL_DLLEXPORT void jl_init_restored_module(jl_value_t *mod) { if (!jl_generating_output() || jl_options.incremental) { jl_module_run_initializer((jl_module_t*)mod); } else { - if (jl_module_init_order == NULL) - jl_module_init_order = jl_alloc_vec_any(0); - jl_array_ptr_1d_push(jl_module_init_order, mod); + jl_add_to_module_init_list(mod); } } diff --git a/src/precompile_utils.c b/src/precompile_utils.c index fc361d8b88e6f..01e8a2040a751 100644 --- a/src/precompile_utils.c +++ b/src/precompile_utils.c @@ -281,6 +281,12 @@ static void *jl_precompile(int all) return native_code; } +static int suppress_precompile = 0; +JL_DLLEXPORT void jl_suppress_precompile(int suppress) +{ + suppress_precompile = suppress; +} + static void *jl_precompile_worklist(jl_array_t *worklist, jl_array_t *extext_methods, jl_array_t *new_ext_cis) { if (!worklist) @@ -289,34 +295,36 @@ static void *jl_precompile_worklist(jl_array_t *worklist, jl_array_t *extext_met // type signatures that were inferred but haven't been compiled jl_array_t *m = jl_alloc_vec_any(0); JL_GC_PUSH1(&m); - size_t i, n = jl_array_nrows(worklist); - for (i = 0; i < n; i++) { - jl_module_t *mod = (jl_module_t*)jl_array_ptr_ref(worklist, i); - assert(jl_is_module(mod)); - foreach_mtable_in_module(mod, precompile_enq_all_specializations_, m); - } - n = jl_array_nrows(extext_methods); - for (i = 0; i < n; i++) { - jl_method_t *method = (jl_method_t*)jl_array_ptr_ref(extext_methods, i); - assert(jl_is_method(method)); - jl_value_t *specializations = jl_atomic_load_relaxed(&method->specializations); - if (!jl_is_svec(specializations)) { - precompile_enq_specialization_((jl_method_instance_t*)specializations, m); + if (!suppress_precompile) { + size_t i, n = jl_array_nrows(worklist); + for (i = 0; i < n; i++) { + jl_module_t *mod = (jl_module_t*)jl_array_ptr_ref(worklist, i); + assert(jl_is_module(mod)); + foreach_mtable_in_module(mod, precompile_enq_all_specializations_, m); } - else { - size_t j, l = jl_svec_len(specializations); - for (j = 0; j < l; j++) { - jl_value_t *mi = jl_svecref(specializations, j); - if (mi != jl_nothing) - precompile_enq_specialization_((jl_method_instance_t*)mi, m); + n = jl_array_nrows(extext_methods); + for (i = 0; i < n; i++) { + jl_method_t *method = (jl_method_t*)jl_array_ptr_ref(extext_methods, i); + assert(jl_is_method(method)); + jl_value_t *specializations = jl_atomic_load_relaxed(&method->specializations); + if (!jl_is_svec(specializations)) { + precompile_enq_specialization_((jl_method_instance_t*)specializations, m); + } + else { + size_t j, l = jl_svec_len(specializations); + for (j = 0; j < l; j++) { + jl_value_t *mi = jl_svecref(specializations, j); + if (mi != jl_nothing) + precompile_enq_specialization_((jl_method_instance_t*)mi, m); + } } } - } - if (new_ext_cis) { - n = jl_array_nrows(new_ext_cis); - for (i = 0; i < n; i++) { - jl_code_instance_t *ci = (jl_code_instance_t*)jl_array_ptr_ref(new_ext_cis, i); - precompile_enq_specialization_(ci->def, m); + if (new_ext_cis) { + n = jl_array_nrows(new_ext_cis); + for (i = 0; i < n; i++) { + jl_code_instance_t *ci = (jl_code_instance_t*)jl_array_ptr_ref(new_ext_cis, i); + precompile_enq_specialization_(ci->def, m); + } } } void *native_code = jl_precompile_(m, 1); diff --git a/src/toplevel.c b/src/toplevel.c index 45143f99a178c..b0163683cf87c 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -1289,6 +1289,21 @@ JL_DLLEXPORT jl_value_t *jl_prepend_cwd(jl_value_t *str) return jl_cstr_to_string(path); } +JL_DLLEXPORT jl_value_t *jl_prepend_string(jl_value_t *prefix, jl_value_t *str) +{ + char path[1024]; + const char *pstr = (const char*)jl_string_data(prefix); + size_t sz = strlen(pstr); + const char *fstr = (const char*)jl_string_data(str); + if (strlen(fstr) + sz >= sizeof(path)) { + jl_errorf("use a bigger buffer for jl_fullpath"); + } + strcpy(path, pstr); + strcpy(path + sz, fstr); + return jl_cstr_to_string(path); +} + + #ifdef __cplusplus } #endif diff --git a/stdlib/InteractiveUtils/src/InteractiveUtils.jl b/stdlib/InteractiveUtils/src/InteractiveUtils.jl index f14e2f7de2f49..aa13fa3cdd31d 100644 --- a/stdlib/InteractiveUtils/src/InteractiveUtils.jl +++ b/stdlib/InteractiveUtils/src/InteractiveUtils.jl @@ -11,7 +11,8 @@ Base.Experimental.@optlevel 1 export apropos, edit, less, code_warntype, code_llvm, code_native, methodswith, varinfo, versioninfo, subtypes, supertypes, @which, @edit, @less, @functionloc, @code_warntype, - @code_typed, @code_lowered, @code_llvm, @code_native, @time_imports, clipboard, @trace_compile, @trace_dispatch + @code_typed, @code_lowered, @code_llvm, @code_native, @time_imports, clipboard, @trace_compile, @trace_dispatch, + @activate import Base.Docs.apropos diff --git a/stdlib/InteractiveUtils/src/codeview.jl b/stdlib/InteractiveUtils/src/codeview.jl index 030955b8e36d8..1aa83a19285ff 100644 --- a/stdlib/InteractiveUtils/src/codeview.jl +++ b/stdlib/InteractiveUtils/src/codeview.jl @@ -239,7 +239,7 @@ function _dump_function(@nospecialize(f), @nospecialize(t), native::Bool, wrappe if !isa(f, Core.OpaqueClosure) src = Base.Compiler.typeinf_code(Base.Compiler.NativeInterpreter(world), mi, true) else - src, rt = Base.get_oc_code_rt(f, tt, true) + src, rt = Base.get_oc_code_rt(nothing, f, tt, true) end src isa Core.CodeInfo || error("failed to infer source for $mi") str = _dump_function_native_assembly(mi, src, wrapper, syntax, debuginfo, binary, raw, params) @@ -248,7 +248,7 @@ function _dump_function(@nospecialize(f), @nospecialize(t), native::Bool, wrappe if !isa(f, Core.OpaqueClosure) src = Base.Compiler.typeinf_code(Base.Compiler.NativeInterpreter(world), mi, true) else - src, rt = Base.get_oc_code_rt(f, tt, true) + src, rt = Base.get_oc_code_rt(nothing, f, tt, true) end src isa Core.CodeInfo || error("failed to infer source for $mi") str = _dump_function_llvm(mi, src, wrapper, !raw, dump_module, optimize, debuginfo, params) diff --git a/stdlib/InteractiveUtils/src/macros.jl b/stdlib/InteractiveUtils/src/macros.jl index a21bf30dbcd6c..68afc40976275 100644 --- a/stdlib/InteractiveUtils/src/macros.jl +++ b/stdlib/InteractiveUtils/src/macros.jl @@ -429,3 +429,64 @@ like the julia arg `--trace-dispatch=stderr` but specifically for a call. """ :@trace_dispatch + +""" + @activate Component + +Activate a newly loaded copy of an otherwise builtin component. The `Component` +to be activated will be resolved using the ordinary rules of module resolution +in the current environment. + +When using `@activate`, additional options for a component may be specified in +square brackets `@activate Compiler[:option1, :option]` + +Currently `@activate Compiler` is the only available component that may be +activatived. + +For `@activate Compiler`, the following options are available: +1. `:reflection` - Activate the compiler for reflection purposes only. + The ordinary reflection functionality in `Base` and `InteractiveUtils`. + Will use the newly loaded compiler. Note however, that these reflection + functions will still interact with the ordinary native cache (both loading + and storing). An incorrect compiler implementation may thus corrupt runtime + state if reflection is used. Use external packages like `Cthulhu.jl` + introspecting compiler behavior with a separated cache partition. + +2. `:codegen` - Activate the compiler for internal codegen purposes. The new compiler + will be invoked whenever the runtime requests compilation. + +`@activate Compiler` without options is equivalent to `@activate Compiler[:reflection]`. + +""" +macro activate(what) + options = Symbol[] + if Meta.isexpr(what, :ref) + Component = what.args[1] + for i = 2:length(what.args) + arg = what.args[i] + if !isa(arg, QuoteNode) || !isa(arg.value, Symbol) + error("Usage Error: Option $arg is not a symbol") + end + push!(options, arg.value) + end + else + Component = what + end + if !isa(Component, Symbol) + error("Usage Error: Component $Component is not a symbol") + end + allowed_components = (:Compiler,) + if !(Component in allowed_components) + error("Usage Error: Component $Component is not recognized. Expected one of $allowed_components") + end + s = gensym() + if Component === :Compiler && isempty(options) + push!(options, :reflection) + end + options = map(options) do opt + Expr(:kw, opt, true) + end + Expr(:toplevel, + esc(:(import $Component as $s)), + esc(:($s.activate!(;$(options...))))) +end diff --git a/stdlib/Makefile b/stdlib/Makefile index ebc40c9db2b12..aacf7ca30e146 100644 --- a/stdlib/Makefile +++ b/stdlib/Makefile @@ -39,7 +39,6 @@ install-$$($(1)_JLL_NAME)_jll: get-$$($(1)_JLL_NAME)_jll endef $(foreach jll,$(JLLS),$(eval $(call download-artifacts-toml,$(jll)))) - STDLIBS = Artifacts Base64 CRC32c Dates FileWatching \ Future InteractiveUtils Libdl LibGit2 LinearAlgebra Logging \ Markdown Mmap Printf Profile Random REPL Serialization \ @@ -56,7 +55,6 @@ ifneq ($(filter $(STDLIBS),$(STDLIBS_EXT)),) $(error ERROR duplicated STDLIBS in list) endif - # Generate symlinks to all stdlibs at usr/share/julia/stdlib/vX.Y/ $(foreach module, $(STDLIBS), $(eval $(call symlink_target,$$(JULIAHOME)/stdlib/$(module),$$(build_datarootdir)/julia/stdlib/$$(VERSDIR),$(module)))) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 5142dd5e7f680..df3a0cad76878 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -5,6 +5,9 @@ module REPLCompletions export completions, shell_completions, bslash_completions, completion_text using Core: Const +# We want to insulate the REPLCompletion module from any changes the user may +# make to the compiler, since it runs by default and the system becomes unusable +# if it breaks. const CC = Base.Compiler using Base.Meta using Base: propertynames, something, IdSet diff --git a/sysimage.mk b/sysimage.mk index d3dee6906ccfa..9e3e52157aa45 100644 --- a/sysimage.mk +++ b/sysimage.mk @@ -23,7 +23,6 @@ $(build_private_libdir)/%.$(SHLIB_EXT): $(build_private_libdir)/%-o.a COMPILER_SRCS := $(addprefix $(JULIAHOME)/, \ base/Base_compiler.jl \ - base/compilerimg.jl \ base/boot.jl \ base/docs/core.jl \ base/abstractarray.jl \ @@ -55,7 +54,7 @@ COMPILER_SRCS := $(addprefix $(JULIAHOME)/, \ base/traits.jl \ base/refvalue.jl \ base/tuple.jl) -COMPILER_SRCS += $(shell find $(JULIAHOME)/base/compiler -name \*.jl) +COMPILER_SRCS += $(shell find $(JULIAHOME)/Compiler/src -name \*.jl) # sort these to remove duplicates BASE_SRCS := $(sort $(shell find $(JULIAHOME)/base -name \*.jl -and -not -name sysimg.jl) \ $(shell find $(BUILDROOT)/base -name \*.jl -and -not -name sysimg.jl)) @@ -65,7 +64,7 @@ RELBUILDROOT := $(call rel_path,$(JULIAHOME)/base,$(BUILDROOT)/base)/ # <-- make $(build_private_libdir)/basecompiler.ji: $(COMPILER_SRCS) @$(call PRINT_JULIA, cd $(JULIAHOME)/base && \ $(call spawn,$(JULIA_EXECUTABLE)) -C "$(JULIA_CPU_TARGET)" $(HEAPLIM) --output-ji $(call cygpath_w,$@).tmp \ - --startup-file=no --warn-overwrite=yes -g$(BOOTSTRAP_DEBUG_LEVEL) -O1 compilerimg.jl) + --startup-file=no --warn-overwrite=yes -g$(BOOTSTRAP_DEBUG_LEVEL) -O1 Base_compiler.jl $(RELBUILDROOT)) @mv $@.tmp $@ $(build_private_libdir)/sys.ji: $(build_private_libdir)/basecompiler.ji $(JULIAHOME)/VERSION $(BASE_SRCS) $(STDLIB_SRCS) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 9fafc9bdca6ad..8a14774e2404f 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -1535,7 +1535,7 @@ let nfields_tfunc(@nospecialize xs...) = @test sizeof_nothrow(String) @test !sizeof_nothrow(Type{String}) @test sizeof_tfunc(Type{Union{Int64, Int32}}) == Const(Core.sizeof(Union{Int64, Int32})) - let PT = Core.PartialStruct(Tuple{Int64,UInt64}, Any[Const(10), UInt64]) + let PT = Core.PartialStruct(Base.Compiler.fallback_lattice, Tuple{Int64,UInt64}, Any[Const(10), UInt64]) @test sizeof_tfunc(PT) === Const(16) @test nfields_tfunc(PT) === Const(2) @test sizeof_nothrow(PT) @@ -3381,9 +3381,9 @@ struct FooPartial b::Int c::Int end -let PT1 = PartialStruct(FooPartial, Any[Const(1), Const(2), Int]), - PT2 = PartialStruct(FooPartial, Any[Const(1), Int, Int]), - PT3 = PartialStruct(FooPartial, Any[Const(1), Int, Const(3)]) +let PT1 = PartialStruct(Base.Compiler.fallback_lattice, FooPartial, Any[Const(1), Const(2), Int]), + PT2 = PartialStruct(Base.Compiler.fallback_lattice, FooPartial, Any[Const(1), Int, Int]), + PT3 = PartialStruct(Base.Compiler.fallback_lattice, FooPartial, Any[Const(1), Int, Const(3)]) @test PT1 βŠ‘ PT2 @test !(PT1 βŠ‘ PT3) && !(PT2 βŠ‘ PT1) @@ -4635,18 +4635,18 @@ end @testset "issue #43784" begin βŠ‘ = Core.Compiler.partialorder(Core.Compiler.fallback_lattice) βŠ” = Core.Compiler.join(Core.Compiler.fallback_lattice) + 𝕃 = Core.Compiler.fallback_lattice Const, PartialStruct = Core.Const, Core.PartialStruct - let init = Base.ImmutableDict{Any,Any}() a = Const(init) - b = PartialStruct(typeof(init), Any[Const(init), Any, Any]) + b = PartialStruct(𝕃, typeof(init), Any[Const(init), Any, Any]) c = a βŠ” b @test a βŠ‘ c && b βŠ‘ c @test c === typeof(init) end let init = Base.ImmutableDict{Any,Any}(1,2) a = Const(init) - b = PartialStruct(typeof(init), Any[Const(getfield(init,1)), Any, Any]) + b = PartialStruct(𝕃, typeof(init), Any[Const(getfield(init,1)), Any, Any]) c = a βŠ” b @test a βŠ‘ c && b βŠ‘ c @test c isa PartialStruct @@ -4654,14 +4654,14 @@ end end let init = Base.ImmutableDict{Number,Number}() a = Const(init) - b = PartialStruct(typeof(init), Any[Const(init), Number, ComplexF64]) + b = PartialStruct(𝕃, typeof(init), Any[Const(init), Number, ComplexF64]) c = a βŠ” b @test a βŠ‘ c && b βŠ‘ c @test c === typeof(init) end let init = Base.ImmutableDict{Number,Number}() - a = PartialStruct(typeof(init), Any[Const(init), ComplexF64, ComplexF64]) - b = PartialStruct(typeof(init), Any[Const(init), Number, ComplexF64]) + a = PartialStruct(𝕃, typeof(init), Any[Const(init), ComplexF64, ComplexF64]) + b = PartialStruct(𝕃, typeof(init), Any[Const(init), Number, ComplexF64]) c = a βŠ” b @test a βŠ‘ c && b βŠ‘ c @test c isa PartialStruct @@ -4669,8 +4669,8 @@ end @test c.fields[3] === ComplexF64 end let init = Base.ImmutableDict{Number,Number}() - a = PartialStruct(typeof(init), Any[Const(init), ComplexF64, ComplexF64]) - b = PartialStruct(typeof(init), Any[Const(init), ComplexF32, Union{ComplexF32,ComplexF64}]) + a = PartialStruct(𝕃, typeof(init), Any[Const(init), ComplexF64, ComplexF64]) + b = PartialStruct(𝕃, typeof(init), Any[Const(init), ComplexF32, Union{ComplexF32,ComplexF64}]) c = a βŠ” b @test a βŠ‘ c && b βŠ‘ c @test c isa PartialStruct @@ -4678,8 +4678,8 @@ end @test c.fields[3] === Complex end let T = Base.ImmutableDict{Number,Number} - a = PartialStruct(T, Any[T]) - b = PartialStruct(T, Any[T, Number, Number]) + a = PartialStruct(𝕃, T, Any[T]) + b = PartialStruct(𝕃, T, Any[T, Number, Number]) @test b βŠ‘ a c = a βŠ” b @test a βŠ‘ c && b βŠ‘ c @@ -4687,7 +4687,7 @@ end @test length(c.fields) == 1 end let T = Base.ImmutableDict{Number,Number} - a = PartialStruct(T, Any[T]) + a = PartialStruct(𝕃, T, Any[T]) b = Const(T()) c = a βŠ” b @test a βŠ‘ c && b βŠ‘ c @@ -4695,7 +4695,7 @@ end end let T = Base.ImmutableDict{Number,Number} a = Const(T()) - b = PartialStruct(T, Any[T]) + b = PartialStruct(𝕃, T, Any[T]) c = a βŠ” b @test a βŠ‘ c && b βŠ‘ c @test c === T @@ -4742,22 +4742,23 @@ end let βŠ‘ = Core.Compiler.partialorder(Core.Compiler.fallback_lattice) βŠ” = Core.Compiler.join(Core.Compiler.fallback_lattice) + 𝕃 = Core.Compiler.fallback_lattice Const, PartialStruct = Core.Const, Core.PartialStruct - @test (Const((1,2)) βŠ‘ PartialStruct(Tuple{Int,Int}, Any[Const(1),Int])) - @test !(Const((1,2)) βŠ‘ PartialStruct(Tuple{Int,Int,Int}, Any[Const(1),Int,Int])) - @test !(Const((1,2,3)) βŠ‘ PartialStruct(Tuple{Int,Int}, Any[Const(1),Int])) - @test (Const((1,2,3)) βŠ‘ PartialStruct(Tuple{Int,Int,Int}, Any[Const(1),Int,Int])) - @test (Const((1,2)) βŠ‘ PartialStruct(Tuple{Int,Vararg{Int}}, Any[Const(1),Vararg{Int}])) - @test (Const((1,2)) βŠ‘ PartialStruct(Tuple{Int,Int,Vararg{Int}}, Any[Const(1),Int,Vararg{Int}])) broken=true - @test (Const((1,2,3)) βŠ‘ PartialStruct(Tuple{Int,Int,Vararg{Int}}, Any[Const(1),Int,Vararg{Int}])) - @test !(PartialStruct(Tuple{Int,Int}, Any[Const(1),Int]) βŠ‘ Const((1,2))) - @test !(PartialStruct(Tuple{Int,Int,Int}, Any[Const(1),Int,Int]) βŠ‘ Const((1,2))) - @test !(PartialStruct(Tuple{Int,Int}, Any[Const(1),Int]) βŠ‘ Const((1,2,3))) - @test !(PartialStruct(Tuple{Int,Int,Int}, Any[Const(1),Int,Int]) βŠ‘ Const((1,2,3))) - @test !(PartialStruct(Tuple{Int,Vararg{Int}}, Any[Const(1),Vararg{Int}]) βŠ‘ Const((1,2))) - @test !(PartialStruct(Tuple{Int,Int,Vararg{Int}}, Any[Const(1),Int,Vararg{Int}]) βŠ‘ Const((1,2))) - @test !(PartialStruct(Tuple{Int,Int,Vararg{Int}}, Any[Const(1),Int,Vararg{Int}]) βŠ‘ Const((1,2,3))) + @test (Const((1,2)) βŠ‘ PartialStruct(𝕃, Tuple{Int,Int}, Any[Const(1),Int])) + @test !(Const((1,2)) βŠ‘ PartialStruct(𝕃, Tuple{Int,Int,Int}, Any[Const(1),Int,Int])) + @test !(Const((1,2,3)) βŠ‘ PartialStruct(𝕃, Tuple{Int,Int}, Any[Const(1),Int])) + @test (Const((1,2,3)) βŠ‘ PartialStruct(𝕃, Tuple{Int,Int,Int}, Any[Const(1),Int,Int])) + @test (Const((1,2)) βŠ‘ PartialStruct(𝕃, Tuple{Int,Vararg{Int}}, Any[Const(1),Vararg{Int}])) + @test (Const((1,2)) βŠ‘ PartialStruct(𝕃, Tuple{Int,Int,Vararg{Int}}, Any[Const(1),Int,Vararg{Int}])) broken=true + @test (Const((1,2,3)) βŠ‘ PartialStruct(𝕃, Tuple{Int,Int,Vararg{Int}}, Any[Const(1),Int,Vararg{Int}])) + @test !(PartialStruct(𝕃, Tuple{Int,Int}, Any[Const(1),Int]) βŠ‘ Const((1,2))) + @test !(PartialStruct(𝕃, Tuple{Int,Int,Int}, Any[Const(1),Int,Int]) βŠ‘ Const((1,2))) + @test !(PartialStruct(𝕃, Tuple{Int,Int}, Any[Const(1),Int]) βŠ‘ Const((1,2,3))) + @test !(PartialStruct(𝕃, Tuple{Int,Int,Int}, Any[Const(1),Int,Int]) βŠ‘ Const((1,2,3))) + @test !(PartialStruct(𝕃, Tuple{Int,Vararg{Int}}, Any[Const(1),Vararg{Int}]) βŠ‘ Const((1,2))) + @test !(PartialStruct(𝕃, Tuple{Int,Int,Vararg{Int}}, Any[Const(1),Int,Vararg{Int}]) βŠ‘ Const((1,2))) + @test !(PartialStruct(𝕃, Tuple{Int,Int,Vararg{Int}}, Any[Const(1),Int,Vararg{Int}]) βŠ‘ Const((1,2,3))) t = Const((false, false)) βŠ” Const((false, true)) @test t isa PartialStruct && length(t.fields) == 2 && t.fields[1] === Const(false) @@ -4899,7 +4900,7 @@ let src = code_typed1() do end # Test that Const βŠ‘ PartialStruct respects vararg -@test Const((1,2)) βŠ‘ PartialStruct(Tuple{Vararg{Int}}, [Const(1), Vararg{Int}]) +@test Const((1,2)) βŠ‘ PartialStruct(Core.Compiler.fallback_lattice, Tuple{Vararg{Int}}, [Const(1), Vararg{Int}]) # Test that semi-concrete interpretation doesn't break on functions with while loops in them. Base.@assume_effects :consistent :effect_free :terminates_globally function pure_annotated_loop(x::Int, y::Int)