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 <noreply@anthropic.com>
This commit is contained in:
2026-03-08 06:53:54 +00:00
parent 9aa97b3dcd
commit 8151f6dc9b
4 changed files with 49 additions and 127 deletions

View File

@@ -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 {

View File

@@ -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) ---

View File

@@ -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];

View File

@@ -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)