diff --git a/src/AstGen.zig b/src/AstGen.zig index 646bc2b..4c8aa30 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -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, }, }); diff --git a/src/Ir.zig b/src/Ir.zig index 3f47ea0..b1c52c8 100644 --- a/src/Ir.zig +++ b/src/Ir.zig @@ -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, diff --git a/src/Sema.zig b/src/Sema.zig index b615284..2690eb7 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -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, - } - } -}; diff --git a/src/Story.zig b/src/Story.zig index d2db85c..2e0e9a4 100644 --- a/src/Story.zig +++ b/src/Story.zig @@ -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| { diff --git a/src/Story/Dumper.zig b/src/Story/Dumper.zig index eda4d7e..3d3ada5 100644 --- a/src/Story/Dumper.zig +++ b/src/Story/Dumper.zig @@ -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, }); diff --git a/src/compile.zig b/src/compile.zig index a9cfe7f..6b56c27 100644 --- a/src/compile.zig +++ b/src/compile.zig @@ -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); diff --git a/src/error_tests.zig b/src/error_tests.zig index d1abaac..51bdac7 100644 --- a/src/error_tests.zig +++ b/src/error_tests.zig @@ -27,6 +27,19 @@ test "compiler: global variable shadowing" { ); } +test "compiler: invalid divert target" { + try testEqual( + \\-> foo + \\ + \\VAR foo = 123 + , + \\: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); diff --git a/src/print_ir.zig b/src/print_ir.zig index ce9dc0c..42ea508 100644 --- a/src/print_ir.zig +++ b/src/print_ir.zig @@ -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); }