Skip to content

Commit

Permalink
wip: generate precompile as part of build process
Browse files Browse the repository at this point in the history
Rate limit · GitHub

Whoa there!

You have triggered an abuse detection mechanism.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

KristofferC committed Jul 19, 2018
1 parent 565bd4d commit fd07681
Showing 14 changed files with 212 additions and 877 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -206,7 +206,7 @@ define sysimg_builder
$$(build_private_libdir)/sys$1-o.a $$(build_private_libdir)/sys$1-bc.a : $$(build_private_libdir)/sys$1-%.a : $$(build_private_libdir)/sys.ji
@$$(call PRINT_JULIA, cd $$(JULIAHOME)/base && \
if ! $$(call spawn,$3) $2 -C "$$(JULIA_CPU_TARGET)" --output-$$* $$(call cygpath_w,$$@).tmp $$(JULIA_SYSIMG_BUILD_FLAGS) \
--startup-file=no --warn-overwrite=yes --sysimage $$(call cygpath_w,$$<) -e nothing; then \
--startup-file=no --warn-overwrite=yes --sysimage $$(call cygpath_w,$$<) $$(JULIAHOME)/contrib/generate_precompile.jl; then \
echo '*** This error is usually fixed by running `make clean`. If the error persists$$(COMMA) try `make cleanall`. ***'; \
false; \
fi )
1 change: 1 addition & 0 deletions base/options.jl
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@ struct JLOptions
warn_overwrite::Int8
can_inline::Int8
polly::Int8
trace_compile::Int8
fast_math::Int8
worker::Int8
cookie::Ptr{UInt8}
746 changes: 0 additions & 746 deletions base/precompile.jl

This file was deleted.

7 changes: 2 additions & 5 deletions base/sysimg.jl
Original file line number Diff line number Diff line change
@@ -521,9 +521,9 @@ let
:SuiteSparse,
:Distributed,
:SharedArrays,
:Pkg,
:Test,
:REPL,
:Pkg,
:Statistics,
]

@@ -921,11 +921,9 @@ Base.init_load_path() # want to be able to find external packages in userimg.jl

let
tot_time_userimg = @elapsed (Base.isfile("userimg.jl") && Base.include(Main, "userimg.jl"))
tot_time_precompile = Base.is_primary_base_module ? (@elapsed Base.include(Base, "precompile.jl")) : 0.0


tot_time_base = (Base.end_base_include - Base.start_base_include) * 10.0^(-9)
tot_time = tot_time_base + Base.tot_time_stdlib[] + tot_time_userimg + tot_time_precompile
tot_time = tot_time_base + Base.tot_time_stdlib[] + tot_time_userimg

println("Sysimage built. Summary:")
print("Total ─────── "); Base.time_print(tot_time * 10^9); print(" \n");
@@ -934,7 +932,6 @@ print("Stdlibs: ──── "); Base.time_print(Base.tot_time_stdlib[] * 10^9);
if isfile("userimg.jl")
print("Userimg: ──── "); Base.time_print(tot_time_userimg * 10^9); print(" "); show(IOContext(stdout, :compact=>true), (tot_time_userimg / tot_time) * 100); println("%")
end
print("Precompile: ─ "); Base.time_print(tot_time_precompile * 10^9); print(" "); show(IOContext(stdout, :compact=>true), (tot_time_precompile / tot_time) * 100); println("%")
end

