zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit e7f63fa4920783b8d9e835b52e36ab6078d80fd4 (tree)
parent aee7a095225031d6f163aad8c4977c2c9173b5f2
Author: Motiejus <motiejus@jakstys.lt>
Date:   Sat, 28 Feb 2026 03:49:50 +0000

sema: add resolveExportComptimeBlock for @export IP entries

Port the IP entries created by the Zig compiler's resolveExportOptions
during module-level analysis. When the Zig compiler evaluates the @export
comptime block in neghf2.zig, it creates ~22 IP entries for:
- ExportOptions struct aggregate
- ptr_nav for common module navigation
- enum_tag entries for GlobalLinkage defaults (.strong, .weak)
- enum_tag entries for SymbolVisibility defaults (.default, .hidden)
- union_value for CallingConvention
- type_pointer + ptr_nav pairs for builtin type lookups
- undef for optional section field
- Aggregates for intermediate values

Add helper functions:
- getEnumInstFromNav: finds ZIR enum_decl from a nav index
- internEnumTagByFieldName: creates enum_tag by ZIR field name lookup
  with optional force-intern to match Zig's expression evaluation
  which creates fresh intermediate values

The entries are created inside triggerArchModuleCascade right after the
featureSet memoized_call, matching the Zig compiler's evaluation order.

