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.