authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2024-11-02 15:22:48+01:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2024-11-08 20:38:22+01:00
log7682ced08ea44d7535031d2c6075dc7ca28722a6
tree047606eace09641e9c46f5f87176f0e30eecb8cf
parent4fbc100959c05ba10e4bfeeca9c7b5cd8216cc57
signaturebadge-check Signed by SSH key SHA256:ZS52FNyUv2WUXvO4njmVaFVO46RHojFuOrxRc4LuKzg

spirv: track global OpVariables properly in assembler

Also cleans up the assembler a bit in general.

2 files changed, 43 insertions(+), 43 deletions(-)

src/codegen/spirv.zig+19-32
......@@ -6529,6 +6529,13 @@ const NavGen = struct {
65296529 return self.todo("implement inline asm with more than 1 output", .{});
65306530 }
65316531
6532 var as = SpvAssembler{
6533 .gpa = self.gpa,
6534 .spv = self.spv,
6535 .func = &self.func,
6536 };
6537 defer as.deinit();
6538
65326539 var output_extra_i = extra_i;
65336540 for (outputs) |output| {
65346541 if (output != .none) {
......@@ -6541,7 +6548,6 @@ const NavGen = struct {
65416548 // TODO: Record output and use it somewhere.
65426549 }
65436550
6544 var input_extra_i = extra_i;
65456551 for (inputs) |input| {
65466552 const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);
65476553 const constraint = std.mem.sliceTo(extra_bytes, 0);
......@@ -6549,36 +6555,6 @@ const NavGen = struct {
65496555 // This equation accounts for the fact that even if we have exactly 4 bytes
65506556 // for the string, we still use the next u32 for the null terminator.
65516557 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
6552 // TODO: Record input and use it somewhere.
6553 _ = input;
6554 }
6555
6556 {
6557 var clobber_i: u32 = 0;
6558 while (clobber_i < clobbers_len) : (clobber_i += 1) {
6559 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
6560 extra_i += clobber.len / 4 + 1;
6561 // TODO: Record clobber and use it somewhere.
6562 }
6563 }
6564
6565 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
6566
6567 var as = SpvAssembler{
6568 .gpa = self.gpa,
6569 .src = asm_source,
6570 .spv = self.spv,
6571 .func = &self.func,
6572 };
6573 defer as.deinit();
6574
6575 for (inputs) |input| {
6576 const extra_bytes = std.mem.sliceAsBytes(self.air.extra[input_extra_i..]);
6577 const constraint = std.mem.sliceTo(extra_bytes, 0);
6578 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
6579 // This equation accounts for the fact that even if we have exactly 4 bytes
6580 // for the string, we still use the next u32 for the null terminator.
6581 input_extra_i += (constraint.len + name.len + (2 + 3)) / 4;
65826558
65836559 if (self.typeOf(input).zigTypeTag(zcu) == .type) {
65846560 // This assembly input is a type instead of a value.
......@@ -6592,7 +6568,18 @@ const NavGen = struct {
65926568 }
65936569 }
65946570
6595 as.assemble() catch |err| switch (err) {
6571 {
6572 var clobber_i: u32 = 0;
6573 while (clobber_i < clobbers_len) : (clobber_i += 1) {
6574 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
6575 extra_i += clobber.len / 4 + 1;
6576 // TODO: Record clobber and use it somewhere.
6577 }
6578 }
6579
6580 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
6581
6582 as.assemble(asm_source) catch |err| switch (err) {
65966583 error.AssembleFail => {
65976584 // TODO: For now the compiler only supports a single error message per decl,
65986585 // so to translate the possible multiple errors from the assembler, emit
src/codegen/spirv/Assembler.zig+24-11
......@@ -151,7 +151,8 @@ gpa: Allocator,
151151errors: std.ArrayListUnmanaged(ErrorMsg) = .empty,
152152
153153/// The source code that is being assembled.
154src: []const u8,
154/// This is set when calling `assemble()`.
155src: []const u8 = undefined,
155156
156157/// The module that this assembly is associated to.
157158/// Instructions like OpType*, OpDecorate, etc are emitted into this module.
......@@ -211,7 +212,10 @@ pub fn deinit(self: *Assembler) void {
211212 self.instruction_map.deinit(self.gpa);
212213}
213214
214pub fn assemble(self: *Assembler) Error!void {
215pub fn assemble(self: *Assembler, src: []const u8) Error!void {
216 self.src = src;
217 self.errors.clearRetainingCapacity();
218
215219 // Populate the opcode map if it isn't already
216220 if (self.instruction_map.count() == 0) {
217221 const instructions = spec.InstructionSet.core.instructions();
......@@ -369,6 +373,7 @@ fn processTypeInstruction(self: *Assembler) !AsmValue {
369373/// - Function-local instructions are emitted in `self.func`.
370374fn processGenericInstruction(self: *Assembler) !?AsmValue {
371375 const operands = self.inst.operands.items;
376 var maybe_spv_decl_index: ?SpvModule.Decl.Index = null;
372377 const section = switch (self.inst.opcode.class()) {
373378 .ConstantCreation => &self.spv.sections.types_globals_constants,
374379 .Annotation => &self.spv.sections.annotations,
......@@ -378,12 +383,15 @@ fn processGenericInstruction(self: *Assembler) !?AsmValue {
378383 .OpExecutionMode, .OpExecutionModeId => &self.spv.sections.execution_modes,
379384 .OpVariable => switch (@as(spec.StorageClass, @enumFromInt(operands[2].value))) {
380385 .Function => &self.func.prologue,
381 .UniformConstant => &self.spv.sections.types_globals_constants,
382 else => {
383 // This is currently disabled because global variables are required to be
384 // emitted in the proper order, and this should be honored in inline assembly
385 // as well.
386 return self.todo("global variables", .{});
386 // These don't need to be marked in the dependency system.
387 // Probably we should add them anyway, then filter out PushConstant globals.
388 .PushConstant => &self.spv.sections.types_globals_constants,
389 else => section: {
390 maybe_spv_decl_index = try self.spv.allocDecl(.global);
391 try self.func.decl_deps.put(self.spv.gpa, maybe_spv_decl_index.?, {});
392 // TODO: In theory this can be non-empty if there is an initializer which depends on another global...
393 try self.spv.declareDeclDeps(maybe_spv_decl_index.?, &.{});
394 break :section &self.spv.sections.types_globals_constants;
387395 },
388396 },
389397 // Default case - to be worked out further.
......@@ -409,7 +417,10 @@ fn processGenericInstruction(self: *Assembler) !?AsmValue {
409417 section.writeDoubleWord(dword);
410418 },
411419 .result_id => {
412 maybe_result_id = self.spv.allocId();
420 maybe_result_id = if (maybe_spv_decl_index) |spv_decl_index|
421 self.spv.declPtr(spv_decl_index).result_id
422 else
423 self.spv.allocId();
413424 try section.ensureUnusedCapacity(self.spv.gpa, 1);
414425 section.writeOperand(IdResult, maybe_result_id.?);
415426 },
......@@ -475,8 +486,8 @@ fn resolveRefId(self: *Assembler, ref: AsmValue.Ref) !IdRef {
475486/// error message has been emitted into `self.errors`.
476487fn parseInstruction(self: *Assembler) !void {
477488 self.inst.opcode = undefined;
478 self.inst.operands.shrinkRetainingCapacity(0);
479 self.inst.string_bytes.shrinkRetainingCapacity(0);
489 self.inst.operands.clearRetainingCapacity();
490 self.inst.string_bytes.clearRetainingCapacity();
480491
481492 const lhs_result_tok = self.currentToken();
482493 const maybe_lhs_result: ?AsmValue.Ref = if (self.eatToken(.result_id_assign)) blk: {
......@@ -848,6 +859,8 @@ fn tokenText(self: Assembler, tok: Token) []const u8 {
848859/// Tokenize `self.src` and put the tokens in `self.tokens`.
849860/// Any errors encountered are appended to `self.errors`.
850861fn tokenize(self: *Assembler) !void {
862 self.tokens.clearRetainingCapacity();
863
851864 var offset: u32 = 0;
852865 while (true) {
853866 const tok = try self.nextToken(offset);