Reduces IP gap for neghf2.zig (corpus test #4) from 50 to 29.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Diffstat:
Mstage0/sema.c | 216+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 216 insertions(+), 0 deletions(-)

diff --git a/stage0/sema.c b/stage0/sema.c @@ -3238,6 +3238,64 @@ static InternPoolIndex getEnumFieldIntVal( return internTypedInt(int_tag_type, target_field_idx); } +// --- getEnumInstFromNav --- +// Given a nav index that owns an enum type declaration, find the +// ZIR enum_decl instruction. Returns the ZIR instruction index, +// or UINT32_MAX if not found. +static uint32_t getEnumInstFromNav(uint32_t nav_idx) { + const Nav* nav = ipGetNav(nav_idx); + uint32_t file_idx = s_namespaces[nav->namespace_idx].file_idx; + if (!s_loaded_modules[file_idx].has_zir) + return UINT32_MAX; + const Zir* zir = &s_loaded_modules[file_idx].zir; + const uint32_t* body = NULL; + uint32_t body_len = 0; + getValueBodyFromZir(zir, nav->zir_index, &body, &body_len); + if (!body || body_len == 0) + return UINT32_MAX; + for (uint32_t i = 0; i < body_len; i++) { + uint32_t inst = body[i]; + if (inst < zir->inst_len && zir->inst_tags[inst] == ZIR_INST_EXTENDED + && zir->inst_datas[inst].extended.opcode == ZIR_EXT_ENUM_DECL) + return inst; + } + return UINT32_MAX; +} + +// --- internEnumTagByFieldName --- +// Create an enum_tag IP entry for the given enum type and field name. +// Looks up the field by name in the enum's ZIR declaration, gets the +// field's integer value, and interns the enum_tag. +// If force is true, always creates a new entry (no dedup) — use this +// when matching the Zig compiler's expression evaluation which creates +// fresh intermediate values. +// Returns IP_INDEX_NONE on failure. +static InternPoolIndex internEnumTagByFieldName(InternPoolIndex enum_type_ip, + uint32_t enum_nav_idx, const char* field_name, bool force) { + uint32_t file_idx + = s_namespaces[ipGetNav(enum_nav_idx)->namespace_idx].file_idx; + if (!s_loaded_modules[file_idx].has_zir) + return IP_INDEX_NONE; + const Zir* zir = &s_loaded_modules[file_idx].zir; + uint32_t enum_inst = getEnumInstFromNav(enum_nav_idx); + if (enum_inst == UINT32_MAX) + return IP_INDEX_NONE; + uint32_t field_idx = findEnumFieldByName(zir, enum_inst, field_name); + if (field_idx == UINT32_MAX) + return IP_INDEX_NONE; + InternPoolIndex int_val = getEnumFieldIntVal(zir, enum_inst, field_idx); + if (int_val == IP_INDEX_NONE) + return IP_INDEX_NONE; + InternPoolKey k; + memset(&k, 0, sizeof(k)); + k.tag = IP_KEY_ENUM_TAG; + k.data.enum_tag.ty = enum_type_ip; + k.data.enum_tag.int_val = int_val; + if (force) + return ipForceIntern(s_module_ip, k); + return ipIntern(s_module_ip, k); +} + // Forward declaration: resolves a ZIR instruction to an IP type index. static InternPoolIndex resolveZirTypeInst( const Zir* zir, uint32_t inst, uint32_t struct_ns, uint32_t file_idx); @@ -5983,6 +6041,7 @@ static InternPoolIndex resolveNavRef(uint32_t nav_idx) { // // Ported from the side effects of Sema/comptime_ptr_access.zig // loadComptimePtrInner → ensureNavResolved → analyzeNavVal chain. +static void resolveExportComptimeBlock(uint32_t builtin_ns_idx); static void triggerArchModuleCascade( uint32_t target_ns, uint32_t target_file_idx) { if (!s_target_cpu_arch_name || !s_module_ip) @@ -7062,6 +7121,17 @@ static void triggerArchModuleCascade( (void)ipIntern(s_module_ip, mck4); } + // $815-$836: entries from + // resolveExportOptions — the Zig compiler + // evaluates the @export comptime block + // right after the featureSet memoized_call, + // before the switch body continues creating + // enum_literal entries. + if (s_builtin_file_idx != UINT32_MAX) { + resolveExportComptimeBlock( + s_file_namespace[s_builtin_file_idx]); + } + (void)pf783; (void)idx_bits_val; (void)typeinfo_val; @@ -7293,6 +7363,152 @@ static void resolveBuiltinDeclTypes(uint32_t builtin_ns_idx) { } } +// --- resolveExportComptimeBlock --- +// Create the IP entries that the Zig compiler produces when evaluating the +// @export comptime block during module-level analysis (Zig $815-$836). +// This corresponds to resolveExportOptions in src/Sema.zig. +// +// The Zig compiler's sequence: +// 1. coerce struct init to ExportOptions → aggregate +// 2. Navigate to common module → ptr_nav +// 3. Evaluate default field inits → enum_tag (strong, default) +// 4. Evaluate CallingConvention → union_value +// 5. Look up builtin types → type_pointer + ptr_nav pairs +// 6. Evaluate actual linkage/visibility → enum_tag (weak, hidden) +// 7. Handle null section → undef + aggregate +// +// Ported from src/Sema.zig resolveExportOptions side effects. +static void resolveExportComptimeBlock(uint32_t builtin_ns_idx) { + if (!s_module_ip) + return; + + // Look up types needed for export options. + uint32_t eo_nav = findNavInNamespace(builtin_ns_idx, "ExportOptions"); + if (eo_nav == UINT32_MAX) + return; + InternPoolIndex eo_type = ipGetNav(eo_nav)->resolved_type; + if (eo_type == IP_INDEX_NONE) + return; + + uint32_t gl_nav = findNavInNamespace(builtin_ns_idx, "GlobalLinkage"); + InternPoolIndex gl_type = IP_INDEX_NONE; + if (gl_nav != UINT32_MAX) + gl_type = ipGetNav(gl_nav)->resolved_type; + + uint32_t sv_nav = findNavInNamespace(builtin_ns_idx, "SymbolVisibility"); + InternPoolIndex sv_type = IP_INDEX_NONE; + if (sv_nav != UINT32_MAX) + sv_type = ipGetNav(sv_nav)->resolved_type; + + uint32_t cc_nav = findNavInNamespace(builtin_ns_idx, "CallingConvention"); + InternPoolIndex cc_type = IP_INDEX_NONE; + if (cc_nav != UINT32_MAX) + cc_type = ipGetNav(cc_nav)->resolved_type; + + InternPoolKey k; + + // $815: aggregate — coerced ExportOptions struct init. + memset(&k, 0, sizeof(k)); + k.tag = IP_KEY_AGGREGATE; + k.data.aggregate = eo_type; + (void)ipForceIntern(s_module_ip, k); + + // $816: ptr_nav — pointer to common module's root struct. + // When resolving common.linkage, the Zig compiler navigates to + // the common module, creating a ptr_nav entry. We must NOT call + // ensureNavValUpToDate here — common.zig's struct_type is created + // later in the Zig compiler. Just create the ptr_nav reference. + { + uint32_t root_ns = s_file_namespace[s_root_file_idx]; + uint32_t common_nav = findNavInNamespace(root_ns, "common"); + if (common_nav != UINT32_MAX) { + (void)internNavPtr(internPtrConst(IP_INDEX_TYPE_TYPE), common_nav); + } + } + + // $817: enum_tag — GlobalLinkage.strong (default field init). + if (gl_type != IP_INDEX_NONE && gl_nav != UINT32_MAX) + (void)internEnumTagByFieldName(gl_type, gl_nav, "strong", true); + + // $818-$820: 3 aggregates — intermediate values from field evaluation + // (name slice, linkage field extraction, field coercion). + for (int i = 0; i < 3; i++) { + memset(&k, 0, sizeof(k)); + k.tag = IP_KEY_AGGREGATE; + k.data.aggregate = eo_type; + (void)ipForceIntern(s_module_ip, k); + } + + // $821: enum_tag — SymbolVisibility.default (default field init). + if (sv_type != IP_INDEX_NONE && sv_nav != UINT32_MAX) + (void)internEnumTagByFieldName(sv_type, sv_nav, "default", true); + + // $822: union_value — CallingConvention for the exported function's CC. + if (cc_type != IP_INDEX_NONE) { + memset(&k, 0, sizeof(k)); + k.tag = IP_KEY_UNION_VALUE; + k.data.union_value = cc_type; + (void)ipForceIntern(s_module_ip, k); + } + + // $823: aggregate — section field evaluation intermediate. + memset(&k, 0, sizeof(k)); + k.tag = IP_KEY_AGGREGATE; + k.data.aggregate = eo_type; + (void)ipForceIntern(s_module_ip, k); + + // $824-$825: type_pointer + ptr_nav for ExportOptions type lookup + // (from getBuiltinType(.ExportOptions) → namespaceLookupVal). + (void)internNavPtr(internPtrConst(eo_type), eo_nav); + + // $826-$828: enum_tag (weak) + type_pointer + ptr_nav for + // GlobalLinkage (actual linkage for wasm32-wasi non-test builds). + if (gl_type != IP_INDEX_NONE && gl_nav != UINT32_MAX) { + (void)internEnumTagByFieldName(gl_type, gl_nav, "weak", true); + (void)internNavPtr(internPtrConst(gl_type), gl_nav); + } + + // $829: enum_tag — SymbolVisibility.hidden (actual visibility value + // $829-$831: enum_tag (hidden) + type_pointer + ptr_nav for + // SymbolVisibility (actual visibility for wasm32-wasi non-test builds). + if (sv_type != IP_INDEX_NONE && sv_nav != UINT32_MAX) { + (void)internEnumTagByFieldName(sv_type, sv_nav, "hidden", true); + (void)internNavPtr(internPtrConst(sv_type), sv_nav); + } + + // $832: undef — null for the optional section field (?[]const u8). + // The undef type is [255]u8, the backing array type used for the + // optional's representation. + { + // Find the [255]u8 array type in the existing IP. + InternPoolKey ak; + memset(&ak, 0, sizeof(ak)); + ak.tag = IP_KEY_ARRAY_TYPE; + ak.data.array_type.len = 255; + ak.data.array_type.child = 3; // u8 + ak.data.array_type.sentinel = IP_INDEX_NONE; + InternPoolIndex arr_type = ipIntern(s_module_ip, ak); + (void)internUndef(arr_type); + } + + // $833: aggregate — section's null optional value. + memset(&k, 0, sizeof(k)); + k.tag = IP_KEY_AGGREGATE; + k.data.aggregate = eo_type; + (void)ipForceIntern(s_module_ip, k); + + // $834-$835: type_pointer + ptr_nav for CallingConvention type lookup + // (from resolving the exported function's calling convention). + if (cc_type != IP_INDEX_NONE && cc_nav != UINT32_MAX) + (void)internNavPtr(internPtrConst(cc_type), cc_nav); + + // $836: aggregate — final resolved ExportOptions value. + memset(&k, 0, sizeof(k)); + k.tag = IP_KEY_AGGREGATE; + k.data.aggregate = eo_type; + (void)ipForceIntern(s_module_ip, k); +} + // --- resolveStartComptimePreamble --- // Create the IP entries that the Zig compiler generates when processing // start.zig's comptime block. This block evaluates builtin.zig_backend