Skip to content

Commit 97ecdb8

Browse files
JeffBezansongbaralditimholytopolarity
authored
add --trim option for generating smaller binaries (#55047)
This adds a command line option `--trim` that builds images where code is only included if it is statically reachable from methods marked using the new function `entrypoint`. Compile-time errors are given for call sites that are too dynamic to allow trimming the call graph (however there is an `unsafe` option if you want to try building anyway to see what happens). The PR has two other components. One is changes to Base that generally allow more code to be compiled in this mode. These changes will either be merged in separate PRs or moved to a separate part of the workflow (where we will build a custom system image for this purpose). The branch is set up this way to make it easy to check out and try the functionality. The other component is everything in the `juliac/` directory, which implements a compiler driver script based on this new option, along with some examples and tests. This will eventually become a package "app" that depends on PackageCompiler and provides a CLI for all of this stuff, so it will not be merged here. To try an example: ``` julia contrib/juliac.jl --output-exe hello --trim test/trimming/hello.jl ``` When stripped the resulting executable is currently about 900kb on my machine. Also includes a lot of work by @topolarity --------- Co-authored-by: Gabriel Baraldi <[email protected]> Co-authored-by: Tim Holy <[email protected]> Co-authored-by: Cody Tapscott <[email protected]>
1 parent ff0a1be commit 97ecdb8

37 files changed

+1338
-89
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ julia-deps: | $(DIRS) $(build_datarootdir)/julia/base $(build_datarootdir)/julia
8282
julia-stdlib: | $(DIRS) julia-deps
8383
@$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/stdlib
8484

85-
julia-base: julia-deps $(build_sysconfdir)/julia/startup.jl $(build_man1dir)/julia.1 $(build_datarootdir)/julia/julia-config.jl
85+
julia-base: julia-deps $(build_sysconfdir)/julia/startup.jl $(build_man1dir)/julia.1 $(build_datarootdir)/julia/julia-config.jl $(build_datarootdir)/julia/juliac.jl $(build_datarootdir)/julia/juliac-buildscript.jl
8686
@$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/base
8787

8888
julia-libccalltest: julia-deps
@@ -181,7 +181,7 @@ $(build_sysconfdir)/julia/startup.jl: $(JULIAHOME)/etc/startup.jl | $(build_sysc
181181
@echo Creating usr/etc/julia/startup.jl
182182
@cp $< $@
183183

184-
$(build_datarootdir)/julia/julia-config.jl: $(JULIAHOME)/contrib/julia-config.jl | $(build_datarootdir)/julia
184+
$(build_datarootdir)/julia/%: $(JULIAHOME)/contrib/% | $(build_datarootdir)/julia
185185
$(INSTALL_M) $< $(dir $@)
186186

187187
$(build_depsbindir)/stringreplace: $(JULIAHOME)/contrib/stringreplace.c | $(build_depsbindir)

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ Julia v1.12 Release Notes
44
New language features
55
---------------------
66

7+
- New option `--trim` for building "trimmed" binaries, where code not provably reachable from entry points
8+
is removed. Entry points can be marked using `Base.Experimental.entrypoint` ([#55047]).
79
- A new keyword argument `usings::Bool` has been added to `names`. By using this, we can now
810
find all the names available in module `A` by `names(A; all=true, imported=true, usings=true)`. ([#54609])
911
- the `@atomic(...)` macro family supports now the reference assignment syntax, e.g.

base/experimental.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,4 +457,18 @@ without adding them to the global method table.
457457
"""
458458
:@MethodTable
459459

460+
"""
461+
Base.Experimental.entrypoint(f, argtypes::Tuple)
462+
463+
Mark a method for inclusion when the `--trim` option is specified.
464+
"""
465+
function entrypoint(@nospecialize(f), @nospecialize(argtypes::Tuple))
466+
entrypoint(Tuple{Core.Typeof(f), argtypes...})
467+
end
468+
469+
function entrypoint(@nospecialize(argt::Type))
470+
ccall(:jl_add_entrypoint, Int32, (Any,), argt)
471+
nothing
472+
end
473+
460474
end

base/libuv.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,10 @@ function uv_return_spawn end
133133
function uv_asynccb end
134134
function uv_timercb end
135135

136-
function reinit_stdio()
136+
reinit_stdio() = _reinit_stdio()
137+
# we need this so it can be called by codegen to print errors, even after
138+
# reinit_stdio has been redefined by the juliac build script.
139+
function _reinit_stdio()
137140
global stdin = init_stdio(ccall(:jl_stdin_stream, Ptr{Cvoid}, ()))::IO
138141
global stdout = init_stdio(ccall(:jl_stdout_stream, Ptr{Cvoid}, ()))::IO
139142
global stderr = init_stdio(ccall(:jl_stderr_stream, Ptr{Cvoid}, ()))::IO

base/options.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ struct JLOptions
5858
permalloc_pkgimg::Int8
5959
heap_size_hint::UInt64
6060
trace_compile_timing::Int8
61+
trim::Int8
6162
end
6263

6364
# This runs early in the sysimage != is not defined yet

base/reflection.jl

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1504,6 +1504,13 @@ struct CodegenParams
15041504
"""
15051505
use_jlplt::Cint
15061506

1507+
"""
1508+
If enabled, only provably reachable code (from functions marked with `entrypoint`) is included
1509+
in the output system image. Errors or warnings can be given for call sites too dynamic to handle.
1510+
The option is disabled by default. (0=>disabled, 1=>safe (static errors), 2=>unsafe, 3=>unsafe plus warnings)
1511+
"""
1512+
trim::Cint
1513+
15071514
"""
15081515
A pointer of type
15091516
@@ -1519,14 +1526,14 @@ struct CodegenParams
15191526
prefer_specsig::Bool=false,
15201527
gnu_pubnames::Bool=true, debug_info_kind::Cint = default_debug_info_kind(),
15211528
debug_info_level::Cint = Cint(JLOptions().debug_level), safepoint_on_entry::Bool=true,
1522-
gcstack_arg::Bool=true, use_jlplt::Bool=true,
1529+
gcstack_arg::Bool=true, use_jlplt::Bool=true, trim::Cint=Cint(0),
15231530
lookup::Ptr{Cvoid}=unsafe_load(cglobal(:jl_rettype_inferred_addr, Ptr{Cvoid})))
15241531
return new(
15251532
Cint(track_allocations), Cint(code_coverage),
15261533
Cint(prefer_specsig),
15271534
Cint(gnu_pubnames), debug_info_kind,
15281535
debug_info_level, Cint(safepoint_on_entry),
1529-
Cint(gcstack_arg), Cint(use_jlplt),
1536+
Cint(gcstack_arg), Cint(use_jlplt), Cint(trim),
15301537
lookup)
15311538
end
15321539
end

base/strings/io.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ function print(io::IO, xs...)
5151
return nothing
5252
end
5353

54+
setfield!(typeof(print).name.mt, :max_args, 10, :monotonic)
55+
5456
"""
5557
println([io::IO], xs...)
5658
@@ -74,6 +76,7 @@ julia> String(take!(io))
7476
"""
7577
println(io::IO, xs...) = print(io, xs..., "\n")
7678

79+
setfield!(typeof(println).name.mt, :max_args, 10, :monotonic)
7780
## conversion of general objects to strings ##
7881

7982
"""
@@ -149,6 +152,7 @@ function print_to_string(xs...)
149152
end
150153
String(_unsafe_take!(s))
151154
end
155+
setfield!(typeof(print_to_string).name.mt, :max_args, 10, :monotonic)
152156

153157
function string_with_env(env, xs...)
154158
if isempty(xs)

contrib/julia-config.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ function ldlibs(doframework)
6767
"julia"
6868
end
6969
if Sys.isunix()
70-
return "-Wl,-rpath,$(shell_escape(libDir())) -Wl,-rpath,$(shell_escape(private_libDir())) -l$libname"
70+
return "-L$(shell_escape(private_libDir())) -Wl,-rpath,$(shell_escape(libDir())) -Wl,-rpath,$(shell_escape(private_libDir())) -l$libname"
7171
else
7272
return "-l$libname -lopenlibm"
7373
end

0 commit comments

Comments
 (0)