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 {...@@ -6529,6 +6529,13 @@ const NavGen = struct {
6529 return self.todo("implement inline asm with more than 1 output", .{});6529 return self.todo("implement inline asm with more than 1 output", .{});
6530 }6530 }
65316531
6532 var as = SpvAssembler{
6533 .gpa = self.gpa,
6534 .spv = self.spv,
6535 .func = &self.func,
6536 };
6537 defer as.deinit();
6538
6532 var output_extra_i = extra_i;6539 var output_extra_i = extra_i;
6533 for (outputs) |output| {6540 for (outputs) |output| {
6534 if (output != .none) {6541 if (output != .none) {
...@@ -6541,7 +6548,6 @@ const NavGen = struct {...@@ -6541,7 +6548,6 @@ const NavGen = struct {
6541 // TODO: Record output and use it somewhere.6548 // TODO: Record output and use it somewhere.
6542 }6549 }
65436550
6544 var input_extra_i = extra_i;
6545 for (inputs) |input| {6551 for (inputs) |input| {
6546 const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);6552 const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);
6547 const constraint = std.mem.sliceTo(extra_bytes, 0);6553 const constraint = std.mem.sliceTo(extra_bytes, 0);
...@@ -6549,36 +6555,6 @@ const NavGen = struct {...@@ -6549,36 +6555,6 @@ const NavGen = struct {
6549 // This equation accounts for the fact that even if we have exactly 4 bytes6555 // This equation accounts for the fact that even if we have exactly 4 bytes
6550 // for the string, we still use the next u32 for the null terminator.6556 // for the string, we still use the next u32 for the null terminator.
6551 extra_i += (constraint.len + name.len + (2 + 3)) / 4;6557 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
6583 if (self.typeOf(input).zigTypeTag(zcu) == .type) {6559 if (self.typeOf(input).zigTypeTag(zcu) == .type) {
6584 // This assembly input is a type instead of a value.6560 // This assembly input is a type instead of a value.
...@@ -6592,7 +6568,18 @@ const NavGen = struct {...@@ -6592,7 +6568,18 @@ const NavGen = struct {
6592 }6568 }
6593 }6569 }
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) {
6596 error.AssembleFail => {6583 error.AssembleFail => {
6597 // TODO: For now the compiler only supports a single error message per decl,6584 // TODO: For now the compiler only supports a single error message per decl,
6598 // so to translate the possible multiple errors from the assembler, emit6585 // so to translate the possible multiple errors from the assembler, emit
src/codegen/spirv/Assembler.zig+24-11
...@@ -151,7 +151,8 @@ gpa: Allocator,...@@ -151,7 +151,8 @@ gpa: Allocator,
151errors: std.ArrayListUnmanaged(ErrorMsg) = .empty,151errors: std.ArrayListUnmanaged(ErrorMsg) = .empty,
152152
153/// The source code that is being assembled.153/// The source code that is being assembled.
154src: []const u8,154/// This is set when calling `assemble()`.
155src: []const u8 = undefined,
155156
156/// The module that this assembly is associated to.157/// The module that this assembly is associated to.
157/// Instructions like OpType*, OpDecorate, etc are emitted into this module.158/// Instructions like OpType*, OpDecorate, etc are emitted into this module.
...@@ -211,7 +212,10 @@ pub fn deinit(self: *Assembler) void {...@@ -211,7 +212,10 @@ pub fn deinit(self: *Assembler) void {
211 self.instruction_map.deinit(self.gpa);212 self.instruction_map.deinit(self.gpa);
212}213}
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
215 // Populate the opcode map if it isn't already219 // Populate the opcode map if it isn't already
216 if (self.instruction_map.count() == 0) {220 if (self.instruction_map.count() == 0) {
217 const instructions = spec.InstructionSet.core.instructions();221 const instructions = spec.InstructionSet.core.instructions();
...@@ -369,6 +373,7 @@ fn processTypeInstruction(self: *Assembler) !AsmValue {...@@ -369,6 +373,7 @@ fn processTypeInstruction(self: *Assembler) !AsmValue {
369/// - Function-local instructions are emitted in `self.func`.373/// - Function-local instructions are emitted in `self.func`.
370fn processGenericInstruction(self: *Assembler) !?AsmValue {374fn processGenericInstruction(self: *Assembler) !?AsmValue {
371 const operands = self.inst.operands.items;375 const operands = self.inst.operands.items;
376 var maybe_spv_decl_index: ?SpvModule.Decl.Index = null;
372 const section = switch (self.inst.opcode.class()) {377 const section = switch (self.inst.opcode.class()) {
373 .ConstantCreation => &self.spv.sections.types_globals_constants,378 .ConstantCreation => &self.spv.sections.types_globals_constants,
374 .Annotation => &self.spv.sections.annotations,379 .Annotation => &self.spv.sections.annotations,
...@@ -378,12 +383,15 @@ fn processGenericInstruction(self: *Assembler) !?AsmValue {...@@ -378,12 +383,15 @@ fn processGenericInstruction(self: *Assembler) !?AsmValue {
378 .OpExecutionMode, .OpExecutionModeId => &self.spv.sections.execution_modes,383 .OpExecutionMode, .OpExecutionModeId => &self.spv.sections.execution_modes,
379 .OpVariable => switch (@as(spec.StorageClass, @enumFromInt(operands[2].value))) {384 .OpVariable => switch (@as(spec.StorageClass, @enumFromInt(operands[2].value))) {
380 .Function => &self.func.prologue,385 .Function => &self.func.prologue,
381 .UniformConstant => &self.spv.sections.types_globals_constants,386 // These don't need to be marked in the dependency system.
382 else => {387 // Probably we should add them anyway, then filter out PushConstant globals.
383 // This is currently disabled because global variables are required to be388 .PushConstant => &self.spv.sections.types_globals_constants,
384 // emitted in the proper order, and this should be honored in inline assembly389 else => section: {
385 // as well.390 maybe_spv_decl_index = try self.spv.allocDecl(.global);
386 return self.todo("global variables", .{});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;
387 },395 },
388 },396 },
389 // Default case - to be worked out further.397 // Default case - to be worked out further.
...@@ -409,7 +417,10 @@ fn processGenericInstruction(self: *Assembler) !?AsmValue {...@@ -409,7 +417,10 @@ fn processGenericInstruction(self: *Assembler) !?AsmValue {
409 section.writeDoubleWord(dword);417 section.writeDoubleWord(dword);
410 },418 },
411 .result_id => {419 .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();
413 try section.ensureUnusedCapacity(self.spv.gpa, 1);424 try section.ensureUnusedCapacity(self.spv.gpa, 1);
414 section.writeOperand(IdResult, maybe_result_id.?);425 section.writeOperand(IdResult, maybe_result_id.?);
415 },426 },
...@@ -475,8 +486,8 @@ fn resolveRefId(self: *Assembler, ref: AsmValue.Ref) !IdRef {...@@ -475,8 +486,8 @@ fn resolveRefId(self: *Assembler, ref: AsmValue.Ref) !IdRef {
475/// error message has been emitted into `self.errors`.486/// error message has been emitted into `self.errors`.
476fn parseInstruction(self: *Assembler) !void {487fn parseInstruction(self: *Assembler) !void {
477 self.inst.opcode = undefined;488 self.inst.opcode = undefined;
478 self.inst.operands.shrinkRetainingCapacity(0);489 self.inst.operands.clearRetainingCapacity();
479 self.inst.string_bytes.shrinkRetainingCapacity(0);490 self.inst.string_bytes.clearRetainingCapacity();
480491
481 const lhs_result_tok = self.currentToken();492 const lhs_result_tok = self.currentToken();
482 const maybe_lhs_result: ?AsmValue.Ref = if (self.eatToken(.result_id_assign)) blk: {493 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 {...@@ -848,6 +859,8 @@ fn tokenText(self: Assembler, tok: Token) []const u8 {
848/// Tokenize `self.src` and put the tokens in `self.tokens`.859/// Tokenize `self.src` and put the tokens in `self.tokens`.
849/// Any errors encountered are appended to `self.errors`.860/// Any errors encountered are appended to `self.errors`.
850fn tokenize(self: *Assembler) !void {861fn tokenize(self: *Assembler) !void {
862 self.tokens.clearRetainingCapacity();
863
851 var offset: u32 = 0;864 var offset: u32 = 0;
852 while (true) {865 while (true) {
853 const tok = try self.nextToken(offset);866 const tok = try self.nextToken(offset);