4848 used_vh_for_parse bool
4949 used_import_vh_for_parse bool
5050 used_virtual_vh_for_parse bool
51- flat_roundtrip_enabled bool // V2_FLAT_ROUNDTRIP=1: route parses through streaming + to_files()
52- flat_check_enabled bool // V2_CHECK_FLAT=1: route type-check through Checker.check_flat
53- markused_flat_enabled bool // V2_MARKUSED_FLAT=1 : route markused through mark_used_flat shim
54- flat_ssa_enabled bool // V2_FLAT_SSA=1 : route the (sequential) native SSA build through build_all_from_flat on the post-transform b.flat. Requires V2_CHECK_FLAT + V2_MARKUSED_FLAT so b.flat is post-transform-populated. Default off .
51+ flat_roundtrip_enabled bool // V2_FLAT_ROUNDTRIP=1: legacy comparison mode; route parses through streaming + to_files().
52+ flat_check_enabled bool // Default on: stream parse/ type-check through FlatAst. V2_LEGACY_AST=1 disables it unless V2_CHECK_FLAT=1 is set.
53+ markused_flat_enabled bool // Default on : route markused through mark_used_flat.
54+ flat_ssa_enabled bool // Default on : route SSA codegen through build_all_from_flat on the post-transform b.flat.
5555 // flat caches the FlatAst representation of b.files. When
5656 // flat_check_enabled is set, parse_batch streams directly into
5757 // flat_builder so b.flat is built incrementally during parsing rather
7171}
7272
7373pub fn new_builder (prefs & pref.Preferences) & Builder {
74+ legacy_ast_enabled := os.getenv ('V2_LEGACY_AST' ) != ''
75+ flat_default_enabled := ! legacy_ast_enabled
7476 unsafe {
7577 return & Builder{
7678 pref: prefs
7779 used_fn_keys: map [string ]bool {}
7880 cached_called_fn_names: map [string ]bool {}
7981 flat_roundtrip_enabled: os.getenv ('V2_FLAT_ROUNDTRIP' ) != ''
80- flat_check_enabled: os.getenv ('V2_CHECK_FLAT' ) != ''
81- markused_flat_enabled: os.getenv ('V2_MARKUSED_FLAT' ) != ''
82- flat_ssa_enabled: os.getenv ('V2_FLAT_SSA' ) != ''
82+ flat_check_enabled: flat_default_enabled || os.getenv ('V2_CHECK_FLAT' ) != ''
83+ markused_flat_enabled: flat_default_enabled || os.getenv ('V2_MARKUSED_FLAT' ) != ''
84+ flat_ssa_enabled: flat_default_enabled || os.getenv ('V2_FLAT_SSA' ) != ''
8385 }
8486 }
8587}
@@ -108,6 +110,20 @@ fn (b &Builder) backend_uses_markused_pruning() bool {
108110 return b.pref.backend != .arm64
109111}
110112
113+ fn (b &Builder) should_build_ssa_from_flat () bool {
114+ return b.flat.files.len > 0 && b.flat_ssa_enabled && b.markused_flat_enabled
115+ && b.flat_check_enabled
116+ }
117+
118+ fn (b &Builder) should_keep_flat_for_codegen () bool {
119+ flat_ssa_codegen := b.flat_ssa_enabled && b.markused_flat_enabled && b.flat_check_enabled
120+ return match b.pref.backend {
121+ .c { flat_ssa_codegen }
122+ .x64 , .arm64 { flat_ssa_codegen || b.native_flat_pipeline_enabled }
123+ else { false }
124+ }
125+ }
126+
111127fn (b &Builder) can_compile_cleanc_locally () bool {
112128 if b.pref == unsafe { nil } {
113129 return true
@@ -263,20 +279,26 @@ pub fn (mut b Builder) build(files []string) {
263279 // transform_files_from_flat, parallel streams per-worker via
264280 // to_files_range. No up-front full rehydration needed in either case.
265281 //
266- // When V2_MARKUSED_FLAT is also enabled, both transform paths route
267- // through their *_to_flat wedge so the post-transform flatten lives
268- // inside the transformer call (one round-trip), avoiding a separate
269- // flatten_files() pass before mark_used_flat .
282+ // Flat markused routes transform through flat-output wedges. Backends that
283+ // can consume flat codegen use the direct flat-output path and drop the
284+ // transformed []ast.File result; legacy backends keep the compatibility
285+ // wedge that returns both flat and files .
270286 mut flat_populated_by_transform := false
287+ transform_flat_only := b.should_keep_flat_for_codegen ()
271288 if sequential_transform {
272289 if use_native_flat_pipeline && ! b.flat_check_enabled {
273290 b.flat = trans.transform_files_to_flat_direct (b.files)
274291 b.files = []ast.File{}
275292 flat_populated_by_transform = true
276293 } else if use_flat_markused {
277- new_flat , files_out := trans.transform_files_to_flat (& b.flat, b.files)
278- b.flat = new_flat
279- b.files = files_out
294+ if transform_flat_only {
295+ b.flat = trans.transform_flat_to_flat_direct (& b.flat, b.files)
296+ b.files = []ast.File{}
297+ } else {
298+ new_flat , files_out := trans.transform_files_to_flat (& b.flat, b.files)
299+ b.flat = new_flat
300+ b.files = files_out
301+ }
280302 flat_populated_by_transform = true
281303 } else if b.flat_check_enabled {
282304 b.files = trans.transform_files_from_flat (& b.flat, b.files)
@@ -289,9 +311,14 @@ pub fn (mut b Builder) build(files []string) {
289311 b.files = []ast.File{}
290312 flat_populated_by_transform = true
291313 } else if use_flat_markused {
292- new_flat , files_out := b.transform_files_parallel_to_flat (mut trans)
293- b.flat = new_flat
294- b.files = files_out
314+ if transform_flat_only {
315+ b.flat = trans.transform_flat_to_flat_direct (& b.flat, b.files)
316+ b.files = []ast.File{}
317+ } else {
318+ new_flat , files_out := b.transform_files_parallel_to_flat (mut trans)
319+ b.flat = new_flat
320+ b.files = files_out
321+ }
295322 flat_populated_by_transform = true
296323 } else if b.flat_check_enabled {
297324 b.files = b.transform_files_parallel_from_flat (mut trans)
@@ -308,17 +335,15 @@ pub fn (mut b Builder) build(files []string) {
308335 b.used_fn_keys = map [string ]bool {}
309336 } else {
310337 mark_used_start := sw.elapsed ()
311- // V2_MARKUSED_FLAT only takes effect when V2_CHECK_FLAT is also on,
312- // since b.flat is only populated when flat_check_enabled streams
313- // parses into flat_builder. Without that, b.flat is empty and the
314- // shim would walk nothing.
338+ // Flat markused consumes the post-transform FlatAst. Legacy comparison
339+ // mode (`V2_LEGACY_AST=1`) can still reach the AST walker unless one of
340+ // the flat env flags explicitly re-enables this path.
315341 //
316- // The transformer mutates b.files but does not write back into
317- // b.flat. Both sequential and parallel paths now populate b.flat
318- // as part of their *_to_flat wedge when V2_MARKUSED_FLAT is on,
319- // so the separate flatten_files() pass is gone. The branch below
320- // remains as a defensive fallback for any future code path that
321- // reaches markused without having set flat_populated_by_transform.
342+ // The transformer mutates b.files but does not write back into b.flat.
343+ // Both sequential and parallel paths now populate b.flat as part of
344+ // their *_to_flat wedge, so the separate flatten_files() pass is gone.
345+ // The branch below remains as a defensive fallback for any future code
346+ // path that reaches markused without setting flat_populated_by_transform.
322347 if use_flat_markused && ! flat_populated_by_transform {
323348 b.flat = ast.flatten_files (b.files)
324349 }
@@ -340,16 +365,11 @@ pub fn (mut b Builder) build(files []string) {
340365 }
341366 mark_used_time := time.Duration (sw.elapsed () - mark_used_start)
342367 print_time ('Mark Used' , mark_used_time)
343- // b.flat is unused by the legacy codegen path. Under -gc none this is a
344- // no-op for peak memory, but keep the lifetime explicit for readers.
345- // When V2_FLAT_SSA or the native flat pipeline is on, the native SSA
346- // build consumes b.flat directly (build_all_from_flat), so keep it alive
347- // through codegen.
348- if ! b.flat_ssa_enabled && ! b.native_flat_pipeline_enabled {
349- b.flat = ast.FlatAst{}
350- }
351368 print_rss ('after markused' )
352369 }
370+ if b.flat_check_enabled && ! b.should_keep_flat_for_codegen () {
371+ b.flat = ast.FlatAst{}
372+ }
353373
354374 // Generate output based on backend
355375 match b.pref.backend {
@@ -601,7 +621,13 @@ fn (mut b Builder) gen_ssa_c() {
601621 ssa_builder.target_os = b.pref.target_os_or_host ()
602622
603623 mut stage_start := sw.elapsed ()
604- ssa_builder.build_all (b.files)
624+ mut built_from_flat := false
625+ if b.should_build_ssa_from_flat () {
626+ ssa_builder.build_all_from_flat (& b.flat)
627+ built_from_flat = true
628+ } else {
629+ ssa_builder.build_all (b.files)
630+ }
605631 print_time ('SSA Build' , time.Duration (sw.elapsed () - stage_start))
606632
607633 // TODO: re-enable SSA optimization once the new builder is mature
@@ -618,6 +644,9 @@ fn (mut b Builder) gen_ssa_c() {
618644 }
619645
620646 if output_name.ends_with ('.c' ) {
647+ if built_from_flat {
648+ b.flat = ast.FlatAst{}
649+ }
621650 stage_start = sw.elapsed ()
622651 mut gen := c.new_gen (mod)
623652 c_source := gen.gen ()
@@ -633,6 +662,12 @@ fn (mut b Builder) gen_ssa_c() {
633662
634663 cc := if b.pref.ccompiler.len > 0 { b.pref.ccompiler } else { configured_cc (b.pref.vroot) }
635664 directive_flags := b.collect_cflags_from_sources ()
665+ if built_from_flat {
666+ // SSA has copied the program into MIR, and directive scanning has read
667+ // source names from the FlatAst. Keep the later C generator/compiler
668+ // working sets clear of the transformed FlatAst.
669+ b.flat = ast.FlatAst{}
670+ }
636671 mut cc_flag_parts := []string {}
637672 env_flags := configured_cflags ()
638673 if env_flags.trim_space () != '' {
@@ -1684,7 +1719,7 @@ fn (mut b Builder) prepare_macos_tiny_candidate_source_files() {
16841719 if ! b.uses_macos_x64_tiny_object (.x64 ) {
16851720 return
16861721 }
1687- b.macos_tiny_candidate_source_files = if b.flat_check_enabled {
1722+ b.macos_tiny_candidate_source_files = if b.flat_check_enabled && b.flat.files.len > 0 {
16881723 b.flat.to_files ()
16891724 } else {
16901725 b.files.clone ()
@@ -1998,6 +2033,12 @@ fn (b &Builder) collect_cflags_from_sources() string {
19982033 scan_paths << file.name
19992034 }
20002035 }
2036+ for ff in b.flat.files {
2037+ name := b.flat.file_name (ff)
2038+ if name != '' {
2039+ scan_paths << name
2040+ }
2041+ }
20012042 cflags_target_os := b.cflags_target_os_for_local_compile ()
20022043 if ! b.pref.skip_builtin {
20032044 target_os := cflags_target_os
@@ -2567,18 +2608,15 @@ fn (mut b Builder) build_native_mir_from_files(files []ast.File, arch pref.Arch,
25672608 }
25682609
25692610 mut stage_start := native_sw.elapsed ()
2570- // V2_FLAT_SSA: route the whole SSA build through the cursor-native
2571- // build_all_from_flat on the post-transform b.flat (kept alive above).
2572- // Sequential only (build_all_from_flat builds fn bodies in-phase). Default off .
2611+ // Route the whole SSA build through the cursor-native build_all_from_flat
2612+ // on the post-transform b.flat (kept alive above). Sequential only
2613+ // (build_all_from_flat builds fn bodies in-phase).
25732614 //
2574- // b.flat is only POST-TRANSFORM when either V2_MARKUSED_FLAT has routed
2575- // transform through transform_files_to_flat, or the native flat pipeline has
2576- // emitted transform output directly into FlatAst. With V2_CHECK_FLAT but NOT
2577- // V2_MARKUSED_FLAT, b.flat stays parse-time, so feeding it here would skip
2578- // transformer rewrites.
2579- build_from_flat := b.flat.files.len > 0
2580- && ((b.flat_ssa_enabled && b.markused_flat_enabled && b.flat_check_enabled)
2581- || (b.native_flat_pipeline_enabled && label == '' ))
2615+ // b.flat is only POST-TRANSFORM when flat markused has routed transform
2616+ // through transform_files_to_flat, or the direct native flat pipeline has
2617+ // emitted transform output directly into FlatAst.
2618+ build_from_flat := b.should_build_ssa_from_flat ()
2619+ || (b.flat.files.len > 0 && b.native_flat_pipeline_enabled && label == '' )
25822620 if build_from_flat {
25832621 ssa_builder.build_all_from_flat (& b.flat)
25842622 // SSA has copied the program into MIR; keep the FlatAst lifetime out of
0 commit comments