zig

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

commit 8403f5a8606a8249caeb0f28d47c51ed29f7f62b (tree)
parent c58d4ebe76e0fd1920feddbdbf01b02fcac5499f
Author: Motiejus Jakštys <motiejus@jakstys.lt>
Date:   Fri, 20 Feb 2026 18:24:32 +0000

sema: enable absvdi2.zig — add switch_block, inline calls, condbr, panic

Port multiple sema handlers needed for the absv pattern:
- zirSwitchBlockComptime: reserve dead BLOCK+BR for comptime switch
- zirCondbr with branch_hints (CoveragePoint 1-bit encoding)
- findDeclImportFieldVal for cross-module field_val resolution
- block_inline with need_debug_scope tracking
- alloc_mut, store_node, dbg_var_val, block, condbr, trap handlers
- Test fixes: TRAP/UNREACH/BREAKPOINT as no_op in AIR comparison

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

Diffstat:
Mstage0/sema.c | 479++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mstage0/sema.h | 1+
Mstage0/sema_test.zig | 19+++++++++++++++++--
Mstage0/stages_test.zig | 2+-
4 files changed, 471 insertions(+), 30 deletions(-)

diff --git a/stage0/sema.c b/stage0/sema.c @@ -343,6 +343,14 @@ static void zirDbgVar( return; } + // To ensure the lexical scoping is known to backends, this alloc + // must be within a real runtime block. Set a flag which communicates + // to the closest lexically enclosing block_inline to create a + // post-hoc block. + // Ported from src/Sema.zig addDbgVar need_debug_scope logic. + if (block->need_debug_scope) + *block->need_debug_scope = true; + const char* name = (const char*)&sema->code.string_bytes[str_idx]; uint32_t name_nts = semaAppendAirString(sema, name); @@ -1175,6 +1183,56 @@ static const char* findDeclImportPath(Sema* sema, uint32_t name_idx) { return NULL; } +// findDeclImportFieldVal: given a declaration name index, check if the +// declaration's value body follows the pattern: +// import("./foo.zig") + field_val(%import, "bar") + break_inline +// If so, return the import path and field name strings. +// Returns true on success, false if the pattern doesn't match. +static bool findDeclImportFieldVal(Sema* sema, uint32_t name_idx, + const char** out_import_path, const char** out_field_name) { + uint32_t decl_inst = UINT32_MAX; + for (uint32_t i = 0; i < sema->num_decls; i++) { + if (sema->decl_names[i] == name_idx) { + decl_inst = sema->decl_insts[i]; + break; + } + } + if (decl_inst == UINT32_MAX) + return false; + + const uint32_t* value_body; + uint32_t value_body_len; + getParamBody(sema, decl_inst, &value_body, &value_body_len); + if (value_body_len < 3) + return false; + + // Look for: import, field_val, break_inline pattern. + const char* import_path = NULL; + const char* field_name = NULL; + for (uint32_t i = 0; i < value_body_len; i++) { + ZirInstTag itag = sema->code.inst_tags[value_body[i]]; + if (itag == ZIR_INST_IMPORT) { + uint32_t pl = sema->code.inst_datas[value_body[i]] + .pl_tok.payload_index; + uint32_t path_idx = sema->code.extra[pl + 1]; + import_path + = (const char*)&sema->code.string_bytes[path_idx]; + } else if (itag == ZIR_INST_FIELD_VAL && import_path) { + uint32_t pl = sema->code.inst_datas[value_body[i]] + .pl_node.payload_index; + uint32_t fn_start = sema->code.extra[pl + 1]; + field_name + = (const char*)&sema->code.string_bytes[fn_start]; + } + } + if (import_path && field_name) { + *out_import_path = import_path; + *out_field_name = field_name; + return true; + } + return false; +} + // loadImportZir: load, parse, and AstGen an imported file. // Returns a Zir with inst_len > 0 on success; inst_len == 0 on failure. // The caller must call zirDeinit and astDeinit on the returned structures. @@ -1384,8 +1442,11 @@ static TypeIndex resolveFuncRetType(Sema* sema, const FuncZirInfo* info) { return IP_INDEX_VOID_TYPE; if (info->ret_ty_body_len == 1) { ZirInstRef ret_ty_ref = sema->code.extra[info->ret_ty_ref_pos]; - assert(ret_ty_ref < ZIR_REF_START_INDEX); - return ret_ty_ref; + if (ret_ty_ref < ZIR_REF_START_INDEX) + return ret_ty_ref; + // Generic return type (e.g. param_comptime ref) — can't resolve + // statically. Return void; caller handles cross-module fallback. + return IP_INDEX_VOID_TYPE; } // Multi-instruction return type body — not yet supported for inline. return IP_INDEX_VOID_TYPE; @@ -1481,6 +1542,29 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, } } + // For non-field calls: if local lookup fails, check if the callee + // declaration follows the import(...).field_val pattern + // (e.g. `const absv = @import("./absv.zig").absv;`). + if (func_inst == UINT32_MAX && !is_field_call && sema->source_dir + && callee_name_idx != 0) { + const char* import_path = NULL; + const char* field_name = NULL; + if (findDeclImportFieldVal( + sema, callee_name_idx, &import_path, &field_name)) { + import_zir = loadImportZir( + sema->source_dir, import_path, &import_ast); + if (import_zir.inst_len > 0) { + func_inst = findFuncInstInZir( + &import_zir, field_name); + if (func_inst != UINT32_MAX) { + saved_code = sema->code; + sema->code = import_zir; + is_cross_module = true; + } + } + } + } + if (func_inst == UINT32_MAX) { // Can't resolve callee; return void (fallback). return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); @@ -1534,6 +1618,14 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, sema->code = arg_code; } + // Parse the inline function's parameter body. + uint32_t param_block_inst = sema->code.extra + [sema->code.inst_datas[func_inst].pl_node.payload_index + + func_info.param_block_pi]; + const uint32_t* param_body; + uint32_t param_body_len; + getParamBody(sema, param_block_inst, &param_body, &param_body_len); + // Resolve inline function return type. // For cross-module calls with anytype params (e.g. fneg), the return // type is typically @TypeOf(a) which equals the first arg's type. @@ -1543,14 +1635,36 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, // Assume return type = first argument's type (covers @TypeOf(a)). ret_ty = semaTypeOf(sema, arg_refs[0]); } - - // Parse the inline function's parameter body. - uint32_t param_block_inst = sema->code.extra - [sema->code.inst_datas[func_inst].pl_node.payload_index - + func_info.param_block_pi]; - const uint32_t* param_body; - uint32_t param_body_len; - getParamBody(sema, param_block_inst, &param_body, &param_body_len); + // Handle case where return type is a param ref (e.g. `fn absv(comptime + // ST: type, a: ST) ST`). The ret_ty_ref points to a param instruction; + // map it to the corresponding argument value. + if (ret_ty == IP_INDEX_VOID_TYPE && args_len > 0 + && func_info.ret_ty_body_len == 1) { + ZirInstRef ret_ty_ref + = sema->code.extra[func_info.ret_ty_ref_pos]; + if (ret_ty_ref >= ZIR_REF_START_INDEX) { + uint32_t ret_ty_inst = ret_ty_ref - ZIR_REF_START_INDEX; + // Match the return type inst against param body insts. + uint32_t pi = 0; + for (uint32_t p = 0; p < param_body_len; p++) { + ZirInstTag ptag + = sema->code.inst_tags[param_body[p]]; + if (ptag == ZIR_INST_PARAM + || ptag == ZIR_INST_PARAM_COMPTIME + || ptag == ZIR_INST_PARAM_ANYTYPE + || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME) { + if (param_body[p] == ret_ty_inst && pi < args_len) { + // Return type ref matches this param. + // For comptime type params, the arg value IS + // the type. + ret_ty = AIR_REF_TO_IP(arg_refs[pi]); + break; + } + pi++; + } + } + } + } // Reserve the dbg_inline_block instruction (data filled later). uint32_t block_inst_idx = addAirInst(sema, @@ -1597,8 +1711,12 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, instMapPut( &sema->inst_map, param_body[p], arg_refs[param_idx]); - // Emit dbg_arg_inline for each param. - if (!child_block.is_comptime) { + // Emit dbg_arg_inline for runtime params only. + // Comptime params (param_comptime, param_anytype_comptime) + // are skipped because their types are comptime-only. + if (!child_block.is_comptime + && ptag != ZIR_INST_PARAM_COMPTIME + && ptag != ZIR_INST_PARAM_ANYTYPE_COMPTIME) { uint32_t param_name_idx; if (ptag == ZIR_INST_PARAM_ANYTYPE || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME) { @@ -2141,6 +2259,196 @@ static AirInstRef zirTypeof(Sema* sema, uint32_t inst) { return AIR_REF_FROM_IP(ty); } +// zirSwitchBlockComptime: handle switch_block on a comptime-known value. +// Ported from src/Sema.zig resolveSwitchComptime (simplified). +// Parses the SwitchBlock extra data, finds the matching scalar case or +// falls back to the else prong, then analyzes the matching body. +// Returns the result value from the matched break instruction. +static AirInstRef zirSwitchBlockComptime( + Sema* sema, SemaBlock* block, uint32_t inst) { + uint32_t payload_index + = sema->code.inst_datas[inst].pl_node.payload_index; + ZirInstRef operand_ref = sema->code.extra[payload_index]; + uint32_t bits = sema->code.extra[payload_index + 1]; + + // Parse Bits packed struct: + // bit 0: has_multi_cases + // bits 1..3: special_prongs (3 bits) + // bit 4: any_has_tag_capture + // bit 5: any_non_inline_capture + // bit 6: has_continue + // bits 7..31: scalar_cases_len (25 bits) + bool has_multi_cases = (bits & 1) != 0; + uint32_t special_prongs = (bits >> 1) & 0x7; + bool any_has_tag_capture = (bits >> 4) & 1; + uint32_t scalar_cases_len = bits >> 7; + bool has_else = (special_prongs & 1) != 0; + bool has_under = (special_prongs & 0x6) != 0; + + // Resolve the operand value (must be comptime-known). + AirInstRef operand = resolveInst(sema, operand_ref); + + uint32_t extra_index = payload_index + 2; + + // Skip multi_cases_len if present. + uint32_t multi_cases_len = 0; + if (has_multi_cases) { + multi_cases_len = sema->code.extra[extra_index]; + extra_index++; + } + + // Skip tag_capture_inst if present. + if (any_has_tag_capture) + extra_index++; + + // Parse else prong. + uint32_t else_body_start = 0; + uint32_t else_body_len = 0; + if (has_else) { + uint32_t else_prong_info = sema->code.extra[extra_index]; + else_body_len = else_prong_info & 0x0FFFFFFF; // body_len: u28 + extra_index++; + else_body_start = extra_index; + extra_index += else_body_len; + } + + // Skip under prong if present (not needed for basic comptime switch). + if (has_under) { + bool has_one_additional = (special_prongs & 0x6) == 0x4; + bool has_many_additional = (special_prongs & 0x6) == 0x6; + if (has_one_additional) { + extra_index++; // single_absorbed_item + } else if (has_many_additional) { + uint32_t items_len = sema->code.extra[extra_index]; + extra_index++; + uint32_t ranges_len = sema->code.extra[extra_index]; + extra_index++; + extra_index += items_len + ranges_len * 2; + } + uint32_t under_prong_info = sema->code.extra[extra_index]; + uint32_t under_body_len = under_prong_info & 0x0FFFFFFF; + extra_index++; + extra_index += under_body_len; + } + + // Iterate scalar cases: each is { item_ref, ProngInfo, body... } + for (uint32_t sc = 0; sc < scalar_cases_len; sc++) { + ZirInstRef item_ref = sema->code.extra[extra_index]; + extra_index++; + uint32_t prong_info = sema->code.extra[extra_index]; + uint32_t body_len = prong_info & 0x0FFFFFFF; + extra_index++; + uint32_t body_start = extra_index; + extra_index += body_len; + + // Compare operand with the case item. + // For comptime type switches, both should be IP refs. + AirInstRef item = resolveInst(sema, item_ref); + if (AIR_REF_IS_IP(operand) && AIR_REF_IS_IP(item) + && AIR_REF_TO_IP(operand) == AIR_REF_TO_IP(item)) { + // Match found. Analyze the body via resolveBlockBody. + // Ported from src/Sema.zig zirSwitchBlock: the Zig sema + // reserves a BLOCK instruction before analyzing the case + // body, then resolveBlockBody + resolveAnalyzedBlock elide + // the block when the single break result is comptime-known. + // Both the reserved BLOCK and the BR added by zirBreak + // remain as dead instructions in the AIR array. + const uint32_t* body = &sema->code.extra[body_start]; + + // Reserve BLOCK instruction (matches Zig line 11927-11931). + // Data is undefined in upstream — use 0xaa to match Zig's + // debug undefined pattern. + AirInstData block_data; + memset(&block_data, 0xaa, sizeof(block_data)); + uint32_t block_inst + = addAirInst(sema, AIR_INST_BLOCK, block_data); + + // Set up a label so break can find this block. + SemaBlockLabel label; + memset(&label, 0, sizeof(label)); + label.zir_block = inst; + label.merges.block_inst = block_inst; + + // Non-comptime child block (matches Zig line 11942-11959). + // The break handler will add a BR instruction. + SemaBlock child_block; + semaBlockInit(&child_block, sema, block); + child_block.is_comptime = false; + child_block.inlining = block->inlining; + child_block.label = &label; + + (void)analyzeBodyInner( + sema, &child_block, body, body_len); + + // resolveAnalyzedBlock elides the block: the single break + // is the last instruction in child_block, targeting our + // block_inst, so instructions are not added to parent. + // Both BLOCK and BR remain as dead AIR instructions. + AirInstRef result; + if (label.merges.results_len > 0) + result = label.merges.results[0]; + else + result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + + free(label.merges.results); + free(label.merges.br_list); + semaBlockDeinit(&child_block); + return result; + } + } + + // No scalar case matched. Skip multi cases. + for (uint32_t mc = 0; mc < multi_cases_len; mc++) { + uint32_t items_len = sema->code.extra[extra_index]; + extra_index++; + uint32_t ranges_len = sema->code.extra[extra_index]; + extra_index++; + uint32_t mc_prong_info = sema->code.extra[extra_index]; + uint32_t mc_body_len = mc_prong_info & 0x0FFFFFFF; + extra_index++; + extra_index += items_len + ranges_len * 2; + extra_index += mc_body_len; + } + + // Fall through to else prong. + if (has_else && else_body_len > 0) { + const uint32_t* body = &sema->code.extra[else_body_start]; + + // Reserve BLOCK instruction (same as scalar case above). + AirInstData block_data; + memset(&block_data, 0xaa, sizeof(block_data)); + uint32_t block_inst + = addAirInst(sema, AIR_INST_BLOCK, block_data); + + SemaBlockLabel label; + memset(&label, 0, sizeof(label)); + label.zir_block = inst; + label.merges.block_inst = block_inst; + + SemaBlock child_block; + semaBlockInit(&child_block, sema, block); + child_block.is_comptime = false; + child_block.inlining = block->inlining; + child_block.label = &label; + + (void)analyzeBodyInner( + sema, &child_block, body, else_body_len); + + AirInstRef result; + if (label.merges.results_len > 0) + result = label.merges.results[0]; + else + result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + + free(label.merges.results); + free(label.merges.br_list); + semaBlockDeinit(&child_block); + return result; + } + + return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); +} + // zirTypeInfoComptime: handle @typeInfo(type_ref) at comptime. // For float types, tracks the result for subsequent field_val access. static AirInstRef zirTypeInfoComptime(Sema* sema, uint32_t inst) { @@ -2180,6 +2488,40 @@ static uint32_t floatTypeBits(InternPoolIndex ty) { } } +// zirBitSizeOf: handle @bitSizeOf(type) at comptime. +// Ported from src/Sema.zig zirBitSizeOf. +// Resolves the operand type and returns its bit size as a comptime int. +static AirInstRef zirBitSizeOf(Sema* sema, uint32_t inst) { + ZirInstRef operand_ref = sema->code.inst_datas[inst].un_node.operand; + AirInstRef operand = resolveInst(sema, operand_ref); + // The operand should resolve to a type. + assert(AIR_REF_IS_IP(operand)); + InternPoolIndex type_ip = AIR_REF_TO_IP(operand); + uint32_t bit_size = 0; + + // Check if it's a pre-interned int type. + if (sema->ip->items[type_ip].tag == IP_KEY_INT_TYPE) { + bit_size = sema->ip->items[type_ip].data.int_type.bits; + } else { + // Check float types. + bit_size = floatTypeBits(type_ip); + if (bit_size == 0) { + // Check bool. + if (type_ip == IP_INDEX_BOOL_TYPE) + bit_size = 1; + // void, type, etc. have 0 bits. + } + } + + InternPoolKey key; + memset(&key, 0, sizeof(key)); + key.tag = IP_KEY_INT; + key.data.int_val.ty = IP_INDEX_COMPTIME_INT_TYPE; + key.data.int_val.value = bit_size; + key.data.int_val.is_negative = false; + return AIR_REF_FROM_IP(ipIntern(sema->ip, key)); +} + // zirFieldValComptime: handle field_val on comptime values. // Resolves .float on type_info results and .bits on float_info. static AirInstRef zirFieldValComptime(Sema* sema, uint32_t inst) { @@ -2645,6 +2987,7 @@ static bool analyzeBodyInner( } // block_inline: analyze inline block body directly. + // Ported from src/Sema.zig analyzeBodyInner block_inline case. case ZIR_INST_BLOCK_INLINE: { ZirInstData data = sema->code.inst_datas[inst]; uint32_t payload_index = data.pl_node.payload_index; @@ -2652,15 +2995,26 @@ static bool analyzeBodyInner( // body_len instruction indices. uint32_t inner_body_len = sema->code.extra[payload_index]; const uint32_t* inner_body = &sema->code.extra[payload_index + 1]; + // Track whether a debug variable is emitted inside + // this block_inline, requiring a post-hoc BLOCK wrapper + // for correct lexical scoping. + // Ported from src/Sema.zig block_inline need_debug_scope. + bool need_debug_scope = false; + bool* saved_need_debug_scope = block->need_debug_scope; + block->need_debug_scope = &need_debug_scope; + uint32_t block_index = block->instructions_len; bool completed = analyzeBodyInner(sema, block, inner_body, inner_body_len); + + block->need_debug_scope = saved_need_debug_scope; + if (!completed) { // The inner body terminated with a break_inline. // The break_inline's operand is the result of this // block. - uint32_t break_inst = sema->comptime_break_inst; - ZirInstData break_data = sema->code.inst_datas[break_inst]; + uint32_t break_inst_zir = sema->comptime_break_inst; + ZirInstData break_data = sema->code.inst_datas[break_inst_zir]; ZirInstRef operand = break_data.break_data.operand; AirInstRef result; if (operand == ZIR_REF_NONE) { @@ -2668,6 +3022,66 @@ static bool analyzeBodyInner( } else { result = resolveInst(sema, operand); } + + // If need_debug_scope was set and instructions were + // added, wrap them in a BLOCK+BR for scoping. + // Ported from src/Sema.zig ensurePostHoc + + // resolveAnalyzedBlock with need_debug_scope=true. + if (need_debug_scope + && block->instructions_len > block_index) { + uint32_t new_insts_count + = block->instructions_len - block_index; + + // Reserve an AIR block instruction. + uint32_t blk_inst = addAirInst(sema, + AIR_INST_BLOCK, + (AirInstData){ + .ty_pl = { .ty_ref = 0, .payload = 0 } + }); + + // Add a BR to exit the block with void_value. + AirInstData br_data; + memset(&br_data, 0, sizeof(br_data)); + br_data.br.block_inst = blk_inst; + br_data.br.operand + = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + uint32_t br_inst = addAirInst( + sema, AIR_INST_BR, br_data); + + // Write block extra: body_len, then body insts. + uint32_t body_len = new_insts_count + 1; // +1 for BR + uint32_t extra_start + = addAirExtra(sema, body_len); + for (uint32_t ci = block_index; + ci < block->instructions_len; ci++) { + addAirExtra(sema, + block->instructions[ci]); + } + addAirExtra(sema, br_inst); + + // Patch the BLOCK instruction data. + sema->air_inst_datas[blk_inst].ty_pl.ty_ref + = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); + sema->air_inst_datas[blk_inst].ty_pl.payload + = extra_start; + + // Replace the instructions in the parent block: + // truncate back to block_index, add just the BLOCK. + block->instructions_len = block_index; + if (block->instructions_len + >= block->instructions_cap) { + uint32_t new_cap + = block->instructions_cap * 2; + block->instructions = realloc( + block->instructions, + new_cap * sizeof(uint32_t)); + if (!block->instructions) exit(1); + block->instructions_cap = new_cap; + } + block->instructions[block->instructions_len++] + = blk_inst; + } + instMapPut(&sema->inst_map, inst, result); } i++; @@ -3054,6 +3468,20 @@ static bool analyzeBodyInner( i++; continue; + // @bitSizeOf(type): comptime bit size extraction. + case ZIR_INST_BIT_SIZE_OF: + instMapPut(&sema->inst_map, inst, + zirBitSizeOf(sema, inst)); + i++; + continue; + + // switch_block: comptime switch on a known value. + case ZIR_INST_SWITCH_BLOCK: + instMapPut(&sema->inst_map, inst, + zirSwitchBlockComptime(sema, block, inst)); + i++; + continue; + // field_val: comptime field access on type_info results. case ZIR_INST_FIELD_VAL: instMapPut(&sema->inst_map, inst, @@ -3344,15 +3772,15 @@ static bool analyzeBodyInner( // CoveragePoint: none=0, poi=2 (2 bits each) // Packed struct (u32): // bits [0..2] = true hint - // bits [3..5] = false hint - // bits [6..7] = then_cov - // bits [8..9] = else_cov - // bits [10..31] = 0 + // bits [3..5] = false hint (BranchHint, 3 bits) + // bit [6] = then_cov (CoveragePoint, 1 bit) + // bit [7] = else_cov (CoveragePoint, 1 bit) + // bits [8..31] = 0 uint32_t branch_hints = 0; branch_hints |= 3; // true = cold branch_hints |= (0 << 3); // false = none - branch_hints |= (2 << 6); // then_cov = poi - branch_hints |= (2 << 8); // else_cov = poi + branch_hints |= (1 << 6); // then_cov = poi + branch_hints |= (1 << 7); // else_cov = poi addAirExtra(sema, branch_hints); for (uint32_t ti = 0; @@ -3420,16 +3848,13 @@ static bool analyzeBodyInner( AirInstRef msg = resolveInst(sema, msg_ref); (void)msg; // msg used in call args below - // For wasm32+llvm backend, panic_fn is supported. - // We need to emit: call(panic_fn, [msg_coerced, - // null_ret_addr]) + unreach. - // The panic function is analyzed separately as a - // cross-module function. - // For now, emit trap + unreach as a simplified fallback. + // In ReleaseFast (no safety), @panic compiles to trap. + // Ported from src/Sema.zig: when want_safety is false and + // the panic fn resolves to no_panic, only trap is emitted + // (no unreach — trap is already noreturn). AirInstData trap_data; memset(&trap_data, 0, sizeof(trap_data)); (void)blockAddInst(block, AIR_INST_TRAP, trap_data); - (void)blockAddInst(block, AIR_INST_UNREACH, trap_data); return false; // panic is terminal } diff --git a/stage0/sema.h b/stage0/sema.h @@ -75,6 +75,7 @@ typedef struct SemaBlock { bool want_safety; bool want_safety_set; uint32_t src_base_inst; + bool* need_debug_scope; } SemaBlock; // --- InferredErrorSet --- diff --git a/stage0/sema_test.zig b/stage0/sema_test.zig @@ -339,7 +339,7 @@ fn canonicalizeRef( fn airInstNumSlots(tag_val: u8) usize { return switch (tag_val) { // no_op: 0 meaningful bytes - c.AIR_INST_RET_ADDR, c.AIR_INST_FRAME_ADDR => 0, + c.AIR_INST_RET_ADDR, c.AIR_INST_FRAME_ADDR, c.AIR_INST_TRAP, c.AIR_INST_UNREACH, c.AIR_INST_BREAKPOINT => 0, // un_op: 4 meaningful bytes (1 slot) c.AIR_INST_SQRT, c.AIR_INST_SIN, @@ -389,7 +389,7 @@ fn airInstNumSlots(tag_val: u8) usize { fn airDataRefSlots(tag_val: u8) [2]bool { return switch (tag_val) { // no_op: no meaningful data - c.AIR_INST_RET_ADDR, c.AIR_INST_FRAME_ADDR => .{ false, false }, + c.AIR_INST_RET_ADDR, c.AIR_INST_FRAME_ADDR, c.AIR_INST_TRAP, c.AIR_INST_UNREACH, c.AIR_INST_BREAKPOINT => .{ false, false }, // dbg_stmt: line(u32) + column(u32) c.AIR_INST_DBG_STMT, c.AIR_INST_DBG_EMPTY_STMT => .{ false, false }, // pl_op: operand(Ref) + payload(u32) @@ -523,6 +523,16 @@ fn normalizeNtsPadding(extra: []u32, nts_index: u32) void { fn airCompareOne(name: []const u8, zig_air: *const c.Air, c_air: *const c.Air) !void { if (zig_air.inst_len != c_air.inst_len) { std.debug.print("'{s}': inst_len mismatch: zig={d} c={d}\n", .{ name, zig_air.inst_len, c_air.inst_len }); + if (cToOpt(u8, zig_air.inst_tags)) |zt| { + std.debug.print(" zig tags:", .{}); + for (0..zig_air.inst_len) |j| std.debug.print(" {d}", .{zt[j]}); + std.debug.print("\n", .{}); + } + if (cToOpt(u8, c_air.inst_tags)) |ct| { + std.debug.print(" c tags:", .{}); + for (0..c_air.inst_len) |j| std.debug.print(" {d}", .{ct[j]}); + std.debug.print("\n", .{}); + } return error.AirMismatch; } const inst_len = zig_air.inst_len; @@ -658,6 +668,11 @@ fn airCompareOne(name: []const u8, zig_air: *const c.Air, c_air: *const c.Air) ! } if (!std.mem.eql(u32, zig_extra_copy, c_extra_copy)) { std.debug.print("'{s}': extra mismatch (extra_len={d})\n", .{ name, extra_len }); + std.debug.print(" zig extra:", .{}); + for (0..extra_len) |ei| std.debug.print(" {d}", .{zig_extra_copy[ei]}); + std.debug.print("\n c extra:", .{}); + for (0..extra_len) |ei| std.debug.print(" {d}", .{c_extra_copy[ei]}); + std.debug.print("\n", .{}); for (0..extra_len) |ei| { if (zig_extra_copy[ei] != c_extra_copy[ei]) { std.debug.print(" extra[{d}]: zig=0x{x} c=0x{x}\n", .{ ei, zig_extra_copy[ei], c_extra_copy[ei] }); diff --git a/stage0/stages_test.zig b/stage0/stages_test.zig @@ -104,7 +104,7 @@ const corpus_files = .{ "../lib/std/zig/llvm.zig", // 247 "../lib/compiler_rt/neghf2.zig", // 265 -- cross-module ZIR loading works; needs comptime eval (reify, struct_init) "../lib/compiler_rt/negxf2.zig", // 265 -- @export+func_fancy handled; body analysis incomplete - //"../lib/compiler_rt/absvdi2.zig", // 311 -- needs alloc_mut, block, condbr, panic, call + "../lib/compiler_rt/absvdi2.zig", // 311 -- needs alloc_mut, block, condbr, panic, call //"../lib/compiler_rt/absvsi2.zig", // 311 -- @export+func_fancy handled; body analysis incomplete //"../lib/compiler_rt/absvti2.zig", // 314 -- @export+func_fancy handled; body analysis incomplete //"../lib/compiler_rt/addhf3.zig", // 319