@@ -134,6 +134,8 @@ fn main() {
134134 mut no_parallel := false
135135 mut parallel_transform := false
136136 mut building_v := false
137+ mut all_backends := false
138+ mut compile_backends := []string {}
137139 mut user_defines := []string {}
138140 mut i := 0
139141 for i < args.len {
@@ -163,6 +165,12 @@ fn main() {
163165 } else if args[i] == '-parallel-transform' || args[i] == '--parallel-transform' {
164166 parallel_transform = true
165167 i++
168+ } else if args[i] == '-all-backends' || args[i] == '--all-backends' {
169+ all_backends = true
170+ i++
171+ } else if args[i] in ['-compile-backend' , '--compile-backend' ] && i + 1 < args.len {
172+ compile_backends << args[i + 1 ]
173+ i + = 2
166174 } else if args[i] == '-d' && i + 1 < args.len {
167175 user_defines << args[i + 1 ]
168176 i + = 2
@@ -209,6 +217,47 @@ fn main() {
209217 output_file = bin_file + '.c'
210218 }
211219
220+ // Decide which backend modules to compile into the output. By default only the C
221+ // backend is built; the arm64/wasm/eval backends (and the whole SSA pipeline that the
222+ // arm64 backend pulls in: v3.ssa + v3.ssa.optimize) are skipped entirely. When compiling
223+ // the V compiler itself this avoids parsing/checking/transforming/cgen-ing ~30k lines of
224+ // unused backend code, which measurably speeds up the self-host build. The `skip_*`
225+ // defines drive two things in lock-step: `$if !skip_* ?` gates in main() make the parser
226+ // drop the dispatch blocks (so the backend symbols are never referenced), and
227+ // resolve_imports skips parsing the corresponding module directories.
228+ // `-all-backends` keeps everything; `-compile-backend <name>` opts a specific backend back
229+ // in; the active `-b` target backend is always force-included.
230+ mut include_arm64 := all_backends
231+ mut include_wasm := all_backends
232+ mut include_eval := all_backends
233+ for cb in compile_backends {
234+ for name in cb.split (',' ) {
235+ match name.trim_space () {
236+ 'arm64' , 'aarch64' { include_arm64 = true }
237+ 'wasm' , 'wasm32' { include_wasm = true }
238+ 'eval' { include_eval = true }
239+ // 'c' is always built; there is no native amd64 backend in v3 yet.
240+ else {}
241+ }
242+ }
243+ }
244+ match backend {
245+ 'arm64' { include_arm64 = true }
246+ 'wasm' { include_wasm = true }
247+ 'eval' { include_eval = true }
248+ else {}
249+ }
250+
251+ if ! include_arm64 {
252+ user_defines << 'skip_arm64'
253+ }
254+ if ! include_wasm {
255+ user_defines << 'skip_wasm'
256+ }
257+ if ! include_eval {
258+ user_defines << 'skip_eval'
259+ }
260+
212261 mut b := bench.new ()
213262 println ('=== v3 benchmark ===' )
214263
@@ -273,14 +322,16 @@ fn main() {
273322 b.step ('check' )
274323
275324 if backend == 'eval' {
276- mut runner := eval.new (prefs)
277- runner.run_files (a) or {
278- eprintln ('error: ${err .msg ()}' )
279- exit (1 )
325+ $if ! skip_eval ? {
326+ mut runner := eval.new (prefs)
327+ runner.run_files (a) or {
328+ eprintln ('error: ${err .msg ()}' )
329+ exit (1 )
330+ }
331+ b.step ('eval' )
332+ b.print_report ()
333+ return
280334 }
281- b.step ('eval' )
282- b.print_report ()
283- return
284335 }
285336
286337 // Mark used functions (dead-code elimination). This is done before transform
@@ -306,21 +357,23 @@ fn main() {
306357 b.step ('annotate types' )
307358
308359 if backend == 'wasm' {
309- // Direct flat-AST-to-WASM native backend. Runs before monomorphize (which
310- // targets generics, not yet supported here). output_file is the exact path
311- // requested via -o (or the <name>.wasm default).
312- mut g := wasm.Gen.new (a, pre_tc, used_fns)
313- g.gen ()
314- g.write (output_file) or {
315- eprintln ('error writing ${output_file }' )
316- exit (1 )
317- }
318- for w in g.warnings_list () {
319- eprintln ('wasm: ${w }' )
360+ $if ! skip_wasm ? {
361+ // Direct flat-AST-to-WASM native backend. Runs before monomorphize (which
362+ // targets generics, not yet supported here). output_file is the exact path
363+ // requested via -o (or the <name>.wasm default).
364+ mut g := wasm.Gen.new (a, pre_tc, used_fns)
365+ g.gen ()
366+ g.write (output_file) or {
367+ eprintln ('error writing ${output_file }' )
368+ exit (1 )
369+ }
370+ for w in g.warnings_list () {
371+ eprintln ('wasm: ${w }' )
372+ }
373+ b.step ('wasm gen' )
374+ b.print_report ()
375+ return
320376 }
321- b.step ('wasm gen' )
322- b.print_report ()
323- return
324377 }
325378
326379 // Monomorphization only adds specialized generic instantiations to `used_fns`. The V
@@ -332,21 +385,23 @@ fn main() {
332385 b.step ('monomorphize' )
333386
334387 if backend == 'arm64' {
335- // SSA + ARM64 native backend
336- mut m := ssa.build_with_used (a, used_fns, pre_tc)
337- b.step ('ssa build' )
338-
339- if is_prod {
340- optimize.optimize (mut m)
341- b.step ('optimize' )
342- }
388+ $if ! skip_arm64 ? {
389+ // SSA + ARM64 native backend
390+ mut m := ssa.build_with_used (a, used_fns, pre_tc)
391+ b.step ('ssa build' )
392+
393+ if is_prod {
394+ optimize.optimize (mut m)
395+ b.step ('optimize' )
396+ }
343397
344- mut g := arm64 .Gen.new (m)
345- g.gen ()
346- b.step ('arm64 gen' )
398+ mut g := arm64 .Gen.new (m)
399+ g.gen ()
400+ b.step ('arm64 gen' )
347401
348- g.write_and_link (bin_file)
349- b.step ('link' )
402+ g.write_and_link (bin_file)
403+ b.step ('link' )
404+ }
350405 } else {
351406 // C backend (default)
352407 mut g := cgen.FlatGen.new ()
@@ -566,12 +621,42 @@ fn path_is_in_dir(path string, dir string) bool {
566621 return real_path == real_dir || real_path.starts_with (real_dir + os.path_separator)
567622}
568623
624+ // skipped_backend_modules lists the importable backend module names that the current
625+ // configuration excludes (driven by the same `skip_*` defines that gate the dispatch in
626+ // main()). The arm64 backend is the only consumer of the SSA pipeline, so skipping it also
627+ // skips v3.ssa and v3.ssa.optimize.
628+ fn skipped_backend_modules (prefs & pref.Preferences) []string {
629+ mut skipped := []string {}
630+ if 'skip_arm64' in prefs.user_defines {
631+ skipped << 'v3.gen.arm64'
632+ skipped << 'v3.ssa'
633+ skipped << 'v3.ssa.optimize'
634+ }
635+ if 'skip_wasm' in prefs.user_defines {
636+ skipped << 'v3.gen.wasm'
637+ }
638+ if 'skip_eval' in prefs.user_defines {
639+ skipped << 'v3.eval'
640+ }
641+ return skipped
642+ }
643+
569644// resolve_imports resolves resolve imports information for v3 entry point.
570645fn resolve_imports (mut a flat.FlatAst, mut p parser.Parser, prefs & pref.Preferences, initial_files []string ) {
571646 mut parsed_modules := map [string ]bool {}
572647 parsed_modules['builtin' ] = true
573648 parsed_modules['main' ] = true
574649
650+ // Backend modules excluded by the active configuration are never parsed: their
651+ // dispatch in main() is gated out by the matching `$if !skip_* ?`, so nothing
652+ // references their symbols. Pre-seeding parsed_modules makes the loop below treat
653+ // them as already handled, so neither v3.v's top-level imports nor any transitive
654+ // import pulls them in. Skipping the arm64 group (v3.gen.arm64 + the v3.ssa SSA
655+ // pipeline) and the wasm/eval backends avoids ~30k lines of work when self-hosting.
656+ for skipped in skipped_backend_modules (prefs) {
657+ parsed_modules[skipped] = true
658+ }
659+
575660 mut first_file := ''
576661 if initial_files.len > 0 {
577662 first_file = initial_files[0 ]
0 commit comments