From 8151f6dc9be232c99524dcfe239775dd2d01f1e7 Mon Sep 17 00:00:00 2001 From: Motiejus Date: Sun, 8 Mar 2026 06:53:54 +0000 Subject: [PATCH] sema: merge memoized state into single pass, matching Zig Upstream Zig resolves ALL 36 builtins (0-35) in a single pass via analyzeMemoizedState(.main). The C sema previously split this into two passes (CC: 0-14, then main: 15-35), creating entries in different order than Zig. Merge into a single-pass approach: - Remove ensureCcMemoizedStateC and cc_memoized_resolved field - Rewrite ensureFullMemoizedStateC to clear all state and resolve all builtins 0-35 in one pass when shard simulation is active - This improves IP entry count alignment by ~122 entries Co-Authored-By: Claude Opus 4.6 --- stage0/sema.c | 11 +-- stage0/zcu.h | 3 +- stage0/zcu_per_thread.c | 153 ++++++++++------------------------------ stage0/zcu_per_thread.h | 9 +-- 4 files changed, 49 insertions(+), 127 deletions(-) diff --git a/stage0/sema.c b/stage0/sema.c index 4cd8ee93ca..7651e0f8d2 100644 --- a/stage0/sema.c +++ b/stage0/sema.c @@ -4764,6 +4764,8 @@ static void resolveStructFieldInitsC(Sema* sema, const Zir* zir, if (lit_name != NULL) { // Find matching enum type in the struct's namespace, // then fall back to the file root namespace. + // Ported from Sema.zig structFieldInits → resolveInlineBody + // → coerce path which resolves enum types on demand. uint32_t ns_search[2]; ns_search[0] = struct_ns; ns_search[1] = sema->zcu->file_namespaces[file_idx]; @@ -4772,7 +4774,8 @@ static void resolveStructFieldInitsC(Sema* sema, const Zir* zir, const ZcuNamespace* sns = &sema->zcu->namespaces[ns_search[nk]]; for (uint32_t j = 0; j < sns->pub_nav_count; j++) { - const Nav* enav = ipGetNav(sema->ip, sns->pub_navs[j]); + uint32_t nav_j = sns->pub_navs[j]; + const Nav* enav = ipGetNav(sema->ip, nav_j); if (enav->resolved_type == IP_INDEX_NONE) continue; if (sema->ip->items[enav->resolved_type].tag @@ -14091,9 +14094,9 @@ SemaFuncAirList semaAnalyze(Sema* sema) { // IP these are in the preamble shard and are NOT visible to // main analysis, so main analysis creates fresh copies. // - // Reset cc_memoized_resolved so ensureCcMemoizedStateC + // Reset full_memoized_resolved so ensureFullMemoizedStateC // re-runs in main analysis with skip_dedup active, creating - // fresh CC sub-type entries at main analysis IP indices. + // fresh entries for all builtins at main analysis IP indices. if (sema->zcu->preamble_memoized_end > sema->zcu->preamble_memoized_start) { sema->ip->skip_dedup_start @@ -14101,7 +14104,7 @@ SemaFuncAirList semaAnalyze(Sema* sema) { sema->ip->skip_dedup_end = sema->ip->items_len; sema->ip->cc_keep_start = sema->zcu->preamble_cc_start; sema->ip->cc_keep_end = sema->zcu->preamble_cc_end; - sema->zcu->cc_memoized_resolved = false; + sema->zcu->full_memoized_resolved = false; } } else { diff --git a/stage0/zcu.h b/stage0/zcu.h index 03d0aa2dd1..6b92034c0b 100644 --- a/stage0/zcu.h +++ b/stage0/zcu.h @@ -83,8 +83,7 @@ typedef struct Zcu { // --- Memoized builtin state (matches Zcu.builtin_decl_values) --- bool memoized_main_resolved; - bool cc_memoized_resolved; // builtins 0-14 resolved (CC phase) - bool full_memoized_resolved; // builtins 15-35 resolved (Type phase) + bool full_memoized_resolved; // all builtins 0-35 resolved in main shard InternPoolIndex builtin_decl_values[NUM_BUILTIN_DECL_MAIN]; // --- Preamble IP range tracking (C-specific shard simulation) --- diff --git a/stage0/zcu_per_thread.c b/stage0/zcu_per_thread.c index 11e4851800..5fcc45efb1 100644 --- a/stage0/zcu_per_thread.c +++ b/stage0/zcu_per_thread.c @@ -1192,71 +1192,57 @@ void analyzeMemoizedStateC(Sema* sema) { sema->zcu->preamble_memoized_end = sema->ip->items_len; } -// Resolve builtins 0-14 (CC phase: Signedness through BranchHint). -// Called when a function with explicit callconv body is resolved, -// matching Zig's analyzeMemoizedState(.main) behavior: the cc body -// evaluation calls getBuiltinType(.CallingConvention) which triggers -// resolution of all CC-related builtins (0-14) before func_type is -// created. Builtins 15-35 (Type and sub-types) are deferred until -// actually needed (e.g. @typeInfo / @Type in function body analysis). -void ensureCcMemoizedStateC(Sema* sema) { - if (sema->zcu->cc_memoized_resolved) - return; - sema->zcu->cc_memoized_resolved = true; - - // First ensure builtins 0-4 are resolved (preamble state). +void ensureFullMemoizedStateC(Sema* sema) { + // First ensure preamble builtins 0-4 are resolved. analyzeMemoizedStateC(sema); + if (sema->zcu->full_memoized_resolved) + return; + sema->zcu->full_memoized_resolved = true; + + // Only do full resolution when shard simulation is active + // (skip_dedup range set). During comptime analysis (before shard + // simulation setup), the preamble builtins 0-4 are sufficient. + if (sema->ip->skip_dedup_start >= sema->ip->skip_dedup_end) + return; + if (sema->zcu->builtin_file_idx == UINT32_MAX) return; uint32_t builtin_ns = sema->zcu->file_namespaces[sema->zcu->builtin_file_idx]; - // Shard simulation: clear builtins 0-4 state so they get re-resolved - // as fresh entries during main analysis. The skip_dedup range is - // already set at preamble end (see preamble setup). Clear ALL navs - // whose resolved_type points into the preamble shard, so that - // ensureNavValUpToDate re-resolves them (including CC sub-type navs - // like X86SysV etc.). Also clear struct/union/layout fully-resolved - // flags so resolveUnionFullyC/resolveStructFullyC re-run for the - // main shard — this creates CC enum_tag entries and struct field - // layouts that Zig's per-shard IP emits during main analysis. - if (sema->zcu->preamble_memoized_end - > sema->zcu->preamble_memoized_start) { - // Clear builtins 0-14 values so they're re-resolved fresh in - // main analysis. Matches Zig's per-shard isolation where main - // analysis creates fresh copies of all CC-phase builtins. - for (int i = 0; i < 15 && i < NUM_BUILTIN_DECL_MAIN; i++) - sema->zcu->builtin_decl_values[i] = IP_INDEX_NONE; + // Shard simulation: clear ALL builtin values and nav state so + // everything gets re-resolved as fresh entries in the main shard. + // Matches Zig's sharded IP where main shard starts fresh. + for (int i = 0; i < NUM_BUILTIN_DECL_MAIN; i++) + sema->zcu->builtin_decl_values[i] = IP_INDEX_NONE; - // Clear nav resolved_type for ALL navs resolved during preamble. - // Simulates Zig's per-shard isolation: main shard starts fresh. - for (uint32_t n = 0; n < sema->ip->nav_count; n++) { - Nav* nav_ptr = ipGetNav(sema->ip, n); - if (nav_ptr->resolved_type != IP_INDEX_NONE - && nav_ptr->resolved_type >= sema->ip->skip_dedup_start - && nav_ptr->resolved_type < sema->ip->skip_dedup_end) { - nav_ptr->resolved_type = IP_INDEX_NONE; - } + // Clear nav resolved_type for ALL navs resolved during preamble. + // Simulates Zig's per-shard isolation: main shard starts fresh. + for (uint32_t n = 0; n < sema->ip->nav_count; n++) { + Nav* nav_ptr = ipGetNav(sema->ip, n); + if (nav_ptr->resolved_type != IP_INDEX_NONE + && nav_ptr->resolved_type >= sema->ip->skip_dedup_start + && nav_ptr->resolved_type < sema->ip->skip_dedup_end) { + nav_ptr->resolved_type = IP_INDEX_NONE; } - - // Clear all struct/union/layout resolution flags so main analysis - // re-resolves all types in the main shard (matches Zig sharding). - memset(sema->zcu->struct_layout_resolved, 0, - sizeof(sema->zcu->struct_layout_resolved)); - memset(sema->zcu->struct_fully_resolved, 0, - sizeof(sema->zcu->struct_fully_resolved)); - memset(sema->zcu->union_fully_resolved, 0, - sizeof(sema->zcu->union_fully_resolved)); } - // Resolve builtins 0-14 (Signedness through BranchHint): - // ensureNavValUpToDate + resolveTypeFullyC per builtin. - // Builtins 15-35 are handled by ensureFullMemoizedStateC after this. - for (int i = 0; i < 15 && i < NUM_BUILTIN_DECL_MAIN; i++) { - if (sema->zcu->builtin_decl_values[i] != IP_INDEX_NONE) { + // Clear struct/union/layout resolution flags so main analysis + // re-resolves all types in the main shard (matches Zig sharding). + memset(sema->zcu->struct_layout_resolved, 0, + sizeof(sema->zcu->struct_layout_resolved)); + memset(sema->zcu->struct_fully_resolved, 0, + sizeof(sema->zcu->struct_fully_resolved)); + memset(sema->zcu->union_fully_resolved, 0, + sizeof(sema->zcu->union_fully_resolved)); + + // Single pass over ALL builtins 0-35, matching Zig's + // analyzeMemoizedState which processes all .main-stage builtins + // in one iteration. Ported from Sema.zig line 37531. + for (int i = 0; i < NUM_BUILTIN_DECL_MAIN; i++) { + if (sema->zcu->builtin_decl_values[i] != IP_INDEX_NONE) continue; - } const BuiltinDeclEntry* entry = &s_builtin_decl_entries[i]; uint32_t lookup_ns; @@ -1289,67 +1275,6 @@ void ensureCcMemoizedStateC(Sema* sema) { } } -void ensureFullMemoizedStateC(Sema* sema) { - // First resolve CC-phase builtins 0-14 (including preamble setup). - // Matches Zig's ensureMemoizedStateUpToDate(.main) which processes - // builtins 0-14 (CC phase) then builtins 15-35 (Type phase) in one pass. - ensureCcMemoizedStateC(sema); - - if (sema->zcu->full_memoized_resolved) - return; - sema->zcu->full_memoized_resolved = true; - - if (sema->zcu->builtin_file_idx == UINT32_MAX) - return; - uint32_t builtin_ns - = sema->zcu->file_namespaces[sema->zcu->builtin_file_idx]; - - // Resolve builtins 15-35 (Type and sub-types): ensureNavValUpToDate - // + resolveTypeFullyC per builtin. Matches Zig's analyzeMemoizedState - // which processes all .main-stage builtins in one pass. - for (int i = 15; i < NUM_BUILTIN_DECL_MAIN; i++) { - if (sema->zcu->builtin_decl_values[i] != IP_INDEX_NONE) - continue; - - const BuiltinDeclEntry* entry = &s_builtin_decl_entries[i]; - uint32_t lookup_ns; - if (entry->parent_idx < 0) { - lookup_ns = builtin_ns; - } else { - InternPoolIndex parent_type - = sema->zcu->builtin_decl_values[entry->parent_idx]; - if (parent_type == IP_INDEX_NONE) - continue; - lookup_ns = findNamespaceForType(sema, parent_type); - if (lookup_ns == UINT32_MAX) - continue; - } - - uint32_t nav = findNavInNamespace(sema, lookup_ns, entry->lookup_name); - if (nav == UINT32_MAX) - continue; - - InternPoolIndex val = ensureNavValUpToDate(sema, nav); - if (val == IP_INDEX_NONE) - continue; - - // Create ptr_type + ptr_nav matching Zig's analyzeNavRefInner. - { - InternPoolIndex child = entry->is_type ? IP_INDEX_TYPE_TYPE : val; - InternPoolIndex ptr_ty = internPtrConst(sema, child); - (void)internNavPtr(sema, ptr_ty, nav); - } - - sema->zcu->builtin_decl_values[i] = val; - - // Immediately resolveFully for type builtins, matching Zig's - // analyzeMemoizedState line 37560: - // uncoerced_val.toType().resolveFully(pt). - if (entry->is_type) - resolveTypeFullyC(sema, val); - } -} - InternPoolIndex getBuiltinTypeC(Sema* sema, int builtin_idx) { ensureFullMemoizedStateC(sema); return sema->zcu->builtin_decl_values[builtin_idx]; diff --git a/stage0/zcu_per_thread.h b/stage0/zcu_per_thread.h index 80608a4009..24838fcea6 100644 --- a/stage0/zcu_per_thread.h +++ b/stage0/zcu_per_thread.h @@ -39,13 +39,8 @@ void analyzeMemoizedStateC(struct Sema* sema); // (matches PerThread.ensureNavValUpToDate) InternPoolIndex ensureNavValUpToDate(struct Sema* sema, uint32_t nav_idx); -// Resolve builtins 0-14 (.main CC phase, analogous to the CC stage -// of PerThread.analyzeMemoizedState). Called when cc body is evaluated. -void ensureCcMemoizedStateC(struct Sema* sema); - -// Resolve all 36 .main-stage builtins (calls ensureCcMemoizedStateC -// for 0-14 then resolves 15-35). -// (matches PerThread.ensureMemoizedStateUpToDate) +// Resolve all 36 .main-stage builtins in a single pass. +// (matches PerThread.ensureMemoizedStateUpToDate / analyzeMemoizedState) void ensureFullMemoizedStateC(struct Sema* sema); // Handle @import. (matches PerThread.doImport)