feat: code generation for simple choice statements, testing machinery

This commit is contained in:
Brett Broadhurst 2026-03-16 17:33:31 -06:00
parent fac5a968e3
commit ee26be6254
Failed to generate hash of commit
13 changed files with 304 additions and 70 deletions

View file

@ -238,6 +238,76 @@ fn irContentFlush(_: *Sema, chunk: *Chunk, _: Ir.Inst.Index) InnerError!Ref {
return chunk.addByteOp(.stream_flush);
}
fn irChoiceBr(sema: *Sema, chunk: *Chunk, inst: Ir.Inst.Index) InnerError!void {
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
const choice_extra = sema.ir.extraData(Ir.Inst.ChoiceBr, data.payload_index);
const options_slice = sema.ir.bodySlice(choice_extra.end, choice_extra.data.cases_len);
var branch_labels: std.ArrayListUnmanaged(usize) = .empty;
try branch_labels.ensureUnusedCapacity(sema.gpa, options_slice.len + 1);
defer branch_labels.deinit(sema.gpa);
for (options_slice) |option_index| {
const case_extra = sema.ir.extraData(Ir.Inst.ChoiceBr.Case, @intFromEnum(option_index));
const case_label = try chunk.addLabel();
branch_labels.appendAssumeCapacity(case_label);
switch (case_extra.data.operand_1) {
.none => {},
else => |content| {
const content_inst = chunk.resolveInst(content);
_ = try chunk.doLoad(content_inst);
_ = try chunk.addByteOp(.stream_push);
},
}
switch (case_extra.data.operand_2) {
.none => {},
else => |content| {
const content_inst = chunk.resolveInst(content);
_ = try chunk.doLoad(content_inst);
_ = try chunk.addByteOp(.stream_push);
},
}
try chunk.addFixupAbsolute(.br_push, case_label);
}
_ = try chunk.addByteOp(.br_table);
_ = try chunk.addByteOp(.br_select_index);
_ = try chunk.addByteOp(.br_dispatch);
for (options_slice, branch_labels.items) |option_index, label| {
const case_extra = sema.ir.extraData(Ir.Inst.ChoiceBr.Case, @intFromEnum(option_index));
const body_slice = sema.ir.bodySlice(case_extra.end, case_extra.data.body_len);
chunk.setLabel(label);
switch (case_extra.data.operand_1) {
.none => {},
else => |content| {
const content_inst = chunk.resolveInst(content);
_ = try chunk.doLoad(content_inst);
_ = try chunk.addByteOp(.stream_push);
},
}
switch (case_extra.data.operand_3) {
.none => {},
else => |content| {
const content_inst = chunk.resolveInst(content);
_ = try chunk.doLoad(content_inst);
_ = try chunk.addByteOp(.stream_push);
},
}
_ = try chunk.addByteOp(.stream_flush);
try blockBodyInner(sema, chunk, body_slice);
}
}
fn irImplicitRet(_: *Sema, chunk: *Chunk, _: Ir.Inst.Index) InnerError!Ref {
return chunk.addByteOp(.exit);
}
fn irDeclRef(sema: *Sema, _: *Chunk, inst: Ir.Inst.Index) InnerError!Ref {
const data = sema.ir.instructions[@intFromEnum(inst)].data.string;
return sema.getGlobal(data.start);
@ -358,6 +428,11 @@ fn blockBodyInner(sema: *Sema, chunk: *Chunk, body: []const Ir.Inst.Index) Inner
},
.content_push => try irContentPush(sema, chunk, inst),
.content_flush => try irContentFlush(sema, chunk, inst),
.choice_br => {
try irChoiceBr(sema, chunk, inst);
continue;
},
.implicit_ret => try irImplicitRet(sema, chunk, inst),
};
try chunk.inst_map.put(gpa, inst, ref);
}
@ -446,6 +521,15 @@ const Chunk = struct {
});
}
fn addFixupAbsolute(chunk: *Chunk, op: Story.Opcode, label: usize) !void {
const code_ref = try chunk.addJumpOp(op);
return chunk.fixups.append(chunk.sema.gpa, .{
.mode = .absolute,
.label_index = @intCast(label),
.code_offset = code_ref.index,
});
}
fn addLabel(chunk: *Chunk) error{OutOfMemory}!usize {
const label_index = chunk.labels.items.len;
try chunk.labels.append(chunk.sema.gpa, .{