authorgravatar for vasyapulopapik@gmail.comKleshzz <vasyapulopapik@gmail.com> 2026-07-22 17:43:11+03:00
committergravatar for alichraghi@noreply.codeberg.orgAli Cheraghi <alichraghi@noreply.codeberg.org> 2026-07-23 11:35:07+02:00
log97527a42c5a09564fa94d491a0c1d48c0fe66e28
treec2c1636021712768229b959a2c95f8832e5ad7eb
parent9a9d8adda017780956de9015db1858f48bc8f509

spirv: improve Assembler source locations, error formatting


1 files changed, 17 insertions(+), 18 deletions(-)

src/codegen/spirv/Assembler.zig+17-18
...@@ -24,6 +24,7 @@ inst: struct {...@@ -24,6 +24,7 @@ inst: struct {
24 opcode: Opcode = undefined,24 opcode: Opcode = undefined,
25 operands: std.ArrayList(Operand) = .empty,25 operands: std.ArrayList(Operand) = .empty,
26 string_bytes: std.ArrayList(u8) = .empty,26 string_bytes: std.ArrayList(u8) = .empty,
27 inst_offset: u32 = 0,
2728
28 fn result(ass: @This()) ?AsmValue.Ref {29 fn result(ass: @This()) ?AsmValue.Ref {
29 for (ass.operands.items[0..@min(ass.operands.items.len, 2)]) |op| {30 for (ass.operands.items[0..@min(ass.operands.items.len, 2)]) |op| {
...@@ -35,7 +36,7 @@ inst: struct {...@@ -35,7 +36,7 @@ inst: struct {
35 return null;36 return null;
36 }37 }
37} = .{},38} = .{},
38value_map: std.array_hash_map.String(AsmValue) = .{},39value_map: std.array_hash_map.String(AsmValue) = .empty,
39inst_map: std.array_hash_map.String(void) = .empty,40inst_map: std.array_hash_map.String(void) = .empty,
4041
41const Operand = union(enum) {42const Operand = union(enum) {
...@@ -82,7 +83,7 @@ pub fn assemble(ass: *Assembler, src: []const u8) Error!void {...@@ -82,7 +83,7 @@ pub fn assemble(ass: *Assembler, src: []const u8) Error!void {
82 if (ass.inst_map.count() == 0) {83 if (ass.inst_map.count() == 0) {
83 const instructions = spec.InstructionSet.core.instructions();84 const instructions = spec.InstructionSet.core.instructions();
84 try ass.inst_map.ensureUnusedCapacity(gpa, @intCast(instructions.len));85 try ass.inst_map.ensureUnusedCapacity(gpa, @intCast(instructions.len));
85 for (spec.InstructionSet.core.instructions(), 0..) |inst, i| {86 for (instructions, 0..) |inst, i| {
86 const entry = try ass.inst_map.getOrPut(gpa, inst.name);87 const entry = try ass.inst_map.getOrPut(gpa, inst.name);
87 assert(entry.index == i);88 assert(entry.index == i);
88 }89 }
...@@ -114,12 +115,13 @@ fn addError(ass: *Assembler, offset: u32, comptime fmt: []const u8, args: anytyp...@@ -114,12 +115,13 @@ fn addError(ass: *Assembler, offset: u32, comptime fmt: []const u8, args: anytyp
114}115}
115116
116fn fail(ass: *Assembler, offset: u32, comptime fmt: []const u8, args: anytype) Error {117fn fail(ass: *Assembler, offset: u32, comptime fmt: []const u8, args: anytype) Error {
118 @branchHint(.cold);
117 try ass.addError(offset, fmt, args);119 try ass.addError(offset, fmt, args);
118 return error.AssembleFail;120 return error.AssembleFail;
119}121}
120122
121fn todo(ass: *Assembler, comptime fmt: []const u8, args: anytype) Error {123fn todo(ass: *Assembler, comptime fmt: []const u8, args: anytype) Error {
122 return ass.fail(0, "todo: " ++ fmt, args);124 return ass.fail(ass.inst.inst_offset, "todo: " ++ fmt, args);
123}125}
124126
125const AsmValue = union(enum) {127const AsmValue = union(enum) {
...@@ -209,9 +211,8 @@ fn processInstruction(ass: *Assembler) !void {...@@ -209,9 +211,8 @@ fn processInstruction(ass: *Assembler) !void {
209 switch (ass.value_map.values()[result_ref]) {211 switch (ass.value_map.values()[result_ref]) {
210 .just_declared => ass.value_map.values()[result_ref] = result,212 .just_declared => ass.value_map.values()[result_ref] = result,
211 else => {213 else => {
212 // TODO: Improve source location.
213 const name = ass.value_map.keys()[result_ref];214 const name = ass.value_map.keys()[result_ref];
214 return ass.fail(0, "duplicate definition of %{s}", .{name});215 return ass.fail(ass.inst.inst_offset, "duplicate definition of %{s}", .{name});
215 },216 },
216 }217 }
217}218}
...@@ -229,12 +230,11 @@ fn processTypeInstruction(ass: *Assembler) !AsmValue {...@@ -229,12 +230,11 @@ fn processTypeInstruction(ass: *Assembler) !AsmValue {
229 0 => .unsigned,230 0 => .unsigned,
230 1 => .signed,231 1 => .signed,
231 else => {232 else => {
232 // TODO: Improve source location.233 return ass.fail(ass.inst.inst_offset, "{} is not a valid signedness (expected 0 or 1)", .{operands[2].literal32});
233 return ass.fail(0, "{} is not a valid signedness (expected 0 or 1)", .{operands[2].literal32});
234 },234 },
235 };235 };
236 const width = std.math.cast(u16, operands[1].literal32) orelse {236 const width = std.math.cast(u16, operands[1].literal32) orelse {
237 return ass.fail(0, "int type of {} bits is too large", .{operands[1].literal32});237 return ass.fail(ass.inst.inst_offset, "int type of {} bits is too large", .{operands[1].literal32});
238 };238 };
239 break :blk try cg.intType(signedness, width);239 break :blk try cg.intType(signedness, width);
240 },240 },
...@@ -243,7 +243,7 @@ fn processTypeInstruction(ass: *Assembler) !AsmValue {...@@ -243,7 +243,7 @@ fn processTypeInstruction(ass: *Assembler) !AsmValue {
243 switch (bits) {243 switch (bits) {
244 16, 32, 64 => {},244 16, 32, 64 => {},
245 else => {245 else => {
246 return ass.fail(0, "{} is not a valid bit count for floats (expected 16, 32 or 64)", .{bits});246 return ass.fail(ass.inst.inst_offset, "{} is not a valid bit count for floats (expected 16, 32 or 64)", .{bits});
247 },247 },
248 }248 }
249 break :blk try cg.floatType(@intCast(bits));249 break :blk try cg.floatType(@intCast(bits));
...@@ -445,11 +445,11 @@ fn processSpecConstVector(ass: *Assembler) !?AsmValue {...@@ -445,11 +445,11 @@ fn processSpecConstVector(ass: *Assembler) !?AsmValue {
445 const gpa = cg.gpa;445 const gpa = cg.gpa;
446 const ty_ref = switch (ass.inst.operands.items[0]) {446 const ty_ref = switch (ass.inst.operands.items[0]) {
447 .ref_id => |i| i,447 .ref_id => |i| i,
448 else => return ass.fail(0, "missing result type", .{}),448 else => return ass.fail(ass.inst.inst_offset, "missing result type", .{}),
449 };449 };
450 const composite_ty_id = switch (try ass.resolveRef(ty_ref)) {450 const composite_ty_id = switch (try ass.resolveRef(ty_ref)) {
451 .ty => |id| id,451 .ty => |id| id,
452 else => return ass.fail(0, "%ty must be a type", .{}),452 else => return ass.fail(ass.inst.inst_offset, "%ty must be a type", .{}),
453 };453 };
454454
455 const globals = &cg.sections.globals;455 const globals = &cg.sections.globals;
...@@ -483,7 +483,7 @@ fn processSpecConstVector(ass: *Assembler) !?AsmValue {...@@ -483,7 +483,7 @@ fn processSpecConstVector(ass: *Assembler) !?AsmValue {
483 }483 }
484484
485 const spec_id_word = std.math.cast(u32, spec_id_base + i) orelse {485 const spec_id_word = std.math.cast(u32, spec_id_base + i) orelse {
486 return ass.fail(0, "SpecId {} does not fit in 32 bits", .{spec_id_base + i});486 return ass.fail(ass.inst.inst_offset, "SpecId {} does not fit in 32 bits", .{spec_id_base + i});
487 };487 };
488 try annotations.emitRaw(gpa, .OpDecorate, 3);488 try annotations.emitRaw(gpa, .OpDecorate, 3);
489 annotations.writeOperand(Id, elem_id);489 annotations.writeOperand(Id, elem_id);
...@@ -505,8 +505,7 @@ fn resolveMaybeForwardRef(ass: *Assembler, ref: AsmValue.Ref) !AsmValue {...@@ -505,8 +505,7 @@ fn resolveMaybeForwardRef(ass: *Assembler, ref: AsmValue.Ref) !AsmValue {
505 switch (value) {505 switch (value) {
506 .just_declared => {506 .just_declared => {
507 const name = ass.value_map.keys()[ref];507 const name = ass.value_map.keys()[ref];
508 // TODO: Improve source location.508 return ass.fail(ass.inst.inst_offset, "self-referential parameter %{s}", .{name});
509 return ass.fail(0, "ass-referential parameter %{s}", .{name});
510 },509 },
511 else => return value,510 else => return value,
512 }511 }
...@@ -518,8 +517,7 @@ fn resolveRef(ass: *Assembler, ref: AsmValue.Ref) !AsmValue {...@@ -518,8 +517,7 @@ fn resolveRef(ass: *Assembler, ref: AsmValue.Ref) !AsmValue {
518 .just_declared => unreachable,517 .just_declared => unreachable,
519 .unresolved_forward_reference => {518 .unresolved_forward_reference => {
520 const name = ass.value_map.keys()[ref];519 const name = ass.value_map.keys()[ref];
521 // TODO: Improve source location.520 return ass.fail(ass.inst.inst_offset, "reference to undeclared result-id %{s}", .{name});
522 return ass.fail(0, "reference to undeclared result-id %{s}", .{name});
523 },521 },
524 else => return value,522 else => return value,
525 }523 }
...@@ -536,6 +534,7 @@ fn parseInstruction(ass: *Assembler) !void {...@@ -536,6 +534,7 @@ fn parseInstruction(ass: *Assembler) !void {
536 ass.inst.opcode = undefined;534 ass.inst.opcode = undefined;
537 ass.inst.operands.clearRetainingCapacity();535 ass.inst.operands.clearRetainingCapacity();
538 ass.inst.string_bytes.clearRetainingCapacity();536 ass.inst.string_bytes.clearRetainingCapacity();
537 ass.inst.inst_offset = ass.currentToken().start;
539538
540 const lhs_result_tok = ass.currentToken();539 const lhs_result_tok = ass.currentToken();
541 const maybe_lhs_result: ?AsmValue.Ref = if (ass.eatToken(.result_id_assign)) blk: {540 const maybe_lhs_result: ?AsmValue.Ref = if (ass.eatToken(.result_id_assign)) blk: {
...@@ -589,8 +588,8 @@ fn parseInstruction(ass: *Assembler) !void {...@@ -589,8 +588,8 @@ fn parseInstruction(ass: *Assembler) !void {
589 .required => if (ass.isAtInstructionBoundary()) {588 .required => if (ass.isAtInstructionBoundary()) {
590 return ass.fail(589 return ass.fail(
591 ass.currentToken().start,590 ass.currentToken().start,
592 "missing required operand", // TODO: Operand name?591 "missing required operand '{s}'",
593 .{},592 .{@tagName(operand.kind)},
594 );593 );
595 } else {594 } else {
596 try ass.parseOperand(operand.kind);595 try ass.parseOperand(operand.kind);