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:
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, ¶m_body, ¶m_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, ¶m_body, ¶m_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