empty!(LOAD_PATH)
1 change: 0 additions & 1 deletion contrib/add_license_to_files.jl
Original file line number Diff line number Diff line change
@@ -30,7 +30,6 @@ const excludedirs = [

const skipfiles = [
"../contrib/add_license_to_files.jl",
"../contrib/fixup_precompile.jl",
# files to check - already copyright
# see: https://github.com/JuliaLang/julia/pull/11073#issuecomment-98099389
"../base/special/trig.jl",
70 changes: 0 additions & 70 deletions contrib/fixup_precompile.jl

This file was deleted.

67 changes: 67 additions & 0 deletions contrib/generate_precompile.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
Base.__init__()

function create_precompile_file()
println("Generating precompile statements...")
t = time()

sysimg = joinpath(dirname(Sys.BINDIR), "lib", "julia", "sys")
tmp = tempname()
if haskey(Base.loaded_modules, Base.PkgId(Base.UUID("3fa0cd96-eef1-5676-8a61-b3b8758bbffb"), "REPL"))
setup = """
@async while true
sleep(0.01)
if isdefined(Base, :active_repl)
exit(0)
end
end
"""
# Record precompile statements when starting a julia session with a repl
run(pipeline(`$(Base.julia_cmd()) --sysimage $sysimg.ji --trace-compile=yes -O0 --banner=no --startup-file=no -e $setup -i`; stderr=tmp))

# Replay a REPL script
repl_replay = joinpath(@__DIR__, "precompile_replay.jl")
if isfile(repl_replay)
run(pipeline(`$(Base.julia_cmd()) --sysimage $sysimg.ji --trace-compile=yes -O0 --startup-file=no $repl_replay`; stderr=tmp, append=true))
end
else
# No REPL, just record the startup
run(pipeline(`$(Base.julia_cmd()) --sysimage $sysimg.ji --trace-compile=yes -O0 --startup-file=no -e0`; stderr=tmp))
end

isfile(tmp) || return

# Replace the FakeTermiaal with a TTYYerminal and filter out everything we compiled in Main
precompiles = readlines(tmp)
new_precompiles = Set{String}()
for statement in precompiles
startswith(statement, "precompile(Tuple{") || continue
statement = replace(statement, "FakeTerminals.FakeTerminal" => "REPL.Terminals.TTYTerminal")
(occursin(r"Main.", statement) || occursin(r"FakeTerminals.", statement)) && continue
push!(new_precompiles, statement)
end

write(tmp, join(sort(collect(new_precompiles)), '\n'))
# Load the precompile statements
let
PrecompileStagingArea = Module()
for (_pkgid, _mod) in Base.loaded_modules
if !(_pkgid.name in ("Main", "Core", "Base"))
@eval PrecompileStagingArea $(Symbol(_mod)) = $_mod
end
end

Base.include(PrecompileStagingArea, tmp)
@eval PrecompileStagingArea begin
# Could startup with REPL banner instead but it is a bit visually noisy, so just precompile it here.
precompile(Tuple{typeof(REPL.banner), REPL.Terminals.TTYTerminal, REPL.Terminals.TTYTerminal})
end

end

print("Precompile statements generated in "), Base.time_print((time() - t) * 10^9)
println()
return
end

create_precompile_file()

67 changes: 67 additions & 0 deletions contrib/precompile_replay.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
Base.__init__()

import REPL
include(joinpath(Sys.STDLIB, "REPL", "test", "FakeTerminals.jl"))
import .FakeTerminals.FakeTerminal
include(joinpath(Sys.STDLIB, "REPL", "test", "fake_repl.jl"))

const CTRL_C = '\x03'
const UP_ARROW = "\e[A"
const DOWN_ARROW = "\e[B"

# TODO: Have a utility to generate this from a real REPL session?
precompile_script = """
2+2
println("Hello World")
@time 1+1
?reinterpret
;pwd
using Ra\t$CTRL_C
\\alpha\t$CTRL_C
\e[200~paste here ;)\e[201~"$CTRL_C
$UP_ARROW$DOWN_ARROW
123\b\b\b$CTRL_C
f(x) = x03
f(1,2)
[][1]
"""


function run_repl()
# Writing ^C to the repl will cause sigint, so let's not die on that
ccall(:jl_exit_on_sigint, Cvoid, (Cint,), 0)

fake_repl() do stdin_write, stdout_read, repl
repl.specialdisplay = REPL.REPLDisplay(repl)
repl.history_file = false

repltask = @async begin
REPL.run_repl(repl)
end

global inc = false
global b = Condition()
global c = Condition()
mod = @__MODULE__
let cmd = "\"Hello REPL\""
write(stdin_write, "$mod.inc || wait($mod.b); r = $cmd; notify($mod.c); r\r")
end
inc = true
notify(b)
wait(c)

write(stdin_write, precompile_script)

s = readavailable(stdout_read)

# Close REPL ^D
write(stdin_write, '\x04')
Base._wait(repltask)

nothing
end
ccall(:jl_exit_on_sigint, Cvoid, (Cint,), 1)
end


run_repl()
12 changes: 6 additions & 6 deletions src/gf.c
Original file line number Diff line number Diff line change
@@ -1097,13 +1097,13 @@ static jl_method_instance_t *jl_mt_assoc_by_type(jl_methtable_t *mt, jl_datatype
if (entry != NULL) {
jl_method_t *m = entry->func.method;
if (!jl_has_call_ambiguities((jl_value_t*)tt, m)) {
#ifdef TRACE_COMPILE
if (!jl_has_free_typevars((jl_value_t*)tt)) {
jl_printf(JL_STDERR, "precompile(");
jl_static_show(JL_STDERR, (jl_value_t*)tt);
jl_printf(JL_STDERR, ")\n");
if (jl_options.trace_compile) {
if (!jl_has_free_typevars((jl_value_t*)tt)) {
jl_printf(JL_STDERR, "precompile(");
jl_static_show(JL_STDERR, (jl_value_t*)tt);
jl_printf(JL_STDERR, ")\n");
}
}
#endif
if (!mt_cache) {
intptr_t nspec = (mt == jl_type_type_mt ? m->nargs + 1 : mt->max_args + 2);
jl_compilation_sig(tt, env, m, nspec, &newparams);
16 changes: 16 additions & 0 deletions src/jloptions.c
Original file line number Diff line number Diff line change
@@ -55,6 +55,11 @@ jl_options_t jl_options = { 0, // quiet
0, // method overwrite warning
1, // can_inline
JL_OPTIONS_POLLY_ON, // polly
#ifdef TRACE_COMPILE
1, // trace_compile
#else
0, // trace_compile
#endif
JL_OPTIONS_FAST_MATH_DEFAULT,
0, // worker
NULL, // cookie
@@ -159,6 +164,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
opt_warn_overwrite,
opt_inline,
opt_polly,
opt_trace_compile,
opt_math_mode,
opt_worker,
opt_bind_to,
@@ -215,6 +221,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
{ "warn-overwrite", required_argument, 0, opt_warn_overwrite },
{ "inline", required_argument, 0, opt_inline },
{ "polly", required_argument, 0, opt_polly },
{ "trace-compile", required_argument, 0, opt_trace_compile },
{ "math-mode", required_argument, 0, opt_math_mode },
{ "handle-signals", required_argument, 0, opt_handle_signals },
// hidden command line options
@@ -567,6 +574,15 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
jl_errorf("julia: invalid argument to --polly (%s)", optarg);
}
break;
case opt_trace_compile:
if (!strcmp(optarg,"yes"))
jl_options.trace_compile = 1;
else if (!strcmp(optarg,"no"))
jl_options.trace_compile = 0;
else {
jl_errorf("julia: invalid argument to --trace-compile (%s)", optarg);
}
break;
case opt_math_mode:
if (!strcmp(optarg,"ieee"))
jl_options.fast_math = JL_OPTIONS_FAST_MATH_OFF;
1 change: 1 addition & 0 deletions src/julia.h
Original file line number Diff line number Diff line change
@@ -1794,6 +1794,7 @@ typedef struct {
int8_t warn_overwrite;
int8_t can_inline;
int8_t polly;
int8_t trace_compile;
int8_t fast_math;
int8_t worker;
const char *cookie;
2 changes: 1 addition & 1 deletion stdlib/REPL/src/REPL.jl
Original file line number Diff line number Diff line change
@@ -179,7 +179,7 @@ struct REPLBackendRef
response_channel::Channel
end

function run_repl(repl::AbstractREPL, consumer::Function = x->nothing)
function run_repl(repl::AbstractREPL, @nospecialize(consumer = x -> nothing))
repl_channel = Channel(1)
response_channel = Channel(1)
backend = start_repl_backend(repl_channel, response_channel)
49 changes: 49 additions & 0 deletions stdlib/REPL/test/fake_repl.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@

using Test

function kill_timer(delay)
# Give ourselves a generous timer here, just to prevent
# this causing e.g. a CI hang when there's something unexpected in the output.
# This is really messy and leaves the process in an undefined state.
# the proper and correct way to do this in real code would be to destroy the
# IO handles: `close(stdout_read); close(stdin_write)`
test_task = current_task()
function kill_test(t)
# **DON'T COPY ME.**
# The correct way to handle timeouts is to close the handle:
# e.g. `close(stdout_read); close(stdin_write)`
schedule(test_task, "hard kill repl test"; error=true)
print(stderr, "WARNING: attempting hard kill of repl test after exceeding timeout\n")
end
return Timer(kill_test, delay)
end

# REPL tests
function fake_repl(@nospecialize(f); options::REPL.Options=REPL.Options(confirm_exit=false))
# Use pipes so we can easily do blocking reads
# In the future if we want we can add a test that the right object
# gets displayed by intercepting the display
input = Pipe()
output = Pipe()
err = Pipe()
Base.link_pipe!(input, reader_supports_async=true, writer_supports_async=true)
Base.link_pipe!(output, reader_supports_async=true, writer_supports_async=true)
Base.link_pipe!(err, reader_supports_async=true, writer_supports_async=true)

repl = REPL.LineEditREPL(FakeTerminal(input.out, output.in, err.in), true)
repl.options = options

hard_kill = kill_timer(900) # Your debugging session starts now. You have 15 minutes. Go.
f(input.in, output.out, repl)
t = @async begin
close(input.in)
close(output.in)
close(err.in)
end
@test read(err.out, String) == ""
#display(read(output.out, String))
#print(read(output.out, String))
Base._wait(t)
close(hard_kill)
nothing
end
Rate limit · GitHub

Whoa there!

You have triggered an abuse detection mechanism.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

0 comments on commit fd07681

Please sign in to comment.