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:
| M | stage0/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