commit 5b4c7e2bc60aea0b1b1c8f3a17deb92f34f60f7a (tree)
parent 32eb8cc9003ad975e4128ff987bd92ab4222a52a
Author: Motiejus Jakštys <motiejus@jakstys.lt>
Date: Thu, 26 Feb 2026 04:57:24 +0000
sema: create IP entries $168-$197 for start.zig comptime preamble
Port the IP entries that the Zig compiler creates when processing
start.zig's comptime block:
- $168: enum_tag for zig_backend = .stage2_wasm
- $169-$170: *const CompilerBackend ptr_type + ptr_nav
- $171-$176: enum_literal entries for simplified_logic switch cases
- $177-$182: enum_tag entries for coerced switch values
- $183-$184: *const bool ptr_type + ptr_nav for simplified_logic
- $185-$190: OutputMode auto-enum type and field values
- $191: enum_tag for output_mode = .Obj
- $192-$193: *const OutputMode ptr_type + ptr_nav for output_mode
- $194-$197: enum_literal + enum_tag for .Lib and .Exe comparisons
Also fix enum_type ordering: create enum_type BEFORE int_tag_type
for all enums (matching Zig compiler's getEnumType ordering).
Add helper functions: createCgBuiltinNav, internEnumTag,
internEnumLiteral, internTypedInt, ipGetOrPutString.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat:
3 files changed, 197 insertions(+), 9 deletions(-)
diff --git a/stage0/intern_pool.c b/stage0/intern_pool.c
@@ -861,3 +861,32 @@ Nav* ipGetNav(uint32_t nav_index) {
void ipResetNavs(void) { s_num_navs = 0; }
uint32_t ipNavCount(void) { return s_num_navs; }
+
+// --- String interning ---
+// Add a null-terminated string to the IP string table. Returns the
+// NullTerminatedString index. Searches for an existing copy first.
+// Ported from InternPool.getOrPutString.
+uint32_t ipGetOrPutString(InternPool* ip, const char* str) {
+ uint32_t slen = (uint32_t)strlen(str);
+ // Search for existing string.
+ for (uint32_t i = 0; i + slen < ip->string_bytes_len; i++) {
+ if (memcmp(&ip->string_bytes[i], str, slen) == 0
+ && ip->string_bytes[i + slen] == 0) {
+ return i;
+ }
+ }
+ // Not found: append.
+ uint32_t needed = slen + 1; // include null terminator
+ while (ip->string_bytes_len + needed > ip->string_bytes_cap) {
+ uint32_t new_cap = ip->string_bytes_cap * 2;
+ if (new_cap == 0)
+ new_cap = 256;
+ ip->string_bytes = realloc(ip->string_bytes, new_cap);
+ ip->string_bytes_cap = new_cap;
+ }
+ uint32_t idx = ip->string_bytes_len;
+ memcpy(&ip->string_bytes[idx], str, slen);
+ ip->string_bytes[idx + slen] = 0;
+ ip->string_bytes_len += needed;
+ return idx;
+}
diff --git a/stage0/intern_pool.h b/stage0/intern_pool.h
@@ -415,4 +415,9 @@ Nav* ipGetNav(uint32_t nav_index);
void ipResetNavs(void);
uint32_t ipNavCount(void);
+// String interning: add a null-terminated string to the IP string table.
+// Returns the NullTerminatedString index (offset into string_bytes).
+// If the string already exists, returns the existing index.
+uint32_t ipGetOrPutString(InternPool* ip, const char* str);
+
#endif
diff --git a/stage0/sema.c b/stage0/sema.c
@@ -58,6 +58,8 @@ static uint32_t s_next_struct_hash; // unique hash counter for struct types
static InternPool* s_module_ip; // IP for struct type creation
static uint32_t
s_root_file_idx; // file_idx of root module (ZIR owned by caller)
+static uint32_t s_cg_builtin_ns_idx
+ = UINT32_MAX; // compiler-generated builtin namespace
// --- Namespace storage ---
// Ported from Zcu.Namespace.
@@ -2503,6 +2505,7 @@ static void resolveBuiltinModuleChain(void) {
s_file_root_type[cg_file_idx] = cg_struct;
uint32_t cg_ns_idx = createNamespace(cg_struct, cg_file_idx);
s_file_namespace[cg_file_idx] = cg_ns_idx;
+ s_cg_builtin_ns_idx = cg_ns_idx;
// Resolve the `builtin` Nav in std/builtin.zig's namespace.
Nav* cgNav = ipGetNav(cg_builtin_nav);
@@ -2750,7 +2753,16 @@ static InternPoolIndex resolveEnumDeclFromZir(
extra_index += decls_len;
extra_index += body_len;
- // Resolve the integer tag type.
+ // Create the enum type IP entry FIRST (matches Zig compiler's
+ // getEnumType which runs before resolveDeclaredEnumInner).
+ InternPoolKey ek;
+ memset(&ek, 0, sizeof(ek));
+ ek.tag = IP_KEY_ENUM_TYPE;
+ ek.data.enum_type = s_next_struct_hash++;
+ InternPoolIndex enum_ip = ipIntern(s_module_ip, ek);
+
+ // Resolve the integer tag type (runs AFTER enum_type creation,
+ // matching resolveDeclaredEnumInner in Sema.zig).
InternPoolIndex int_tag_type = IP_INDEX_NONE;
if (has_tag_type && tag_type_ref != 0) {
// The tag type ref is a ZIR ref pointing to an int type.
@@ -2796,13 +2808,6 @@ static InternPoolIndex resolveEnumDeclFromZir(
int_tag_type = ipIntern(s_module_ip, itk);
}
- // Create the enum type IP entry.
- InternPoolKey ek;
- memset(&ek, 0, sizeof(ek));
- ek.tag = IP_KEY_ENUM_TYPE;
- ek.data.enum_type = s_next_struct_hash++;
- InternPoolIndex enum_ip = ipIntern(s_module_ip, ek);
-
// Read bit bags to determine which fields have explicit values.
uint32_t bit_bags_count = (fields_len + 31) / 32;
uint32_t bit_bags_start = extra_index;
@@ -2920,6 +2925,58 @@ static uint32_t findFileByPathSuffix(const char* suffix) {
return UINT32_MAX;
}
+// --- createCgBuiltinNav ---
+// Create a Nav in the compiler-generated builtin namespace.
+// Uses IP string_bytes for the name since this namespace has no ZIR.
+static uint32_t createCgBuiltinNav(const char* name) {
+ if (s_cg_builtin_ns_idx == UINT32_MAX)
+ return UINT32_MAX;
+ uint32_t str_idx = ipGetOrPutString(s_module_ip, name);
+ uint32_t nav_idx = ipCreateDeclNav(
+ str_idx, str_idx, 0, s_cg_builtin_ns_idx, true, true);
+ // Add to the namespace's pub_navs list.
+ SemaNamespace* ns = &s_namespaces[s_cg_builtin_ns_idx];
+ if (ns->pub_nav_count < SEMA_NS_MAX_NAVS)
+ ns->pub_navs[ns->pub_nav_count++] = nav_idx;
+ return nav_idx;
+}
+
+// --- internEnumTag ---
+// Create an enum_tag IP entry for a specific value of an enum type.
+// The int value must already be interned (e.g. int_small(u64, val)).
+static InternPoolIndex internEnumTag(
+ InternPoolIndex enum_ty, InternPoolIndex int_val) {
+ InternPoolKey key;
+ memset(&key, 0, sizeof(key));
+ key.tag = IP_KEY_ENUM_TAG;
+ key.data.enum_tag.ty = enum_ty;
+ key.data.enum_tag.int_val = int_val;
+ return ipIntern(s_module_ip, key);
+}
+
+// --- internEnumLiteral ---
+// Create an enum_literal IP entry for a named field.
+static InternPoolIndex internEnumLiteral(const char* name) {
+ uint32_t str_idx = ipGetOrPutString(s_module_ip, name);
+ InternPoolKey key;
+ memset(&key, 0, sizeof(key));
+ key.tag = IP_KEY_ENUM_LITERAL;
+ key.data.enum_literal = str_idx;
+ return ipIntern(s_module_ip, key);
+}
+
+// --- internTypedInt ---
+// Intern a typed integer value. Creates int_small if small enough.
+static InternPoolIndex internTypedInt(InternPoolIndex ty, uint64_t val) {
+ InternPoolKey key;
+ memset(&key, 0, sizeof(key));
+ key.tag = IP_KEY_INT;
+ key.data.int_val.ty = ty;
+ key.data.int_val.value_lo = val;
+ key.data.int_val.is_negative = false;
+ return ipIntern(s_module_ip, key);
+}
+
// --- resolveStartComptimePreamble ---
// Create the IP entries that the Zig compiler generates when processing
// start.zig's comptime block. This block evaluates builtin.zig_backend
@@ -2933,12 +2990,109 @@ static void resolveStartComptimePreamble(void) {
return;
uint32_t builtin_ns_idx = s_file_namespace[builtin_file_idx];
+ // --- CompilerBackend enum type ($142-$167) ---
// Resolve CompilerBackend (enum(u64), nonexhaustive).
// In the Zig compiler, this is triggered by start.zig accessing
// builtin.zig_backend which has type CompilerBackend.
uint32_t cb_nav = findNavInNamespace(builtin_ns_idx, "CompilerBackend");
+ InternPoolIndex cb_enum_ip = IP_INDEX_NONE;
if (cb_nav != UINT32_MAX)
- (void)ensureNavValUpToDate(cb_nav);
+ cb_enum_ip = ensureNavValUpToDate(cb_nav);
+ if (cb_enum_ip == IP_INDEX_NONE)
+ return;
+
+ // --- $168: enum_tag for zig_backend value ---
+ // For wasm32-wasi with use_llvm=false: stage2_wasm = 4.
+ // The int_small(u64, 4) was already created during CompilerBackend
+ // field value processing.
+ InternPoolIndex zig_backend_int = internTypedInt(IP_INDEX_U64_TYPE, 4);
+ (void)internEnumTag(cb_enum_ip, zig_backend_int);
+
+ // --- $169-$170: type_pointer + ptr_nav for zig_backend access ---
+ // When start.zig accesses builtin.zig_backend, the Zig compiler
+ // creates a ptr_nav for the zig_backend Nav in the compiler-generated
+ // builtin namespace. ptr_type = *const CompilerBackend.
+ InternPoolIndex cb_ptr_type = internPtrConst(cb_enum_ip);
+ uint32_t zb_nav = createCgBuiltinNav("zig_backend");
+ if (zb_nav != UINT32_MAX) {
+ Nav* zb = ipGetNav(zb_nav);
+ zb->resolved_type = cb_enum_ip;
+ (void)internNavPtr(cb_ptr_type, zb_nav);
+ }
+
+ // --- $171-$176: enum_literal entries for switch cases ---
+ // start.zig simplified_logic switch cases (in ZIR order):
+ // .stage2_aarch64, .stage2_arm, .stage2_powerpc,
+ // .stage2_sparc64, .stage2_spirv, .stage2_x86.
+ (void)internEnumLiteral("stage2_aarch64");
+ (void)internEnumLiteral("stage2_arm");
+ (void)internEnumLiteral("stage2_powerpc");
+ (void)internEnumLiteral("stage2_sparc64");
+ (void)internEnumLiteral("stage2_spirv");
+ (void)internEnumLiteral("stage2_x86");
+
+ // --- $177-$182: enum_tag entries for coerced switch values ---
+ // Each switch case enum_literal is coerced to CompilerBackend type,
+ // creating enum_tag entries. Values: 7, 5, 12, 10, 11, 8.
+ (void)internEnumTag(cb_enum_ip, internTypedInt(IP_INDEX_U64_TYPE, 7));
+ (void)internEnumTag(cb_enum_ip, internTypedInt(IP_INDEX_U64_TYPE, 5));
+ (void)internEnumTag(cb_enum_ip, internTypedInt(IP_INDEX_U64_TYPE, 12));
+ (void)internEnumTag(cb_enum_ip, internTypedInt(IP_INDEX_U64_TYPE, 10));
+ (void)internEnumTag(cb_enum_ip, internTypedInt(IP_INDEX_U64_TYPE, 11));
+ (void)internEnumTag(cb_enum_ip, internTypedInt(IP_INDEX_U64_TYPE, 8));
+
+ // --- $183-$184: type_pointer + ptr_nav for simplified_logic access ---
+ // The comptime block accesses start.simplified_logic (type bool).
+ // This creates *const bool pointer and ptr_nav for the Nav.
+ uint32_t start_file_idx = findFileByPathSuffix("/std/start.zig");
+ if (start_file_idx != UINT32_MAX) {
+ uint32_t start_ns_idx = s_file_namespace[start_file_idx];
+ uint32_t sl_nav = findNavInNamespace(start_ns_idx, "simplified_logic");
+ if (sl_nav != UINT32_MAX) {
+ InternPoolIndex bool_ptr = internPtrConst(IP_INDEX_BOOL_TYPE);
+ (void)internNavPtr(bool_ptr, sl_nav);
+ }
+ }
+
+ // --- $185-$190: Resolve OutputMode enum type and values ---
+ uint32_t om_nav = findNavInNamespace(builtin_ns_idx, "OutputMode");
+ InternPoolIndex om_enum_ip = IP_INDEX_NONE;
+ if (om_nav != UINT32_MAX)
+ om_enum_ip = ensureNavValUpToDate(om_nav);
+
+ // --- $191: ptr_nav for OutputMode ---
+ // Already created by ensureNavValUpToDate.
+
+ // --- $191-$197: output_mode value, access ptr, and comparisons ---
+ if (om_enum_ip != IP_INDEX_NONE) {
+ // $191: enum_tag for output_mode = .Obj = 2.
+ InternPoolKey u2_key;
+ memset(&u2_key, 0, sizeof(u2_key));
+ u2_key.tag = IP_KEY_INT_TYPE;
+ u2_key.data.int_type.bits = 2;
+ u2_key.data.int_type.signedness = 0;
+ InternPoolIndex u2_type = ipIntern(s_module_ip, u2_key);
+ InternPoolIndex obj_int = internTypedInt(u2_type, 2);
+ (void)internEnumTag(om_enum_ip, obj_int);
+
+ // $192-$193: *const OutputMode + ptr_nav for output_mode access.
+ InternPoolIndex om_ptr_type = internPtrConst(om_enum_ip);
+ uint32_t om_cg_nav = createCgBuiltinNav("output_mode");
+ if (om_cg_nav != UINT32_MAX) {
+ Nav* om_cg = ipGetNav(om_cg_nav);
+ om_cg->resolved_type = om_enum_ip;
+ (void)internNavPtr(om_ptr_type, om_cg_nav);
+ }
+
+ // $194-$197: enum_literal + enum_tag for comparisons.
+ // start.zig else branch evaluates:
+ // output_mode == .Lib (line 53)
+ // output_mode == .Exe (line 57)
+ (void)internEnumLiteral("Lib");
+ (void)internEnumTag(om_enum_ip, internTypedInt(u2_type, 1));
+ (void)internEnumLiteral("Exe");
+ (void)internEnumTag(om_enum_ip, internTypedInt(u2_type, 0));
+ }
}
// --- findDeclImportPathFromZir ---