| ... | ... | @@ -24,6 +24,7 @@ inst: struct { |
| 24 | 24 | opcode: Opcode = undefined, |
| 25 | 25 | operands: std.ArrayList(Operand) = .empty, |
| 26 | 26 | string_bytes: std.ArrayList(u8) = .empty, |
| 27 | inst_offset: u32 = 0, |
| 27 | 28 | |
| 28 | 29 | fn result(ass: @This()) ?AsmValue.Ref { |
| 29 | 30 | for (ass.operands.items[0..@min(ass.operands.items.len, 2)]) |op| { |
| ... | ... | @@ -35,7 +36,7 @@ inst: struct { |
| 35 | 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 | 40 | inst_map: std.array_hash_map.String(void) = .empty, |
| 40 | 41 | |
| 41 | 42 | const Operand = union(enum) { |
| ... | ... | @@ -82,7 +83,7 @@ pub fn assemble(ass: *Assembler, src: []const u8) Error!void { |
| 82 | 83 | if (ass.inst_map.count() == 0) { |
| 83 | 84 | const instructions = spec.InstructionSet.core.instructions(); |
| 84 | 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 | 87 | const entry = try ass.inst_map.getOrPut(gpa, inst.name); |
| 87 | 88 | assert(entry.index == i); |
| 88 | 89 | } |
| ... | ... | @@ -114,12 +115,13 @@ fn addError(ass: *Assembler, offset: u32, comptime fmt: []const u8, args: anytyp |
| 114 | 115 | } |
| 115 | 116 | |
| 116 | 117 | fn fail(ass: *Assembler, offset: u32, comptime fmt: []const u8, args: anytype) Error { |
| 118 | @branchHint(.cold); |
| 117 | 119 | try ass.addError(offset, fmt, args); |
| 118 | 120 | return error.AssembleFail; |
| 119 | 121 | } |
| 120 | 122 | |
| 121 | 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 | 127 | const AsmValue = union(enum) { |
| ... | ... | @@ -209,9 +211,8 @@ fn processInstruction(ass: *Assembler) !void { |
| 209 | 211 | switch (ass.value_map.values()[result_ref]) { |
| 210 | 212 | .just_declared => ass.value_map.values()[result_ref] = result, |
| 211 | 213 | else => { |
| 212 | | // TODO: Improve source location. |
| 213 | 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 | 230 | 0 => .unsigned, |
| 230 | 231 | 1 => .signed, |
| 231 | 232 | else => { |
| 232 | | // TODO: Improve source location. |
| 233 | | return ass.fail(0, "{} is not a valid signedness (expected 0 or 1)", .{operands[2].literal32}); |
| 233 | return ass.fail(ass.inst.inst_offset, "{} is not a valid signedness (expected 0 or 1)", .{operands[2].literal32}); |
| 234 | 234 | }, |
| 235 | 235 | }; |
| 236 | 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 | 239 | break :blk try cg.intType(signedness, width); |
| 240 | 240 | }, |
| ... | ... | @@ -243,7 +243,7 @@ fn processTypeInstruction(ass: *Assembler) !AsmValue { |
| 243 | 243 | switch (bits) { |
| 244 | 244 | 16, 32, 64 => {}, |
| 245 | 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 | 249 | break :blk try cg.floatType(@intCast(bits)); |
| ... | ... | @@ -445,11 +445,11 @@ fn processSpecConstVector(ass: *Assembler) !?AsmValue { |
| 445 | 445 | const gpa = cg.gpa; |
| 446 | 446 | const ty_ref = switch (ass.inst.operands.items[0]) { |
| 447 | 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 | 450 | const composite_ty_id = switch (try ass.resolveRef(ty_ref)) { |
| 451 | 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 | 455 | const globals = &cg.sections.globals; |
| ... | ... | @@ -483,7 +483,7 @@ fn processSpecConstVector(ass: *Assembler) !?AsmValue { |
| 483 | 483 | } |
| 484 | 484 | |
| 485 | 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 | 488 | try annotations.emitRaw(gpa, .OpDecorate, 3); |
| 489 | 489 | annotations.writeOperand(Id, elem_id); |
| ... | ... | @@ -505,8 +505,7 @@ fn resolveMaybeForwardRef(ass: *Assembler, ref: AsmValue.Ref) !AsmValue { |
| 505 | 505 | switch (value) { |
| 506 | 506 | .just_declared => { |
| 507 | 507 | const name = ass.value_map.keys()[ref]; |
| 508 | | // TODO: Improve source location. |
| 509 | | return ass.fail(0, "ass-referential parameter %{s}", .{name}); |
| 508 | return ass.fail(ass.inst.inst_offset, "self-referential parameter %{s}", .{name}); |
| 510 | 509 | }, |
| 511 | 510 | else => return value, |
| 512 | 511 | } |
| ... | ... | @@ -518,8 +517,7 @@ fn resolveRef(ass: *Assembler, ref: AsmValue.Ref) !AsmValue { |
| 518 | 517 | .just_declared => unreachable, |
| 519 | 518 | .unresolved_forward_reference => { |
| 520 | 519 | const name = ass.value_map.keys()[ref]; |
| 521 | | // TODO: Improve source location. |
| 522 | | return ass.fail(0, "reference to undeclared result-id %{s}", .{name}); |
| 520 | return ass.fail(ass.inst.inst_offset, "reference to undeclared result-id %{s}", .{name}); |
| 523 | 521 | }, |
| 524 | 522 | else => return value, |
| 525 | 523 | } |
| ... | ... | @@ -536,6 +534,7 @@ fn parseInstruction(ass: *Assembler) !void { |
| 536 | 534 | ass.inst.opcode = undefined; |
| 537 | 535 | ass.inst.operands.clearRetainingCapacity(); |
| 538 | 536 | ass.inst.string_bytes.clearRetainingCapacity(); |
| 537 | ass.inst.inst_offset = ass.currentToken().start; |
| 539 | 538 | |
| 540 | 539 | const lhs_result_tok = ass.currentToken(); |
| 541 | 540 | const maybe_lhs_result: ?AsmValue.Ref = if (ass.eatToken(.result_id_assign)) blk: { |
| ... | ... | @@ -589,8 +588,8 @@ fn parseInstruction(ass: *Assembler) !void { |
| 589 | 588 | .required => if (ass.isAtInstructionBoundary()) { |
| 590 | 589 | return ass.fail( |
| 591 | 590 | ass.currentToken().start, |
| 592 | | "missing required operand", // TODO: Operand name? |
| 593 | | .{}, |
| 591 | "missing required operand '{s}'", |
| 592 | .{@tagName(operand.kind)}, |
| 594 | 593 | ); |
| 595 | 594 | } else { |
| 596 | 595 | try ass.parseOperand(operand.kind); |