feat: error reporting for global reference usage

This commit is contained in:
Brett Broadhurst 2026-03-19 01:54:33 -06:00
parent 47351cd6f9
commit be297047d1
Failed to generate hash of commit
8 changed files with 401 additions and 371 deletions

View file

@ -392,30 +392,50 @@ const GenIr = struct {
});
}
fn addPayloadNode(gi: *GenIr, tag: Ir.Inst.Tag, extra: anytype) !Ir.Inst.Ref {
const gpa = gi.astgen.gpa;
try gi.instructions.ensureUnusedCapacity(gpa, 1);
try gi.astgen.instructions.ensureUnusedCapacity(gpa, 1);
fn addPayloadNode(
gen: *GenIr,
tag: Ir.Inst.Tag,
node: *const Ast.Node,
extra: anytype,
) !Ir.Inst.Ref {
const gpa = gen.astgen.gpa;
try gen.instructions.ensureUnusedCapacity(gpa, 1);
try gen.astgen.instructions.ensureUnusedCapacity(gpa, 1);
const payload_index = try gi.astgen.addExtra(extra);
const new_index: Ir.Inst.Index = @enumFromInt(gi.astgen.instructions.items.len);
gi.astgen.instructions.appendAssumeCapacity(.{
const extra_index = try gen.astgen.addExtra(extra);
const new_index: Ir.Inst.Index = @enumFromInt(gen.astgen.instructions.items.len);
gen.astgen.instructions.appendAssumeCapacity(.{
.tag = tag,
.data = .{ .payload = .{
.payload_index = payload_index,
.extra_index = extra_index,
.src_offset = @intCast(node.loc.start),
} },
});
gi.instructions.appendAssumeCapacity(new_index);
gen.instructions.appendAssumeCapacity(new_index);
return new_index.toRef();
}
fn addPayloadNodeWithIndex(
gen: *GenIr,
tag: Ir.Inst.Tag,
node: *const Ast.Node,
extra_index: u32,
) !Ir.Inst.Ref {
return gen.add(.{ .tag = tag, .data = .{
.payload = .{
.extra_index = extra_index,
.src_offset = @intCast(node.loc.start),
},
} });
}
fn makeBlockInst(gi: *GenIr, tag: Ir.Inst.Tag) !Ir.Inst.Index {
const inst_index: Ir.Inst.Index = @enumFromInt(gi.astgen.instructions.items.len);
const gpa = gi.astgen.gpa;
try gi.astgen.instructions.append(gpa, .{
.tag = tag,
.data = .{
.payload = .{ .payload_index = undefined },
.payload = undefined,
},
});
return inst_index;
@ -431,11 +451,11 @@ const GenIr = struct {
try self.astgen.instructions.append(gpa, .{
.tag = .decl_knot,
.data = .{
.payload = .{ .payload_index = undefined },
.payload = undefined,
},
});
const inst_data = &self.astgen.instructions.items[@intFromEnum(new_index)].data;
inst_data.payload.payload_index = self.astgen.addExtraAssumeCapacity(
inst_data.payload.extra_index = self.astgen.addExtraAssumeCapacity(
Ir.Inst.Knot{ .body_len = @intCast(body.len) },
);
@ -443,75 +463,50 @@ const GenIr = struct {
return new_index;
}
fn addVar(self: *GenIr) !Ir.Inst.Index {
const gpa = self.astgen.gpa;
const new_index: Ir.Inst.Index = @enumFromInt(self.astgen.instructions.items.len);
const body = self.instructionsSlice();
fn addVar(gen: *GenIr, node: *const Ast.Node) !Ir.Inst.Index {
const gpa = gen.astgen.gpa;
const body = gen.instructionsSlice();
const extra_len = @typeInfo(Ir.Inst.Var).@"struct".fields.len + body.len;
try self.astgen.extra.ensureUnusedCapacity(gpa, extra_len);
try self.astgen.instructions.ensureUnusedCapacity(gpa, 1);
try self.instructions.ensureUnusedCapacity(gpa, 1);
try gen.astgen.extra.ensureUnusedCapacity(gpa, extra_len);
try gen.astgen.instructions.ensureUnusedCapacity(gpa, 1);
self.astgen.instructions.appendAssumeCapacity(.{ .tag = .decl_var, .data = .{
.payload = .{ .payload_index = self.astgen.addExtraAssumeCapacity(
Ir.Inst.Var{ .body_len = @intCast(body.len) },
) },
} });
self.astgen.appendBlockBody(body);
self.instructions.appendAssumeCapacity(new_index);
return new_index;
const extra_index = gen.astgen.addExtraAssumeCapacity(Ir.Inst.Var{
.body_len = @intCast(body.len),
});
const new_index = try gen.addPayloadNodeWithIndex(.decl_var, node, extra_index);
gen.astgen.appendBlockBody(body);
gen.instructions.appendAssumeCapacity(new_index.toIndex().?);
return new_index.toIndex().?;
}
fn addCondBr(self: *GenIr, tag: Ir.Inst.Tag) !Ir.Inst.Index {
const gpa = self.astgen.gpa;
try self.instructions.ensureUnusedCapacity(gpa, 1);
try self.astgen.instructions.ensureUnusedCapacity(gpa, 1);
fn addCondBr(gen: *GenIr, tag: Ir.Inst.Tag) !Ir.Inst.Index {
const gpa = gen.astgen.gpa;
try gen.instructions.ensureUnusedCapacity(gpa, 1);
try gen.astgen.instructions.ensureUnusedCapacity(gpa, 1);
const new_index: Ir.Inst.Index = @enumFromInt(self.astgen.instructions.items.len);
self.astgen.instructions.appendAssumeCapacity(.{
const new_index: Ir.Inst.Index = @enumFromInt(gen.astgen.instructions.items.len);
gen.astgen.instructions.appendAssumeCapacity(.{
.tag = tag,
.data = .{ .payload = .{
.payload_index = undefined,
} },
.data = .{ .payload = undefined },
});
self.instructions.appendAssumeCapacity(new_index);
gen.instructions.appendAssumeCapacity(new_index);
return new_index;
}
fn addBreak(
self: *GenIr,
gen: *GenIr,
tag: Ir.Inst.Tag,
node: *const Ast.Node,
block_inst: Ir.Inst.Index,
) !Ir.Inst.Index {
const gpa = self.astgen.gpa;
try self.instructions.ensureUnusedCapacity(gpa, 1);
const new_index = try self.makeBreak(tag, block_inst);
self.instructions.appendAssumeCapacity(new_index);
return new_index;
}
fn makeBreak(
self: *GenIr,
tag: Ir.Inst.Tag,
block_inst: Ir.Inst.Index,
) !Ir.Inst.Index {
const gpa = self.astgen.gpa;
) !Ir.Inst.Ref {
const gpa = gen.astgen.gpa;
const extra_len = @typeInfo(Ir.Inst.Break).@"struct".fields.len;
try self.astgen.instructions.ensureUnusedCapacity(gpa, 1);
try self.astgen.extra.ensureUnusedCapacity(gpa, extra_len);
try gen.astgen.extra.ensureUnusedCapacity(gpa, extra_len);
const new_index: Ir.Inst.Index = @enumFromInt(self.astgen.instructions.items.len);
self.astgen.instructions.appendAssumeCapacity(.{
.tag = tag,
.data = .{ .payload = .{
.payload_index = self.astgen.addExtraAssumeCapacity(
Ir.Inst.Break{ .block_inst = block_inst },
),
} },
});
return new_index;
const extra_index = gen.astgen.addExtraAssumeCapacity(
Ir.Inst.Break{ .block_inst = block_inst },
);
return gen.addPayloadNodeWithIndex(tag, node, extra_index);
}
fn setBlockBody(self: *GenIr, inst: Ir.Inst.Index) !void {
@ -521,7 +516,7 @@ const GenIr = struct {
try self.astgen.extra.ensureUnusedCapacity(gpa, extra_len);
const inst_data = &self.astgen.instructions.items[@intFromEnum(inst)].data;
inst_data.payload.payload_index = self.astgen.addExtraAssumeCapacity(
inst_data.payload.extra_index = self.astgen.addExtraAssumeCapacity(
Ir.Inst.Block{ .body_len = @intCast(body.len) },
);
self.astgen.appendBlockBody(body);
@ -599,7 +594,7 @@ fn setDeclaration(
}
const inst_data = &astgen.instructions.items[@intFromEnum(decl_index)].data;
inst_data.payload.payload_index = astgen.addExtraAssumeCapacity(
inst_data.payload.extra_index = astgen.addExtraAssumeCapacity(
Ir.Inst.Declaration{ .name = args.name, .value = args.ref },
);
@ -630,7 +625,7 @@ fn setCondBrPayload(
try astgen.extra.ensureUnusedCapacity(astgen.gpa, extra_len);
const inst_data = &astgen.instructions.items[@intFromEnum(condbr)].data;
inst_data.payload.payload_index = astgen.addExtraAssumeCapacity(
inst_data.payload.extra_index = astgen.addExtraAssumeCapacity(
Ir.Inst.CondBr{
.condition = cond,
.then_body_len = @intCast(then_body_len),
@ -856,13 +851,13 @@ fn ifStmt(
defer then_block.unstack();
try blockStmt(&then_block, scope, then_node);
_ = try then_block.addBreak(.@"break", block);
_ = try then_block.addBreak(.@"break", then_node, block);
var else_block = parent_block.makeSubBlock();
defer else_block.unstack();
if (then_node == last_prong) {
_ = try else_block.addBreak(.@"break", block);
_ = try else_block.addBreak(.@"break", then_node, block);
} else {
const block_node = last_prong.data.bin.rhs.?;
try blockStmt(&else_block, scope, block_node);
@ -900,13 +895,13 @@ fn ifChain(
var then_block = parent_block.makeSubBlock();
defer then_block.unstack();
try blockStmt(&then_block, scope, body_node);
_ = try then_block.addBreak(.@"break", block_inst);
_ = try then_block.addBreak(.@"break", body_node, block_inst);
var else_block = parent_block.makeSubBlock();
defer else_block.unstack();
const next_branches = branch_list[1..];
_ = try ifChain(parent_block, scope, next_branches);
_ = try else_block.addBreak(.@"break", block_inst);
_ = try else_block.addBreak(.@"break", body_node, block_inst);
try setCondBrPayload(condbr, cond_inst, &then_block, &else_block);
return @enumFromInt(0);
}
@ -960,7 +955,7 @@ fn switchStmt(
var case_block = parent_block.makeSubBlock();
defer case_block.unstack();
_ = try blockStmt(&case_block, scope, case_stmt.data.bin.rhs.?);
_ = try case_block.addBreak(.@"break", switch_br);
_ = try case_block.addBreak(.@"break", case_stmt, switch_br);
const body = case_block.instructionsSlice();
const case_extra_len = @typeInfo(Ir.Inst.SwitchBr.Case).@"struct".fields.len + body.len;
@ -982,7 +977,7 @@ fn switchStmt(
var case_block = parent_block.makeSubBlock();
defer case_block.unstack();
_ = try blockStmt(&case_block, scope, else_branch.data.bin.rhs.?);
_ = try case_block.addBreak(.@"break", switch_br);
_ = try case_block.addBreak(.@"break", else_branch, switch_br);
const else_body = case_block.instructionsSlice();
const extra_len =
@ -990,13 +985,14 @@ fn switchStmt(
try astgen.extra.ensureUnusedCapacity(gpa, extra_len);
astgen.instructions.items[@intFromEnum(switch_br)].data.payload = .{
.payload_index = astgen.addExtraAssumeCapacity(
.extra_index = astgen.addExtraAssumeCapacity(
Ir.Inst.SwitchBr{
.operand = cond_inst,
.cases_len = @intCast(switch_cases.len),
.else_body_len = @intCast(else_body.len),
},
),
.src_offset = @intCast(stmt_node.loc.start),
};
astgen.extra.appendSliceAssumeCapacity(case_indexes.items[0..]);
astgen.appendBlockBody(else_body);
@ -1111,11 +1107,12 @@ fn choiceStmt(
try astgen.extra.ensureUnusedCapacity(gpa, extra_len);
astgen.instructions.items[@intFromEnum(choice_br)].data.payload = .{
.payload_index = astgen.addExtraAssumeCapacity(
.extra_index = astgen.addExtraAssumeCapacity(
Ir.Inst.ChoiceBr{
.cases_len = @intCast(choice_branches.len),
},
),
.src_offset = @intCast(stmt_node.loc.start),
};
astgen.extra.appendSliceAssumeCapacity(case_indexes.items[0..]);
}
@ -1132,12 +1129,12 @@ const Callee = union(enum) {
fn fieldAccess(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Ir.Inst.Ref {
assert(node.tag == .selector_expr);
const data = node.data.bin;
assert(data.rhs.?.tag == .identifier);
const field_str = try gi.astgen.strFromNode(data.rhs.?);
const rhs = data.rhs.?;
assert(rhs.tag == .identifier);
const field_str = try gi.astgen.strFromNode(rhs);
const lhs = try expr(gi, scope, data.lhs.?);
return gi.addPayloadNode(.field_ptr, Ir.Inst.Field{
return gi.addPayloadNode(.field_ptr, rhs, Ir.Inst.Field{
.lhs = lhs,
.field_name_start = field_str.index,
});
@ -1205,22 +1202,19 @@ fn callExpr(
}
switch (callee) {
.direct => |callee_obj| {
const payload_index = try addExtra(astgen, Ir.Inst.Call{
const tag = if (call == .divert) .divert else .call;
const extra_index = try addExtra(astgen, Ir.Inst.Call{
.callee = callee_obj,
.args_len = @intCast(args_count),
});
if (args_count != 0) {
try astgen.extra.appendSlice(gpa, astgen.scratch.items[scratch_top..]);
}
return gi.add(.{
.tag = if (call == .divert) .divert else .call,
.data = .{ .payload = .{
.payload_index = payload_index,
} },
});
return gi.addPayloadNodeWithIndex(tag, callee_node, extra_index);
},
.field => |callee_field| {
const payload_index = try addExtra(astgen, Ir.Inst.FieldCall{
const tag = if (call == .divert) .field_divert else .field_call;
const extra_index = try addExtra(astgen, Ir.Inst.FieldCall{
.obj_ptr = callee_field.obj_ptr,
.field_name_start = callee_field.field_name_start,
.args_len = @intCast(args_count),
@ -1228,12 +1222,7 @@ fn callExpr(
if (args_count != 0) {
try astgen.extra.appendSlice(gpa, astgen.scratch.items[scratch_top..]);
}
return gi.add(.{
.tag = if (call == .divert) .field_divert else .field_call,
.data = .{ .payload = .{
.payload_index = payload_index,
} },
});
return gi.addPayloadNodeWithIndex(tag, callee_node, extra_index);
},
}
}
@ -1246,29 +1235,17 @@ fn divertExpr(gi: *GenIr, scope: *Scope, node: *const Ast.Node) !void {
const callee = try calleeExpr(gi, scope, lhs);
switch (callee) {
.direct => |callee_obj| {
const payload_index = try addExtra(gi.astgen, Ir.Inst.Call{
_ = try gi.addPayloadNode(.divert, lhs, Ir.Inst.Call{
.callee = callee_obj,
.args_len = @intCast(0),
});
_ = try gi.add(.{
.tag = .divert,
.data = .{ .payload = .{
.payload_index = payload_index,
} },
});
},
.field => |callee_field| {
const payload_index = try addExtra(gi.astgen, Ir.Inst.FieldCall{
_ = try gi.addPayloadNode(.field_divert, lhs, Ir.Inst.FieldCall{
.obj_ptr = callee_field.obj_ptr,
.field_name_start = callee_field.field_name_start,
.args_len = @intCast(0),
});
_ = try gi.add(.{
.tag = .field_divert,
.data = .{ .payload = .{
.payload_index = payload_index,
} },
});
},
}
},
@ -1309,17 +1286,14 @@ fn varDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
const expr_node = decl_node.data.bin.rhs.?;
const decl_inst = try gi.add(.{
.tag = .declaration,
.data = .{ .payload = .{
.payload_index = undefined,
} },
.data = .{ .payload = undefined },
});
var decl_block = gi.makeSubBlock();
defer decl_block.unstack();
_ = try expr(&decl_block, scope, expr_node);
const var_inst = try decl_block.addVar();
const var_inst = try decl_block.addVar(decl_node);
const name_str = try astgen.strFromNode(identifier_node);
try setDeclaration(decl_inst.toIndex().?, .{
.tag = .variable,
@ -1365,9 +1339,7 @@ fn defaultBlock(
const astgen = gi.astgen;
const decl_inst = try gi.addAsIndex(.{
.tag = .declaration,
.data = .{ .payload = .{
.payload_index = undefined,
} },
.data = .{ .payload = undefined },
});
var decl_scope = gi.makeSubBlock();
@ -1400,9 +1372,7 @@ fn stitchDeclInner(
const identifier_node = prototype_data.lhs.?;
const decl_inst = try gi.addAsIndex(.{
.tag = .declaration,
.data = .{ .payload = .{
.payload_index = undefined,
} },
.data = .{ .payload = undefined },
});
var decl_block = gi.makeSubBlock();
@ -1440,12 +1410,12 @@ fn stitchDeclInner(
fn stitchDecl(gi: *GenIr, parent_scope: *Scope, decl_node: *const Ast.Node) InnerError!void {
const knot_data = decl_node.data.bin;
const prototype_node = knot_data.lhs.?;
const body_node = knot_data.rhs.?;
const prototype_node = knot_data.lhs;
const body_node = knot_data.rhs;
var decl_scope = parent_scope.makeChild();
defer decl_scope.deinit();
return stitchDeclInner(gi, &decl_scope, decl_node, prototype_node, body_node);
return stitchDeclInner(gi, &decl_scope, decl_node, prototype_node.?, body_node);
}
fn functionDecl(_: *GenIr, _: *Scope, _: *const Ast.Node) InnerError!void {}
@ -1484,7 +1454,7 @@ fn file(gi: *GenIr, scope: *Scope, file_node: *const Ast.Node) InnerError!void {
const file_inst = try gi.addAsIndex(.{
.tag = .file,
.data = .{
.payload = .{ .payload_index = undefined },
.payload = undefined,
},
});

View file

@ -213,7 +213,9 @@ pub const Inst = struct {
pub const Data = union {
payload: struct {
payload_index: u32,
/// Offset into `extra`.
extra_index: u32,
src_offset: u32,
},
un: struct {
lhs: Ref,

View file

@ -12,9 +12,9 @@ arena: std.mem.Allocator,
tree: Ast,
ir: Ir,
constants: std.ArrayListUnmanaged(Compilation.Constant) = .empty,
constant_map: std.AutoHashMapUnmanaged(Compilation.Constant, u32) = .empty,
constants_map: std.AutoHashMapUnmanaged(Compilation.Constant, u32) = .empty,
globals_map: std.AutoHashMapUnmanaged(u32, u32) = .empty,
knots: std.ArrayListUnmanaged(Compilation.Knot) = .empty,
globals: std.ArrayListUnmanaged(u32) = .empty,
errors: *std.ArrayListUnmanaged(Compilation.Error),
const InnerError = error{
@ -34,17 +34,8 @@ const Ref = union(enum) {
local: u32,
};
pub fn deinit(sema: *Sema) void {
const gpa = sema.gpa;
sema.constants.deinit(gpa);
sema.constant_map.deinit(gpa);
sema.globals.deinit(gpa);
sema.knots.deinit(gpa);
sema.* = undefined;
}
pub const SrcLoc = struct {
byte_offset: u32,
src_offset: u32,
};
fn fail(
@ -55,7 +46,7 @@ fn fail(
) error{ OutOfMemory, AnalysisFail } {
// TODO: Revisit this
const message = try std.fmt.allocPrint(sema.arena, format, args);
const loc = compile.findLineColumn(sema.tree.source, src.byte_offset);
const loc = compile.findLineColumn(sema.tree.source, src.src_offset);
try sema.errors.append(sema.gpa, .{
.line = loc.line,
.column = loc.column,
@ -65,47 +56,247 @@ fn fail(
return error.AnalysisFail;
}
fn getConstant(sema: *Sema, data: Compilation.Constant) !Ref {
const gpa = sema.gpa;
if (sema.constant_map.get(data)) |index| {
/// Intern a constant.
fn getOrPutConstant(sema: *Sema, data: Compilation.Constant) error{OutOfMemory}!Ref {
if (sema.constants_map.get(data)) |index| {
return .{ .constant = index };
} else {
const gpa = sema.gpa;
const index = sema.constants.items.len;
try sema.constants.append(gpa, data);
try sema.constant_map.put(gpa, data, @intCast(index));
try sema.constants_map.put(gpa, data, @intCast(index));
return .{ .constant = @intCast(index) };
}
}
fn addGlobal(sema: *Sema, name: Ir.NullTerminatedString) !Ref {
const gpa = sema.gpa;
const interned = try sema.getConstant(.{ .string = name });
try sema.globals.append(gpa, interned.constant);
return .{ .global = interned.constant };
/// Intern an integer as a story constant.
fn getOrPutInt(sema: *Sema, value: u64) error{OutOfMemory}!Ref {
return sema.getOrPutConstant(.{ .integer = value });
}
fn getGlobal(sema: *Sema, name: Ir.NullTerminatedString) !?Ref {
const interned = try sema.getConstant(.{ .string = name });
for (sema.ir.globals) |global| {
if (global.name == name) {
return .{ .global = interned.constant };
/// Intern a string as a story constant.
fn getOrPutStr(sema: *Sema, value: Ir.NullTerminatedString) error{OutOfMemory}!Ref {
return sema.getOrPutConstant(.{ .string = value });
}
pub fn deinit(sema: *Sema) void {
const gpa = sema.gpa;
sema.constants.deinit(gpa);
sema.constants_map.deinit(gpa);
sema.globals_map.deinit(gpa);
sema.knots.deinit(gpa);
sema.* = undefined;
}
const Chunk = struct {
sema: *Sema,
knot: *Compilation.Knot,
labels: std.ArrayListUnmanaged(Label) = .empty,
fixups: std.ArrayListUnmanaged(Fixup) = .empty,
inst_map: std.AutoHashMapUnmanaged(Ir.Inst.Index, Ref) = .empty,
constant_map: std.AutoHashMapUnmanaged(u32, u32) = .empty,
const dummy_address = 0xffffffff;
const Label = struct {
code_offset: usize,
};
const Fixup = struct {
mode: enum {
relative,
absolute,
},
label_index: u32,
code_offset: u32,
};
fn deinit(chunk: *Chunk, gpa: std.mem.Allocator) void {
chunk.fixups.deinit(gpa);
chunk.labels.deinit(gpa);
chunk.inst_map.deinit(gpa);
chunk.constant_map.deinit(gpa);
}
fn addByteOp(chunk: *Chunk, op: Story.Opcode) error{OutOfMemory}!Ref {
const gpa = chunk.sema.gpa;
const bytecode = &chunk.knot.bytecode;
const byte_index = bytecode.items.len;
try bytecode.append(gpa, @intFromEnum(op));
return .{ .index = @intCast(byte_index) };
}
fn addConstOp(chunk: *Chunk, op: Story.Opcode, arg: u8) error{OutOfMemory}!Ref {
const gpa = chunk.sema.gpa;
const bytecode = &chunk.knot.bytecode;
const byte_index = bytecode.items.len;
try bytecode.ensureUnusedCapacity(gpa, 2);
bytecode.appendAssumeCapacity(@intFromEnum(op));
bytecode.appendAssumeCapacity(arg);
return .{ .index = @intCast(byte_index) };
}
fn addJumpOp(chunk: *Chunk, op: Story.Opcode) error{OutOfMemory}!Ref {
const gpa = chunk.sema.gpa;
const bytecode = &chunk.knot.bytecode;
try bytecode.ensureUnusedCapacity(gpa, 3);
bytecode.appendAssumeCapacity(@intFromEnum(op));
bytecode.appendAssumeCapacity(0xff);
bytecode.appendAssumeCapacity(0xff);
return .{ .index = @intCast(bytecode.items.len - 2) };
}
fn addFixup(chunk: *Chunk, op: Story.Opcode, label: usize) !void {
const code_ref = try chunk.addJumpOp(op);
return chunk.fixups.append(chunk.sema.gpa, .{
.mode = .relative,
.label_index = @intCast(label),
.code_offset = code_ref.index,
});
}
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, .{
.code_offset = dummy_address,
});
return label_index;
}
fn setLabel(chunk: *Chunk, label_index: usize) void {
const code_offset = chunk.knot.bytecode.items.len;
assert(label_index <= chunk.labels.items.len);
const label_data = &chunk.labels.items[label_index];
label_data.code_offset = code_offset;
}
fn resolveInst(chunk: *Chunk, ref: Ir.Inst.Ref) Ref {
if (ref.toIndex()) |index| {
return chunk.inst_map.get(index).?;
}
switch (ref) {
.bool_true => return .bool_true,
.bool_false => return .bool_false,
else => return .{ .constant = @intFromEnum(ref) },
}
}
return null;
fn resolveLabels(chunk: *Chunk) !void {
const start_index = 0;
const end_index = chunk.fixups.items.len;
const bytecode = &chunk.knot.bytecode;
for (chunk.fixups.items[start_index..end_index]) |fixup| {
const label = chunk.labels.items[fixup.label_index];
assert(label.code_offset != dummy_address);
const target_offset: usize = switch (fixup.mode) {
.relative => label.code_offset - fixup.code_offset - 2,
.absolute => label.code_offset,
};
if (target_offset >= std.math.maxInt(u16)) {
std.debug.print("Too much code to jump over!\n", .{});
return error.InvalidJump;
}
assert(bytecode.capacity >= label.code_offset + 2);
bytecode.items[fixup.code_offset] = @intCast((target_offset >> 8) & 0xff);
bytecode.items[fixup.code_offset + 1] = @intCast(target_offset & 0xff);
}
}
/// Intern a reference to a global constant within this chunk.
fn getOrPutConstantIndex(chunk: *Chunk, global_index: u32) !u32 {
const gpa = chunk.sema.gpa;
if (chunk.constant_map.get(global_index)) |local_index| return local_index;
const local_index: u32 = @intCast(chunk.knot.constants.items.len);
try chunk.knot.constants.append(gpa, global_index);
try chunk.constant_map.put(gpa, global_index, local_index);
return local_index;
}
fn doLoad(chunk: *Chunk, ref: Ref) InnerError!Ref {
switch (ref) {
.bool_true => return chunk.addByteOp(.true),
.bool_false => return chunk.addByteOp(.false),
.none => return ref,
.constant => |global_index| {
const local_index = try chunk.getOrPutConstantIndex(global_index);
return chunk.addConstOp(.load_const, @intCast(local_index));
},
.global => |global_index| {
const local_index = try chunk.getOrPutConstantIndex(global_index);
return chunk.addConstOp(.load_global, @intCast(local_index));
},
.local => |id| return chunk.addConstOp(.load, @intCast(id)),
.index => return ref,
}
}
};
fn analyzeArithmeticArg(
sema: *Sema,
chunk: *Chunk,
arg: Ref,
arg_src: SrcLoc,
) !void {
switch (arg) {
.global => |index| {
const g = sema.ir.globals[index];
switch (g.tag) {
.variable => {
const name = try sema.getOrPutStr(g.name);
const local_index = try chunk.getOrPutConstantIndex(name.constant);
_ = try chunk.addConstOp(.load_global, @intCast(local_index));
},
.knot => return fail(sema, arg_src, "invalid operand to arithmetic expression", .{}),
}
},
.constant => |index| {
const local_index = try chunk.getOrPutConstantIndex(index);
_ = try chunk.addConstOp(.load_const, @intCast(local_index));
},
else => {},
}
}
fn analyzeDivertTarget(sema: *Sema, chunk: *Chunk, src: SrcLoc, callee: Ref) !Ref {
switch (callee) {
.global => |global_index| {
const g = sema.ir.globals[global_index];
switch (g.tag) {
.knot => {
const name = try sema.getOrPutStr(g.name);
const local_index = try chunk.getOrPutConstantIndex(name.constant);
return chunk.addConstOp(.load_global, @intCast(local_index));
},
.variable => return fail(sema, src, "invalid divert target", .{}),
}
},
else => unreachable,
}
}
fn irInteger(sema: *Sema, inst: Ir.Inst.Index) InnerError!Ref {
const value = sema.ir.instructions[@intFromEnum(inst)].data.int;
return sema.getConstant(.{ .integer = value });
return sema.getOrPutConstant(.{ .integer = value });
}
fn irString(sema: *Sema, inst: Ir.Inst.Index) InnerError!Ref {
const data = sema.ir.instructions[@intFromEnum(inst)].data.str;
return sema.getConstant(.{ .string = data.start });
return sema.getOrPutStr(data.start);
}
fn irUnary(
fn irUnaryOp(
sema: *Sema,
chunk: *Chunk,
inst: Ir.Inst.Index,
@ -113,11 +304,11 @@ fn irUnary(
) InnerError!Ref {
const data = sema.ir.instructions[@intFromEnum(inst)].data.un;
const lhs = chunk.resolveInst(data.lhs);
_ = try chunk.doLoad(lhs);
try sema.analyzeArithmeticArg(chunk, lhs, .{ .src_offset = 0 });
return chunk.addByteOp(op);
}
fn irBinary(
fn irBinaryOp(
sema: *Sema,
chunk: *Chunk,
inst: Ir.Inst.Index,
@ -127,8 +318,8 @@ fn irBinary(
const lhs = chunk.resolveInst(data.lhs);
const rhs = chunk.resolveInst(data.rhs);
_ = try chunk.doLoad(lhs);
_ = try chunk.doLoad(rhs);
try sema.analyzeArithmeticArg(chunk, lhs, .{ .src_offset = 0 });
try sema.analyzeArithmeticArg(chunk, rhs, .{ .src_offset = 0 });
return chunk.addByteOp(op);
}
@ -165,7 +356,7 @@ fn irLoad(sema: *Sema, chunk: *Chunk, inst: Ir.Inst.Index) InnerError!Ref {
fn irCondBr(sema: *Sema, chunk: *Chunk, inst: Ir.Inst.Index) InnerError!Ref {
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
const extra = sema.ir.extraData(Ir.Inst.CondBr, data.payload_index);
const extra = sema.ir.extraData(Ir.Inst.CondBr, data.extra_index);
const then_body = sema.ir.bodySlice(extra.end, extra.data.then_body_len);
const else_body = sema.ir.bodySlice(extra.end + then_body.len, extra.data.else_body_len);
const else_label = try chunk.addLabel();
@ -193,14 +384,14 @@ fn irBreak(sema: *Sema, inst: Ir.Inst.Index) InnerError!void {
fn irBlock(sema: *Sema, chunk: *Chunk, inst: Ir.Inst.Index) InnerError!void {
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
const extra = sema.ir.extraData(Ir.Inst.Block, data.payload_index);
const extra = sema.ir.extraData(Ir.Inst.Block, data.extra_index);
const body = sema.ir.bodySlice(extra.end, extra.data.body_len);
return blockBodyInner(sema, chunk, body);
}
fn irSwitchBr(sema: *Sema, chunk: *Chunk, inst: Ir.Inst.Index) InnerError!void {
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
const extra = sema.ir.extraData(Ir.Inst.SwitchBr, data.payload_index);
const extra = sema.ir.extraData(Ir.Inst.SwitchBr, data.extra_index);
const cases_slice = sema.ir.bodySlice(extra.end, extra.data.cases_len);
var case_labels: std.ArrayListUnmanaged(usize) = .empty;
@ -264,7 +455,7 @@ fn irContentFlush(_: *Sema, chunk: *Chunk, _: Ir.Inst.Index) InnerError!Ref {
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 choice_extra = sema.ir.extraData(Ir.Inst.ChoiceBr, data.extra_index);
const options_slice = sema.ir.bodySlice(choice_extra.end, choice_extra.data.cases_len);
var branch_labels: std.ArrayListUnmanaged(usize) = .empty;
@ -332,20 +523,13 @@ fn irImplicitRet(_: *Sema, chunk: *Chunk, _: Ir.Inst.Index) InnerError!Ref {
return chunk.addByteOp(.exit);
}
fn resolveGlobal(
sema: *Sema,
byte_offset: u32,
global_name: Ir.NullTerminatedString,
) !Ref {
if (try sema.getGlobal(global_name)) |global| {
return global;
}
return fail(sema, .{ .byte_offset = byte_offset }, "unknown global variable", .{});
}
fn irDeclRef(sema: *Sema, _: *Chunk, inst: Ir.Inst.Index) InnerError!Ref {
const data = sema.ir.instructions[@intFromEnum(inst)].data.str_tok;
return resolveGlobal(sema, data.src_offset, data.start);
const str = try sema.getOrPutStr(data.start);
if (sema.globals_map.get(str.constant)) |global_index| {
return .{ .global = global_index };
}
return fail(sema, .{ .src_offset = data.src_offset }, "unknown global variable", .{});
}
fn irDeclVar(
@ -355,7 +539,7 @@ fn irDeclVar(
inst: Ir.Inst.Index,
) InnerError!void {
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
const extra = sema.ir.extraData(Ir.Inst.Block, data.payload_index);
const extra = sema.ir.extraData(Ir.Inst.Block, data.extra_index);
const body = sema.ir.bodySlice(extra.end, extra.data.body_len);
try blockBodyInner(sema, chunk, body);
// FIXME: hack
@ -364,8 +548,8 @@ fn irDeclVar(
const val = chunk.resolveInst(last_inst);
_ = try chunk.doLoad(val);
}
const global = try sema.addGlobal(name);
_ = try chunk.addConstOp(.store_global, @intCast(global.global));
const interned_str = try sema.getOrPutStr(name);
_ = try chunk.addConstOp(.store_global, @intCast(interned_str.constant));
_ = try chunk.addByteOp(.pop);
}
@ -376,7 +560,7 @@ fn irDeclKnot(
) InnerError!void {
const gpa = sema.gpa;
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
const extra = sema.ir.extraData(Ir.Inst.Knot, data.payload_index);
const extra = sema.ir.extraData(Ir.Inst.Knot, data.extra_index);
var knot: Compilation.Knot = .{
.name = name_ref,
@ -398,7 +582,7 @@ fn irDeclKnot(
fn irDeclaration(sema: *Sema, parent_chunk: ?*Chunk, inst: Ir.Inst.Index) !void {
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
const extra = sema.ir.extraData(Ir.Inst.Declaration, data.payload_index).data;
const extra = sema.ir.extraData(Ir.Inst.Declaration, data.extra_index).data;
const value_data = sema.ir.instructions[@intFromEnum(extra.value)];
switch (value_data.tag) {
.decl_var => try irDeclVar(sema, parent_chunk.?, extra.name, extra.value),
@ -422,16 +606,16 @@ fn irDivert(
.field => Ir.Inst.FieldCall,
};
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
const extra = sema.ir.extraData(ExtraType, data.payload_index);
const args_len = extra.data.args_len;
const extra = sema.ir.extraData(ExtraType, data.extra_index);
const body = sema.ir.extra[extra.end..];
const callee = switch (kind) {
.direct => chunk.resolveInst(extra.data.callee),
.field => chunk.resolveInst(extra.data.obj_ptr),
};
_ = try chunk.doLoad(callee);
const callee_src: SrcLoc = .{ .src_offset = data.src_offset };
_ = try analyzeDivertTarget(sema, chunk, callee_src, callee);
const args_len = extra.data.args_len;
var arg_start: u32 = args_len;
var i: u32 = 0;
while (i < args_len) : (i += 1) {
@ -487,23 +671,23 @@ fn blockBodyInner(sema: *Sema, chunk: *Chunk, body: []const Ir.Inst.Index) Inner
continue;
},
.load => try irLoad(sema, chunk, inst),
.add => try irBinary(sema, chunk, inst, .add),
.sub => try irBinary(sema, chunk, inst, .sub),
.mul => try irBinary(sema, chunk, inst, .mul),
.div => try irBinary(sema, chunk, inst, .div),
.mod => try irBinary(sema, chunk, inst, .mod),
.neg => try irUnary(sema, chunk, inst, .neg),
.not => try irUnary(sema, chunk, inst, .not),
.cmp_eq => try irBinary(sema, chunk, inst, .cmp_eq),
.add => try irBinaryOp(sema, chunk, inst, .add),
.sub => try irBinaryOp(sema, chunk, inst, .sub),
.mul => try irBinaryOp(sema, chunk, inst, .mul),
.div => try irBinaryOp(sema, chunk, inst, .div),
.mod => try irBinaryOp(sema, chunk, inst, .mod),
.neg => try irUnaryOp(sema, chunk, inst, .neg),
.not => try irUnaryOp(sema, chunk, inst, .not),
.cmp_eq => try irBinaryOp(sema, chunk, inst, .cmp_eq),
.cmp_neq => blk: {
_ = try irBinary(sema, chunk, inst, .cmp_eq);
_ = try irBinaryOp(sema, chunk, inst, .cmp_eq);
const tmp = try chunk.addByteOp(.not);
break :blk tmp;
},
.cmp_lt => try irBinary(sema, chunk, inst, .cmp_lt),
.cmp_lte => try irBinary(sema, chunk, inst, .cmp_lte),
.cmp_gt => try irBinary(sema, chunk, inst, .cmp_gt),
.cmp_gte => try irBinary(sema, chunk, inst, .cmp_gte),
.cmp_lt => try irBinaryOp(sema, chunk, inst, .cmp_lt),
.cmp_lte => try irBinaryOp(sema, chunk, inst, .cmp_lte),
.cmp_gt => try irBinaryOp(sema, chunk, inst, .cmp_gt),
.cmp_gte => try irBinaryOp(sema, chunk, inst, .cmp_gte),
.decl_ref => try irDeclRef(sema, chunk, inst),
.int => try irInteger(sema, inst),
.str => try irString(sema, inst),
@ -542,162 +726,23 @@ fn blockBodyInner(sema: *Sema, chunk: *Chunk, body: []const Ir.Inst.Index) Inner
pub fn analyzeFile(sema: *Sema, inst: Ir.Inst.Index) InnerError!void {
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
const extra = sema.ir.extraData(Ir.Inst.Block, data.payload_index);
const extra = sema.ir.extraData(Ir.Inst.Block, data.extra_index);
const body = sema.ir.bodySlice(extra.end, extra.data.body_len);
// FIXME: We are going to get burned by this if we don't formalize it.
// Adding common constants to the constant pool.
_ = try sema.getConstant(.{ .integer = 0 });
_ = try sema.getConstant(.{ .integer = 1 });
const static_constants = &[_]Compilation.Constant{
.{ .integer = 0 },
.{ .integer = 1 },
};
for (static_constants) |sc| {
_ = try sema.getOrPutConstant(sc);
}
try sema.globals_map.ensureUnusedCapacity(sema.gpa, @intCast(sema.ir.globals.len));
for (sema.ir.globals, 0..) |global, global_index| {
const interned = try sema.getOrPutStr(global.name);
sema.globals_map.putAssumeCapacity(interned.constant, @intCast(global_index));
}
for (body) |body_index| try irDeclaration(sema, null, body_index);
}
const Chunk = struct {
sema: *Sema,
knot: *Compilation.Knot,
labels: std.ArrayListUnmanaged(Label) = .empty,
fixups: std.ArrayListUnmanaged(Fixup) = .empty,
inst_map: std.AutoHashMapUnmanaged(Ir.Inst.Index, Ref) = .empty,
const dummy_address = 0xffffffff;
const Label = struct {
code_offset: usize,
};
const Fixup = struct {
mode: enum {
relative,
absolute,
},
label_index: u32,
code_offset: u32,
};
fn deinit(chunk: *Chunk, gpa: std.mem.Allocator) void {
chunk.fixups.deinit(gpa);
chunk.labels.deinit(gpa);
chunk.inst_map.deinit(gpa);
}
fn addByteOp(chunk: *Chunk, op: Story.Opcode) error{OutOfMemory}!Ref {
const gpa = chunk.sema.gpa;
const bytecode = &chunk.knot.bytecode;
const byte_index = bytecode.items.len;
try bytecode.append(gpa, @intFromEnum(op));
return .{ .index = @intCast(byte_index) };
}
fn addConstOp(chunk: *Chunk, op: Story.Opcode, arg: u8) error{OutOfMemory}!Ref {
const gpa = chunk.sema.gpa;
const bytecode = &chunk.knot.bytecode;
const byte_index = bytecode.items.len;
try bytecode.ensureUnusedCapacity(gpa, 2);
bytecode.appendAssumeCapacity(@intFromEnum(op));
bytecode.appendAssumeCapacity(arg);
return .{ .index = @intCast(byte_index) };
}
fn addJumpOp(chunk: *Chunk, op: Story.Opcode) error{OutOfMemory}!Ref {
const gpa = chunk.sema.gpa;
const bytecode = &chunk.knot.bytecode;
try bytecode.ensureUnusedCapacity(gpa, 3);
bytecode.appendAssumeCapacity(@intFromEnum(op));
bytecode.appendAssumeCapacity(0xff);
bytecode.appendAssumeCapacity(0xff);
return .{ .index = @intCast(bytecode.items.len - 2) };
}
fn resolveInst(chunk: *Chunk, ref: Ir.Inst.Ref) Ref {
if (ref.toIndex()) |index| {
return chunk.inst_map.get(index).?;
}
switch (ref) {
.bool_true => return .bool_true,
.bool_false => return .bool_false,
else => return .{ .constant = @intFromEnum(ref) },
}
}
fn addFixup(chunk: *Chunk, op: Story.Opcode, label: usize) !void {
const code_ref = try chunk.addJumpOp(op);
return chunk.fixups.append(chunk.sema.gpa, .{
.mode = .relative,
.label_index = @intCast(label),
.code_offset = code_ref.index,
});
}
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, .{
.code_offset = dummy_address,
});
return label_index;
}
fn setLabel(chunk: *Chunk, label_index: usize) void {
const code_offset = chunk.knot.bytecode.items.len;
assert(label_index <= chunk.labels.items.len);
const label_data = &chunk.labels.items[label_index];
label_data.code_offset = code_offset;
}
fn resolveLabels(chunk: *Chunk) !void {
const start_index = 0;
const end_index = chunk.fixups.items.len;
const bytecode = &chunk.knot.bytecode;
for (chunk.fixups.items[start_index..end_index]) |fixup| {
const label = chunk.labels.items[fixup.label_index];
assert(label.code_offset != dummy_address);
const target_offset: usize = switch (fixup.mode) {
.relative => label.code_offset - fixup.code_offset - 2,
.absolute => label.code_offset,
};
if (target_offset >= std.math.maxInt(u16)) {
std.debug.print("Too much code to jump over!\n", .{});
return error.InvalidJump;
}
assert(bytecode.capacity >= label.code_offset + 2);
bytecode.items[fixup.code_offset] = @intCast((target_offset >> 8) & 0xff);
bytecode.items[fixup.code_offset + 1] = @intCast(target_offset & 0xff);
}
}
fn doLoad(chunk: *Chunk, ref: Ref) InnerError!Ref {
const gpa = chunk.sema.gpa;
switch (ref) {
.bool_true => return chunk.addByteOp(.true),
.bool_false => return chunk.addByteOp(.false),
.none => return ref,
.constant => |id| {
// TODO: This isn't great. New constant indexes are
// created each time.
const ref_const = chunk.knot.constants.items.len;
try chunk.knot.constants.append(gpa, id);
return chunk.addConstOp(.load_const, @intCast(ref_const));
},
.global => |id| {
// TODO: This isn't great. New constant indexes are
// created each time.
const ref_const = chunk.knot.constants.items.len;
try chunk.knot.constants.append(gpa, id);
return chunk.addConstOp(.load_global, @intCast(ref_const));
},
.local => |id| return chunk.addConstOp(.load, @intCast(id)),
.index => return ref,
}
}
};

View file

@ -132,6 +132,7 @@ pub fn dump(story: *Story, writer: *std.Io.Writer) !void {
try writer.writeAll("\n");
try writer.writeAll("=== Knots ===\n");
try writer.flush();
var knots_iter = story.globals.iterator();
while (knots_iter.next()) |entry| {

View file

@ -2,6 +2,7 @@ const std = @import("std");
const Story = @import("../Story.zig");
const Object = Story.Object;
const Opcode = Story.Opcode;
const assert = std.debug.assert;
const Dumper = @This();
story: *const Story,
@ -35,9 +36,10 @@ fn dumpGlobalInst(
const arg = context.bytes[offset + 1];
const constant_index = context.const_pool[arg];
const global_constant = d.story.constants_pool.items[constant_index];
assert(global_constant.tag == .string);
const global_name: *Object.String = @ptrCast(global_constant);
const name_bytes = global_name.bytes[0..global_name.length];
try d.writer.print("{s} {x} '{s}'\n", .{ @tagName(op), arg, name_bytes });
return offset + 2;
}
@ -134,9 +136,10 @@ pub fn dump(d: Dumper, path: *const Object.ContentPath) !void {
const name_object = path.name;
const name_bytes = name_object.bytes[0..name_object.length];
try d.writer.print("=== {s}(args: {d}, locals: {d}) ===\n", .{
try d.writer.print("=== {s}(args: {d}, constants: {d}, locals: {d}) ===\n", .{
name_bytes,
path.arity,
path.const_pool.len,
path.locals_count,
});

View file

@ -15,7 +15,6 @@ pub const Compilation = struct {
errors: []Error,
knots: []Knot,
constants: []Constant,
globals: []u32,
pub const Error = struct {
line: usize,
@ -138,7 +137,6 @@ pub const Compilation = struct {
.ir = ir,
.errors = try errors.toOwnedSlice(gpa),
.constants = if (fatal) &.{} else try sema.constants.toOwnedSlice(gpa),
.globals = if (fatal) &.{} else try sema.globals.toOwnedSlice(gpa),
.knots = if (fatal) &.{} else try sema.knots.toOwnedSlice(gpa),
};
}
@ -146,7 +144,7 @@ pub const Compilation = struct {
pub fn setupStoryRuntime(cu: *Compilation, gpa: std.mem.Allocator, story: *Story) !void {
assert(cu.errors.len == 0);
const globals_len = cu.globals.len + cu.knots.len;
const globals_len = cu.ir.globals.len;
const constants_pool = &story.constants_pool;
try constants_pool.ensureUnusedCapacity(gpa, cu.constants.len);
try story.globals.ensureUnusedCapacity(gpa, @intCast(globals_len));
@ -166,9 +164,8 @@ pub const Compilation = struct {
},
}
}
for (cu.globals) |global_index| {
const str = cu.constants[global_index];
const name_bytes = cu.ir.nullTerminatedString(str.string);
for (cu.ir.globals) |global| {
const name_bytes = cu.ir.nullTerminatedString(global.name);
story.globals.putAssumeCapacity(name_bytes, null);
}
for (cu.knots) |*knot| {
@ -195,7 +192,6 @@ pub const Compilation = struct {
gpa.free(cu.knots);
gpa.free(cu.errors);
gpa.free(cu.globals);
gpa.free(cu.constants);
cu.ir.deinit(gpa);

View file

@ -27,6 +27,19 @@ test "compiler: global variable shadowing" {
);
}
test "compiler: invalid divert target" {
try testEqual(
\\-> foo
\\
\\VAR foo = 123
,
\\<STDIN>:1:4: error: invalid divert target
\\1 | -> foo
\\ | ^
\\
);
}
fn testEqual(source_bytes: [:0]const u8, expected_error: []const u8) !void {
const gpa = std.testing.allocator;
var allocating = std.io.Writer.Allocating.init(gpa);

View file

@ -77,7 +77,7 @@ pub const Writer = struct {
fn writeFieldPtrInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
const data = self.code.instructions[@intFromEnum(inst)].data.payload;
const extra = self.code.extraData(Ir.Inst.Field, data.payload_index).data;
const extra = self.code.extraData(Ir.Inst.Field, data.extra_index).data;
try self.writeInstRef(w, extra.lhs);
try w.writeAll(", ");
try self.writeStringRef(w, extra.field_name_start);
@ -97,7 +97,7 @@ pub const Writer = struct {
.field => Ir.Inst.FieldCall,
};
const data = self.code.instructions[@intFromEnum(inst)].data.payload;
const extra = self.code.extraData(ExtraType, data.payload_index);
const extra = self.code.extraData(ExtraType, data.extra_index);
const args_len = extra.data.args_len;
const body = self.code.extra[extra.end..];
@ -130,13 +130,13 @@ pub const Writer = struct {
fn writeBreakInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
const data = self.code.instructions[@intFromEnum(inst)].data.payload;
const extra = self.code.extraData(Ir.Inst.Break, data.payload_index);
const extra = self.code.extraData(Ir.Inst.Break, data.extra_index);
try self.writeInstIndex(w, extra.data.block_inst);
}
fn writeCondbrInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
const data = self.code.instructions[@intFromEnum(inst)].data.payload;
const extra = self.code.extraData(Ir.Inst.CondBr, data.payload_index);
const extra = self.code.extraData(Ir.Inst.CondBr, data.extra_index);
const then_body = self.code.bodySlice(extra.end, extra.data.then_body_len);
const else_body = self.code.bodySlice(extra.end + then_body.len, extra.data.else_body_len);
try self.writeInstRef(w, extra.data.condition);
@ -148,7 +148,7 @@ pub const Writer = struct {
fn writeSwitchBrInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
const data = self.code.instructions[@intFromEnum(inst)].data.payload;
const extra = self.code.extraData(Ir.Inst.SwitchBr, data.payload_index);
const extra = self.code.extraData(Ir.Inst.SwitchBr, data.extra_index);
const cases_slice = self.code.bodySlice(extra.end, extra.data.cases_len);
try self.writeInstRef(w, extra.data.operand);
@ -181,7 +181,7 @@ pub const Writer = struct {
fn writeChoiceBrInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
const data = self.code.instructions[@intFromEnum(inst)].data.payload;
const extra = self.code.extraData(Ir.Inst.ChoiceBr, data.payload_index);
const extra = self.code.extraData(Ir.Inst.ChoiceBr, data.extra_index);
const options_slice = self.code.bodySlice(extra.end, extra.data.cases_len);
for (options_slice) |option_index| {
const case_extra = self.code.extraData(Ir.Inst.ChoiceBr.Case, @intFromEnum(option_index));
@ -206,7 +206,7 @@ pub const Writer = struct {
fn writeKnotDeclInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
const data = self.code.instructions[@intFromEnum(inst)].data.payload;
const extra = self.code.extraData(Ir.Inst.Knot, data.payload_index);
const extra = self.code.extraData(Ir.Inst.Knot, data.extra_index);
const body_slice = self.code.bodySlice(extra.end, extra.data.body_len);
try w.writeAll("body=");
try self.writeBodyInner(w, body_slice);
@ -214,7 +214,7 @@ pub const Writer = struct {
fn writeVarDeclInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
const data = self.code.instructions[@intFromEnum(inst)].data.payload;
const extra = self.code.extraData(Ir.Inst.Knot, data.payload_index);
const extra = self.code.extraData(Ir.Inst.Knot, data.extra_index);
const body_slice = self.code.bodySlice(extra.end, extra.data.body_len);
try w.writeAll("body=");
try self.writeBodyInner(w, body_slice);
@ -222,7 +222,7 @@ pub const Writer = struct {
fn writeDeclarationInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
const data = self.code.instructions[@intFromEnum(inst)].data.payload;
const extra = self.code.extraData(Ir.Inst.Declaration, data.payload_index);
const extra = self.code.extraData(Ir.Inst.Declaration, data.extra_index);
try w.writeAll("name=");
try self.writeStringRef(w, extra.data.name);
try w.writeAll(", ");
@ -249,14 +249,14 @@ pub const Writer = struct {
fn writeBlockInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
const data = self.code.instructions[@intFromEnum(inst)].data.payload;
const extra = self.code.extraData(Ir.Inst.Block, data.payload_index);
const extra = self.code.extraData(Ir.Inst.Block, data.extra_index);
const body_slice = self.code.bodySlice(extra.end, extra.data.body_len);
try self.writeBodyInner(w, body_slice);
}
fn writeFileInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
const data = self.code.instructions[@intFromEnum(inst)].data.payload;
const extra = self.code.extraData(Ir.Inst.Block, data.payload_index);
const extra = self.code.extraData(Ir.Inst.Block, data.extra_index);
const body_slice = self.code.bodySlice(extra.end, extra.data.body_len);
try self.writeBodyInner(w, body_slice);
}