zig

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

commit 0f68ac18ed55888abae3e455ee82e62811a737aa (tree)
parent edf1ec738ef0b6601a4ad55fb58e8a72fe5d27b4
Author: Motiejus <motiejus@jakstys.lt>
Date:   Fri, 27 Feb 2026 16:25:15 +0000

sema: extract CPU model features from ZIR decl_literals

Add IP_KEY_AGGREGATE and IP_KEY_UNION_VALUE hash/equality support in
intern_pool.c. These key types were falling through to the default
memcmp, which could produce incorrect results.

In triggerArchModuleCascade, extract the CPU model's features from ZIR
by scanning for ZIR_INST_DECL_LITERAL instructions between the CALL
and STRUCT_INIT instructions in the model's value body. This creates
IP entries [707-720]: feature array type, enum_tags for each feature,
aggregate, and a pointer/slice chain.

The key insight is that AstGen encodes `.bulk_memory_opt` etc. as
decl_literal (tag 142), not enum_literal (tag 141) — they resolve
against the expected type's declarations in a typed context.

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

Diffstat:
Mstage0/intern_pool.c | 10++++++++++
Mstage0/sema.c | 163++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 172 insertions(+), 1 deletion(-)

diff --git a/stage0/intern_pool.c b/stage0/intern_pool.c @@ -156,6 +156,12 @@ static uint32_t ipHashKey(const InternPoolKey* key) { case IP_KEY_SLICE: h = ipHashCombine(h, key->data.slice); break; + case IP_KEY_AGGREGATE: + h = ipHashCombine(h, key->data.aggregate); + break; + case IP_KEY_UNION_VALUE: + h = ipHashCombine(h, key->data.union_value); + break; default: /* For other tag types, just use the tag hash. */ break; @@ -275,6 +281,10 @@ static bool ipKeysEqual(const InternPoolKey* a, const InternPoolKey* b) { == b->data.func_type.noalias_bits; case IP_KEY_SLICE: return a->data.slice == b->data.slice; + case IP_KEY_AGGREGATE: + return a->data.aggregate == b->data.aggregate; + case IP_KEY_UNION_VALUE: + return a->data.union_value == b->data.union_value; default: /* Fallback: memcmp the entire data union. */ return memcmp(&a->data, &b->data, sizeof(a->data)) == 0; diff --git a/stage0/sema.c b/stage0/sema.c @@ -5753,12 +5753,13 @@ static void triggerArchModuleCascade( // cCallingConvention cascade (demand-driven from loadComptimePtrInner // dereferencing cpu.model which is *const CpuModel). // Find it in the cpu struct's namespace using s_target_cpu_model_name. + uint32_t model_nav = UINT32_MAX; if (cpu_nav != UINT32_MAX && s_target_cpu_model_name) { const Nav* cpu_n = ipGetNav(cpu_nav); if (cpu_n->resolved_type != IP_INDEX_NONE) { uint32_t cpu_ns = findNamespaceForType(cpu_n->resolved_type); if (cpu_ns != UINT32_MAX) { - uint32_t model_nav + model_nav = findNavInNamespace(cpu_ns, s_target_cpu_model_name); if (model_nav != UINT32_MAX) (void)resolveNavRef(model_nav); @@ -5914,6 +5915,166 @@ static void triggerArchModuleCascade( } } + // $707-$720: Evaluate the CPU model's feature set. + // The Zig compiler evaluates featureSet(&[_]Feature{...}) from the + // CPU model's struct literal. This creates the feature array type, + // enum_tag entries for each feature, an aggregate for the array value, + // and a pointer/slice chain. + // Ported from the comptime evaluation of CpuModel.features init. + if (model_nav != UINT32_MAX && feature != UINT32_MAX) { + InternPoolIndex feature_type = ipGetNav(feature)->resolved_type; + if (feature_type != IP_INDEX_NONE) { + // Get the Feature enum's ZIR instruction (for field lookup). + const Nav* feat_n = ipGetNav(feature); + uint32_t feat_file = s_namespaces[feat_n->namespace_idx].file_idx; + const Zir* feat_zir = &s_loaded_modules[feat_file].zir; + const uint32_t* feat_vb = NULL; + uint32_t feat_vb_len = 0; + getValueBodyFromZir( + feat_zir, feat_n->zir_index, &feat_vb, &feat_vb_len); + uint32_t enum_inst = UINT32_MAX; + for (uint32_t i = 0; i < feat_vb_len; i++) { + if (feat_vb[i] < feat_zir->inst_len + && feat_zir->inst_tags[feat_vb[i]] == ZIR_INST_EXTENDED + && feat_zir->inst_datas[feat_vb[i]].extended.opcode + == ZIR_EXT_ENUM_DECL) { + enum_inst = feat_vb[i]; + break; + } + } + + if (enum_inst != UINT32_MAX) { + // Get the model nav's ZIR value body. + const Nav* model_n = ipGetNav(model_nav); + uint32_t model_file + = s_namespaces[model_n->namespace_idx].file_idx; + const Zir* model_zir = &s_loaded_modules[model_file].zir; + const uint32_t* mvb = NULL; + uint32_t mvb_len = 0; + getValueBodyFromZir( + model_zir, model_n->zir_index, &mvb, &mvb_len); + + if (mvb && mvb_len > 0) { + // Find the call instruction and the next + // top-level instruction in the value body. + // The enum_literal instructions for the feature + // array are between these two (as sub-insts + // not in the value body's top-level list). + uint32_t call_inst = UINT32_MAX; + uint32_t after_call = UINT32_MAX; + for (uint32_t i = 0; i < mvb_len; i++) { + uint32_t mi = mvb[i]; + if (mi < model_zir->inst_len + && model_zir->inst_tags[mi] == ZIR_INST_CALL) { + call_inst = mi; + if (i + 1 < mvb_len) + after_call = mvb[i + 1]; + break; + } + } + + // Phase 1: Scan ZIR instructions between the + // call and the next top-level inst for + // decl_literal instructions matching Feature + // enum fields. + uint32_t feat_indices[64]; + uint32_t feat_count = 0; + if (call_inst != UINT32_MAX && after_call != UINT32_MAX) { + for (uint32_t zi = call_inst + 1; zi < after_call; + zi++) { + if (zi >= model_zir->inst_len) + continue; + if (model_zir->inst_tags[zi] + != ZIR_INST_DECL_LITERAL) + continue; + // decl_literal uses pl_node; payload is + // Field {lhs: Ref, field_name_start: u32}. + uint32_t pi = model_zir->inst_datas[zi] + .pl_node.payload_index; + uint32_t nsi = model_zir->extra[pi + 1]; + const char* fn + = (const char*)&model_zir->string_bytes[nsi]; + uint32_t fidx + = findEnumFieldByName(feat_zir, enum_inst, fn); + if (fidx != UINT32_MAX && feat_count < 64) + feat_indices[feat_count++] = fidx; + } + } + if (feat_count > 0) { + // $707: type_array_small [N]Feature. + InternPoolKey ak; + memset(&ak, 0, sizeof(ak)); + ak.tag = IP_KEY_ARRAY_TYPE; + ak.data.array_type.len = feat_count; + ak.data.array_type.child = feature_type; + ak.data.array_type.sentinel = IP_INDEX_NONE; + InternPoolIndex arr_type = ipIntern(s_module_ip, ak); + + // $708-$714: enum_tag for each feature. + for (uint32_t i = 0; i < feat_count; i++) { + InternPoolIndex iv = getEnumFieldIntVal( + feat_zir, enum_inst, feat_indices[i]); + if (iv != IP_INDEX_NONE) + (void)internEnumTag(feature_type, iv); + } + + // $715: aggregate (array value). + InternPoolKey agk; + memset(&agk, 0, sizeof(agk)); + agk.tag = IP_KEY_AGGREGATE; + agk.data.aggregate = arr_type; + InternPoolIndex agg = ipIntern(s_module_ip, agk); + + // $716: *const [N]Feature. + InternPoolIndex arr_ptr = internPtrConst(arr_type); + + // $717: ptr_uav. + InternPoolKey uav; + memset(&uav, 0, sizeof(uav)); + uav.tag = IP_KEY_PTR_UAV; + uav.data.ptr_uav.ty = arr_ptr; + uav.data.ptr_uav.val = agg; + InternPoolIndex uav_ptr = ipIntern(s_module_ip, uav); + + // $718: ptr_uav_aligned. + InternPoolKey uaa; + memset(&uaa, 0, sizeof(uaa)); + uaa.tag = IP_KEY_PTR_UAV_ALIGNED; + uaa.data.ptr_uav_aligned.ty = arr_ptr; + uaa.data.ptr_uav_aligned.val = agg; + uaa.data.ptr_uav_aligned.orig_ty = uav_ptr; + InternPoolIndex aligned = ipIntern(s_module_ip, uaa); + + // $719: int_usize(N). + InternPoolIndex len_val + = internTypedInt(IP_INDEX_USIZE_TYPE, feat_count); + + // $720: ptr_slice. + // Slice type []const Feature should already + // exist from FeatureSetFns evaluation ($702). + InternPoolKey slk; + memset(&slk, 0, sizeof(slk)); + slk.tag = IP_KEY_PTR_TYPE; + slk.data.ptr_type.child = feature_type; + slk.data.ptr_type.sentinel = IP_INDEX_NONE; + slk.data.ptr_type.flags + = PTR_FLAGS_SIZE_SLICE | PTR_FLAGS_IS_CONST; + slk.data.ptr_type.packed_offset = 0; + InternPoolIndex slice_ty = ipIntern(s_module_ip, slk); + + InternPoolKey psk; + memset(&psk, 0, sizeof(psk)); + psk.tag = IP_KEY_PTR_SLICE; + psk.data.ptr_slice.ty = slice_ty; + psk.data.ptr_slice.ptr = aligned; + psk.data.ptr_slice.len = len_val; + (void)ipIntern(s_module_ip, psk); + } + } + } + } + } + (void)target_file_idx; }