| ... | @@ -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, |
| 27 | | 28 | |
| 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 | } = .{}, |
| 38 | value_map: std.array_hash_map.String(AsmValue) = .{}, | 39 | value_map: std.array_hash_map.String(AsmValue) = .empty, |
| 39 | inst_map: std.array_hash_map.String(void) = .empty, | 40 | inst_map: std.array_hash_map.String(void) = .empty, |
| 40 | | 41 | |
| 41 | const Operand = union(enum) { | 42 | const 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 | } |
| 115 | | 116 | |
| 116 | fn fail(ass: *Assembler, offset: u32, comptime fmt: []const u8, args: anytype) Error { | 117 | fn 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 | } |
| 120 | | 122 | |
| 121 | fn todo(ass: *Assembler, comptime fmt: []const u8, args: anytype) Error { | 123 | fn 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 | } |
| 124 | | 126 | |
| 125 | const AsmValue = union(enum) { | 127 | const 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 | }; |
| 454 | | 454 | |
| 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 | } |
| 484 | | 484 | |
| 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; |
| 539 | | 538 | |
| 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); |