Skip to content

Commit 150e87c

Browse files
committed
v2: dead-strip unused functions in arm64 linker, reduce binary from 9.4MB to 5.85MB
- Add linker-level dead function stripping: BFS reachability from roots (_main, __v_init_consts_*), compact text section, fix up relocations and symbol addresses - Add should_skip_store optimization: skip redundant store-then-load when value is consumed by the immediately next instruction (arithmetic, cmp, store, load, GEP, trunc, zext), with operand[1] forwarding via x10 - SP-relative addressing during sp_adjusted periods (sp_adjust_amt tracking) - Merge fn_starts/fn_ends/fn_names/fn_sym_ids and stats in parallel workers - should_skip_store: skip store-to-stack when value is consumed by the next instruction (arith, cmp, store, load, GEP, trunc, zext); includes operand[1] forwarding to x10 for binary ops. Eliminates 57K/211K stores - Remove redundant canonicalize_narrow_int_result after cache hits (value already canonicalized before store). Saves ~58K SXTW/AND instructions - Non-invalidating last_store cache: value stays in register after consumption, enabling multi-use of same cached value - SP-relative addressing during sp_adjusted periods via sp_adjust_amt tracking, avoiding 2-3 instruction FP-relative fallback
1 parent a011c78 commit 150e87c

8 files changed

Lines changed: 738 additions & 148 deletions

File tree

vlib/v2/builder/builder.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -837,7 +837,7 @@ fn (mut b Builder) gen_cleanc_source_with_cache_init_calls(modules []string, cac
837837

838838
fn (mut b Builder) gen_cleanc_source_with_options(modules []string, export_const_symbols bool, cache_bundle_name string, cached_init_calls []string, use_markused bool) string {
839839
mut gen_files := b.files.clone()
840-
if cached_init_calls.len > 0 && b.can_use_cached_core_headers() {
840+
if cached_init_calls.len > 0 && b.can_use_cached_core_headers_for_parse() {
841841
mut p := parser.Parser.new(b.pref)
842842
header_files := p.parse_files(b.core_cached_parse_paths(), mut b.file_set)
843843
gen_files << header_files

vlib/v2/builder/cache_headers.v

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,36 @@ fn (b &Builder) header_stamp_for_modules(modules []string) string {
238238
return lines.join('\n')
239239
}
240240

241+
// can_use_cached_core_headers_for_parse checks whether .vh header files
242+
// exist, are non-empty, and their stamp matches the current source/compiler
243+
// file timestamps. The .o stamp validation is skipped (the gen phase
244+
// rebuilds stale .o files via ensure_cached_module_object), but the .vh
245+
// stamp IS validated so that stale headers trigger a full parse — otherwise
246+
// the gen phase would regenerate .o from incomplete .vh ASTs.
247+
fn (b &Builder) can_use_cached_core_headers_for_parse() bool {
248+
if b.pref.no_cache || b.pref.skip_builtin {
249+
return false
250+
}
251+
if !b.ensure_core_cache_dir() {
252+
return false
253+
}
254+
// Validate .vh header stamp (no cc/cc_flags — only source + compiler timestamps).
255+
expected_header_stamp := b.header_stamp_for_modules(core_cached_module_paths)
256+
current_header_stamp := os.read_file(b.core_headers_stamp_path()) or { return false }
257+
if current_header_stamp != expected_header_stamp {
258+
return false
259+
}
260+
for header_path in b.core_header_paths() {
261+
if !os.exists(header_path) {
262+
return false
263+
}
264+
if os.file_size(header_path) == 0 {
265+
return false
266+
}
267+
}
268+
return true
269+
}
270+
241271
fn (b &Builder) can_use_cached_core_headers() bool {
242272
if b.pref.no_cache || b.pref.skip_builtin {
243273
return false

vlib/v2/builder/parse.v

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,9 @@ fn (mut b Builder) parse_files(files []string) []ast.File {
1313
skip_builtin := b.pref.skip_builtin
1414
mut use_core_headers := false
1515
if !skip_builtin {
16-
use_core_headers = false
17-
// SSA/C and native backends need full core module bodies (not .vh summaries),
18-
// otherwise runtime helpers can be lowered to stubs.
19-
if b.pref.backend in [.c, .cleanc, .x64, .arm64] {
20-
use_core_headers = false
21-
}
16+
// -prod builds with a valid header cache can use lightweight .vh
17+
// summaries instead of fully parsing every core module source file.
18+
use_core_headers = b.pref.is_prod && b.can_use_cached_core_headers_for_parse()
2219
if use_core_headers {
2320
core_files := b.core_cached_parse_paths()
2421
parsed_core_files := parser_reused.parse_files(core_files, mut b.file_set)

vlib/v2/builder/parse_d_parallel.v

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,9 @@ fn (mut b Builder) parse_files_parallel(files []string) []ast.File {
6565
}
6666
skip_builtin := b.pref.skip_builtin
6767
if !skip_builtin {
68-
use_core_headers := false
69-
// SSA/C and native backends need full core module bodies (not .vh summaries),
70-
// otherwise runtime helpers can be lowered to stubs.
71-
use_core_headers2 := if b.pref.backend in [.c, .cleanc, .x64, .arm64] {
72-
false
73-
} else {
74-
use_core_headers
75-
}
68+
// -prod builds with a valid header cache can use lightweight .vh
69+
// summaries instead of fully parsing every core module source file.
70+
use_core_headers2 := b.pref.is_prod && b.can_use_cached_core_headers_for_parse()
7671
// Parse builtin and its dependencies
7772
// Mark them as parsed first to prevent re-parsing via imports
7873
for module_path in core_cached_module_paths {

0 commit comments

Comments
 (0)