diff options
| author | Robin Voetter <robin@voetter.nl> | 2024-11-09 21:50:33 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-11-09 21:50:33 +0100 |
| commit | 62f4a6b4d877ce03d1e6f59cf794b5ebc6ea41d0 (patch) | |
| tree | f86e516ad8fc2b14b43c57a48ba3a3db3a10e41a /src/codegen/spirv | |
| parent | 35201e9d9338537a92de2ff89ea23dcd22ce4e52 (diff) | |
| parent | 9cd7b8359c435a7a0c1309cbf529a100d5422b4f (diff) | |
| download | zig-62f4a6b4d877ce03d1e6f59cf794b5ebc6ea41d0.tar.gz zig-62f4a6b4d877ce03d1e6f59cf794b5ebc6ea41d0.zip | |
Merge pull request #21937 from Snektron/spirv-vulkan-ptrs
spirv: miscellaneous vulkan + zig stuff
Diffstat (limited to 'src/codegen/spirv')
| -rw-r--r-- | src/codegen/spirv/Assembler.zig | 105 |
1 files changed, 91 insertions, 14 deletions
diff --git a/src/codegen/spirv/Assembler.zig b/src/codegen/spirv/Assembler.zig index 9e39f2ed09..2cfb590273 100644 --- a/src/codegen/spirv/Assembler.zig +++ b/src/codegen/spirv/Assembler.zig @@ -45,6 +45,9 @@ const Token = struct { pipe, /// =. equals, + /// $identifier. This is used (for now) for constant values, like integers. + /// These can be used in place of a normal `value`. + placeholder, fn name(self: Tag) []const u8 { return switch (self) { @@ -56,6 +59,7 @@ const Token = struct { .string => "<string literal>", .pipe => "'|'", .equals => "'='", + .placeholder => "<placeholder>", }; } }; @@ -128,12 +132,19 @@ const AsmValue = union(enum) { /// This result-value represents a type registered into the module's type system. ty: IdRef, + /// This is a pre-supplied constant integer value. + constant: u32, + /// Retrieve the result-id of this AsmValue. Asserts that this AsmValue /// is of a variant that allows the result to be obtained (not an unresolved /// forward declaration, not in the process of being declared, etc). pub fn resultId(self: AsmValue) IdRef { return switch (self) { - .just_declared, .unresolved_forward_reference => unreachable, + .just_declared, + .unresolved_forward_reference, + // TODO: Lower this value as constant? + .constant, + => unreachable, .value => |result| result, .ty => |result| result, }; @@ -151,7 +162,8 @@ gpa: Allocator, errors: std.ArrayListUnmanaged(ErrorMsg) = .empty, /// The source code that is being assembled. -src: []const u8, +/// This is set when calling `assemble()`. +src: []const u8 = undefined, /// The module that this assembly is associated to. /// Instructions like OpType*, OpDecorate, etc are emitted into this module. @@ -211,7 +223,10 @@ pub fn deinit(self: *Assembler) void { self.instruction_map.deinit(self.gpa); } -pub fn assemble(self: *Assembler) Error!void { +pub fn assemble(self: *Assembler, src: []const u8) Error!void { + self.src = src; + self.errors.clearRetainingCapacity(); + // Populate the opcode map if it isn't already if (self.instruction_map.count() == 0) { const instructions = spec.InstructionSet.core.instructions(); @@ -369,6 +384,7 @@ fn processTypeInstruction(self: *Assembler) !AsmValue { /// - Function-local instructions are emitted in `self.func`. fn processGenericInstruction(self: *Assembler) !?AsmValue { const operands = self.inst.operands.items; + var maybe_spv_decl_index: ?SpvModule.Decl.Index = null; const section = switch (self.inst.opcode.class()) { .ConstantCreation => &self.spv.sections.types_globals_constants, .Annotation => &self.spv.sections.annotations, @@ -378,13 +394,16 @@ fn processGenericInstruction(self: *Assembler) !?AsmValue { .OpExecutionMode, .OpExecutionModeId => &self.spv.sections.execution_modes, .OpVariable => switch (@as(spec.StorageClass, @enumFromInt(operands[2].value))) { .Function => &self.func.prologue, - .UniformConstant => &self.spv.sections.types_globals_constants, - else => { - // This is currently disabled because global variables are required to be - // emitted in the proper order, and this should be honored in inline assembly - // as well. - return self.todo("global variables", .{}); + .Input, .Output => section: { + maybe_spv_decl_index = try self.spv.allocDecl(.global); + try self.func.decl_deps.put(self.spv.gpa, maybe_spv_decl_index.?, {}); + // TODO: In theory this can be non-empty if there is an initializer which depends on another global... + try self.spv.declareDeclDeps(maybe_spv_decl_index.?, &.{}); + break :section &self.spv.sections.types_globals_constants; }, + // These don't need to be marked in the dependency system. + // Probably we should add them anyway, then filter out PushConstant globals. + else => &self.spv.sections.types_globals_constants, }, // Default case - to be worked out further. else => &self.func.body, @@ -409,7 +428,10 @@ fn processGenericInstruction(self: *Assembler) !?AsmValue { section.writeDoubleWord(dword); }, .result_id => { - maybe_result_id = self.spv.allocId(); + maybe_result_id = if (maybe_spv_decl_index) |spv_decl_index| + self.spv.declPtr(spv_decl_index).result_id + else + self.spv.allocId(); try section.ensureUnusedCapacity(self.spv.gpa, 1); section.writeOperand(IdResult, maybe_result_id.?); }, @@ -475,8 +497,8 @@ fn resolveRefId(self: *Assembler, ref: AsmValue.Ref) !IdRef { /// error message has been emitted into `self.errors`. fn parseInstruction(self: *Assembler) !void { self.inst.opcode = undefined; - self.inst.operands.shrinkRetainingCapacity(0); - self.inst.string_bytes.shrinkRetainingCapacity(0); + self.inst.operands.clearRetainingCapacity(); + self.inst.string_bytes.clearRetainingCapacity(); const lhs_result_tok = self.currentToken(); const maybe_lhs_result: ?AsmValue.Ref = if (self.eatToken(.result_id_assign)) blk: { @@ -654,6 +676,22 @@ fn parseRefId(self: *Assembler) !void { fn parseLiteralInteger(self: *Assembler) !void { const tok = self.currentToken(); + if (self.eatToken(.placeholder)) { + const name = self.tokenText(tok)[1..]; + const value = self.value_map.get(name) orelse { + return self.fail(tok.start, "invalid placeholder '${s}'", .{name}); + }; + switch (value) { + .constant => |literal32| { + try self.inst.operands.append(self.gpa, .{ .literal32 = literal32 }); + }, + else => { + return self.fail(tok.start, "value '{s}' cannot be used as placeholder", .{name}); + }, + } + return; + } + try self.expectToken(.value); // According to the SPIR-V machine readable grammar, a LiteralInteger // may consist of one or more words. From the SPIR-V docs it seems like there @@ -669,6 +707,22 @@ fn parseLiteralInteger(self: *Assembler) !void { fn parseLiteralExtInstInteger(self: *Assembler) !void { const tok = self.currentToken(); + if (self.eatToken(.placeholder)) { + const name = self.tokenText(tok)[1..]; + const value = self.value_map.get(name) orelse { + return self.fail(tok.start, "invalid placeholder '${s}'", .{name}); + }; + switch (value) { + .constant => |literal32| { + try self.inst.operands.append(self.gpa, .{ .literal32 = literal32 }); + }, + else => { + return self.fail(tok.start, "value '{s}' cannot be used as placeholder", .{name}); + }, + } + return; + } + try self.expectToken(.value); const text = self.tokenText(tok); const value = std.fmt.parseInt(u32, text, 0) catch { @@ -745,6 +799,22 @@ fn parseContextDependentNumber(self: *Assembler) !void { fn parseContextDependentInt(self: *Assembler, signedness: std.builtin.Signedness, width: u32) !void { const tok = self.currentToken(); + if (self.eatToken(.placeholder)) { + const name = self.tokenText(tok)[1..]; + const value = self.value_map.get(name) orelse { + return self.fail(tok.start, "invalid placeholder '${s}'", .{name}); + }; + switch (value) { + .constant => |literal32| { + try self.inst.operands.append(self.gpa, .{ .literal32 = literal32 }); + }, + else => { + return self.fail(tok.start, "value '{s}' cannot be used as placeholder", .{name}); + }, + } + return; + } + try self.expectToken(.value); if (width == 0 or width > 2 * @bitSizeOf(spec.Word)) { @@ -848,6 +918,8 @@ fn tokenText(self: Assembler, tok: Token) []const u8 { /// Tokenize `self.src` and put the tokens in `self.tokens`. /// Any errors encountered are appended to `self.errors`. fn tokenize(self: *Assembler) !void { + self.tokens.clearRetainingCapacity(); + var offset: u32 = 0; while (true) { const tok = try self.nextToken(offset); @@ -890,6 +962,7 @@ fn nextToken(self: *Assembler, start_offset: u32) !Token { string, string_end, escape, + placeholder, } = .start; var token_start = start_offset; var offset = start_offset; @@ -917,6 +990,10 @@ fn nextToken(self: *Assembler, start_offset: u32) !Token { offset += 1; break; }, + '$' => { + state = .placeholder; + tag = .placeholder; + }, else => { state = .value; tag = .value; @@ -932,11 +1009,11 @@ fn nextToken(self: *Assembler, start_offset: u32) !Token { ' ', '\t', '\r', '\n', '=', '|' => break, else => {}, }, - .result_id => switch (c) { + .result_id, .placeholder => switch (c) { '_', 'a'...'z', 'A'...'Z', '0'...'9' => {}, ' ', '\t', '\r', '\n', '=', '|' => break, else => { - try self.addError(offset, "illegal character in result-id", .{}); + try self.addError(offset, "illegal character in result-id or placeholder", .{}); // Again, probably a forgotten delimiter here. break; }, |
