diff --git a/stage0/sema.c b/stage0/sema.c index 6fd1d8cba9..ccdd5a9e02 100644 --- a/stage0/sema.c +++ b/stage0/sema.c @@ -527,6 +527,9 @@ static TypeIndex semaTypeOf(Sema* sema, AirInstRef ref) { case AIR_INST_CMP_GT: case AIR_INST_CMP_NEQ: return IP_INDEX_BOOL_TYPE; + // ty: type from ty_ref field (alloc uses ty variant). + case AIR_INST_ALLOC: + return AIR_REF_TO_IP(sema->air_inst_datas[inst_idx].ty.ty_ref); // ty_op / ty_pl: type from ty_ref field. case AIR_INST_BITCAST: case AIR_INST_INTCAST: @@ -2427,6 +2430,40 @@ static AirInstRef zirDeclLiteralComptime(Sema* sema, uint32_t inst) { return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); } +// --- mergesAppend --- +// Append a result and br instruction index to a SemaBlockMerges. +static void mergesAppend(SemaBlockMerges* merges, + AirInstRef result, uint32_t br_inst) { + if (merges->results_len >= merges->results_cap) { + uint32_t new_cap + = (merges->results_cap == 0) ? 4 : merges->results_cap * 2; + merges->results + = realloc(merges->results, new_cap * sizeof(AirInstRef)); + merges->br_list + = realloc(merges->br_list, new_cap * sizeof(uint32_t)); + if (!merges->results || !merges->br_list) + exit(1); + merges->results_cap = new_cap; + merges->br_list_cap = new_cap; + } + merges->results[merges->results_len++] = result; + merges->br_list[merges->br_list_len++] = br_inst; +} + +// --- findBlockLabel --- +// Walk up the block parent chain to find the SemaBlockLabel whose +// zir_block matches the given ZIR block instruction index. +static SemaBlockLabel* findBlockLabel(SemaBlock* block, uint32_t zir_block) { + SemaBlock* b = block; + while (b) { + if (b->label && b->label->zir_block == zir_block) + return b->label; + b = b->parent; + } + assert(0 && "findBlockLabel: label not found"); + return NULL; +} + // --- analyzeBodyInner --- // Ported from src/Sema.zig analyzeBodyInner. // Main dispatch loop: iterates over ZIR instructions in a body and @@ -3057,6 +3094,363 @@ static bool analyzeBodyInner( i++; continue; + // alloc_mut: mutable variable allocation. + // Ported from src/Sema.zig zirAllocMut. + // Creates AIR_INST_ALLOC with pointer type *T. + case ZIR_INST_ALLOC_MUT: { + ZirInstRef type_ref + = sema->code.inst_datas[inst].un_node.operand; + AirInstRef resolved = resolveInst(sema, type_ref); + assert(AIR_REF_IS_IP(resolved)); + 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, + blockAddInst(block, AIR_INST_ALLOC, data)); + i++; + continue; + } + + // block: runtime block. + // Ported from src/Sema.zig zirBlock / resolveAnalyzedBlock. + 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]; + + // Reserve an AIR block instruction (data filled later). + uint32_t block_inst_idx = addAirInst(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; + + (void)analyzeBodyInner( + sema, &child_block, inner_body, inner_body_len); + + // Resolve the block: write extra data and patch type. + AirInstRef block_result; + if (label.merges.results_len == 0) { + // No breaks (noreturn block): copy instructions 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]; + } + block_result = AIR_REF_FROM_INST( + child_block.instructions + [child_block.instructions_len - 1]); + } else if (label.merges.results_len == 1) { + // Single break: check if last instruction is the + // break. + uint32_t last_inst_idx + = child_block.instructions_len - 1; + uint32_t last_inst + = child_block.instructions[last_inst_idx]; + bool elide = false; + if (sema->air_inst_tags[last_inst] == AIR_INST_BR) { + if (sema->air_inst_datas[last_inst].br.block_inst + == block_inst_idx) { + elide = true; + } + } + 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 + = addAirExtra(sema, + child_block.instructions_len); + for (uint32_t ci = 0; + ci < child_block.instructions_len; ci++) { + addAirExtra(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. + sema->air_inst_datas + [label.merges.br_list[0]].br.operand + = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + + block_result = label.merges.results[0]; + } + } 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 = resolvePeerType(sema, + label.merges.results[0], + label.merges.results[1]); + + uint32_t extra_start + = addAirExtra(sema, + child_block.instructions_len); + for (uint32_t ci = 0; + ci < child_block.instructions_len; ci++) { + addAirExtra(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; + + 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; + } + + // condbr: conditional branch. + // Ported from src/Sema.zig zirCondbr. + 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 cond = resolveInst(sema, condition_ref); + + // Analyze then-body in a sub-block. + 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; + (void)analyzeBodyInner( + sema, &then_block, then_body, then_body_len); + + // Analyze else-body in a sub-block. + 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; + (void)analyzeBodyInner( + sema, &else_block, else_body, else_body_len); + + // Emit AIR_INST_COND_BR. + // Extra data: {then_body_len, else_body_len, branch_hints, + // then_body[...], else_body[...]} + uint32_t extra_start + = addAirExtra(sema, then_block.instructions_len); + addAirExtra(sema, else_block.instructions_len); + // Branch hints: cold for then (panic path), poi for both + // coverage points. + // Packed: true=cold(3), false=none(0), + // then_cov=poi(2), else_cov=poi(2) + // BranchHint encoding: none=0, likely=1, unlikely=2, + // cold=3 + // CoveragePoint encoding: none=0, poi=2 + // Layout: true(3bits) | false(3bits) | then_cov(2bits) | + // else_cov(2bits) | padding(22bits) + // Actually need to match upstream encoding exactly. + // std.builtin.BranchHint: none=0, likely=1, unlikely=2, + // cold=3 (3 bits each) + // CoveragePoint: none=0, poi=2 (2 bits each) + // Packed struct (u32): + // bits [0..2] = true hint + // bits [3..5] = false hint + // bits [6..7] = then_cov + // bits [8..9] = else_cov + // bits [10..31] = 0 + uint32_t branch_hints = 0; + branch_hints |= 3; // true = cold + branch_hints |= (0 << 3); // false = none + branch_hints |= (2 << 6); // then_cov = poi + branch_hints |= (2 << 8); // else_cov = poi + addAirExtra(sema, branch_hints); + + for (uint32_t ti = 0; + ti < then_block.instructions_len; ti++) { + addAirExtra(sema, then_block.instructions[ti]); + } + for (uint32_t ei = 0; + ei < else_block.instructions_len; ei++) { + addAirExtra(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)blockAddInst(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); + assert(label != NULL); + + // 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 + = blockAddInst(block, AIR_INST_BR, br_data); + + // Record merge result. + mergesAppend(&label->merges, + operand, AIR_REF_TO_INST(br_ref)); + return false; // break is terminal + } + + // 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 + + // For wasm32+llvm backend, panic_fn is supported. + // We need to emit: call(panic_fn, [msg_coerced, + // null_ret_addr]) + unreach. + // The panic function is analyzed separately as a + // cross-module function. + // For now, emit trap + unreach as a simplified fallback. + AirInstData trap_data; + memset(&trap_data, 0, sizeof(trap_data)); + (void)blockAddInst(block, AIR_INST_TRAP, trap_data); + (void)blockAddInst(block, AIR_INST_UNREACH, trap_data); + return false; // panic is terminal + } + + // unreachable: emit AIR unreach. + // Ported from src/Sema.zig zirUnreachable. + case ZIR_INST_UNREACHABLE: { + AirInstData data; + memset(&data, 0, sizeof(data)); + (void)blockAddInst(block, AIR_INST_UNREACH, data); + return false; // unreachable is terminal + } + + // ensure_result_used: check that result is void/noreturn. + // Ported from src/Sema.zig zirEnsureResultUsed. + case ZIR_INST_ENSURE_RESULT_USED: { + // For void/noreturn results, this is a no-op. + // Non-void results would be a compile error. + i++; + continue; + } + // For all other instructions, produce a void mapping and skip. // As handlers are implemented, they will replace this default. default: { diff --git a/stage0/stages_test.zig b/stage0/stages_test.zig index 3e737cc525..15077cd52f 100644 --- a/stage0/stages_test.zig +++ b/stage0/stages_test.zig @@ -104,7 +104,7 @@ const corpus_files = .{ "../lib/std/zig/llvm.zig", // 247 "../lib/compiler_rt/neghf2.zig", // 265 -- cross-module ZIR loading works; needs comptime eval (reify, struct_init) "../lib/compiler_rt/negxf2.zig", // 265 -- @export+func_fancy handled; body analysis incomplete - //"../lib/compiler_rt/absvdi2.zig", // 311 -- @export+func_fancy handled; body analysis incomplete + //"../lib/compiler_rt/absvdi2.zig", // 311 -- needs alloc_mut, block, condbr, panic, call //"../lib/compiler_rt/absvsi2.zig", // 311 -- @export+func_fancy handled; body analysis incomplete //"../lib/compiler_rt/absvti2.zig", // 314 -- @export+func_fancy handled; body analysis incomplete //"../lib/compiler_rt/addhf3.zig", // 319