From f177872d48e42ce6abcdbf034fabd645bd5dbe23 Mon Sep 17 00:00:00 2001 From: Motiejus Date: Mon, 23 Feb 2026 22:15:06 +0000 Subject: [PATCH] sema: extract 28 functions from analyzeBodyInner dispatch table Refactor the monolithic analyzeBodyInner switch into named functions matching the upstream Zig Sema.zig naming convention (zirRetImplicit, zirRetNode, zirFloat, zirBlock, etc.). The switch body now serves as a clean dispatch table. Co-Authored-By: Claude Opus 4.6 --- stage0/sema.c | 2818 ++++++++++++++++++++++--------------------------- 1 file changed, 1286 insertions(+), 1532 deletions(-) diff --git a/stage0/sema.c b/stage0/sema.c index c7ac8b08f5..85120cd891 100644 --- a/stage0/sema.c +++ b/stage0/sema.c @@ -6337,6 +6337,1223 @@ static SemaBlockLabel* findBlockLabel(SemaBlock* block, uint32_t zir_block) { return NULL; // label not found (e.g. comptime analysis fallback) } +// Forward declarations for mutually recursive functions. +static bool analyzeBodyInner( + Sema* sema, SemaBlock* block, const uint32_t* body, uint32_t body_len); +static uint8_t analyzeBodyRuntimeBreak( + Sema* sema, SemaBlock* block, const uint32_t* body, uint32_t body_len); + +// --- zirRetImplicit --- +// Ported from src/Sema.zig zirRetImplicit / analyzeRet. +static void zirRetImplicit(Sema* sema, SemaBlock* block, uint32_t inst) { + (void)inst; + AirInstRef operand = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + if (block->inlining) { + SemaBlockInlining* inl = block->inlining; + if (block->is_comptime) { + inl->comptime_result = operand; + inl->comptime_returned = true; + return; + } + AirInstData br_data; + memset(&br_data, 0, sizeof(br_data)); + br_data.br.block_inst = inl->merges.block_inst; + br_data.br.operand = operand; + AirInstRef br_ref = semaAddInst(block, AIR_INST_BR, br_data); + mergesAppend(&inl->merges, operand, AIR_REF_TO_INST(br_ref)); + return; + } + if (sema->fn_ret_ty != TYPE_NONE && !block->is_comptime) { + AirInstData ret_data; + memset(&ret_data, 0, sizeof(ret_data)); + ret_data.un_op.operand = operand; + (void)semaAddInst(block, AIR_INST_RET, ret_data); + } +} + +// --- zirRetNode --- +// Ported from src/Sema.zig zirRetNode / analyzeRet. +static void zirRetNode(Sema* sema, SemaBlock* block, uint32_t inst) { + ZirInstRef operand_ref = sema->code.inst_datas[inst].un_node.operand; + AirInstRef operand = resolveInst(sema, operand_ref); + operand = semaCoerce(sema, block, sema->fn_ret_ty, operand); + if (block->inlining) { + SemaBlockInlining* inl = block->inlining; + if (block->is_comptime) { + inl->comptime_result = operand; + inl->comptime_returned = true; + return; + } + AirInstData br_data; + memset(&br_data, 0, sizeof(br_data)); + br_data.br.block_inst = inl->merges.block_inst; + br_data.br.operand = operand; + AirInstRef br_ref = semaAddInst(block, AIR_INST_BR, br_data); + mergesAppend(&inl->merges, operand, AIR_REF_TO_INST(br_ref)); + return; + } + AirInstData ret_data; + memset(&ret_data, 0, sizeof(ret_data)); + ret_data.un_op.operand = operand; + (void)semaAddInst(block, AIR_INST_RET, ret_data); +} + +// --- zirRetLoad --- +// Ported from src/Sema.zig zirRetLoad / analyzeRet. +static void zirRetLoad(Sema* sema, SemaBlock* block, uint32_t inst) { + ZirInstRef operand_ref = sema->code.inst_datas[inst].un_node.operand; + AirInstRef ret_ptr = resolveInst(sema, operand_ref); + if (block->is_comptime || block->inlining) { + TypeIndex ptr_ty = semaTypeOf(sema, ret_ptr); + TypeIndex elem_ty = ptrChildType(sema->ip, ptr_ty); + AirInstData load_data; + memset(&load_data, 0, sizeof(load_data)); + load_data.ty_op.ty_ref = AIR_REF_FROM_IP(elem_ty); + load_data.ty_op.operand = ret_ptr; + AirInstRef operand = semaAddInst(block, AIR_INST_LOAD, load_data); + if (block->inlining) { + SemaBlockInlining* inl = block->inlining; + if (block->is_comptime) { + inl->comptime_result = operand; + inl->comptime_returned = true; + return; + } + AirInstData br_data; + memset(&br_data, 0, sizeof(br_data)); + br_data.br.block_inst = inl->merges.block_inst; + br_data.br.operand = operand; + AirInstRef br_ref = semaAddInst(block, AIR_INST_BR, br_data); + mergesAppend(&inl->merges, operand, AIR_REF_TO_INST(br_ref)); + return; + } + return; + } + AirInstData ret_data; + memset(&ret_data, 0, sizeof(ret_data)); + ret_data.un_op.operand = ret_ptr; + (void)semaAddInst(block, AIR_INST_RET_LOAD, ret_data); +} + +// --- zirFloat --- +// Ported from src/Sema.zig zirFloat. +static AirInstRef zirFloat(Sema* sema, uint32_t inst) { + double val = sema->code.inst_datas[inst].float_val; + InternPoolKey key; + memset(&key, 0, sizeof(key)); + key.tag = IP_KEY_FLOAT; + key.data.float_val.ty = IP_INDEX_COMPTIME_FLOAT_TYPE; + key.data.float_val.val = val; + return AIR_REF_FROM_IP(ipIntern(sema->ip, key)); +} + +// --- zirExtended --- +// Ported from src/Sema.zig: handle extended opcodes. +static AirInstRef zirExtended(Sema* sema, SemaBlock* block, uint32_t inst) { + uint16_t opcode = sema->code.inst_datas[inst].extended.opcode; + if (opcode == ZIR_EXT_STRUCT_DECL) { + zirStructDecl(sema, block, inst); + return AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); + } + if (opcode == ZIR_EXT_REIFY) { + return zirReifyComptime(sema, inst); + } + if (opcode == ZIR_EXT_BRANCH_HINT) { + uint32_t payload_index = sema->code.inst_datas[inst].extended.operand; + ZirInstRef hint_ref = sema->code.extra[payload_index + 1]; + AirInstRef resolved = resolveInst(sema, hint_ref); + if (AIR_REF_IS_IP(resolved) && sema->branch_hint < 0) { + InternPoolKey key + = ipIndexToKey(sema->ip, AIR_REF_TO_IP(resolved)); + if (key.tag == IP_KEY_INT) { + sema->branch_hint = (int8_t)key.data.int_val.value_lo; + } + } + return AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); + } + if (opcode == ZIR_EXT_ALLOC) { + uint16_t small = sema->code.inst_datas[inst].extended.small; + uint32_t payload_index = sema->code.inst_datas[inst].extended.operand; + if (small & 0x1) { + uint32_t ei = payload_index + 1; + ZirInstRef type_ref = sema->code.extra[ei]; + AirInstRef resolved = resolveInst(sema, type_ref); + TypeIndex elem_ty = IP_INDEX_VOID_TYPE; + if (AIR_REF_IS_IP(resolved)) + elem_ty = AIR_REF_TO_IP(resolved); + InternPoolKey pkey; + memset(&pkey, 0, sizeof(pkey)); + pkey.tag = IP_KEY_PTR_TYPE; + pkey.data.ptr_type.child = elem_ty; + TypeIndex ptr_ty = ipIntern(sema->ip, pkey); + AirInstData adata; + memset(&adata, 0, sizeof(adata)); + adata.ty.ty_ref = AIR_REF_FROM_IP(ptr_ty); + return semaAddInst(block, AIR_INST_ALLOC, adata); + } + AirInstData adata; + memset(&adata, 0, sizeof(adata)); + AirInstRef air_ref + = semaAddInst(block, AIR_INST_INFERRED_ALLOC, adata); + assert(sema->num_ia < 16); + uint32_t ia_idx = sema->num_ia++; + sema->ia_air[ia_idx] = AIR_REF_TO_INST(air_ref); + sema->ia_store[ia_idx] = UINT32_MAX; + sema->ia_is_const[ia_idx] = (small & 0x4) != 0; + return air_ref; + } + if (opcode == ZIR_EXT_INPLACE_ARITH_RESULT_TY) { + uint32_t operand = sema->code.inst_datas[inst].extended.operand; + AirInstRef lhs = resolveInst(sema, operand); + TypeIndex lhs_ty = semaTypeOf(sema, lhs); + return AIR_REF_FROM_IP(lhs_ty); + } + return AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); +} + +// --- zirBlockInline --- +// Ported from src/Sema.zig analyzeBodyInner block_inline case. +// Returns true if the instruction was handled (caller should continue), +// false if a comptime break propagated to an outer block. +static bool zirBlockInline(Sema* sema, SemaBlock* block, uint32_t inst) { + ZirInstData data = sema->code.inst_datas[inst]; + uint32_t payload_index = data.pl_node.payload_index; + uint32_t inner_body_len = sema->code.extra[payload_index]; + const uint32_t* inner_body = &sema->code.extra[payload_index + 1]; + 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; + uint32_t saved_cbi = sema->comptime_break_inst; + sema->comptime_break_inst = UINT32_MAX; + + analyzeBodyInner(sema, block, inner_body, inner_body_len); + + bool hit_comptime_break = (sema->comptime_break_inst != UINT32_MAX); + block->need_debug_scope = saved_need_debug_scope; + + if (readBool(&need_debug_scope)) { + if (!hit_comptime_break) { + if (saved_need_debug_scope) { + *saved_need_debug_scope = true; + } + } else { + uint32_t new_insts_count = block->instructions_len - block_index; + uint32_t blk_inst = semaAddInstAsIndex(sema, AIR_INST_BLOCK, + (AirInstData) { .ty_pl = { .ty_ref = 0, .payload = 0 } }); + 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 = semaAddInstAsIndex(sema, AIR_INST_BR, br_data); + uint32_t body_len2 = new_insts_count + 1; + uint32_t extra_start = semaAddExtra(sema, body_len2); + for (uint32_t ci = block_index; ci < block->instructions_len; + ci++) { + semaAddExtra(sema, block->instructions[ci]); + } + semaAddExtra(sema, br_inst); + 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; + 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; + } + } + + if (hit_comptime_break) { + uint32_t break_inst_zir = sema->comptime_break_inst; + ZirInstData break_data_zir = sema->code.inst_datas[break_inst_zir]; + uint32_t break_payload = break_data_zir.break_data.payload_index; + uint32_t break_block_inst = sema->code.extra[break_payload]; + ZirInstRef operand = break_data_zir.break_data.operand; + if (break_block_inst == inst) { + sema->comptime_break_inst = saved_cbi; + AirInstRef result; + if (operand == ZIR_REF_NONE) { + result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } else { + result = resolveInst(sema, operand); + } + instMapPut(&sema->inst_map, inst, result); + } else { + return false; + } + } else { + sema->comptime_break_inst = saved_cbi; + } + return true; +} + +// --- zirExport --- +// Ported from src/Sema.zig zirExport. +static void zirExport(Sema* sema, uint32_t inst) { + uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index; + uint32_t exported_ref = sema->code.extra[payload_index]; + if (exported_ref >= ZIR_REF_START_INDEX) { + uint32_t exported_inst = exported_ref - ZIR_REF_START_INDEX; + ZirInstTag etag = sema->code.inst_tags[exported_inst]; + if (etag == ZIR_INST_DECL_REF || etag == ZIR_INST_DECL_VAL) { + uint32_t name_idx + = sema->code.inst_datas[exported_inst].str_tok.start; + if (sema->num_exported_decl_names < MAX_EXPORTED_DECL_NAMES) { + s_exported_decl_names[sema->num_exported_decl_names++] + = name_idx; + } + } + } + instMapPut(&sema->inst_map, inst, AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE)); +} + +// --- zirMinMax --- +// Ported from src/Sema.zig zirMinMax. +static AirInstRef zirMinMax(Sema* sema, SemaBlock* block, uint32_t inst) { + bool is_max = (sema->code.inst_tags[inst] == ZIR_INST_MAX); + uint32_t pl = sema->code.inst_datas[inst].pl_node.payload_index; + ZirInstRef zir_lhs = sema->code.extra[pl]; + ZirInstRef zir_rhs = sema->code.extra[pl + 1]; + AirInstRef min_lhs = resolveInst(sema, zir_lhs); + AirInstRef min_rhs = resolveInst(sema, zir_rhs); + int64_t lv, rv; + if (isComptimeInt(sema, min_lhs, &lv) + && isComptimeInt(sema, min_rhs, &rv)) { + int64_t result_val + = is_max ? (lv > rv ? lv : rv) : (lv < rv ? lv : rv); + 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_lo + = (uint64_t)(result_val < 0 ? -result_val : result_val); + key.data.int_val.is_negative = (result_val < 0); + return AIR_REF_FROM_IP(ipIntern(sema->ip, key)); + } + TypeIndex peer_ty = semaResolvePeerTypes(sema, min_lhs, min_rhs); + AirInstRef c_lhs = semaCoerce(sema, block, peer_ty, min_lhs); + AirInstRef c_rhs = semaCoerce(sema, block, peer_ty, min_rhs); + AirInstData data; + memset(&data, 0, sizeof(data)); + data.bin_op.lhs = c_lhs; + data.bin_op.rhs = c_rhs; + return semaAddInst(block, is_max ? AIR_INST_MAX : AIR_INST_MIN, data); +} + +// --- zirRetPtr --- +// Ported from src/Sema.zig zirRetPtr. +static AirInstRef zirRetPtr(Sema* sema, SemaBlock* block) { + TypeIndex ret_ty = sema->fn_ret_ty; + if (ret_ty == TYPE_NONE) + ret_ty = IP_INDEX_VOID_TYPE; + InternPoolKey pk; + memset(&pk, 0, sizeof(pk)); + pk.tag = IP_KEY_PTR_TYPE; + pk.data.ptr_type.child = ret_ty; + pk.data.ptr_type.flags = 0; + TypeIndex ptr_ty = ipIntern(sema->ip, pk); + AirInstData d; + memset(&d, 0, sizeof(d)); + d.ty.ty_ref = AIR_REF_FROM_IP(ptr_ty); + return semaAddInst(block, AIR_INST_ALLOC, d); +} + +// --- zirRetType --- +// Ported from src/Sema.zig: obtains the return type. +static AirInstRef zirRetType(Sema* sema) { + if (sema->fn_ret_ty != TYPE_NONE) + return AIR_REF_FROM_IP(sema->fn_ret_ty); + return AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); +} + +// --- zirElemType --- +// Ported from src/Sema.zig zirElemType. +static AirInstRef zirElemType(Sema* sema, uint32_t inst) { + ZirInstRef operand_ref = sema->code.inst_datas[inst].un_node.operand; + AirInstRef resolved = resolveInst(sema, operand_ref); + if (!AIR_REF_IS_IP(resolved)) + return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + TypeIndex ptr_ty = AIR_REF_TO_IP(resolved); + TypeIndex elem_ty = ptrChildType(sema->ip, ptr_ty); + return AIR_REF_FROM_IP(elem_ty); +} + +// --- zirTypeofBuiltin --- +// Ported from src/Sema.zig zirTypeofBuiltin. +static AirInstRef zirTypeofBuiltin( + Sema* sema, SemaBlock* block, uint32_t inst) { + ZirInstData data = sema->code.inst_datas[inst]; + uint32_t payload_index = data.pl_node.payload_index; + uint32_t inner_body_len = sema->code.extra[payload_index]; + const uint32_t* inner_body = &sema->code.extra[payload_index + 1]; + SemaBlock ct_block; + semaBlockInit(&ct_block, sema, block); + ct_block.is_comptime = true; + ct_block.is_typeof = true; + ct_block.inlining = block->inlining; + bool completed + = analyzeBodyInner(sema, &ct_block, inner_body, inner_body_len); + AirInstRef result; + if (!completed) { + uint32_t break_inst = sema->comptime_break_inst; + ZirInstData break_data = sema->code.inst_datas[break_inst]; + ZirInstRef operand = break_data.break_data.operand; + if (operand == ZIR_REF_NONE) { + result = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); + } else { + AirInstRef resolved = resolveInst(sema, operand); + TypeIndex ty = semaTypeOf(sema, resolved); + result = AIR_REF_FROM_IP(ty); + } + } else { + result = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); + } + semaBlockDeinit(&ct_block); + return result; +} + +// --- zirBlockComptime --- +// Ported from src/Sema.zig: block_comptime handling. +static void zirBlockComptime(Sema* sema, SemaBlock* block, uint32_t inst) { + ZirInstData data = sema->code.inst_datas[inst]; + uint32_t payload_index = data.pl_node.payload_index; + uint32_t inner_body_len = sema->code.extra[payload_index + 1]; + const uint32_t* inner_body = &sema->code.extra[payload_index + 2]; + SemaBlock ct_block; + semaBlockInit(&ct_block, sema, block); + ct_block.is_comptime = true; + ct_block.inlining = block->inlining; + bool completed + = analyzeBodyInner(sema, &ct_block, inner_body, inner_body_len); + if (!completed) { + uint32_t break_inst = sema->comptime_break_inst; + ZirInstData break_data = sema->code.inst_datas[break_inst]; + ZirInstRef operand = break_data.break_data.operand; + AirInstRef result; + if (operand == ZIR_REF_NONE) { + result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } else { + result = resolveInst(sema, operand); + } + instMapPut(&sema->inst_map, inst, result); + } + semaBlockDeinit(&ct_block); +} + +// --- zirEnumLiteral --- +// Ported from src/Sema.zig zirEnumLiteral. +static AirInstRef zirEnumLiteral(Sema* sema, uint32_t inst) { + uint32_t name_start = sema->code.inst_datas[inst].str_tok.start; + const char* name = (const char*)&sema->code.string_bytes[name_start]; + InternPoolKey key; + memset(&key, 0, sizeof(key)); + key.tag = IP_KEY_ENUM_LITERAL; + key.data.enum_literal = simpleStringHash(name); + return AIR_REF_FROM_IP(ipIntern(sema->ip, key)); +} + +// --- zirOptEuBasePtrInit --- +// Ported from Sema.zig zirOptEuBasePtrInit. +static AirInstRef zirOptEuBasePtrInit(Sema* sema, uint32_t inst) { + ZirInstRef operand = sema->code.inst_datas[inst].un_node.operand; + return resolveInst(sema, operand); +} + +// --- zirAlloc --- +// Ported from src/Sema.zig zirAlloc / zirAllocMut. +static void zirAlloc(Sema* sema, SemaBlock* block, uint32_t inst) { + ZirInstRef type_ref = sema->code.inst_datas[inst].un_node.operand; + AirInstRef resolved = resolveInst(sema, type_ref); + if (!AIR_REF_IS_IP(resolved)) + return; + TypeIndex elem_ty = AIR_REF_TO_IP(resolved); + InternPoolKey key; + memset(&key, 0, sizeof(key)); + key.tag = IP_KEY_PTR_TYPE; + key.data.ptr_type.child = elem_ty; + key.data.ptr_type.flags = 0; + TypeIndex ptr_ty = ipIntern(sema->ip, key); + AirInstData data; + memset(&data, 0, sizeof(data)); + data.ty.ty_ref = AIR_REF_FROM_IP(ptr_ty); + instMapPut( + &sema->inst_map, inst, semaAddInst(block, AIR_INST_ALLOC, data)); +} + +// --- zirAllocInferred --- +// Ported from src/Sema.zig zirAllocInferred. +static void zirAllocInferred( + Sema* sema, SemaBlock* block, uint32_t inst, bool is_const) { + AirInstData data; + memset(&data, 0, sizeof(data)); + AirInstRef air_ref = semaAddInst(block, AIR_INST_INFERRED_ALLOC, data); + instMapPut(&sema->inst_map, inst, air_ref); + assert(sema->num_ia < 16); + uint32_t ia_idx = sema->num_ia++; + sema->ia_air[ia_idx] = AIR_REF_TO_INST(air_ref); + sema->ia_store[ia_idx] = UINT32_MAX; + sema->ia_is_const[ia_idx] = is_const; +} + +// --- zirStoreToInferredPtr --- +// Ported from src/Sema.zig zirStoreToInferredPtr / +// storeToInferredAlloc. +static void zirStoreToInferredPtr( + Sema* sema, SemaBlock* block, uint32_t inst) { + uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index; + ZirInstRef ptr_ref = sema->code.extra[payload_index]; + ZirInstRef val_ref = sema->code.extra[payload_index + 1]; + AirInstRef ptr = resolveInst(sema, ptr_ref); + AirInstRef val = resolveInst(sema, val_ref); + AirInstData data; + memset(&data, 0, sizeof(data)); + data.bin_op.lhs = ptr; + data.bin_op.rhs = val; + AirInstRef store_ref = semaAddInst(block, AIR_INST_STORE, data); + uint32_t ptr_inst = AIR_REF_TO_INST(ptr); + for (uint32_t k = 0; k < sema->num_ia; k++) { + if (sema->ia_air[k] == ptr_inst) { + sema->ia_store[k] = AIR_REF_TO_INST(store_ref); + break; + } + } +} + +// --- zirResolveInferredAlloc --- +// Ported from src/Sema.zig zirResolveInferredAlloc. +static void zirResolveInferredAlloc( + Sema* sema, SemaBlock* block, uint32_t inst) { + ZirInstRef alloc_ref = sema->code.inst_datas[inst].un_node.operand; + AirInstRef ptr = resolveInst(sema, alloc_ref); + uint32_t ptr_inst = AIR_REF_TO_INST(ptr); + uint32_t ia_idx = UINT32_MAX; + for (uint32_t k = 0; k < sema->num_ia; k++) { + if (sema->ia_air[k] == ptr_inst) { + ia_idx = k; + break; + } + } + if (ia_idx == UINT32_MAX) { + instMapPut(&sema->inst_map, inst, ptr); + return; + } + assert(sema->ia_store[ia_idx] != UINT32_MAX); + uint32_t store_inst = sema->ia_store[ia_idx]; + AirInstRef rhs = sema->air_inst_datas[store_inst].bin_op.rhs; + TypeIndex elem_ty = semaTypeOf(sema, rhs); + InternPoolKey key; + memset(&key, 0, sizeof(key)); + key.tag = IP_KEY_PTR_TYPE; + key.data.ptr_type.child = elem_ty; + key.data.ptr_type.flags = 0; + TypeIndex ptr_ty = ipIntern(sema->ip, key); + sema->air_inst_tags[ptr_inst] = (uint8_t)AIR_INST_ALLOC; + AirInstData alloc_data; + memset(&alloc_data, 0, sizeof(alloc_data)); + alloc_data.ty.ty_ref = AIR_REF_FROM_IP(ptr_ty); + sema->air_inst_datas[ptr_inst] = alloc_data; + { + AirInstRef store_lhs = sema->air_inst_datas[store_inst].bin_op.lhs; + AirInstRef store_rhs = sema->air_inst_datas[store_inst].bin_op.rhs; + AirInstRef coerced = semaCoerce(sema, block, elem_ty, store_rhs); + AirInstData store_data; + memset(&store_data, 0, sizeof(store_data)); + store_data.bin_op.lhs = store_lhs; + store_data.bin_op.rhs = coerced; + uint32_t new_store_idx + = semaAddInstAsIndex(sema, AIR_INST_STORE, store_data); + sema->air_inst_tags[store_inst] = sema->air_inst_tags[new_store_idx]; + sema->air_inst_datas[store_inst] = sema->air_inst_datas[new_store_idx]; + } + if (sema->ia_is_const[ia_idx]) { + InternPoolKey ckey; + memset(&ckey, 0, sizeof(ckey)); + ckey.tag = IP_KEY_PTR_TYPE; + ckey.data.ptr_type.child = elem_ty; + ckey.data.ptr_type.flags = 1; + TypeIndex const_ptr_ty = ipIntern(sema->ip, ckey); + AirInstData bc_data; + memset(&bc_data, 0, sizeof(bc_data)); + bc_data.ty_op.ty_ref = AIR_REF_FROM_IP(const_ptr_ty); + bc_data.ty_op.operand = ptr; + ptr = semaAddInst(block, AIR_INST_BITCAST, bc_data); + } + instMapPut(&sema->inst_map, inst, ptr); +} + +// --- zirMakePtrConst --- +// Ported from src/Sema.zig zirMakePtrConst / makePtrConst. +static AirInstRef zirMakePtrConst( + Sema* sema, SemaBlock* block, uint32_t inst) { + ZirInstRef alloc_ref = sema->code.inst_datas[inst].un_node.operand; + AirInstRef alloc_ptr = resolveInst(sema, alloc_ref); + TypeIndex ptr_ty = semaTypeOf(sema, alloc_ptr); + TypeIndex child = ptrChildType(sema->ip, ptr_ty); + InternPoolKey ckey; + memset(&ckey, 0, sizeof(ckey)); + ckey.tag = IP_KEY_PTR_TYPE; + ckey.data.ptr_type.child = child; + ckey.data.ptr_type.flags = PTR_FLAGS_IS_CONST; + TypeIndex const_ptr_ty = ipIntern(sema->ip, ckey); + AirInstData bc_data; + memset(&bc_data, 0, sizeof(bc_data)); + bc_data.ty_op.ty_ref = AIR_REF_FROM_IP(const_ptr_ty); + bc_data.ty_op.operand = alloc_ptr; + return semaAddInst(block, AIR_INST_BITCAST, bc_data); +} + +// --- zirBlock --- +// Ported from src/Sema.zig zirBlock / resolveAnalyzedBlock. +// Returns true if the instruction was handled (caller should continue), +// false if terminal (caller should return false). +static bool zirBlock(Sema* sema, SemaBlock* block, uint32_t inst) { + ZirInstData bdata = sema->code.inst_datas[inst]; + uint32_t payload_index = bdata.pl_node.payload_index; + uint32_t inner_body_len = sema->code.extra[payload_index]; + const uint32_t* inner_body = &sema->code.extra[payload_index + 1]; + + if (block->is_comptime) { + SemaBlockLabel label; + memset(&label, 0, sizeof(label)); + label.zir_block = inst; + 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; + + SemaBlock child_block; + semaBlockInit(&child_block, sema, block); + child_block.is_comptime = true; + child_block.want_safety = block->want_safety; + child_block.want_safety_set = block->want_safety_set; + child_block.inlining = block->inlining; + child_block.label = &label; + + bool body_completed + = analyzeBodyInner(sema, &child_block, inner_body, inner_body_len); + + for (uint32_t ci = 0; ci < child_block.instructions_len; ci++) { + 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++] + = child_block.instructions[ci]; + } + + block->need_debug_scope = saved_need_debug_scope; + + if (readBool(&need_debug_scope)) { + uint32_t new_insts_count = block->instructions_len - block_index; + uint32_t blk_inst = semaAddInstAsIndex(sema, AIR_INST_BLOCK, + (AirInstData) { .ty_pl = { .ty_ref = 0, .payload = 0 } }); + 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 = semaAddInstAsIndex(sema, AIR_INST_BR, br_data); + uint32_t body_len2 = new_insts_count + 1; + uint32_t extra_start = semaAddExtra(sema, body_len2); + for (uint32_t ci = block_index; ci < block->instructions_len; + ci++) { + semaAddExtra(sema, block->instructions[ci]); + } + semaAddExtra(sema, br_inst); + 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; + 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; + } + + AirInstRef result; + if (!body_completed && label.merges.results_len == 0) { + free(label.merges.results); + free(label.merges.br_list); + semaBlockDeinit(&child_block); + return false; + } else if (!body_completed) { + result = label.merges.results[0]; + } else { + result = AIR_REF_FROM_IP(IP_INDEX_UNREACHABLE_VALUE); + } + + free(label.merges.results); + free(label.merges.br_list); + semaBlockDeinit(&child_block); + instMapPut(&sema->inst_map, inst, result); + return true; + } + + // Runtime block: reserve an AIR block instruction. + uint32_t block_inst_idx = semaAddInstAsIndex(sema, AIR_INST_BLOCK, + (AirInstData) { .ty_pl = { .ty_ref = 0, .payload = 0 } }); + SemaBlockLabel label; + memset(&label, 0, sizeof(label)); + label.zir_block = inst; + label.merges.block_inst = block_inst_idx; + + SemaBlock child_block; + semaBlockInit(&child_block, sema, block); + child_block.is_comptime = false; + child_block.want_safety = block->want_safety; + child_block.want_safety_set = block->want_safety_set; + child_block.inlining = block->inlining; + child_block.label = &label; + + bool need_debug_scope = false; + child_block.need_debug_scope = &need_debug_scope; + + uint32_t saved_comptime_break = sema->comptime_break_inst; + sema->comptime_break_inst = UINT32_MAX; + bool body_completed + = analyzeBodyInner(sema, &child_block, inner_body, inner_body_len); + bool is_comptime_break = (sema->comptime_break_inst != UINT32_MAX); + if (!is_comptime_break) + sema->comptime_break_inst = saved_comptime_break; + + if (is_comptime_break && label.merges.results_len == 0) { + if (need_debug_scope && child_block.instructions_len > 0) { + AirInstData br_data; + memset(&br_data, 0, sizeof(br_data)); + br_data.br.block_inst = block_inst_idx; + br_data.br.operand = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + uint32_t br_inst = semaAddInstAsIndex(sema, AIR_INST_BR, br_data); + uint32_t body_len2 = child_block.instructions_len + 1; + uint32_t extra_start = semaAddExtra(sema, body_len2); + for (uint32_t ci = 0; ci < child_block.instructions_len; ci++) { + semaAddExtra(sema, child_block.instructions[ci]); + } + semaAddExtra(sema, br_inst); + sema->air_inst_datas[block_inst_idx].ty_pl.ty_ref + = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); + sema->air_inst_datas[block_inst_idx].ty_pl.payload = extra_start; + 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++] = block_inst_idx; + } else { + for (uint32_t ci = 0; ci < child_block.instructions_len; ci++) { + 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++] + = child_block.instructions[ci]; + } + } + + uint32_t break_inst = sema->comptime_break_inst; + ZirInstData bdata2 = sema->code.inst_datas[break_inst]; + uint32_t break_block_inst + = sema->code.extra[bdata2.break_data.payload_index + 1]; + + if (break_block_inst == inst) { + AirInstRef ct_result + = resolveInst(sema, bdata2.break_data.operand); + sema->comptime_break_inst = saved_comptime_break; + free(label.merges.results); + free(label.merges.br_list); + semaBlockDeinit(&child_block); + instMapPut(&sema->inst_map, inst, ct_result); + return true; + } + free(label.merges.results); + free(label.merges.br_list); + semaBlockDeinit(&child_block); + return false; + } + + (void)body_completed; + AirInstRef block_result; + if (label.merges.results_len == 0) { + if (need_debug_scope && block->need_debug_scope) + *block->need_debug_scope = true; + for (uint32_t ci = 0; ci < child_block.instructions_len; ci++) { + 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++] + = child_block.instructions[ci]; + } + free(label.merges.results); + free(label.merges.br_list); + semaBlockDeinit(&child_block); + return false; + } else if (label.merges.results_len == 1) { + uint32_t last_inst_idx = child_block.instructions_len - 1; + uint32_t last_inst = child_block.instructions[last_inst_idx]; + bool elide = !need_debug_scope + && sema->air_inst_tags[last_inst] == AIR_INST_BR + && sema->air_inst_datas[last_inst].br.block_inst == block_inst_idx; + if (elide) { + for (uint32_t ci = 0; ci < last_inst_idx; ci++) { + 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++] + = child_block.instructions[ci]; + } + block_result = label.merges.results[0]; + } else { + 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++] = block_inst_idx; + + uint32_t extra_start + = semaAddExtra(sema, child_block.instructions_len); + for (uint32_t ci = 0; ci < child_block.instructions_len; ci++) { + semaAddExtra(sema, child_block.instructions[ci]); + } + sema->air_inst_datas[block_inst_idx].ty_pl.ty_ref + = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); + sema->air_inst_datas[block_inst_idx].ty_pl.payload = extra_start; + sema->air_inst_datas[label.merges.br_list[0]].br.operand + = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + block_result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } + } else { + 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++] = block_inst_idx; + + TypeIndex resolved_ty = semaResolvePeerTypes( + sema, label.merges.results[0], label.merges.results[1]); + + uint32_t extra_start + = semaAddExtra(sema, child_block.instructions_len); + for (uint32_t ci = 0; ci < child_block.instructions_len; ci++) { + semaAddExtra(sema, child_block.instructions[ci]); + } + sema->air_inst_datas[block_inst_idx].ty_pl.ty_ref + = AIR_REF_FROM_IP(resolved_ty); + sema->air_inst_datas[block_inst_idx].ty_pl.payload = extra_start; + + for (uint32_t mi = 0; mi < label.merges.results_len; mi++) { + uint32_t br = label.merges.br_list[mi]; + AirInstRef br_operand = sema->air_inst_datas[br].br.operand; + TypeIndex br_ty = semaTypeOf(sema, br_operand); + if (br_ty == resolved_ty) + continue; + SemaBlock coerce_block; + semaBlockInit(&coerce_block, sema, block); + coerce_block.is_comptime = false; + coerce_block.want_safety = block->want_safety; + coerce_block.want_safety_set = block->want_safety_set; + coerce_block.inlining = block->inlining; + AirInstRef coerced + = semaCoerce(sema, &coerce_block, resolved_ty, br_operand); + if (coerce_block.instructions_len == 0) { + sema->air_inst_datas[br].br.operand = coerced; + } else { + assert(coerce_block + .instructions[coerce_block.instructions_len - 1] + == AIR_REF_TO_INST(coerced)); + uint32_t sub_bl = coerce_block.instructions_len + 1; + uint32_t sub_br_idx = semaAddInstAsIndex(sema, AIR_INST_BR, + (AirInstData) { + .br = { .block_inst = label.merges.block_inst, + .operand = coerced } }); + uint32_t sub_extra = semaAddExtra(sema, sub_bl); + for (uint32_t si = 0; si < coerce_block.instructions_len; + si++) { + semaAddExtra(sema, coerce_block.instructions[si]); + } + semaAddExtra(sema, sub_br_idx); + sema->air_inst_tags[br] = AIR_INST_BLOCK; + sema->air_inst_datas[br].ty_pl.ty_ref + = AIR_REF_FROM_IP(resolved_ty); + sema->air_inst_datas[br].ty_pl.payload = sub_extra; + } + semaBlockDeinit(&coerce_block); + } + + if (resolved_ty == IP_INDEX_VOID_TYPE) { + for (uint32_t mi = 0; mi < label.merges.results_len; mi++) { + sema->air_inst_datas[label.merges.br_list[mi]].br.operand + = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } + block_result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } else { + block_result = AIR_REF_FROM_INST(block_inst_idx); + } + } + + free(label.merges.results); + free(label.merges.br_list); + semaBlockDeinit(&child_block); + instMapPut(&sema->inst_map, inst, block_result); + return true; +} + +// --- zirBoolBr --- +// Ported from src/Sema.zig zirBoolBr. +static AirInstRef zirBoolBr(Sema* sema, SemaBlock* block, uint32_t inst) { + bool is_bool_or = (sema->code.inst_tags[inst] == ZIR_INST_BOOL_BR_OR); + uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index; + ZirInstRef lhs_ref = sema->code.extra[payload_index]; + uint32_t rhs_body_len = sema->code.extra[payload_index + 1]; + const uint32_t* rhs_body = &sema->code.extra[payload_index + 2]; + AirInstRef lhs = resolveInst(sema, lhs_ref); + lhs = semaCoerce(sema, block, IP_INDEX_BOOL_TYPE, lhs); + + // Comptime-known LHS: short-circuit. + if (lhs == AIR_REF_FROM_IP(IP_INDEX_BOOL_TRUE) && is_bool_or) + return AIR_REF_FROM_IP(IP_INDEX_BOOL_TRUE); + if (lhs == AIR_REF_FROM_IP(IP_INDEX_BOOL_FALSE) && !is_bool_or) + return AIR_REF_FROM_IP(IP_INDEX_BOOL_FALSE); + // Comptime-known LHS, evaluate RHS body on parent block. + if (lhs == AIR_REF_FROM_IP(IP_INDEX_BOOL_TRUE) + || lhs == AIR_REF_FROM_IP(IP_INDEX_BOOL_FALSE)) { + bool completed = analyzeBodyInner(sema, block, rhs_body, rhs_body_len); + AirInstRef rhs_result; + if (!completed) { + uint32_t bi = sema->comptime_break_inst; + ZirInstRef operand = sema->code.inst_datas[bi].break_data.operand; + rhs_result = resolveInst(sema, operand); + } else { + rhs_result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } + rhs_result = semaCoerce(sema, block, IP_INDEX_BOOL_TYPE, rhs_result); + return rhs_result; + } + + // Runtime: emit BLOCK(bool) + COND_BR with then/else bodies. + uint32_t block_inst_idx = semaAddInstAsIndex(sema, AIR_INST_BLOCK, + (AirInstData) { + .ty_pl = { .ty_ref = AIR_REF_FROM_IP(IP_INDEX_BOOL_TYPE), + .payload = 0 } }); + + SemaBlockLabel label; + memset(&label, 0, sizeof(label)); + label.zir_block = inst; + label.merges.block_inst = block_inst_idx; + + SemaBlock child_block; + semaBlockInit(&child_block, sema, block); + child_block.is_comptime = block->is_comptime; + child_block.want_safety = block->want_safety; + child_block.want_safety_set = block->want_safety_set; + child_block.inlining = block->inlining; + child_block.label = &label; + child_block.need_debug_scope = NULL; + + SemaBlock then_block; + semaBlockInit(&then_block, sema, &child_block); + then_block.is_comptime = false; + then_block.want_safety = block->want_safety; + then_block.want_safety_set = block->want_safety_set; + then_block.inlining = block->inlining; + then_block.label = child_block.label; + then_block.need_debug_scope = NULL; + + SemaBlock else_block; + semaBlockInit(&else_block, sema, &child_block); + else_block.is_comptime = false; + else_block.want_safety = block->want_safety; + else_block.want_safety_set = block->want_safety_set; + else_block.inlining = block->inlining; + else_block.label = child_block.label; + else_block.need_debug_scope = NULL; + + SemaBlock* lhs_block = is_bool_or ? &then_block : &else_block; + SemaBlock* rhs_block = is_bool_or ? &else_block : &then_block; + + // LHS block: emit BR with short-circuit result. + AirInstRef lhs_result = is_bool_or ? AIR_REF_FROM_IP(IP_INDEX_BOOL_TRUE) + : AIR_REF_FROM_IP(IP_INDEX_BOOL_FALSE); + { + AirInstData br_data; + memset(&br_data, 0, sizeof(br_data)); + br_data.br.block_inst = block_inst_idx; + br_data.br.operand = lhs_result; + AirInstRef br_ref = semaAddInst(lhs_block, AIR_INST_BR, br_data); + mergesAppend(&label.merges, lhs_result, AIR_REF_TO_INST(br_ref)); + } + + // Save/restore branch hint across RHS body. + int8_t parent_hint = sema->branch_hint; + sema->branch_hint = -1; + + // RHS block: evaluate body (ends with break_inline). + bool rhs_completed + = analyzeBodyInner(sema, rhs_block, rhs_body, rhs_body_len); + AirInstRef rhs_result; + if (!rhs_completed) { + uint32_t bi = sema->comptime_break_inst; + ZirInstRef operand = sema->code.inst_datas[bi].break_data.operand; + rhs_result = resolveInst(sema, operand); + } else { + rhs_result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } + rhs_result = semaCoerce(sema, rhs_block, IP_INDEX_BOOL_TYPE, rhs_result); + + // Emit BR for RHS result. + { + AirInstData br_data; + memset(&br_data, 0, sizeof(br_data)); + br_data.br.block_inst = block_inst_idx; + br_data.br.operand = rhs_result; + AirInstRef br_ref = semaAddInst(rhs_block, AIR_INST_BR, br_data); + mergesAppend(&label.merges, rhs_result, AIR_REF_TO_INST(br_ref)); + } + + uint8_t rhs_hint + = (sema->branch_hint >= 0) ? (uint8_t)sema->branch_hint : 0; + sema->branch_hint = parent_hint; + + // Emit COND_BR in child_block. + uint32_t extra_start = semaAddExtra(sema, then_block.instructions_len); + semaAddExtra(sema, else_block.instructions_len); + uint32_t branch_hints_packed = 0; + if (is_bool_or) { + branch_hints_packed |= (uint32_t)(rhs_hint & 0x7) << 3; + } else { + branch_hints_packed |= (uint32_t)(rhs_hint & 0x7); + } + branch_hints_packed |= (1u << 6); + branch_hints_packed |= (1u << 7); + semaAddExtra(sema, branch_hints_packed); + + for (uint32_t ti = 0; ti < then_block.instructions_len; ti++) { + semaAddExtra(sema, then_block.instructions[ti]); + } + for (uint32_t ei = 0; ei < else_block.instructions_len; ei++) { + semaAddExtra(sema, else_block.instructions[ei]); + } + + AirInstData cond_data; + memset(&cond_data, 0, sizeof(cond_data)); + cond_data.pl_op.operand = lhs; + cond_data.pl_op.payload = extra_start; + (void)semaAddInst(&child_block, AIR_INST_COND_BR, cond_data); + + // Resolve the block. + 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++] = block_inst_idx; + + uint32_t blk_extra_start + = semaAddExtra(sema, child_block.instructions_len); + for (uint32_t ci = 0; ci < child_block.instructions_len; ci++) { + semaAddExtra(sema, child_block.instructions[ci]); + } + sema->air_inst_datas[block_inst_idx].ty_pl.payload = blk_extra_start; + + free(label.merges.results); + free(label.merges.br_list); + semaBlockDeinit(&else_block); + semaBlockDeinit(&then_block); + semaBlockDeinit(&child_block); + return AIR_REF_FROM_INST(block_inst_idx); +} + +// --- zirCondbr --- +// Ported from src/Sema.zig zirCondbr. +// Returns the result of analyzeBodyInner for comptime paths, +// or false for runtime paths (condbr is always terminal). +static bool zirCondbr(Sema* sema, SemaBlock* block, uint32_t inst) { + uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index; + ZirInstRef condition_ref = sema->code.extra[payload_index]; + uint32_t then_body_len = sema->code.extra[payload_index + 1]; + uint32_t else_body_len = sema->code.extra[payload_index + 2]; + const uint32_t* then_body = &sema->code.extra[payload_index + 3]; + const uint32_t* else_body + = &sema->code.extra[payload_index + 3 + then_body_len]; + + AirInstRef uncasted_cond = resolveInst(sema, condition_ref); + AirInstRef cond + = semaCoerce(sema, block, IP_INDEX_BOOL_TYPE, uncasted_cond); + + if (cond == AIR_REF_FROM_IP(IP_INDEX_BOOL_TRUE)) + return analyzeBodyInner(sema, block, then_body, then_body_len); + if (cond == AIR_REF_FROM_IP(IP_INDEX_BOOL_FALSE)) + return analyzeBodyInner(sema, block, else_body, else_body_len); + + SemaBlock then_block; + semaBlockInit(&then_block, sema, block); + then_block.is_comptime = false; + then_block.want_safety = block->want_safety; + then_block.want_safety_set = block->want_safety_set; + then_block.inlining = block->inlining; + then_block.label = block->label; + then_block.need_debug_scope = NULL; + uint8_t true_hint + = analyzeBodyRuntimeBreak(sema, &then_block, then_body, then_body_len); + + SemaBlock else_block; + semaBlockInit(&else_block, sema, block); + else_block.is_comptime = false; + else_block.want_safety = block->want_safety; + else_block.want_safety_set = block->want_safety_set; + else_block.inlining = block->inlining; + else_block.label = block->label; + else_block.need_debug_scope = NULL; + uint8_t false_hint + = analyzeBodyRuntimeBreak(sema, &else_block, else_body, else_body_len); + + uint32_t extra_start = semaAddExtra(sema, then_block.instructions_len); + semaAddExtra(sema, else_block.instructions_len); + uint32_t branch_hints_packed = 0; + branch_hints_packed |= (uint32_t)(true_hint & 0x7); + branch_hints_packed |= (uint32_t)(false_hint & 0x7) << 3; + branch_hints_packed |= (1u << 6); + branch_hints_packed |= (1u << 7); + semaAddExtra(sema, branch_hints_packed); + + for (uint32_t ti = 0; ti < then_block.instructions_len; ti++) { + semaAddExtra(sema, then_block.instructions[ti]); + } + for (uint32_t ei = 0; ei < else_block.instructions_len; ei++) { + semaAddExtra(sema, else_block.instructions[ei]); + } + + AirInstData cond_data; + memset(&cond_data, 0, sizeof(cond_data)); + cond_data.pl_op.operand = cond; + cond_data.pl_op.payload = extra_start; + (void)semaAddInst(block, AIR_INST_COND_BR, cond_data); + + semaBlockDeinit(&then_block); + semaBlockDeinit(&else_block); + return false; +} + +// --- zirBreak --- +// Ported from src/Sema.zig zirBreak. +// Returns true if the break was a no-op (label not found), +// false if the break was executed (terminal). +static bool zirBreak(Sema* sema, SemaBlock* block, uint32_t inst) { + ZirInstRef operand_ref = sema->code.inst_datas[inst].break_data.operand; + uint32_t extra_pi = sema->code.inst_datas[inst].break_data.payload_index; + uint32_t zir_block_inst = sema->code.extra[extra_pi + 1]; + + AirInstRef operand; + if (operand_ref == ZIR_REF_NONE) { + operand = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } else { + operand = resolveInst(sema, operand_ref); + } + + SemaBlockLabel* label = findBlockLabel(block, zir_block_inst); + if (!label) + return true; + + if (block->is_comptime) { + mergesAppend(&label->merges, operand, 0); + return false; + } + + AirInstData br_data; + memset(&br_data, 0, sizeof(br_data)); + br_data.br.block_inst = label->merges.block_inst; + br_data.br.operand = operand; + AirInstRef br_ref = semaAddInst(block, AIR_INST_BR, br_data); + mergesAppend(&label->merges, operand, AIR_REF_TO_INST(br_ref)); + return false; +} + +// --- zirPanic --- +// Ported from src/Sema.zig zirPanic. +static void zirPanic(Sema* sema, SemaBlock* block, uint32_t inst) { + ZirInstRef msg_ref = sema->code.inst_datas[inst].un_node.operand; + AirInstRef msg = resolveInst(sema, msg_ref); + (void)msg; + if (sema->branch_hint < 0) { + sema->branch_hint = 3; // cold + } + AirInstData trap_data; + memset(&trap_data, 0, sizeof(trap_data)); + (void)semaAddInst(block, AIR_INST_TRAP, trap_data); +} + +// --- zirUnreachable --- +// Ported from src/Sema.zig zirUnreachable. +static void zirUnreachable(Sema* sema, SemaBlock* block, uint32_t inst) { + (void)inst; + if (block->want_safety && sema->branch_hint < 0) { + sema->branch_hint = 3; // cold + } + AirInstData data; + memset(&data, 0, sizeof(data)); + (void)semaAddInst(block, AIR_INST_UNREACH, data); +} + +// --- zirIntType --- +// Ported from src/Sema.zig zirIntType. +static AirInstRef zirIntType(Sema* sema, uint32_t inst) { + uint8_t signedness = sema->code.inst_datas[inst].int_type.signedness; + uint16_t bit_count = sema->code.inst_datas[inst].int_type.bit_count; + InternPoolKey key; + memset(&key, 0, sizeof(key)); + key.tag = IP_KEY_INT_TYPE; + key.data.int_type.bits = bit_count; + key.data.int_type.signedness = signedness; + return AIR_REF_FROM_IP(ipIntern(sema->ip, key)); +} + +// --- zirCoercePtrElemTy --- +// Ported from src/Sema.zig zirCoercePtrElemTy. +static AirInstRef zirCoercePtrElemTy(Sema* sema, uint32_t inst) { + uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index; + ZirInstRef val_ref = sema->code.extra[payload_index + 1]; + return resolveInst(sema, val_ref); +} + // --- analyzeBodyInner --- // Ported from src/Sema.zig analyzeBodyInner. // Main dispatch loop: iterates over ZIR instructions in a body and @@ -6372,166 +7589,17 @@ static bool analyzeBodyInner( sema->comptime_break_inst = inst; return false; - // ret_implicit: implicit return at end of function/block. - // Ported from src/Sema.zig zirRetImplicit / analyzeRet. - case ZIR_INST_RET_IMPLICIT: { - // Ported from src/Sema.zig zirRetImplicit → analyzeRet. - // When inlining, rewrite ret as br to inline block. - AirInstRef operand = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); - if (block->inlining) { - SemaBlockInlining* inl = block->inlining; - if (block->is_comptime) { - inl->comptime_result = operand; - inl->comptime_returned = true; - return false; - } - // Runtime inlining: rewrite ret as br. - AirInstData br_data; - memset(&br_data, 0, sizeof(br_data)); - br_data.br.block_inst = inl->merges.block_inst; - br_data.br.operand = operand; - AirInstRef br_ref = semaAddInst(block, AIR_INST_BR, br_data); - if (inl->merges.results_len >= inl->merges.results_cap) { - uint32_t new_cap = (inl->merges.results_cap == 0) - ? 4 - : inl->merges.results_cap * 2; - inl->merges.results = realloc( - inl->merges.results, new_cap * sizeof(AirInstRef)); - inl->merges.br_list = realloc( - inl->merges.br_list, new_cap * sizeof(uint32_t)); - if (!inl->merges.results || !inl->merges.br_list) - exit(1); - inl->merges.results_cap = new_cap; - inl->merges.br_list_cap = new_cap; - } - inl->merges.results[inl->merges.results_len++] = operand; - inl->merges.br_list[inl->merges.br_list_len++] - = AIR_REF_TO_INST(br_ref); - return false; - } - if (sema->fn_ret_ty != TYPE_NONE && !block->is_comptime) { - // Inside a function body: emit ret instruction. - AirInstData ret_data; - memset(&ret_data, 0, sizeof(ret_data)); - ret_data.un_op.operand = operand; - (void)semaAddInst(block, AIR_INST_RET, ret_data); - } + case ZIR_INST_RET_IMPLICIT: + zirRetImplicit(sema, block, inst); return false; - } - // ret_node: explicit `return ;`. - // Ported from src/Sema.zig zirRetNode / analyzeRet. - case ZIR_INST_RET_NODE: { - ZirInstRef operand_ref - = sema->code.inst_datas[inst].un_node.operand; - AirInstRef operand = resolveInst(sema, operand_ref); - // Coerce the operand to the function return type. - operand = semaCoerce(sema, block, sema->fn_ret_ty, operand); - - if (block->inlining) { - // Ported from src/Sema.zig analyzeRet (inlining path). - SemaBlockInlining* inl = block->inlining; - // Comptime inlining: signal ComptimeReturn instead of - // emitting a BR. Ported from Sema.zig lines 18955-18964. - if (block->is_comptime) { - inl->comptime_result = operand; - inl->comptime_returned = true; - return false; - } - // Runtime inlining: rewrite ret as br to the inline block. - AirInstData br_data; - memset(&br_data, 0, sizeof(br_data)); - br_data.br.block_inst = inl->merges.block_inst; - br_data.br.operand = operand; - AirInstRef br_ref = semaAddInst(block, AIR_INST_BR, br_data); - // Record merge result and br instruction. - if (inl->merges.results_len >= inl->merges.results_cap) { - uint32_t new_cap = (inl->merges.results_cap == 0) - ? 4 - : inl->merges.results_cap * 2; - inl->merges.results = realloc( - inl->merges.results, new_cap * sizeof(AirInstRef)); - inl->merges.br_list = realloc( - inl->merges.br_list, new_cap * sizeof(uint32_t)); - if (!inl->merges.results || !inl->merges.br_list) - exit(1); - inl->merges.results_cap = new_cap; - inl->merges.br_list_cap = new_cap; - } - inl->merges.results[inl->merges.results_len++] = operand; - inl->merges.br_list[inl->merges.br_list_len++] - = AIR_REF_TO_INST(br_ref); - return false; - } - - AirInstData ret_data; - memset(&ret_data, 0, sizeof(ret_data)); - ret_data.un_op.operand = operand; - // ReleaseFast: use ret, not ret_safe (no safety checks). - (void)semaAddInst(block, AIR_INST_RET, ret_data); + case ZIR_INST_RET_NODE: + zirRetNode(sema, block, inst); return false; - } - // ret_load: return by loading from the return pointer. - // Ported from src/Sema.zig zirRetLoad / analyzeRet. - case ZIR_INST_RET_LOAD: { - ZirInstRef operand_ref - = sema->code.inst_datas[inst].un_node.operand; - AirInstRef ret_ptr = resolveInst(sema, operand_ref); - - if (block->is_comptime || block->inlining) { - // Load from the return pointer. - TypeIndex ptr_ty = semaTypeOf(sema, ret_ptr); - TypeIndex elem_ty = ptrChildType(sema->ip, ptr_ty); - AirInstData load_data; - memset(&load_data, 0, sizeof(load_data)); - load_data.ty_op.ty_ref = AIR_REF_FROM_IP(elem_ty); - load_data.ty_op.operand = ret_ptr; - AirInstRef operand - = semaAddInst(block, AIR_INST_LOAD, load_data); - - if (block->inlining) { - SemaBlockInlining* inl = block->inlining; - if (block->is_comptime) { - inl->comptime_result = operand; - inl->comptime_returned = true; - return false; - } - // Runtime inlining: rewrite ret as br. - AirInstData br_data; - memset(&br_data, 0, sizeof(br_data)); - br_data.br.block_inst = inl->merges.block_inst; - br_data.br.operand = operand; - AirInstRef br_ref - = semaAddInst(block, AIR_INST_BR, br_data); - if (inl->merges.results_len >= inl->merges.results_cap) { - uint32_t new_cap = (inl->merges.results_cap == 0) - ? 4 - : inl->merges.results_cap * 2; - inl->merges.results = realloc( - inl->merges.results, new_cap * sizeof(AirInstRef)); - inl->merges.br_list = realloc( - inl->merges.br_list, new_cap * sizeof(uint32_t)); - if (!inl->merges.results || !inl->merges.br_list) - exit(1); - inl->merges.results_cap = new_cap; - inl->merges.br_list_cap = new_cap; - } - inl->merges.results[inl->merges.results_len++] = operand; - inl->merges.br_list[inl->merges.br_list_len++] - = AIR_REF_TO_INST(br_ref); - return false; - } - return false; - } - - // Non-inlining runtime: emit AIR ret_load. - AirInstData ret_data; - memset(&ret_data, 0, sizeof(ret_data)); - ret_data.un_op.operand = ret_ptr; - (void)semaAddInst(block, AIR_INST_RET_LOAD, ret_data); + case ZIR_INST_RET_LOAD: + zirRetLoad(sema, block, inst); return false; - } // func/func_inferred/func_fancy: function declaration. // Ported from src/Sema.zig zirFunc / zirFuncFancy. @@ -6603,216 +7671,21 @@ static bool analyzeBodyInner( i++; continue; - // float: comptime float literal (fits in f64). - // Ported from src/Sema.zig zirFloat. - case ZIR_INST_FLOAT: { - double val = sema->code.inst_datas[inst].float_val; - InternPoolKey key; - memset(&key, 0, sizeof(key)); - key.tag = IP_KEY_FLOAT; - key.data.float_val.ty = IP_INDEX_COMPTIME_FLOAT_TYPE; - key.data.float_val.val = val; - instMapPut(&sema->inst_map, inst, - AIR_REF_FROM_IP(ipIntern(sema->ip, key))); + case ZIR_INST_FLOAT: + instMapPut(&sema->inst_map, inst, zirFloat(sema, inst)); i++; continue; - } - // extended: handle extended opcodes. - case ZIR_INST_EXTENDED: { - uint16_t opcode = sema->code.inst_datas[inst].extended.opcode; - AirInstRef air_ref; - if (opcode == ZIR_EXT_STRUCT_DECL) { - zirStructDecl(sema, block, inst); - air_ref = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); - } else if (opcode == ZIR_EXT_REIFY) { - air_ref = zirReifyComptime(sema, inst); - } else if (opcode == ZIR_EXT_BRANCH_HINT) { - // Ported from src/Sema.zig zirBranchHint. - // Extra: {src_node, operand (ZIR ref)}. - uint32_t payload_index - = sema->code.inst_datas[inst].extended.operand; - ZirInstRef hint_ref = sema->code.extra[payload_index + 1]; - AirInstRef resolved = resolveInst(sema, hint_ref); - if (AIR_REF_IS_IP(resolved) && sema->branch_hint < 0) { - InternPoolKey key - = ipIndexToKey(sema->ip, AIR_REF_TO_IP(resolved)); - if (key.tag == IP_KEY_INT) { - sema->branch_hint = (int8_t)key.data.int_val.value_lo; - } - } - air_ref = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); - } else if (opcode == ZIR_EXT_ALLOC) { - // Ported from src/Sema.zig zirAllocExtended. - // Extended alloc with optional type and alignment. - uint16_t small = sema->code.inst_datas[inst].extended.small; - uint32_t payload_index - = sema->code.inst_datas[inst].extended.operand; - if (small & 0x1) { // has_type - uint32_t ei = payload_index + 1; // skip src_node - ZirInstRef type_ref = sema->code.extra[ei]; - AirInstRef resolved = resolveInst(sema, type_ref); - TypeIndex elem_ty = IP_INDEX_VOID_TYPE; - if (AIR_REF_IS_IP(resolved)) - elem_ty = AIR_REF_TO_IP(resolved); - InternPoolKey pkey; - memset(&pkey, 0, sizeof(pkey)); - pkey.tag = IP_KEY_PTR_TYPE; - pkey.data.ptr_type.child = elem_ty; - TypeIndex ptr_ty = ipIntern(sema->ip, pkey); - AirInstData adata; - memset(&adata, 0, sizeof(adata)); - adata.ty.ty_ref = AIR_REF_FROM_IP(ptr_ty); - air_ref = semaAddInst(block, AIR_INST_ALLOC, adata); - } else { - // No type: inferred alloc with alignment. - // Same as alloc_inferred but from extended. - AirInstData adata; - memset(&adata, 0, sizeof(adata)); - air_ref - = semaAddInst(block, AIR_INST_INFERRED_ALLOC, adata); - assert(sema->num_ia < 16); - uint32_t ia_idx = sema->num_ia++; - sema->ia_air[ia_idx] = AIR_REF_TO_INST(air_ref); - sema->ia_store[ia_idx] = UINT32_MAX; - sema->ia_is_const[ia_idx] = (small & 0x4) != 0; - } - } else if (opcode == ZIR_EXT_INPLACE_ARITH_RESULT_TY) { - // Ported from src/Sema.zig zirInplaceArithResultTy. - // operand is the ZIR ref of the loaded LHS value. - // For non-pointer types, the result type is the LHS type. - uint32_t operand - = sema->code.inst_datas[inst].extended.operand; - AirInstRef lhs = resolveInst(sema, operand); - TypeIndex lhs_ty = semaTypeOf(sema, lhs); - air_ref = AIR_REF_FROM_IP(lhs_ty); - } else { - air_ref = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); - } - instMapPut(&sema->inst_map, inst, air_ref); + case ZIR_INST_EXTENDED: + instMapPut(&sema->inst_map, inst, zirExtended(sema, block, inst)); i++; continue; - } - // 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; - // Extra data at payload_index: body_len followed by - // 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; - - // Save/restore comptime_break_inst to detect actual - // break_inline vs terminal instruction. - // Ported from src/Sema.zig block_inline ComptimeBreak - // detection (line 1798-1811). - uint32_t saved_cbi = sema->comptime_break_inst; - sema->comptime_break_inst = UINT32_MAX; - - analyzeBodyInner(sema, block, inner_body, inner_body_len); - - bool hit_comptime_break - = (sema->comptime_break_inst != UINT32_MAX); - - block->need_debug_scope = saved_need_debug_scope; - - if (readBool(&need_debug_scope)) { - if (!hit_comptime_break) { - // Body completed normally or hit terminal instruction - // with 0 merges. Propagate need_debug_scope to parent. - // Ported from src/Sema.zig resolveAnalyzedBlock - // 0-merge case (line 5985-5991). - if (saved_need_debug_scope) { - *saved_need_debug_scope = true; - } - } else { - // Break inline with need_debug_scope: create - // BLOCK+BR wrapper. Ported from src/Sema.zig - // resolveAnalyzedBlock 1-merge comptime case - // (line 6031-6058). - uint32_t new_insts_count - = block->instructions_len - block_index; - - uint32_t blk_inst - = semaAddInstAsIndex(sema, AIR_INST_BLOCK, - (AirInstData) { - .ty_pl = { .ty_ref = 0, .payload = 0 } }); - 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 - = semaAddInstAsIndex(sema, AIR_INST_BR, br_data); - - uint32_t body_len2 = new_insts_count + 1; - uint32_t extra_start = semaAddExtra(sema, body_len2); - for (uint32_t ci = block_index; - ci < block->instructions_len; ci++) { - semaAddExtra(sema, block->instructions[ci]); - } - semaAddExtra(sema, br_inst); - - 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; - - 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; - } - } - - if (hit_comptime_break) { - // break_inline occurred. Check if it targets this - // block or an outer one. - // Ported from src/Sema.zig block_inline (line 1856-1862). - uint32_t break_inst_zir = sema->comptime_break_inst; - ZirInstData break_data_zir - = sema->code.inst_datas[break_inst_zir]; - uint32_t break_payload - = break_data_zir.break_data.payload_index; - uint32_t break_block_inst = sema->code.extra[break_payload]; - ZirInstRef operand = break_data_zir.break_data.operand; - - if (break_block_inst == inst) { - // Break targets this block_inline. Consume it. - sema->comptime_break_inst = saved_cbi; - AirInstRef result; - if (operand == ZIR_REF_NONE) { - result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); - } else { - result = resolveInst(sema, operand); - } - instMapPut(&sema->inst_map, inst, result); - } else { - // Break targets outer block — propagate. - // comptime_break_inst already set. - return false; - } - } else { - // No break occurred. Restore comptime_break_inst. - sema->comptime_break_inst = saved_cbi; - } + case ZIR_INST_BLOCK_INLINE: + if (!zirBlockInline(sema, block, inst)) + return false; i++; continue; - } // declaration: only appears in container-type declaration // lists, not in analyzed bodies. Skip it. @@ -6820,32 +7693,10 @@ static bool analyzeBodyInner( i++; continue; - // export: @export builtin. Record the exported declaration name - // so that zirFunc can later analyze the target function's body. - // Ported from src/Sema.zig zirExport (subset: only extracts - // the declaration name from simple decl_ref/decl_val targets). - case ZIR_INST_EXPORT: { - uint32_t payload_index - = sema->code.inst_datas[inst].pl_node.payload_index; - uint32_t exported_ref = sema->code.extra[payload_index]; - if (exported_ref >= ZIR_REF_START_INDEX) { - uint32_t exported_inst = exported_ref - ZIR_REF_START_INDEX; - ZirInstTag etag = sema->code.inst_tags[exported_inst]; - if (etag == ZIR_INST_DECL_REF || etag == ZIR_INST_DECL_VAL) { - uint32_t name_idx - = sema->code.inst_datas[exported_inst].str_tok.start; - if (sema->num_exported_decl_names - < MAX_EXPORTED_DECL_NAMES) { - s_exported_decl_names[sema->num_exported_decl_names++] - = name_idx; - } - } - } - instMapPut( - &sema->inst_map, inst, AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE)); + case ZIR_INST_EXPORT: + zirExport(sema, inst); i++; continue; - } // Arithmetic: add, sub, mul and wrapping variants. case ZIR_INST_ADD: @@ -6952,47 +7803,11 @@ static bool analyzeBodyInner( i++; continue; - // @min / @max: comptime min/max of two values. - // Ported from src/Sema.zig zirMinMax. case ZIR_INST_MIN: - case ZIR_INST_MAX: { - bool is_max = (sema->code.inst_tags[inst] == ZIR_INST_MAX); - uint32_t pl = sema->code.inst_datas[inst].pl_node.payload_index; - ZirInstRef zir_lhs = sema->code.extra[pl]; - ZirInstRef zir_rhs = sema->code.extra[pl + 1]; - AirInstRef min_lhs = resolveInst(sema, zir_lhs); - AirInstRef min_rhs = resolveInst(sema, zir_rhs); - int64_t lv, rv; - if (isComptimeInt(sema, min_lhs, &lv) - && isComptimeInt(sema, min_rhs, &rv)) { - int64_t result_val - = is_max ? (lv > rv ? lv : rv) : (lv < rv ? lv : rv); - 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_lo - = (uint64_t)(result_val < 0 ? -result_val : result_val); - key.data.int_val.is_negative = (result_val < 0); - instMapPut(&sema->inst_map, inst, - AIR_REF_FROM_IP(ipIntern(sema->ip, key))); - } else { - // Runtime: emit AIR_INST_MAX or AIR_INST_MIN. - TypeIndex peer_ty - = semaResolvePeerTypes(sema, min_lhs, min_rhs); - AirInstRef c_lhs = semaCoerce(sema, block, peer_ty, min_lhs); - AirInstRef c_rhs = semaCoerce(sema, block, peer_ty, min_rhs); - AirInstData data; - memset(&data, 0, sizeof(data)); - data.bin_op.lhs = c_lhs; - data.bin_op.rhs = c_rhs; - instMapPut(&sema->inst_map, inst, - semaAddInst( - block, is_max ? AIR_INST_MAX : AIR_INST_MIN, data)); - } + case ZIR_INST_MAX: + instMapPut(&sema->inst_map, inst, zirMinMax(sema, block, inst)); i++; continue; - } // @bitCast. case ZIR_INST_BITCAST: @@ -7136,37 +7951,13 @@ static bool analyzeBodyInner( i++; continue; - // ret_ptr: get pointer to return value location. - // When inlining, emits ALLOC; otherwise emits ret_ptr. - // Ported from src/Sema.zig zirRetPtr. - case ZIR_INST_RET_PTR: { - TypeIndex ret_ty = sema->fn_ret_ty; - if (ret_ty == TYPE_NONE) - ret_ty = IP_INDEX_VOID_TYPE; - InternPoolKey pk; - memset(&pk, 0, sizeof(pk)); - pk.tag = IP_KEY_PTR_TYPE; - pk.data.ptr_type.child = ret_ty; - pk.data.ptr_type.flags = 0; - TypeIndex ptr_ty = ipIntern(sema->ip, pk); - AirInstData d; - memset(&d, 0, sizeof(d)); - d.ty.ty_ref = AIR_REF_FROM_IP(ptr_ty); - AirInstRef ref = semaAddInst(block, AIR_INST_ALLOC, d); - instMapPut(&sema->inst_map, inst, ref); + case ZIR_INST_RET_PTR: + instMapPut(&sema->inst_map, inst, zirRetPtr(sema, block)); i++; continue; - } - // ret_type: obtains the return type of the in-scope function. case ZIR_INST_RET_TYPE: - if (sema->fn_ret_ty != TYPE_NONE) { - instMapPut( - &sema->inst_map, inst, AIR_REF_FROM_IP(sema->fn_ret_ty)); - } else { - instMapPut(&sema->inst_map, inst, - AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE)); - } + instMapPut(&sema->inst_map, inst, zirRetType(sema)); i++; continue; @@ -7177,24 +7968,10 @@ static bool analyzeBodyInner( i++; continue; - // elem_type: extract the element type from a pointer type. - // Ported from src/Sema.zig zirElemType. - case ZIR_INST_ELEM_TYPE: { - ZirInstRef operand_ref - = sema->code.inst_datas[inst].un_node.operand; - AirInstRef resolved = resolveInst(sema, operand_ref); - if (!AIR_REF_IS_IP(resolved)) { - instMapPut(&sema->inst_map, inst, - AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE)); - i++; - continue; - } - TypeIndex ptr_ty = AIR_REF_TO_IP(resolved); - TypeIndex elem_ty = ptrChildType(sema->ip, ptr_ty); - instMapPut(&sema->inst_map, inst, AIR_REF_FROM_IP(elem_ty)); + case ZIR_INST_ELEM_TYPE: + instMapPut(&sema->inst_map, inst, zirElemType(sema, inst)); i++; continue; - } // @TypeOf(operand): resolve the type of the operand. case ZIR_INST_TYPEOF: @@ -7202,79 +7979,16 @@ static bool analyzeBodyInner( i++; continue; - // @TypeOf builtin with block body: analyze body, then typeof. - // Uses a child block and saves/restores AIR state. - case ZIR_INST_TYPEOF_BUILTIN: { - ZirInstData data = sema->code.inst_datas[inst]; - uint32_t payload_index = data.pl_node.payload_index; - uint32_t inner_body_len = sema->code.extra[payload_index]; - const uint32_t* inner_body = &sema->code.extra[payload_index + 1]; - - SemaBlock ct_block; - semaBlockInit(&ct_block, sema, block); - ct_block.is_comptime = true; - ct_block.is_typeof = true; - ct_block.inlining = block->inlining; - - // Upstream does NOT save/restore air_inst_len here. - // AIR instructions created during typeof body are orphaned - // but persist in the flat array (matching upstream behavior). - bool completed = analyzeBodyInner( - sema, &ct_block, inner_body, inner_body_len); - - AirInstRef result; - if (!completed) { - uint32_t break_inst = sema->comptime_break_inst; - ZirInstData break_data = sema->code.inst_datas[break_inst]; - ZirInstRef operand = break_data.break_data.operand; - if (operand == ZIR_REF_NONE) { - result = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); - } else { - AirInstRef resolved = resolveInst(sema, operand); - TypeIndex ty = semaTypeOf(sema, resolved); - result = AIR_REF_FROM_IP(ty); - } - } else { - result = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); - } - semaBlockDeinit(&ct_block); - instMapPut(&sema->inst_map, inst, result); + case ZIR_INST_TYPEOF_BUILTIN: + instMapPut( + &sema->inst_map, inst, zirTypeofBuiltin(sema, block, inst)); i++; continue; - } - // block_comptime: like block_inline but with extra reason word. - // Creates a temporary child block and evaluates comptime body. - case ZIR_INST_BLOCK_COMPTIME: { - ZirInstData data = sema->code.inst_datas[inst]; - uint32_t payload_index = data.pl_node.payload_index; - // Payload: reason (1 word), body_len (1 word), body... - uint32_t inner_body_len = sema->code.extra[payload_index + 1]; - const uint32_t* inner_body = &sema->code.extra[payload_index + 2]; - SemaBlock ct_block; - semaBlockInit(&ct_block, sema, block); - ct_block.is_comptime = true; - ct_block.inlining = block->inlining; - - bool completed = analyzeBodyInner( - sema, &ct_block, inner_body, inner_body_len); - - if (!completed) { - uint32_t break_inst = sema->comptime_break_inst; - ZirInstData break_data = sema->code.inst_datas[break_inst]; - ZirInstRef operand = break_data.break_data.operand; - AirInstRef result; - if (operand == ZIR_REF_NONE) { - result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); - } else { - result = resolveInst(sema, operand); - } - instMapPut(&sema->inst_map, inst, result); - } - semaBlockDeinit(&ct_block); + case ZIR_INST_BLOCK_COMPTIME: + zirBlockComptime(sema, block, inst); i++; continue; - } // @typeInfo(type): comptime type info extraction. case ZIR_INST_TYPE_INFO: @@ -7288,21 +8002,10 @@ static bool analyzeBodyInner( i++; continue; - // enum_literal: intern as IP_KEY_ENUM_LITERAL. - // Ported from src/Sema.zig zirEnumLiteral. - case ZIR_INST_ENUM_LITERAL: { - uint32_t name_start = sema->code.inst_datas[inst].str_tok.start; - const char* name - = (const char*)&sema->code.string_bytes[name_start]; - InternPoolKey key; - memset(&key, 0, sizeof(key)); - key.tag = IP_KEY_ENUM_LITERAL; - key.data.enum_literal = simpleStringHash(name); - instMapPut(&sema->inst_map, inst, - AIR_REF_FROM_IP(ipIntern(sema->ip, key))); + case ZIR_INST_ENUM_LITERAL: + instMapPut(&sema->inst_map, inst, zirEnumLiteral(sema, inst)); i++; continue; - } // switch_block: comptime switch on a known value. case ZIR_INST_SWITCH_BLOCK: @@ -7318,16 +8021,10 @@ static bool analyzeBodyInner( i++; continue; - // opt_eu_base_ptr_init: pass-through for struct init. - // Ported from Sema.zig zirOptEuBasePtrInit: resolves to the - // alloc pointer unchanged (for non-error-union/optional types). - case ZIR_INST_OPT_EU_BASE_PTR_INIT: { - ZirInstRef operand = sema->code.inst_datas[inst].un_node.operand; - AirInstRef resolved_op = resolveInst(sema, operand); - instMapPut(&sema->inst_map, inst, resolved_op); + case ZIR_INST_OPT_EU_BASE_PTR_INIT: + instMapPut(&sema->inst_map, inst, zirOptEuBasePtrInit(sema, inst)); i++; continue; - } // field_ptr / struct_init_field_ptr: runtime struct field pointer // access. struct_init_field_ptr uses the same pl_node + Field format @@ -7370,984 +8067,65 @@ static bool analyzeBodyInner( i++; continue; - // alloc_mut: mutable variable allocation. - // Ported from src/Sema.zig zirAlloc / zirAllocMut. - // Creates AIR_INST_ALLOC with pointer type *T. case ZIR_INST_ALLOC: - case ZIR_INST_ALLOC_MUT: { - ZirInstRef type_ref = sema->code.inst_datas[inst].un_node.operand; - AirInstRef resolved = resolveInst(sema, type_ref); - if (!AIR_REF_IS_IP(resolved)) { - i++; - continue; - } - TypeIndex elem_ty = AIR_REF_TO_IP(resolved); - // Create pointer type *T (mutable, size one). - InternPoolKey key; - memset(&key, 0, sizeof(key)); - key.tag = IP_KEY_PTR_TYPE; - key.data.ptr_type.child = elem_ty; - key.data.ptr_type.flags = 0; // mutable, size one - TypeIndex ptr_ty = ipIntern(sema->ip, key); - AirInstData data; - memset(&data, 0, sizeof(data)); - data.ty.ty_ref = AIR_REF_FROM_IP(ptr_ty); - instMapPut(&sema->inst_map, inst, - semaAddInst(block, AIR_INST_ALLOC, data)); + case ZIR_INST_ALLOC_MUT: + zirAlloc(sema, block, inst); i++; continue; - } - // alloc_inferred / alloc_inferred_mut: variable allocation with - // inferred type (const or mutable). - // Ported from src/Sema.zig zirAllocInferred. - // Emits AIR_INST_INFERRED_ALLOC placeholder; resolved later by - // resolve_inferred_alloc. case ZIR_INST_ALLOC_INFERRED: - case ZIR_INST_ALLOC_INFERRED_MUT: { - AirInstData data; - memset(&data, 0, sizeof(data)); - AirInstRef air_ref - = semaAddInst(block, AIR_INST_INFERRED_ALLOC, data); - instMapPut(&sema->inst_map, inst, air_ref); - // Track for resolve_inferred_alloc. - assert(sema->num_ia < 16); - uint32_t ia_idx = sema->num_ia++; - sema->ia_air[ia_idx] = AIR_REF_TO_INST(air_ref); - sema->ia_store[ia_idx] = UINT32_MAX; - sema->ia_is_const[ia_idx] = (inst_tag == ZIR_INST_ALLOC_INFERRED); + zirAllocInferred(sema, block, inst, true); i++; continue; - } - - // store_to_inferred_ptr: store to an inferred allocation. - // Ported from src/Sema.zig zirStoreToInferredPtr / - // storeToInferredAlloc. - // Emits a placeholder STORE; the type is resolved later by - // resolve_inferred_alloc. - case ZIR_INST_STORE_TO_INFERRED_PTR: { - uint32_t payload_index - = sema->code.inst_datas[inst].pl_node.payload_index; - ZirInstRef ptr_ref = sema->code.extra[payload_index]; - ZirInstRef val_ref = sema->code.extra[payload_index + 1]; - AirInstRef ptr = resolveInst(sema, ptr_ref); - AirInstRef val = resolveInst(sema, val_ref); - AirInstData data; - memset(&data, 0, sizeof(data)); - data.bin_op.lhs = ptr; - data.bin_op.rhs = val; - AirInstRef store_ref = semaAddInst(block, AIR_INST_STORE, data); - // Track the store for the inferred alloc. - uint32_t ptr_inst = AIR_REF_TO_INST(ptr); - for (uint32_t k = 0; k < sema->num_ia; k++) { - if (sema->ia_air[k] == ptr_inst) { - sema->ia_store[k] = AIR_REF_TO_INST(store_ref); - break; - } - } + case ZIR_INST_ALLOC_INFERRED_MUT: + zirAllocInferred(sema, block, inst, false); i++; continue; - } - // resolve_inferred_alloc: resolve the type of an inferred alloc. - // Ported from src/Sema.zig zirResolveInferredAlloc. - // Patches INFERRED_ALLOC → ALLOC with the resolved pointer type. - case ZIR_INST_RESOLVE_INFERRED_ALLOC: { - ZirInstRef alloc_ref = sema->code.inst_datas[inst].un_node.operand; - AirInstRef ptr = resolveInst(sema, alloc_ref); - uint32_t ptr_inst = AIR_REF_TO_INST(ptr); - // Find tracked inferred alloc. - uint32_t ia_idx = UINT32_MAX; - for (uint32_t k = 0; k < sema->num_ia; k++) { - if (sema->ia_air[k] == ptr_inst) { - ia_idx = k; - break; - } - } - if (ia_idx == UINT32_MAX) { - // Skip unresolved inferred allocs (comptime or - // unhandled variant). - instMapPut(&sema->inst_map, inst, ptr); - i++; - continue; - } - // Get the store's RHS type. - assert(sema->ia_store[ia_idx] != UINT32_MAX); - uint32_t store_inst = sema->ia_store[ia_idx]; - AirInstRef rhs = sema->air_inst_datas[store_inst].bin_op.rhs; - TypeIndex elem_ty = semaTypeOf(sema, rhs); - // Create pointer type *T (mutable, size one). - InternPoolKey key; - memset(&key, 0, sizeof(key)); - key.tag = IP_KEY_PTR_TYPE; - key.data.ptr_type.child = elem_ty; - key.data.ptr_type.flags = 0; // mutable, size one - TypeIndex ptr_ty = ipIntern(sema->ip, key); - // Patch INFERRED_ALLOC → ALLOC. - sema->air_inst_tags[ptr_inst] = (uint8_t)AIR_INST_ALLOC; - AirInstData alloc_data; - memset(&alloc_data, 0, sizeof(alloc_data)); - alloc_data.ty.ty_ref = AIR_REF_FROM_IP(ptr_ty); - sema->air_inst_datas[ptr_inst] = alloc_data; - // Re-do the store with proper coercion. - // Ported from Sema.zig resolveInferredAlloc re-patching loop. - // storePtr2 creates a replacement store in a sub-block; - // the placeholder store's tag+data are patched in place. - { - AirInstRef store_lhs - = sema->air_inst_datas[store_inst].bin_op.lhs; - AirInstRef store_rhs - = sema->air_inst_datas[store_inst].bin_op.rhs; - // Coerce the operand to the resolved element type. - AirInstRef coerced - = semaCoerce(sema, block, elem_ty, store_rhs); - // Create replacement STORE instruction (adds to - // air_instructions but NOT to block). - AirInstData store_data; - memset(&store_data, 0, sizeof(store_data)); - store_data.bin_op.lhs = store_lhs; - store_data.bin_op.rhs = coerced; - uint32_t new_store_idx - = semaAddInstAsIndex(sema, AIR_INST_STORE, store_data); - // Patch the placeholder store with the replacement. - sema->air_inst_tags[store_inst] - = sema->air_inst_tags[new_store_idx]; - sema->air_inst_datas[store_inst] - = sema->air_inst_datas[new_store_idx]; - } - // If const, add bitcast to const pointer type. - // Ported from Sema.zig makePtrConst. - if (sema->ia_is_const[ia_idx]) { - InternPoolKey ckey; - memset(&ckey, 0, sizeof(ckey)); - ckey.tag = IP_KEY_PTR_TYPE; - ckey.data.ptr_type.child = elem_ty; - ckey.data.ptr_type.flags = 1; // const - TypeIndex const_ptr_ty = ipIntern(sema->ip, ckey); - AirInstData bc_data; - memset(&bc_data, 0, sizeof(bc_data)); - bc_data.ty_op.ty_ref = AIR_REF_FROM_IP(const_ptr_ty); - bc_data.ty_op.operand = ptr; - ptr = semaAddInst(block, AIR_INST_BITCAST, bc_data); - } - instMapPut(&sema->inst_map, inst, ptr); + case ZIR_INST_STORE_TO_INFERRED_PTR: + zirStoreToInferredPtr(sema, block, inst); i++; continue; - } - // make_ptr_const: convert mutable alloc pointer to const. - // Ported from src/Sema.zig zirMakePtrConst / makePtrConst. - // Emits AIR bitcast from *T to *const T. - case ZIR_INST_MAKE_PTR_CONST: { - ZirInstRef alloc_ref = sema->code.inst_datas[inst].un_node.operand; - AirInstRef alloc_ptr = resolveInst(sema, alloc_ref); - TypeIndex ptr_ty = semaTypeOf(sema, alloc_ptr); - TypeIndex child = ptrChildType(sema->ip, ptr_ty); - // Create *const T pointer type. - InternPoolKey ckey; - memset(&ckey, 0, sizeof(ckey)); - ckey.tag = IP_KEY_PTR_TYPE; - ckey.data.ptr_type.child = child; - ckey.data.ptr_type.flags = PTR_FLAGS_IS_CONST; - TypeIndex const_ptr_ty = ipIntern(sema->ip, ckey); - AirInstData bc_data; - memset(&bc_data, 0, sizeof(bc_data)); - bc_data.ty_op.ty_ref = AIR_REF_FROM_IP(const_ptr_ty); - bc_data.ty_op.operand = alloc_ptr; - AirInstRef result = semaAddInst(block, AIR_INST_BITCAST, bc_data); - instMapPut(&sema->inst_map, inst, result); + case ZIR_INST_RESOLVE_INFERRED_ALLOC: + zirResolveInferredAlloc(sema, block, inst); i++; continue; - } - // block: runtime block. - // Ported from src/Sema.zig zirBlock / resolveAnalyzedBlock. - // In Zig, comptime .block redirects to .block_inline (no AIR - // BLOCK pre-allocated). We handle that here by checking - // is_comptime BEFORE allocating the AIR instruction. - case ZIR_INST_BLOCK: { - ZirInstData bdata = sema->code.inst_datas[inst]; - uint32_t payload_index = bdata.pl_node.payload_index; - uint32_t inner_body_len = sema->code.extra[payload_index]; - const uint32_t* inner_body = &sema->code.extra[payload_index + 1]; - - if (block->is_comptime) { - SemaBlockLabel label; - memset(&label, 0, sizeof(label)); - label.zir_block = inst; - // Track need_debug_scope for BLOCK wrapping. - 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; - - SemaBlock child_block; - semaBlockInit(&child_block, sema, block); - child_block.is_comptime = true; - child_block.want_safety = block->want_safety; - child_block.want_safety_set = block->want_safety_set; - child_block.inlining = block->inlining; - child_block.label = &label; - - bool body_completed = analyzeBodyInner( - sema, &child_block, inner_body, inner_body_len); - - // Copy any child instructions to parent block. - for (uint32_t ci = 0; ci < child_block.instructions_len; - ci++) { - 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++] - = child_block.instructions[ci]; - } - - block->need_debug_scope = saved_need_debug_scope; - - // Wrap in BLOCK+BR if debug scope is needed. - if (readBool(&need_debug_scope)) { - uint32_t new_insts_count - = block->instructions_len - block_index; - - uint32_t blk_inst - = semaAddInstAsIndex(sema, AIR_INST_BLOCK, - (AirInstData) { - .ty_pl = { .ty_ref = 0, .payload = 0 } }); - 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 - = semaAddInstAsIndex(sema, AIR_INST_BR, br_data); - - uint32_t body_len2 = new_insts_count + 1; - uint32_t extra_start = semaAddExtra(sema, body_len2); - for (uint32_t ci = block_index; - ci < block->instructions_len; ci++) { - semaAddExtra(sema, block->instructions[ci]); - } - semaAddExtra(sema, br_inst); - - 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; - - 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; - } - - AirInstRef result; - if (!body_completed && label.merges.results_len == 0) { - // ComptimeReturn or break to outer block: - // propagate upward. - free(label.merges.results); - free(label.merges.br_list); - semaBlockDeinit(&child_block); - return false; - } else if (!body_completed) { - // Break targeted this block. - result = label.merges.results[0]; - } else { - // Body completed normally (no break). - result = AIR_REF_FROM_IP(IP_INDEX_UNREACHABLE_VALUE); - } - - free(label.merges.results); - free(label.merges.br_list); - semaBlockDeinit(&child_block); - instMapPut(&sema->inst_map, inst, result); - i++; - continue; - } - - // Runtime block: reserve an AIR block instruction - // (data filled later). - uint32_t block_inst_idx = semaAddInstAsIndex(sema, AIR_INST_BLOCK, - (AirInstData) { .ty_pl = { .ty_ref = 0, .payload = 0 } }); - // Set up label so break instructions can find this block. - SemaBlockLabel label; - memset(&label, 0, sizeof(label)); - label.zir_block = inst; - label.merges.block_inst = block_inst_idx; - - SemaBlock child_block; - semaBlockInit(&child_block, sema, block); - child_block.is_comptime = false; - child_block.want_safety = block->want_safety; - child_block.want_safety_set = block->want_safety_set; - child_block.inlining = block->inlining; - child_block.label = &label; - - // Track whether a debug variable is emitted inside - // this block, requiring the block to be preserved. - // Ported from src/Sema.zig resolveBlockBody. - bool need_debug_scope = false; - child_block.need_debug_scope = &need_debug_scope; - - uint32_t saved_comptime_break = sema->comptime_break_inst; - sema->comptime_break_inst = UINT32_MAX; - bool body_completed = analyzeBodyInner( - sema, &child_block, inner_body, inner_body_len); - bool is_comptime_break = (sema->comptime_break_inst != UINT32_MAX); - if (!is_comptime_break) - sema->comptime_break_inst = saved_comptime_break; - - // Resolve the block: write extra data and patch type. - // Ported from src/Sema.zig resolveBlockBody / - // resolveAnalyzedBlock. - - // Handle ComptimeBreak: a break_inline was encountered in - // the body. Ported from Sema.zig resolveBlockBody line 5915. - if (is_comptime_break && label.merges.results_len == 0) { - // Copy child instructions to parent block. - if (need_debug_scope && child_block.instructions_len > 0) { - // Wrap in BLOCK for scoping. - AirInstData br_data; - memset(&br_data, 0, sizeof(br_data)); - br_data.br.block_inst = block_inst_idx; - br_data.br.operand = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); - uint32_t br_inst - = semaAddInstAsIndex(sema, AIR_INST_BR, br_data); - uint32_t body_len2 = child_block.instructions_len + 1; - uint32_t extra_start = semaAddExtra(sema, body_len2); - for (uint32_t ci = 0; ci < child_block.instructions_len; - ci++) { - semaAddExtra(sema, child_block.instructions[ci]); - } - semaAddExtra(sema, br_inst); - sema->air_inst_datas[block_inst_idx].ty_pl.ty_ref - = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); - sema->air_inst_datas[block_inst_idx].ty_pl.payload - = extra_start; - 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++] - = block_inst_idx; - } else { - // Copy instructions directly to parent. - for (uint32_t ci = 0; ci < child_block.instructions_len; - ci++) { - 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++] - = child_block.instructions[ci]; - } - } - - // Check if the comptime break targets this block. - uint32_t break_inst = sema->comptime_break_inst; - ZirInstData bdata2 = sema->code.inst_datas[break_inst]; - uint32_t break_block_inst - = sema->code.extra[bdata2.break_data.payload_index + 1]; - - if (break_block_inst == inst) { - // Break targets this block: resolve operand. - AirInstRef ct_result - = resolveInst(sema, bdata2.break_data.operand); - sema->comptime_break_inst = saved_comptime_break; - free(label.merges.results); - free(label.merges.br_list); - semaBlockDeinit(&child_block); - instMapPut(&sema->inst_map, inst, ct_result); - i++; - continue; - } else { - // Break targets outer block: propagate. - free(label.merges.results); - free(label.merges.br_list); - semaBlockDeinit(&child_block); - return false; - } - } - - (void)body_completed; - AirInstRef block_result; - if (label.merges.results_len == 0) { - // No breaks (noreturn block): copy instructions to - // parent. If need_debug_scope, forward to parent. - // Ported from Sema.zig line 1978: isNoReturn check - // causes break from analyzeBodyInner loop — remaining - // ZIR instructions in this body are NOT processed. - if (need_debug_scope && block->need_debug_scope) - *block->need_debug_scope = true; - for (uint32_t ci = 0; ci < child_block.instructions_len; - ci++) { - 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++] - = child_block.instructions[ci]; - } - free(label.merges.results); - free(label.merges.br_list); - semaBlockDeinit(&child_block); - return false; - } else if (label.merges.results_len == 1) { - // Single break: check if last instruction is the - // break. Only elide if need_debug_scope is false. - // Ported from src/Sema.zig resolveAnalyzedBlock. - uint32_t last_inst_idx = child_block.instructions_len - 1; - uint32_t last_inst = child_block.instructions[last_inst_idx]; - bool elide = !need_debug_scope - && sema->air_inst_tags[last_inst] == AIR_INST_BR - && sema->air_inst_datas[last_inst].br.block_inst - == block_inst_idx; - if (elide) { - // Elide the block: copy instructions (excluding - // trailing br) to parent. - for (uint32_t ci = 0; ci < last_inst_idx; ci++) { - 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++] - = child_block.instructions[ci]; - } - block_result = label.merges.results[0]; - } else { - // Need runtime block. Since result is - // comptime-known (void), use void block type. - // Emit the block instruction in the parent. - 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++] - = block_inst_idx; - - uint32_t extra_start - = semaAddExtra(sema, child_block.instructions_len); - for (uint32_t ci = 0; ci < child_block.instructions_len; - ci++) { - semaAddExtra(sema, child_block.instructions[ci]); - } - sema->air_inst_datas[block_inst_idx].ty_pl.ty_ref - = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); - sema->air_inst_datas[block_inst_idx].ty_pl.payload - = extra_start; - - // Rewrite the break operand to void. - // Ported from Sema.zig resolveAnalyzedBlock: - // void-typed blocks return void_value. - sema->air_inst_datas[label.merges.br_list[0]].br.operand - = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); - - block_result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); - } - } else { - // Multiple breaks: need full runtime block with peer - // type resolution. - 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++] - = block_inst_idx; - - TypeIndex resolved_ty = semaResolvePeerTypes( - sema, label.merges.results[0], label.merges.results[1]); - - uint32_t extra_start - = semaAddExtra(sema, child_block.instructions_len); - for (uint32_t ci = 0; ci < child_block.instructions_len; - ci++) { - semaAddExtra(sema, child_block.instructions[ci]); - } - sema->air_inst_datas[block_inst_idx].ty_pl.ty_ref - = AIR_REF_FROM_IP(resolved_ty); - sema->air_inst_datas[block_inst_idx].ty_pl.payload - = extra_start; - - // Coerce BR operands to the resolved peer type. - // Ported from src/Sema.zig resolveAnalyzedBlock - // lines 6125-6140. - for (uint32_t mi = 0; mi < label.merges.results_len; mi++) { - uint32_t br = label.merges.br_list[mi]; - AirInstRef br_operand - = sema->air_inst_datas[br].br.operand; - TypeIndex br_ty = semaTypeOf(sema, br_operand); - if (br_ty == resolved_ty) - continue; - // Coerce. For constant→constant (e.g. - // comptime_int→concrete int), this produces - // no new AIR instructions. - SemaBlock coerce_block; - semaBlockInit(&coerce_block, sema, block); - coerce_block.is_comptime = false; - coerce_block.want_safety = block->want_safety; - coerce_block.want_safety_set = block->want_safety_set; - coerce_block.inlining = block->inlining; - AirInstRef coerced = semaCoerce( - sema, &coerce_block, resolved_ty, br_operand); - if (coerce_block.instructions_len == 0) { - sema->air_inst_datas[br].br.operand = coerced; - } else { - // Runtime coercion: wrap BR in - // a sub-block. Ported from - // Sema.zig lines 6141-6170. - assert(coerce_block - .instructions[coerce_block.instructions_len - - 1] - == AIR_REF_TO_INST(coerced)); - uint32_t sub_bl = coerce_block.instructions_len + 1; - uint32_t sub_br_idx - = semaAddInstAsIndex(sema, AIR_INST_BR, - (AirInstData) { .br - = { .block_inst = label.merges.block_inst, - .operand = coerced } }); - uint32_t sub_extra = semaAddExtra(sema, sub_bl); - for (uint32_t si = 0; - si < coerce_block.instructions_len; si++) { - semaAddExtra(sema, coerce_block.instructions[si]); - } - semaAddExtra(sema, sub_br_idx); - sema->air_inst_tags[br] = AIR_INST_BLOCK; - sema->air_inst_datas[br].ty_pl.ty_ref - = AIR_REF_FROM_IP(resolved_ty); - sema->air_inst_datas[br].ty_pl.payload = sub_extra; - } - semaBlockDeinit(&coerce_block); - } - - // Ported from Sema.zig resolveAnalyzedBlock: - // void-typed blocks return void_value, rewriting - // all br operands to void_value. - if (resolved_ty == IP_INDEX_VOID_TYPE) { - for (uint32_t mi = 0; mi < label.merges.results_len; - mi++) { - sema->air_inst_datas[label.merges.br_list[mi]] - .br.operand - = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); - } - block_result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); - } else { - block_result = AIR_REF_FROM_INST(block_inst_idx); - } - } - - free(label.merges.results); - free(label.merges.br_list); - semaBlockDeinit(&child_block); - instMapPut(&sema->inst_map, inst, block_result); - i++; - continue; - } - - // bool_br_and / bool_br_or: short-circuiting boolean operators. - // Ported from src/Sema.zig zirBoolBr. - case ZIR_INST_BOOL_BR_AND: - case ZIR_INST_BOOL_BR_OR: { - bool is_bool_or - = (sema->code.inst_tags[inst] == ZIR_INST_BOOL_BR_OR); - uint32_t payload_index - = sema->code.inst_datas[inst].pl_node.payload_index; - ZirInstRef lhs_ref = sema->code.extra[payload_index]; - uint32_t rhs_body_len = sema->code.extra[payload_index + 1]; - const uint32_t* rhs_body = &sema->code.extra[payload_index + 2]; - AirInstRef lhs = resolveInst(sema, lhs_ref); - lhs = semaCoerce(sema, block, IP_INDEX_BOOL_TYPE, lhs); - - // Comptime-known LHS: short-circuit. - if (lhs == AIR_REF_FROM_IP(IP_INDEX_BOOL_TRUE) && is_bool_or) { - instMapPut(&sema->inst_map, inst, - AIR_REF_FROM_IP(IP_INDEX_BOOL_TRUE)); - i++; - continue; - } - if (lhs == AIR_REF_FROM_IP(IP_INDEX_BOOL_FALSE) && !is_bool_or) { - instMapPut(&sema->inst_map, inst, - AIR_REF_FROM_IP(IP_INDEX_BOOL_FALSE)); - i++; - continue; - } - // Comptime-known LHS, evaluate RHS body on parent block. - if (lhs == AIR_REF_FROM_IP(IP_INDEX_BOOL_TRUE) - || lhs == AIR_REF_FROM_IP(IP_INDEX_BOOL_FALSE)) { - bool completed - = analyzeBodyInner(sema, block, rhs_body, rhs_body_len); - AirInstRef rhs_result; - if (!completed) { - uint32_t bi = sema->comptime_break_inst; - ZirInstRef operand - = sema->code.inst_datas[bi].break_data.operand; - rhs_result = resolveInst(sema, operand); - } else { - rhs_result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); - } - rhs_result - = semaCoerce(sema, block, IP_INDEX_BOOL_TYPE, rhs_result); - instMapPut(&sema->inst_map, inst, rhs_result); - i++; - continue; - } - - // Runtime: emit BLOCK(bool) + COND_BR with then/else bodies. - uint32_t block_inst_idx = semaAddInstAsIndex(sema, AIR_INST_BLOCK, - (AirInstData) { - .ty_pl = { .ty_ref = AIR_REF_FROM_IP(IP_INDEX_BOOL_TYPE), - .payload = 0 } }); - - SemaBlockLabel label; - memset(&label, 0, sizeof(label)); - label.zir_block = inst; - label.merges.block_inst = block_inst_idx; - - SemaBlock child_block; - semaBlockInit(&child_block, sema, block); - // Inherit comptime from parent (matches Zig Sema.zig). - child_block.is_comptime = block->is_comptime; - child_block.want_safety = block->want_safety; - child_block.want_safety_set = block->want_safety_set; - child_block.inlining = block->inlining; - child_block.label = &label; - child_block.need_debug_scope = NULL; - - SemaBlock then_block; - semaBlockInit(&then_block, sema, &child_block); - then_block.is_comptime = false; - then_block.want_safety = block->want_safety; - then_block.want_safety_set = block->want_safety_set; - then_block.inlining = block->inlining; - then_block.label = child_block.label; - then_block.need_debug_scope = NULL; - - SemaBlock else_block; - semaBlockInit(&else_block, sema, &child_block); - else_block.is_comptime = false; - else_block.want_safety = block->want_safety; - else_block.want_safety_set = block->want_safety_set; - else_block.inlining = block->inlining; - else_block.label = child_block.label; - else_block.need_debug_scope = NULL; - - SemaBlock* lhs_block = is_bool_or ? &then_block : &else_block; - SemaBlock* rhs_block = is_bool_or ? &else_block : &then_block; - - // LHS block: emit BR with short-circuit result. - AirInstRef lhs_result = is_bool_or - ? AIR_REF_FROM_IP(IP_INDEX_BOOL_TRUE) - : AIR_REF_FROM_IP(IP_INDEX_BOOL_FALSE); - { - AirInstData br_data; - memset(&br_data, 0, sizeof(br_data)); - br_data.br.block_inst = block_inst_idx; - br_data.br.operand = lhs_result; - AirInstRef br_ref - = semaAddInst(lhs_block, AIR_INST_BR, br_data); - mergesAppend( - &label.merges, lhs_result, AIR_REF_TO_INST(br_ref)); - } - - // Save/restore branch hint across RHS body. - int8_t parent_hint = sema->branch_hint; - sema->branch_hint = -1; - - // RHS block: evaluate body (ends with break_inline). - bool rhs_completed - = analyzeBodyInner(sema, rhs_block, rhs_body, rhs_body_len); - AirInstRef rhs_result; - if (!rhs_completed) { - uint32_t bi = sema->comptime_break_inst; - ZirInstRef operand - = sema->code.inst_datas[bi].break_data.operand; - rhs_result = resolveInst(sema, operand); - } else { - rhs_result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); - } - rhs_result - = semaCoerce(sema, rhs_block, IP_INDEX_BOOL_TYPE, rhs_result); - - // Emit BR for RHS result. - { - AirInstData br_data; - memset(&br_data, 0, sizeof(br_data)); - br_data.br.block_inst = block_inst_idx; - br_data.br.operand = rhs_result; - AirInstRef br_ref - = semaAddInst(rhs_block, AIR_INST_BR, br_data); - mergesAppend( - &label.merges, rhs_result, AIR_REF_TO_INST(br_ref)); - } - - uint8_t rhs_hint - = (sema->branch_hint >= 0) ? (uint8_t)sema->branch_hint : 0; - sema->branch_hint = parent_hint; - - // Emit COND_BR in child_block. - uint32_t extra_start - = semaAddExtra(sema, then_block.instructions_len); - semaAddExtra(sema, else_block.instructions_len); - // Branch hints. - uint32_t branch_hints_packed = 0; - if (is_bool_or) { - // true=none, false=rhs_hint - branch_hints_packed |= (uint32_t)(rhs_hint & 0x7) << 3; - } else { - // true=rhs_hint, false=none - branch_hints_packed |= (uint32_t)(rhs_hint & 0x7); - } - branch_hints_packed |= (1u << 6); // then_cov = poi - branch_hints_packed |= (1u << 7); // else_cov = poi - semaAddExtra(sema, branch_hints_packed); - - for (uint32_t ti = 0; ti < then_block.instructions_len; ti++) { - semaAddExtra(sema, then_block.instructions[ti]); - } - for (uint32_t ei = 0; ei < else_block.instructions_len; ei++) { - semaAddExtra(sema, else_block.instructions[ei]); - } - - AirInstData cond_data; - memset(&cond_data, 0, sizeof(cond_data)); - cond_data.pl_op.operand = lhs; - cond_data.pl_op.payload = extra_start; - (void)semaAddInst(&child_block, AIR_INST_COND_BR, cond_data); - - // Resolve the block (same as zirBlock resolveAnalyzedBlock). - // bool_br always has 2 merges (lhs + rhs BR). - 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++] = block_inst_idx; - - uint32_t blk_extra_start - = semaAddExtra(sema, child_block.instructions_len); - for (uint32_t ci = 0; ci < child_block.instructions_len; ci++) { - semaAddExtra(sema, child_block.instructions[ci]); - } - sema->air_inst_datas[block_inst_idx].ty_pl.payload - = blk_extra_start; - - free(label.merges.results); - free(label.merges.br_list); - semaBlockDeinit(&else_block); - semaBlockDeinit(&then_block); - semaBlockDeinit(&child_block); + case ZIR_INST_MAKE_PTR_CONST: instMapPut( - &sema->inst_map, inst, AIR_REF_FROM_INST(block_inst_idx)); + &sema->inst_map, inst, zirMakePtrConst(sema, block, inst)); i++; continue; - } - // condbr / condbr_inline: conditional branch. - // Ported from src/Sema.zig zirCondbr. - // condbr_inline has the same data layout; used for comptime if. - case ZIR_INST_CONDBR_INLINE: - case ZIR_INST_CONDBR: { - uint32_t payload_index - = sema->code.inst_datas[inst].pl_node.payload_index; - ZirInstRef condition_ref = sema->code.extra[payload_index]; - uint32_t then_body_len = sema->code.extra[payload_index + 1]; - uint32_t else_body_len = sema->code.extra[payload_index + 2]; - const uint32_t* then_body = &sema->code.extra[payload_index + 3]; - const uint32_t* else_body - = &sema->code.extra[payload_index + 3 + then_body_len]; - - AirInstRef uncasted_cond = resolveInst(sema, condition_ref); - AirInstRef cond - = semaCoerce(sema, block, IP_INDEX_BOOL_TYPE, uncasted_cond); - - // Comptime-known condition: only analyze the taken branch - // on the parent block (matches Zig sema line 18343-18353). - if (cond == AIR_REF_FROM_IP(IP_INDEX_BOOL_TRUE)) { - return analyzeBodyInner(sema, block, then_body, then_body_len); - } - if (cond == AIR_REF_FROM_IP(IP_INDEX_BOOL_FALSE)) { - return analyzeBodyInner(sema, block, else_body, else_body_len); - } - // Analyze then-body in a sub-block, collecting branch hint. - // Upstream (Sema.zig line 18364): need_debug_scope = null - // because this body is emitted regardless. - SemaBlock then_block; - semaBlockInit(&then_block, sema, block); - then_block.is_comptime = false; - then_block.want_safety = block->want_safety; - then_block.want_safety_set = block->want_safety_set; - then_block.inlining = block->inlining; - then_block.label = block->label; - then_block.need_debug_scope = NULL; - uint8_t true_hint = analyzeBodyRuntimeBreak( - sema, &then_block, then_body, then_body_len); - - // Analyze else-body in a sub-block, collecting branch hint. - // Upstream: need_debug_scope = null (body emitted regardless). - SemaBlock else_block; - semaBlockInit(&else_block, sema, block); - else_block.is_comptime = false; - else_block.want_safety = block->want_safety; - else_block.want_safety_set = block->want_safety_set; - else_block.inlining = block->inlining; - else_block.label = block->label; - else_block.need_debug_scope = NULL; - uint8_t false_hint = analyzeBodyRuntimeBreak( - sema, &else_block, else_body, else_body_len); - // Extra data: {then_body_len, else_body_len, branch_hints, - // then_body[...], else_body[...]} - uint32_t extra_start - = semaAddExtra(sema, then_block.instructions_len); - semaAddExtra(sema, else_block.instructions_len); - // BranchHints packed struct (u32): - // bits [0..2] = true hint (BranchHint, 3 bits) - // 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_packed = 0; - branch_hints_packed |= (uint32_t)(true_hint & 0x7); - branch_hints_packed |= (uint32_t)(false_hint & 0x7) << 3; - branch_hints_packed |= (1u << 6); // then_cov = poi - branch_hints_packed |= (1u << 7); // else_cov = poi - semaAddExtra(sema, branch_hints_packed); - - for (uint32_t ti = 0; ti < then_block.instructions_len; ti++) { - semaAddExtra(sema, then_block.instructions[ti]); - } - for (uint32_t ei = 0; ei < else_block.instructions_len; ei++) { - semaAddExtra(sema, else_block.instructions[ei]); - } - - AirInstData cond_data; - memset(&cond_data, 0, sizeof(cond_data)); - cond_data.pl_op.operand = cond; - cond_data.pl_op.payload = extra_start; - (void)semaAddInst(block, AIR_INST_COND_BR, cond_data); - - semaBlockDeinit(&then_block); - semaBlockDeinit(&else_block); - return false; // condbr is terminal - } - - // break: break from a runtime block. - // Ported from src/Sema.zig zirBreak. - case ZIR_INST_BREAK: { - ZirInstRef operand_ref - = sema->code.inst_datas[inst].break_data.operand; - uint32_t extra_pi - = sema->code.inst_datas[inst].break_data.payload_index; - uint32_t zir_block_inst = sema->code.extra[extra_pi + 1]; - - AirInstRef operand; - if (operand_ref == ZIR_REF_NONE) { - operand = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); - } else { - operand = resolveInst(sema, operand_ref); - } - - // Find the label for the target block. - SemaBlockLabel* label = findBlockLabel(block, zir_block_inst); - if (!label) { - i++; - continue; - } - - if (block->is_comptime) { - // Comptime break: record result without emitting BR. - // Ported from src/Sema.zig zirBreak comptime path. - mergesAppend(&label->merges, operand, 0); + case ZIR_INST_BLOCK: + if (!zirBlock(sema, block, inst)) return false; - } + i++; + continue; + case ZIR_INST_BOOL_BR_AND: + case ZIR_INST_BOOL_BR_OR: + instMapPut(&sema->inst_map, inst, zirBoolBr(sema, block, inst)); + i++; + continue; - // Emit AIR br instruction. - AirInstData br_data; - memset(&br_data, 0, sizeof(br_data)); - br_data.br.block_inst = label->merges.block_inst; - br_data.br.operand = operand; - AirInstRef br_ref = semaAddInst(block, AIR_INST_BR, br_data); + case ZIR_INST_CONDBR_INLINE: + case ZIR_INST_CONDBR: + return zirCondbr(sema, block, inst); - // Record merge result. - mergesAppend(&label->merges, operand, AIR_REF_TO_INST(br_ref)); - return false; // break is terminal - } + case ZIR_INST_BREAK: + if (!zirBreak(sema, block, inst)) + return false; + i++; + continue; - // panic: @panic builtin. - // Ported from src/Sema.zig zirPanic. - // For the bootstrap, we emit call to the panic function + - // unreach. - case ZIR_INST_PANIC: { - // Resolve the string message operand. - ZirInstRef msg_ref = sema->code.inst_datas[inst].un_node.operand; - AirInstRef msg = resolveInst(sema, msg_ref); - (void)msg; // msg used in call args below + case ZIR_INST_PANIC: + zirPanic(sema, block, inst); + return false; - // Set branch hint to cold (panic paths are cold). - // Only if no hint already set (user hints override). - // Ported from src/Sema.zig zirPanic. - if (sema->branch_hint < 0) { - sema->branch_hint = 3; // cold - } - - // 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)semaAddInst(block, AIR_INST_TRAP, trap_data); - return false; // panic is terminal - } - - // unreachable: emit AIR unreach. - // Ported from src/Sema.zig zirUnreachable. - case ZIR_INST_UNREACHABLE: { - // Set branch hint to cold when safety is active. - // Ported from src/Sema.zig analyzeUnreachable. - if (block->want_safety && sema->branch_hint < 0) { - sema->branch_hint = 3; // cold - } - AirInstData data; - memset(&data, 0, sizeof(data)); - (void)semaAddInst(block, AIR_INST_UNREACH, data); - return false; // unreachable is terminal - } + case ZIR_INST_UNREACHABLE: + zirUnreachable(sema, block, inst); + return false; // ensure_result_used: check that result is void/noreturn. // Ported from src/Sema.zig zirEnsureResultUsed. @@ -8358,39 +8136,15 @@ static bool analyzeBodyInner( continue; } - // int_type: create an integer type from signedness and bit count. - // Ported from src/Sema.zig zirIntType. - case ZIR_INST_INT_TYPE: { - uint8_t signedness - = sema->code.inst_datas[inst].int_type.signedness; - uint16_t bit_count - = sema->code.inst_datas[inst].int_type.bit_count; - InternPoolKey key; - memset(&key, 0, sizeof(key)); - key.tag = IP_KEY_INT_TYPE; - key.data.int_type.bits = bit_count; - key.data.int_type.signedness = signedness; - instMapPut(&sema->inst_map, inst, - AIR_REF_FROM_IP(ipIntern(sema->ip, key))); + case ZIR_INST_INT_TYPE: + instMapPut(&sema->inst_map, inst, zirIntType(sema, inst)); i++; continue; - } - // coerce_ptr_elem_ty: coerce a value so that a reference to it - // would be coercible to a given pointer type. - // Ported from src/Sema.zig zirCoercePtrElemTy. - // Uses pl_node with Bin payload: lhs=pointer type, rhs=value. - case ZIR_INST_COERCE_PTR_ELEM_TY: { - uint32_t payload_index - = sema->code.inst_datas[inst].pl_node.payload_index; - ZirInstRef val_ref = sema->code.extra[payload_index + 1]; - AirInstRef val = resolveInst(sema, val_ref); - // For single pointers with matching types (the common case - // in our bootstrap), the coercion is a no-op. - instMapPut(&sema->inst_map, inst, val); + case ZIR_INST_COERCE_PTR_ELEM_TY: + instMapPut(&sema->inst_map, inst, zirCoercePtrElemTy(sema, inst)); i++; continue; - } // For all other instructions, produce a void mapping and skip. // As handlers are implemented, they will replace this default.