sema: add alloc, block, condbr, store, call, panic handlers for absv pattern

Add handlers needed for cross-module inline calls with control flow
(absv.zig pattern): alloc_mut, store_node, block, condbr, panic,
call (for @panic → builtin.call), unreach, dbg_var_ptr, alloc in
semaTypeOf, mergesAppend, findBlockLabel, and extended semaCoerce
for signed int types.

absvdi2.zig still needs more work (semaCoerce for additional type
combinations).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-20 16:51:53 +00:00
parent 2adbfeaf8e
commit c58d4ebe76
2 changed files with 395 additions and 1 deletions

View File

@@ -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: {

View File

@@ -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