Skip to content

Commit

Permalink
inference-directed codegen via CodeInstance edges and typeinf_ext_top…
Browse files Browse the repository at this point in the history
…level APIs (#56880)

This is building upon the many efforts around using CodeInstance
everywhere (especially as the return value from jl_type_infer and the
input format to edges) by moving a lot of hard-coded algorithms that
were previously in C (such as `recursive_compile_graph` and
`jl_ci_cache_lookup`), and which were therefore previously also slightly
broken (especially with concurrent environments), into Julia's
Compiler.jl code, where we can most likely maintain them much better
going forward. See descriptions in the individual commits for some of
the specifics of the changes and fixes, and how to change existing code
to use these API correctly. In followup stages, most code relevant to
precompile_utils, trim, and even the allocation-checker should consider
being moved into Julia also now, since being written in C/C++ is
currently providing negative value for maintaining those, and the change
in the API boundary should now make that additional conversion easier.

Gives a considerably smaller system image, despite having more code, by
being better algorithms which avoid allocating permanent garbage: 155 MB
-> 147MB in `.text`

Makes a slightly larger Pkg.ji file cache, hopefully mainly due to being
more strategic about what code is compiled (because that logic is mostly
now in Julia instead of C), as it appear to have both inferred and
compiled about 10% more code according to high level analysis of it:
$ du -sh usr/share/julia/compiled/v1.12/
237M    # on PR
222M    # on master
  • Loading branch information
vtjnash authored Jan 2, 2025
2 parents 0741f9b + 9876823 commit ed2cb49
Show file tree
Hide file tree
Showing 21 changed files with 678 additions and 602 deletions.
19 changes: 16 additions & 3 deletions Compiler/src/bootstrap.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ function activate_codegen!()
end

function bootstrap!()
global bootstrapping_compiler = true
let time() = ccall(:jl_clock_now, Float64, ())
println("Compiling the compiler. This may take several minutes ...")
interp = NativeInterpreter()

ssa_inlining_pass!_tt = Tuple{typeof(ssa_inlining_pass!), IRCode, InliningState{NativeInterpreter}, Bool}
optimize_tt = Tuple{typeof(optimize), NativeInterpreter, OptimizationState{NativeInterpreter}, InferenceResult}
Expand All @@ -45,13 +45,15 @@ function bootstrap!()
end
end
starttime = time()
methods = Any[]
world = get_world_counter()
for f in fs
if isa(f, DataType) && f.name === typename(Tuple)
tt = f
else
tt = Tuple{typeof(f), Vararg{Any}}
end
matches = _methods_by_ftype(tt, 10, get_world_counter())::Vector
matches = _methods_by_ftype(tt, 10, world)::Vector
if isempty(matches)
println(stderr, "WARNING: no matching method found for `", tt, "`")
else
Expand All @@ -62,14 +64,25 @@ function bootstrap!()
for i = 1:length(params)
params[i] = unwraptv(params[i])
end
typeinf_type(interp, m.method, Tuple{params...}, m.sparams)
mi = specialize_method(m.method, Tuple{params...}, m.sparams)
#isa_compileable_sig(mi) || println(stderr, "WARNING: inferring `", mi, "` which isn't expected to be called.")
push!(methods, mi)
end
end
end
codeinfos = typeinf_ext_toplevel(methods, [world], false)
for i = 1:2:length(codeinfos)
ci = codeinfos[i]::CodeInstance
src = codeinfos[i + 1]::CodeInfo
isa_compileable_sig(ci.def) || continue # println(stderr, "WARNING: compiling `", ci.def, "` which isn't expected to be called.")
ccall(:jl_add_codeinst_to_jit, Cvoid, (Any, Any), ci, src)
end
endtime = time()
println("Base.Compiler ──── ", sub_float(endtime,starttime), " seconds")
end
activate_codegen!()
global bootstrapping_compiler = false
nothing
end

function activate!(; reflection=true, codegen=false)
Expand Down
18 changes: 9 additions & 9 deletions Compiler/src/tfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,10 @@ macro nospecs(ex)
push!(names, arg)
end
@assert isexpr(body, :block)
if !isempty(names)
lin = first(body.args)::LineNumberNode
nospec = Expr(:macrocall, Symbol("@nospecialize"), lin, names...)
insert!(body.args, 2, nospec)
end
isempty(names) && throw(ArgumentError("no arguments for @nospec"))
lin = first(body.args)::LineNumberNode
nospec = Expr(:macrocall, GlobalRef(@__MODULE__, :var"@nospecialize"), lin, names...)
insert!(body.args, 2, nospec)
return esc(ex)
end

Expand Down Expand Up @@ -2115,7 +2114,7 @@ add_tfunc(memoryrefoffset, 1, 1, memoryrefoffset_tfunc, 5)
return true
end

@nospecs function memoryref_elemtype(@nospecialize mem)
@nospecs function memoryref_elemtype(mem)
m = widenconst(mem)
if !has_free_typevars(m) && m <: GenericMemoryRef
m0 = m
Expand All @@ -2131,7 +2130,7 @@ end
return Any
end

@nospecs function _memoryref_elemtype(@nospecialize mem)
@nospecs function _memoryref_elemtype(mem)
m = widenconst(mem)
if !has_free_typevars(m) && m <: GenericMemoryRef
m0 = m
Expand Down Expand Up @@ -2166,7 +2165,7 @@ end
end

# whether getindex for the elements can potentially throw UndefRef
function array_type_undefable(@nospecialize(arytype))
@nospecs function array_type_undefable(arytype)
arytype = unwrap_unionall(arytype)
if isa(arytype, Union)
return array_type_undefable(arytype.a) || array_type_undefable(arytype.b)
Expand Down Expand Up @@ -2247,7 +2246,7 @@ end
return boundscheck Bool && memtype GenericMemoryRef && order Symbol
end

@nospecs function memorynew_nothrow(argtypes::Vector{Any})
function memorynew_nothrow(argtypes::Vector{Any})
if !(argtypes[1] isa Const && argtypes[2] isa Const)
return false
end
Expand All @@ -2263,6 +2262,7 @@ end
overflows = checked_smul_int(len, elsz)[2]
return !overflows
end

# Query whether the given builtin is guaranteed not to throw given the `argtypes`.
# `argtypes` can be assumed not to contain varargs.
function _builtin_nothrow(𝕃::AbstractLattice, @nospecialize(f::Builtin), argtypes::Vector{Any},
Expand Down
Loading

2 comments on commit ed2cb49

@vtjnash
Copy link
Member Author

@vtjnash vtjnash commented on ed2cb49 Jan 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nanosoldier runbenchmarks(ALL, isdaily = true)

@nanosoldier
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your benchmark job has completed - possible performance regressions were detected. A full report can be found here.

Please sign in to comment.