authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-01-29 15:59:42+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-01-29 15:59:42+02:00
log9f16d9ed07275209946b9e733c30be1bb0a1ae33
treeb4a226f51cb417231dd8e8835173210abc4e7b94
parente288148f60770a2cfa4c64f832b599172c383d36
parent98ee39d1b0ed516428c611d8dc1e52d21c786f97
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10665 from Snektron/spirv-improvements

spir-v improvements

7 files changed, 3145 insertions(+), 587 deletions(-)

src/codegen/spirv.zig+346-395
...@@ -4,9 +4,6 @@ const Target = std.Target;...@@ -4,9 +4,6 @@ const Target = std.Target;
4const log = std.log.scoped(.codegen);4const log = std.log.scoped(.codegen);
5const assert = std.debug.assert;5const assert = std.debug.assert;
66
7const spec = @import("spirv/spec.zig");
8const Opcode = spec.Opcode;
9
10const Module = @import("../Module.zig");7const Module = @import("../Module.zig");
11const Decl = Module.Decl;8const Decl = Module.Decl;
12const Type = @import("../type.zig").Type;9const Type = @import("../type.zig").Type;
...@@ -15,187 +12,78 @@ const LazySrcLoc = Module.LazySrcLoc;...@@ -15,187 +12,78 @@ const LazySrcLoc = Module.LazySrcLoc;
15const Air = @import("../Air.zig");12const Air = @import("../Air.zig");
16const Liveness = @import("../Liveness.zig");13const Liveness = @import("../Liveness.zig");
1714
18pub const Word = u32;15const spec = @import("spirv/spec.zig");
19pub const ResultId = u32;16const Opcode = spec.Opcode;
17const Word = spec.Word;
18const IdRef = spec.IdRef;
19const IdResult = spec.IdResult;
20const IdResultType = spec.IdResultType;
21
22const SpvModule = @import("spirv/Module.zig");
23const SpvSection = @import("spirv/Section.zig");
24const SpvType = @import("spirv/type.zig").Type;
2025
21pub const TypeMap = std.HashMap(Type, u32, Type.HashContext64, std.hash_map.default_max_load_percentage);26const InstMap = std.AutoHashMapUnmanaged(Air.Inst.Index, IdRef);
22pub const InstMap = std.AutoHashMap(Air.Inst.Index, ResultId);
2327
24const IncomingBlock = struct {28const IncomingBlock = struct {
25 src_label_id: ResultId,29 src_label_id: IdRef,
26 break_value_id: ResultId,30 break_value_id: IdRef,
27};31};
2832
29pub const BlockMap = std.AutoHashMap(Air.Inst.Index, struct {33pub const BlockMap = std.AutoHashMapUnmanaged(Air.Inst.Index, struct {
30 label_id: ResultId,34 label_id: IdRef,
31 incoming_blocks: *std.ArrayListUnmanaged(IncomingBlock),35 incoming_blocks: *std.ArrayListUnmanaged(IncomingBlock),
32});36});
3337
34pub fn writeOpcode(code: *std.ArrayList(Word), opcode: Opcode, arg_count: u16) !void {38/// This structure is used to compile a declaration, and contains all relevant meta-information to deal with that.
35 const word_count: Word = arg_count + 1;39pub const DeclGen = struct {
36 try code.append((word_count << 16) | @enumToInt(opcode));40 /// The Zig module that we are generating decls for.
37}
38
39pub fn writeInstruction(code: *std.ArrayList(Word), opcode: Opcode, args: []const Word) !void {
40 try writeOpcode(code, opcode, @intCast(u16, args.len));
41 try code.appendSlice(args);
42}
43
44pub fn writeInstructionWithString(code: *std.ArrayList(Word), opcode: Opcode, args: []const Word, str: []const u8) !void {
45 // Str needs to be written zero-terminated, so we need to add one to the length.
46 const zero_terminated_len = str.len + 1;
47 const str_words = (zero_terminated_len + @sizeOf(Word) - 1) / @sizeOf(Word);
48
49 try writeOpcode(code, opcode, @intCast(u16, args.len + str_words));
50 try code.ensureUnusedCapacity(args.len + str_words);
51 code.appendSliceAssumeCapacity(args);
52
53 // TODO: Not actually sure whether this is correct for big-endian.
54 // See https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#Literal
55 var i: usize = 0;
56 while (i < zero_terminated_len) : (i += @sizeOf(Word)) {
57 var word: Word = 0;
58
59 var j: usize = 0;
60 while (j < @sizeOf(Word) and i + j < str.len) : (j += 1) {
61 word |= @as(Word, str[i + j]) << @intCast(std.math.Log2Int(Word), j * std.meta.bitCount(u8));
62 }
63
64 code.appendAssumeCapacity(word);
65 }
66}
67
68/// This structure represents a SPIR-V (binary) module being compiled, and keeps track of all relevant information.
69/// That includes the actual instructions, the current result-id bound, and data structures for querying result-id's
70/// of data which needs to be persistent over different calls to Decl code generation.
71pub const SPIRVModule = struct {
72 /// A general-purpose allocator which may be used to allocate temporary resources required for compilation.
73 gpa: Allocator,
74
75 /// The parent module.
76 module: *Module,41 module: *Module,
7742
78 /// SPIR-V instructions return result-ids. This variable holds the module-wide counter for these.43 /// The SPIR-V module code should be put in.
79 next_result_id: ResultId,44 spv: *SpvModule,
80
81 /// Code of the actual SPIR-V binary, divided into the relevant logical sections.
82 /// Note: To save some bytes, these could also be unmanaged, but since there is only one instance of SPIRVModule
83 /// and this removes some clutter in the rest of the backend, it's fine like this.
84 binary: struct {
85 /// OpCapability and OpExtension instructions (in that order).
86 capabilities_and_extensions: std.ArrayList(Word),
87
88 /// OpString, OpSourceExtension, OpSource, OpSourceContinued.
89 debug_strings: std.ArrayList(Word),
90
91 /// Type declaration instructions, constant instructions, global variable declarations, OpUndef instructions.
92 types_globals_constants: std.ArrayList(Word),
93
94 /// Regular functions.
95 fn_decls: std.ArrayList(Word),
96 },
97
98 /// Global type cache to reduce the amount of generated types.
99 types: TypeMap,
100
101 /// Cache for results of OpString instructions for module file names fed to OpSource.
102 /// Since OpString is pretty much only used for those, we don't need to keep track of all strings,
103 /// just the ones for OpLine. Note that OpLine needs the result of OpString, and not that of OpSource.
104 file_names: std.StringHashMap(ResultId),
105
106 pub fn init(gpa: Allocator, module: *Module) SPIRVModule {
107 return .{
108 .gpa = gpa,
109 .module = module,
110 .next_result_id = 1, // 0 is an invalid SPIR-V result ID.
111 .binary = .{
112 .capabilities_and_extensions = std.ArrayList(Word).init(gpa),
113 .debug_strings = std.ArrayList(Word).init(gpa),
114 .types_globals_constants = std.ArrayList(Word).init(gpa),
115 .fn_decls = std.ArrayList(Word).init(gpa),
116 },
117 .types = TypeMap.init(gpa),
118 .file_names = std.StringHashMap(ResultId).init(gpa),
119 };
120 }
121
122 pub fn deinit(self: *SPIRVModule) void {
123 self.file_names.deinit();
124 self.types.deinit();
125
126 self.binary.fn_decls.deinit();
127 self.binary.types_globals_constants.deinit();
128 self.binary.debug_strings.deinit();
129 self.binary.capabilities_and_extensions.deinit();
130 }
131
132 pub fn allocResultId(self: *SPIRVModule) Word {
133 defer self.next_result_id += 1;
134 return self.next_result_id;
135 }
136
137 pub fn resultIdBound(self: *SPIRVModule) Word {
138 return self.next_result_id;
139 }
140
141 fn resolveSourceFileName(self: *SPIRVModule, decl: *Decl) !ResultId {
142 const path = decl.getFileScope().sub_file_path;
143 const result = try self.file_names.getOrPut(path);
144 if (!result.found_existing) {
145 result.value_ptr.* = self.allocResultId();
146 try writeInstructionWithString(&self.binary.debug_strings, .OpString, &[_]Word{result.value_ptr.*}, path);
147 try writeInstruction(&self.binary.debug_strings, .OpSource, &[_]Word{
148 @enumToInt(spec.SourceLanguage.Unknown), // TODO: Register Zig source language.
149 0, // TODO: Zig version as u32?
150 result.value_ptr.*,
151 });
152 }
15345
154 return result.value_ptr.*;46 /// The decl we are currently generating code for.
155 }47 decl: *Decl,
156};
157
158/// This structure is used to compile a declaration, and contains all relevant meta-information to deal with that.
159pub const DeclGen = struct {
160 /// The SPIR-V module code should be put in.
161 spv: *SPIRVModule,
16248
49 /// The intermediate code of the declaration we are currently generating. Note: If
50 /// the declaration is not a function, this value will be undefined!
163 air: Air,51 air: Air,
52
53 /// The liveness analysis of the intermediate code for the declaration we are currently generating.
54 /// Note: If the declaration is not a function, this value will be undefined!
164 liveness: Liveness,55 liveness: Liveness,
16556
166 /// An array of function argument result-ids. Each index corresponds with the57 /// An array of function argument result-ids. Each index corresponds with the
167 /// function argument of the same index.58 /// function argument of the same index.
168 args: std.ArrayList(ResultId),59 args: std.ArrayListUnmanaged(IdRef) = .{},
16960
170 /// A counter to keep track of how many `arg` instructions we've seen yet.61 /// A counter to keep track of how many `arg` instructions we've seen yet.
171 next_arg_index: u32,62 next_arg_index: u32,
17263
173 /// A map keeping track of which instruction generated which result-id.64 /// A map keeping track of which instruction generated which result-id.
174 inst_results: InstMap,65 inst_results: InstMap = .{},
17566
176 /// We need to keep track of result ids for block labels, as well as the 'incoming'67 /// We need to keep track of result ids for block labels, as well as the 'incoming'
177 /// blocks for a block.68 /// blocks for a block.
178 blocks: BlockMap,69 blocks: BlockMap = .{},
17970
180 /// The label of the SPIR-V block we are currently generating.71 /// The label of the SPIR-V block we are currently generating.
181 current_block_label_id: ResultId,72 current_block_label_id: IdRef,
18273
183 /// The actual instructions for this function. We need to declare all locals in74 /// The actual instructions for this function. We need to declare all locals in
184 /// the first block, and because we don't know which locals there are going to be,75 /// the first block, and because we don't know which locals there are going to be,
185 /// we're just going to generate everything after the locals-section in this array.76 /// we're just going to generate everything after the locals-section in this array.
186 /// Note: It will not contain OpFunction, OpFunctionParameter, OpVariable and the77 /// Note: It will not contain OpFunction, OpFunctionParameter, OpVariable and the
187 /// initial OpLabel. These will be generated into spv.binary.fn_decls directly.78 /// initial OpLabel. These will be generated into spv.sections.functions directly.
188 code: std.ArrayList(Word),79 code: SpvSection = .{},
18980
190 /// The decl we are currently generating code for.81 /// If `gen` returned `Error.CodegenFail`, this contains an explanatory message.
191 decl: *Decl,
192
193 /// If `gen` returned `Error.AnalysisFail`, this contains an explanatory message.
194 /// Memory is owned by `module.gpa`.82 /// Memory is owned by `module.gpa`.
195 error_msg: ?*Module.ErrorMsg,83 error_msg: ?*Module.ErrorMsg,
19684
197 /// Possible errors the `gen` function may return.85 /// Possible errors the `gen` function may return.
198 const Error = error{ AnalysisFail, OutOfMemory };86 const Error = error{ CodegenFail, OutOfMemory };
19987
200 /// This structure is used to return information about a type typically used for88 /// This structure is used to return information about a type typically used for
201 /// arithmetic operations. These types may either be integers, floats, or a vector89 /// arithmetic operations. These types may either be integers, floats, or a vector
...@@ -244,18 +132,15 @@ pub const DeclGen = struct {...@@ -244,18 +132,15 @@ pub const DeclGen = struct {
244132
245 /// Initialize the common resources of a DeclGen. Some fields are left uninitialized,133 /// Initialize the common resources of a DeclGen. Some fields are left uninitialized,
246 /// only set when `gen` is called.134 /// only set when `gen` is called.
247 pub fn init(spv: *SPIRVModule) DeclGen {135 pub fn init(module: *Module, spv: *SpvModule) DeclGen {
248 return .{136 return .{
137 .module = module,
249 .spv = spv,138 .spv = spv,
139 .decl = undefined,
250 .air = undefined,140 .air = undefined,
251 .liveness = undefined,141 .liveness = undefined,
252 .args = std.ArrayList(ResultId).init(spv.gpa),
253 .next_arg_index = undefined,142 .next_arg_index = undefined,
254 .inst_results = InstMap.init(spv.gpa),
255 .blocks = BlockMap.init(spv.gpa),
256 .current_block_label_id = undefined,143 .current_block_label_id = undefined,
257 .code = std.ArrayList(Word).init(spv.gpa),
258 .decl = undefined,
259 .error_msg = undefined,144 .error_msg = undefined,
260 };145 };
261 }146 }
...@@ -265,6 +150,7 @@ pub const DeclGen = struct {...@@ -265,6 +150,7 @@ pub const DeclGen = struct {
265 /// returns such a reportable error, it is valid to be called again for a different decl.150 /// returns such a reportable error, it is valid to be called again for a different decl.
266 pub fn gen(self: *DeclGen, decl: *Decl, air: Air, liveness: Liveness) !?*Module.ErrorMsg {151 pub fn gen(self: *DeclGen, decl: *Decl, air: Air, liveness: Liveness) !?*Module.ErrorMsg {
267 // Reset internal resources, we don't want to re-allocate these.152 // Reset internal resources, we don't want to re-allocate these.
153 self.decl = decl;
268 self.air = air;154 self.air = air;
269 self.liveness = liveness;155 self.liveness = liveness;
270 self.args.items.len = 0;156 self.args.items.len = 0;
...@@ -272,35 +158,50 @@ pub const DeclGen = struct {...@@ -272,35 +158,50 @@ pub const DeclGen = struct {
272 self.inst_results.clearRetainingCapacity();158 self.inst_results.clearRetainingCapacity();
273 self.blocks.clearRetainingCapacity();159 self.blocks.clearRetainingCapacity();
274 self.current_block_label_id = undefined;160 self.current_block_label_id = undefined;
275 self.code.items.len = 0;161 self.code.reset();
276 self.decl = decl;
277 self.error_msg = null;162 self.error_msg = null;
278163
279 try self.genDecl();164 self.genDecl() catch |err| switch (err) {
280 return self.error_msg;165 error.CodegenFail => return self.error_msg,
166 else => |others| return others,
167 };
168
169 return null;
281 }170 }
282171
283 /// Free resources owned by the DeclGen.172 /// Free resources owned by the DeclGen.
284 pub fn deinit(self: *DeclGen) void {173 pub fn deinit(self: *DeclGen) void {
285 self.args.deinit();174 self.args.deinit(self.spv.gpa);
286 self.inst_results.deinit();175 self.inst_results.deinit(self.spv.gpa);
287 self.blocks.deinit();176 self.blocks.deinit(self.spv.gpa);
288 self.code.deinit();177 self.code.deinit(self.spv.gpa);
289 }178 }
290179
180 /// Return the target which we are currently compiling for.
291 fn getTarget(self: *DeclGen) std.Target {181 fn getTarget(self: *DeclGen) std.Target {
292 return self.spv.module.getTarget();182 return self.module.getTarget();
293 }183 }
294184
295 fn fail(self: *DeclGen, comptime format: []const u8, args: anytype) Error {185 fn fail(self: *DeclGen, comptime format: []const u8, args: anytype) Error {
296 @setCold(true);186 @setCold(true);
297 const src: LazySrcLoc = .{ .node_offset = 0 };187 const src: LazySrcLoc = .{ .node_offset = 0 };
298 const src_loc = src.toSrcLoc(self.decl);188 const src_loc = src.toSrcLoc(self.decl);
299 self.error_msg = try Module.ErrorMsg.create(self.spv.module.gpa, src_loc, format, args);189 assert(self.error_msg == null);
300 return error.AnalysisFail;190 self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, format, args);
191 return error.CodegenFail;
301 }192 }
302193
303 fn resolve(self: *DeclGen, inst: Air.Inst.Ref) !ResultId {194 fn todo(self: *DeclGen, comptime format: []const u8, args: anytype) Error {
195 @setCold(true);
196 const src: LazySrcLoc = .{ .node_offset = 0 };
197 const src_loc = src.toSrcLoc(self.decl);
198 assert(self.error_msg == null);
199 self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, "TODO (SPIR-V): " ++ format, args);
200 return error.CodegenFail;
201 }
202
203 /// Fetch the result-id for a previously generated instruction or constant.
204 fn resolve(self: *DeclGen, inst: Air.Inst.Ref) !IdRef {
304 if (self.air.value(inst)) |val| {205 if (self.air.value(inst)) |val| {
305 return self.genConstant(self.air.typeOf(inst), val);206 return self.genConstant(self.air.typeOf(inst), val);
306 }207 }
...@@ -308,9 +209,13 @@ pub const DeclGen = struct {...@@ -308,9 +209,13 @@ pub const DeclGen = struct {
308 return self.inst_results.get(index).?; // Assertion means instruction does not dominate usage.209 return self.inst_results.get(index).?; // Assertion means instruction does not dominate usage.
309 }210 }
310211
311 fn beginSPIRVBlock(self: *DeclGen, label_id: ResultId) !void {212 /// Start a new SPIR-V block, Emits the label of the new block, and stores which
312 try writeInstruction(&self.code, .OpLabel, &[_]Word{label_id});213 /// block we are currently generating.
313 self.current_block_label_id = label_id;214 /// Note that there is no such thing as nested blocks like in ZIR or AIR, so we don't need to
215 /// keep track of the previous block.
216 fn beginSpvBlock(self: *DeclGen, label_id: IdResult) !void {
217 try self.code.emit(self.spv.gpa, .OpLabel, .{ .id_result = label_id });
218 self.current_block_label_id = label_id.toRef();
314 }219 }
315220
316 /// SPIR-V requires enabling specific integer sizes through capabilities, and so if they are not enabled, we need221 /// SPIR-V requires enabling specific integer sizes through capabilities, and so if they are not enabled, we need
...@@ -392,32 +297,37 @@ pub const DeclGen = struct {...@@ -392,32 +297,37 @@ pub const DeclGen = struct {
392 const int_info = ty.intInfo(target);297 const int_info = ty.intInfo(target);
393 // TODO: Maybe it's useful to also return this value.298 // TODO: Maybe it's useful to also return this value.
394 const maybe_backing_bits = self.backingIntBits(int_info.bits);299 const maybe_backing_bits = self.backingIntBits(int_info.bits);
395 break :blk ArithmeticTypeInfo{ .bits = int_info.bits, .is_vector = false, .signedness = int_info.signedness, .class = if (maybe_backing_bits) |backing_bits|300 break :blk ArithmeticTypeInfo{
396 if (backing_bits == int_info.bits)301 .bits = int_info.bits,
397 ArithmeticTypeInfo.Class.integer302 .is_vector = false,
303 .signedness = int_info.signedness,
304 .class = if (maybe_backing_bits) |backing_bits|
305 if (backing_bits == int_info.bits)
306 ArithmeticTypeInfo.Class.integer
307 else
308 ArithmeticTypeInfo.Class.strange_integer
398 else309 else
399 ArithmeticTypeInfo.Class.strange_integer310 .composite_integer,
400 else311 };
401 .composite_integer };
402 },312 },
403 // As of yet, there is no vector support in the self-hosted compiler.313 // As of yet, there is no vector support in the self-hosted compiler.
404 .Vector => self.fail("TODO: SPIR-V backend: implement arithmeticTypeInfo for Vector", .{}),314 .Vector => self.todo("implement arithmeticTypeInfo for Vector", .{}),
405 // TODO: For which types is this the case?315 // TODO: For which types is this the case?
406 else => self.fail("TODO: SPIR-V backend: implement arithmeticTypeInfo for {}", .{ty}),316 else => self.todo("implement arithmeticTypeInfo for {}", .{ty}),
407 };317 };
408 }318 }
409319
410 /// Generate a constant representing `val`.320 /// Generate a constant representing `val`.
411 /// TODO: Deduplication?321 /// TODO: Deduplication?
412 fn genConstant(self: *DeclGen, ty: Type, val: Value) Error!ResultId {322 fn genConstant(self: *DeclGen, ty: Type, val: Value) Error!IdRef {
413 const target = self.getTarget();323 const target = self.getTarget();
414 const code = &self.spv.binary.types_globals_constants;324 const section = &self.spv.sections.types_globals_constants;
415 const result_id = self.spv.allocResultId();325 const result_id = self.spv.allocId();
416 const result_type_id = try self.genType(ty);326 const result_type_id = try self.resolveTypeId(ty);
417327
418 if (val.isUndef()) {328 if (val.isUndef()) {
419 try writeInstruction(code, .OpUndef, &[_]Word{ result_type_id, result_id });329 try section.emit(self.spv.gpa, .OpUndef, .{ .id_result_type = result_type_id, .id_result = result_id });
420 return result_id;330 return result_id.toRef();
421 }331 }
422332
423 switch (ty.zigTypeTag()) {333 switch (ty.zigTypeTag()) {
...@@ -425,101 +335,96 @@ pub const DeclGen = struct {...@@ -425,101 +335,96 @@ pub const DeclGen = struct {
425 const int_info = ty.intInfo(target);335 const int_info = ty.intInfo(target);
426 const backing_bits = self.backingIntBits(int_info.bits) orelse {336 const backing_bits = self.backingIntBits(int_info.bits) orelse {
427 // Integers too big for any native type are represented as "composite integers": An array of largestSupportedIntBits.337 // Integers too big for any native type are represented as "composite integers": An array of largestSupportedIntBits.
428 return self.fail("TODO: SPIR-V backend: implement composite int constants for {}", .{ty});338 return self.todo("implement composite int constants for {}", .{ty});
429 };339 };
430340
431 // We can just use toSignedInt/toUnsignedInt here as it returns u64 - a type large enough to hold any341 // We can just use toSignedInt/toUnsignedInt here as it returns u64 - a type large enough to hold any
432 // SPIR-V native type (up to i/u64 with Int64). If SPIR-V ever supports native ints of a larger size, this342 // SPIR-V native type (up to i/u64 with Int64). If SPIR-V ever supports native ints of a larger size, this
433 // might need to be updated.343 // might need to be updated.
434 assert(self.largestSupportedIntBits() <= std.meta.bitCount(u64));344 assert(self.largestSupportedIntBits() <= std.meta.bitCount(u64));
345
346 // Note, value is required to be sign-extended, so we don't need to mask off the upper bits.
347 // See https://www.khronos.org/registry/SPIR-V/specs/unified1/SPIRV.html#Literal
435 var int_bits = if (ty.isSignedInt()) @bitCast(u64, val.toSignedInt()) else val.toUnsignedInt();348 var int_bits = if (ty.isSignedInt()) @bitCast(u64, val.toSignedInt()) else val.toUnsignedInt();
436349
437 // Mask the low bits which make up the actual integer. This is to make sure that negative values350 const value: spec.LiteralContextDependentNumber = switch (backing_bits) {
438 // only use the actual bits of the type.351 1...32 => .{ .uint32 = @truncate(u32, int_bits) },
439 // TODO: Should this be the backing type bits or the actual type bits?352 33...64 => .{ .uint64 = int_bits },
440 int_bits &= (@as(u64, 1) << @intCast(u6, backing_bits)) - 1;353 else => unreachable,
441354 };
442 switch (backing_bits) {355
443 0 => unreachable,356 try section.emit(self.spv.gpa, .OpConstant, .{
444 1...32 => try writeInstruction(code, .OpConstant, &[_]Word{357 .id_result_type = result_type_id,
445 result_type_id,358 .id_result = result_id,
446 result_id,359 .value = value,
447 @truncate(u32, int_bits),360 });
448 }),
449 33...64 => try writeInstruction(code, .OpConstant, &[_]Word{
450 result_type_id,
451 result_id,
452 @truncate(u32, int_bits),
453 @truncate(u32, int_bits >> @bitSizeOf(u32)),
454 }),
455 else => unreachable, // backing_bits is bounded by largestSupportedIntBits.
456 }
457 },361 },
458 .Bool => {362 .Bool => {
459 const opcode: Opcode = if (val.toBool()) .OpConstantTrue else .OpConstantFalse;363 const operands = .{ .id_result_type = result_type_id, .id_result = result_id };
460 try writeInstruction(code, opcode, &[_]Word{ result_type_id, result_id });364 if (val.toBool()) {
365 try section.emit(self.spv.gpa, .OpConstantTrue, operands);
366 } else {
367 try section.emit(self.spv.gpa, .OpConstantFalse, operands);
368 }
461 },369 },
462 .Float => {370 .Float => {
463 // At this point we are guaranteed that the target floating point type is supported, otherwise the function371 // At this point we are guaranteed that the target floating point type is supported, otherwise the function
464 // would have exited at genType(ty).372 // would have exited at resolveTypeId(ty).
465373
466 // f16 and f32 require one word of storage. f64 requires 2, low-order first.374 const value: spec.LiteralContextDependentNumber = switch (ty.floatBits(target)) {
467375 // Prevent upcasting to f32 by bitcasting and writing as a uint32.
468 switch (ty.floatBits(target)) {376 16 => .{ .uint32 = @bitCast(u16, val.toFloat(f16)) },
469 16 => try writeInstruction(code, .OpConstant, &[_]Word{ result_type_id, result_id, @bitCast(u16, val.toFloat(f16)) }),377 32 => .{ .float32 = val.toFloat(f32) },
470 32 => try writeInstruction(code, .OpConstant, &[_]Word{ result_type_id, result_id, @bitCast(u32, val.toFloat(f32)) }),378 64 => .{ .float64 = val.toFloat(f64) },
471 64 => {379 128 => unreachable, // Filtered out in the call to resolveTypeId.
472 const float_bits = @bitCast(u64, val.toFloat(f64));380 // TODO: Insert case for long double when the layout for that is determined?
473 try writeInstruction(code, .OpConstant, &[_]Word{
474 result_type_id,
475 result_id,
476 @truncate(u32, float_bits),
477 @truncate(u32, float_bits >> @bitSizeOf(u32)),
478 });
479 },
480 128 => unreachable, // Filtered out in the call to genType.
481 // TODO: Insert case for long double when the layout for that is determined.
482 else => unreachable,381 else => unreachable,
483 }382 };
383
384 try section.emit(self.spv.gpa, .OpConstant, .{
385 .id_result_type = result_type_id,
386 .id_result = result_id,
387 .value = value,
388 });
484 },389 },
485 .Void => unreachable,390 .Void => unreachable,
486 else => return self.fail("TODO: SPIR-V backend: constant generation of type {}", .{ty}),391 else => return self.todo("constant generation of type {}", .{ty}),
487 }392 }
488393
489 return result_id;394 return result_id.toRef();
490 }395 }
491396
492 fn genType(self: *DeclGen, ty: Type) Error!ResultId {397 /// Turn a Zig type into a SPIR-V Type, and return its type result-id.
493 // We can't use getOrPut here so we can recursively generate types.398 fn resolveTypeId(self: *DeclGen, ty: Type) !IdResultType {
494 if (self.spv.types.get(ty)) |already_generated| {399 return self.spv.typeResultId(try self.resolveType(ty));
495 return already_generated;400 }
496 }
497401
402 /// Turn a Zig type into a SPIR-V Type, and return a reference to it.
403 fn resolveType(self: *DeclGen, ty: Type) Error!SpvType.Ref {
498 const target = self.getTarget();404 const target = self.getTarget();
499 const code = &self.spv.binary.types_globals_constants;405 return switch (ty.zigTypeTag()) {
500 const result_id = self.spv.allocResultId();406 .Void => try self.spv.resolveType(SpvType.initTag(.void)),
501407 .Bool => blk: {
502 switch (ty.zigTypeTag()) {408 // TODO: SPIR-V booleans are opaque. For local variables this is fine, but for structs
503 .Void => try writeInstruction(code, .OpTypeVoid, &[_]Word{result_id}),409 // members we want to use integer types instead.
504 .Bool => try writeInstruction(code, .OpTypeBool, &[_]Word{result_id}),410 break :blk try self.spv.resolveType(SpvType.initTag(.bool));
505 .Int => {411 },
412 .Int => blk: {
506 const int_info = ty.intInfo(target);413 const int_info = ty.intInfo(target);
507 const backing_bits = self.backingIntBits(int_info.bits) orelse {414 const backing_bits = self.backingIntBits(int_info.bits) orelse {
508 // Integers too big for any native type are represented as "composite integers": An array of largestSupportedIntBits.415 // TODO: Integers too big for any native type are represented as "composite integers":
509 return self.fail("TODO: SPIR-V backend: implement composite int {}", .{ty});416 // An array of largestSupportedIntBits.
417 return self.todo("Implement composite int type {}", .{ty});
510 };418 };
511419
512 // TODO: If backing_bits != int_info.bits, a duplicate type might be generated here.420 const payload = try self.spv.arena.create(SpvType.Payload.Int);
513 try writeInstruction(code, .OpTypeInt, &[_]Word{421 payload.* = .{
514 result_id,422 .width = backing_bits,
515 backing_bits,423 .signedness = int_info.signedness,
516 switch (int_info.signedness) {424 };
517 .unsigned => 0,425 break :blk try self.spv.resolveType(SpvType.initPayload(&payload.base));
518 .signed => 1,
519 },
520 });
521 },426 },
522 .Float => {427 .Float => blk: {
523 // We can (and want) not really emulate floating points with other floating point types like with the integer types,428 // We can (and want) not really emulate floating points with other floating point types like with the integer types,
524 // so if the float is not supported, just return an error.429 // so if the float is not supported, just return an error.
525 const bits = ty.floatBits(target);430 const bits = ty.floatBits(target);
...@@ -535,37 +440,34 @@ pub const DeclGen = struct {...@@ -535,37 +440,34 @@ pub const DeclGen = struct {
535 return self.fail("Floating point width of {} bits is not supported for the current SPIR-V feature set", .{bits});440 return self.fail("Floating point width of {} bits is not supported for the current SPIR-V feature set", .{bits});
536 }441 }
537442
538 try writeInstruction(code, .OpTypeFloat, &[_]Word{ result_id, bits });443 const payload = try self.spv.arena.create(SpvType.Payload.Float);
444 payload.* = .{
445 .width = bits,
446 };
447 break :blk try self.spv.resolveType(SpvType.initPayload(&payload.base));
539 },448 },
540 .Fn => {449 .Fn => blk: {
541 // We only support zig-calling-convention functions, no varargs.450 // We only support zig-calling-convention functions, no varargs.
542 if (ty.fnCallingConvention() != .Unspecified)451 if (ty.fnCallingConvention() != .Unspecified)
543 return self.fail("Unsupported calling convention for SPIR-V", .{});452 return self.fail("Unsupported calling convention for SPIR-V", .{});
544 if (ty.fnIsVarArgs())453 if (ty.fnIsVarArgs())
545 return self.fail("VarArgs unsupported for SPIR-V", .{});454 return self.fail("VarArgs functions are unsupported for SPIR-V", .{});
546
547 // In order to avoid a temporary here, first generate all the required types and then simply look them up
548 // when generating the function type.
549 const params = ty.fnParamLen();
550 var i: usize = 0;
551 while (i < params) : (i += 1) {
552 _ = try self.genType(ty.fnParamType(i));
553 }
554455
555 const return_type_id = try self.genType(ty.fnReturnType());456 const param_types = try self.spv.arena.alloc(SpvType.Ref, ty.fnParamLen());
457 for (param_types) |*param, i| {
458 param.* = try self.resolveType(ty.fnParamType(i));
459 }
556460
557 // result id + result type id + parameter type ids.461 const return_type = try self.resolveType(ty.fnReturnType());
558 try writeOpcode(code, .OpTypeFunction, 2 + @intCast(u16, ty.fnParamLen()));
559 try code.appendSlice(&.{ result_id, return_type_id });
560462
561 i = 0;463 const payload = try self.spv.arena.create(SpvType.Payload.Function);
562 while (i < params) : (i += 1) {464 payload.* = .{ .return_type = return_type, .parameters = param_types };
563 const param_type_id = self.spv.types.get(ty.fnParamType(i)).?;465 break :blk try self.spv.resolveType(SpvType.initPayload(&payload.base));
564 try code.append(param_type_id);466 },
565 }467 .Pointer => {
468 // This type can now be properly implemented, but we still need to implement the storage classes as proper address spaces.
469 return self.todo("Implement type Pointer properly", .{});
566 },470 },
567 // When recursively generating a type, we cannot infer the pointer's storage class. See genPointerType.
568 .Pointer => return self.fail("Cannot create pointer with unknown storage class", .{}),
569 .Vector => {471 .Vector => {
570 // Although not 100% the same, Zig vectors map quite neatly to SPIR-V vectors (including many integer and float operations472 // Although not 100% the same, Zig vectors map quite neatly to SPIR-V vectors (including many integer and float operations
571 // which work on them), so simply use those.473 // which work on them), so simply use those.
...@@ -575,41 +477,42 @@ pub const DeclGen = struct {...@@ -575,41 +477,42 @@ pub const DeclGen = struct {
575 // is adequate at all for this.477 // is adequate at all for this.
576478
577 // TODO: Vectors are not yet supported by the self-hosted compiler itself it seems.479 // TODO: Vectors are not yet supported by the self-hosted compiler itself it seems.
578 return self.fail("TODO: SPIR-V backend: implement type Vector", .{});480 return self.todo("Implement type Vector", .{});
579 },481 },
482
580 .Null,483 .Null,
581 .Undefined,484 .Undefined,
582 .EnumLiteral,485 .EnumLiteral,
583 .ComptimeFloat,486 .ComptimeFloat,
584 .ComptimeInt,487 .ComptimeInt,
585 .Type,488 .Type,
586 => unreachable, // Must be const or comptime.489 => unreachable, // Must be comptime.
587490
588 .BoundFn => unreachable, // this type will be deleted from the language.491 .BoundFn => unreachable, // this type will be deleted from the language.
589492
590 else => |tag| return self.fail("TODO: SPIR-V backend: implement type {}s", .{tag}),493 else => |tag| return self.todo("Implement zig type '{}'", .{tag}),
591 }494 };
592
593 try self.spv.types.putNoClobber(ty, result_id);
594 return result_id;
595 }495 }
596496
597 /// SPIR-V requires pointers to have a storage class (address space), and so we have a special function for that.497 /// SPIR-V requires pointers to have a storage class (address space), and so we have a special function for that.
598 /// TODO: The result of this needs to be cached.498 /// TODO: The result of this needs to be cached.
599 fn genPointerType(self: *DeclGen, ty: Type, storage_class: spec.StorageClass) !ResultId {499 fn genPointerType(self: *DeclGen, ty: Type, storage_class: spec.StorageClass) !IdResultType {
600 assert(ty.zigTypeTag() == .Pointer);500 assert(ty.zigTypeTag() == .Pointer);
601501
602 const code = &self.spv.binary.types_globals_constants;502 const result_id = self.spv.allocId();
603 const result_id = self.spv.allocResultId();
604503
605 // TODO: There are many constraints which are ignored for now: We may only create pointers to certain types, and to other types504 // TODO: There are many constraints which are ignored for now: We may only create pointers to certain types, and to other types
606 // if more capabilities are enabled. For example, we may only create pointers to f16 if Float16Buffer is enabled.505 // if more capabilities are enabled. For example, we may only create pointers to f16 if Float16Buffer is enabled.
607 // These also relates to the pointer's address space.506 // These also relates to the pointer's address space.
608 const child_id = try self.genType(ty.elemType());507 const child_id = try self.resolveTypeId(ty.elemType());
609508
610 try writeInstruction(code, .OpTypePointer, &[_]Word{ result_id, @enumToInt(storage_class), child_id });509 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{
510 .id_result = result_id,
511 .storage_class = storage_class,
512 .type = child_id.toRef(),
513 });
611514
612 return result_id;515 return result_id.toResultType();
613 }516 }
614517
615 fn genDecl(self: *DeclGen) !void {518 fn genDecl(self: *DeclGen) !void {
...@@ -618,41 +521,47 @@ pub const DeclGen = struct {...@@ -618,41 +521,47 @@ pub const DeclGen = struct {
618521
619 if (decl.val.castTag(.function)) |_| {522 if (decl.val.castTag(.function)) |_| {
620 assert(decl.ty.zigTypeTag() == .Fn);523 assert(decl.ty.zigTypeTag() == .Fn);
621 const prototype_id = try self.genType(decl.ty);524 const prototype_id = try self.resolveTypeId(decl.ty);
622 try writeInstruction(&self.spv.binary.fn_decls, .OpFunction, &[_]Word{525 try self.spv.sections.functions.emit(self.spv.gpa, .OpFunction, .{
623 self.spv.types.get(decl.ty.fnReturnType()).?, // This type should be generated along with the prototype.526 .id_result_type = try self.resolveTypeId(decl.ty.fnReturnType()),
624 result_id,527 .id_result = result_id,
625 @bitCast(Word, spec.FunctionControl{}), // TODO: We can set inline here if the type requires it.528 .function_control = .{}, // TODO: We can set inline here if the type requires it.
626 prototype_id,529 .function_type = prototype_id.toRef(),
627 });530 });
628531
629 const params = decl.ty.fnParamLen();532 const params = decl.ty.fnParamLen();
630 var i: usize = 0;533 var i: usize = 0;
631534
632 try self.args.ensureUnusedCapacity(params);535 try self.args.ensureUnusedCapacity(self.spv.gpa, params);
633 while (i < params) : (i += 1) {536 while (i < params) : (i += 1) {
634 const param_type_id = self.spv.types.get(decl.ty.fnParamType(i)).?;537 const param_type_id = try self.resolveTypeId(decl.ty.fnParamType(i));
635 const arg_result_id = self.spv.allocResultId();538 const arg_result_id = self.spv.allocId();
636 try writeInstruction(&self.spv.binary.fn_decls, .OpFunctionParameter, &[_]Word{ param_type_id, arg_result_id });539 try self.spv.sections.functions.emit(self.spv.gpa, .OpFunctionParameter, .{
637 self.args.appendAssumeCapacity(arg_result_id);540 .id_result_type = param_type_id,
541 .id_result = arg_result_id,
542 });
543 self.args.appendAssumeCapacity(arg_result_id.toRef());
638 }544 }
639545
640 // TODO: This could probably be done in a better way...546 // TODO: This could probably be done in a better way...
641 const root_block_id = self.spv.allocResultId();547 const root_block_id = self.spv.allocId();
642548
643 // We need to generate the label directly in the fn_decls here because we're going to write the local variables after549 // We need to generate the label directly in the functions section here because we're going to write the local variables after
644 // here. Since we're not generating in self.code, we're just going to bypass self.beginSPIRVBlock here.550 // here. Since we're not generating in self.code, we're just going to bypass self.beginSpvBlock here.
645 try writeInstruction(&self.spv.binary.fn_decls, .OpLabel, &[_]Word{root_block_id});551 try self.spv.sections.functions.emit(self.spv.gpa, .OpLabel, .{
646 self.current_block_label_id = root_block_id;552 .id_result = root_block_id,
553 });
554 self.current_block_label_id = root_block_id.toRef();
647555
648 const main_body = self.air.getMainBody();556 const main_body = self.air.getMainBody();
649 try self.genBody(main_body);557 try self.genBody(main_body);
650558
651 // Append the actual code into the fn_decls section.559 // Append the actual code into the functions section.
652 try self.spv.binary.fn_decls.appendSlice(self.code.items);560 try self.spv.sections.functions.append(self.spv.gpa, self.code);
653 try writeInstruction(&self.spv.binary.fn_decls, .OpFunctionEnd, &[_]Word{});561 try self.spv.sections.functions.emit(self.spv.gpa, .OpFunctionEnd, {});
654 } else {562 } else {
655 return self.fail("TODO: SPIR-V backend: generate decl type {}", .{decl.ty.zigTypeTag()});563 // TODO
564 // return self.todo("generate decl type {}", .{decl.ty.zigTypeTag()});
656 }565 }
657 }566 }
658567
...@@ -666,9 +575,9 @@ pub const DeclGen = struct {...@@ -666,9 +575,9 @@ pub const DeclGen = struct {
666 const air_tags = self.air.instructions.items(.tag);575 const air_tags = self.air.instructions.items(.tag);
667 const result_id = switch (air_tags[inst]) {576 const result_id = switch (air_tags[inst]) {
668 // zig fmt: off577 // zig fmt: off
669 .add, .addwrap => try self.airArithOp(inst, .{.OpFAdd, .OpIAdd, .OpIAdd}),578 .add, .addwrap => try self.airArithOp(inst, .OpFAdd, .OpIAdd, .OpIAdd),
670 .sub, .subwrap => try self.airArithOp(inst, .{.OpFSub, .OpISub, .OpISub}),579 .sub, .subwrap => try self.airArithOp(inst, .OpFSub, .OpISub, .OpISub),
671 .mul, .mulwrap => try self.airArithOp(inst, .{.OpFMul, .OpIMul, .OpIMul}),580 .mul, .mulwrap => try self.airArithOp(inst, .OpFMul, .OpIMul, .OpIMul),
672581
673 .bit_and => try self.airBinOpSimple(inst, .OpBitwiseAnd),582 .bit_and => try self.airBinOpSimple(inst, .OpBitwiseAnd),
674 .bit_or => try self.airBinOpSimple(inst, .OpBitwiseOr),583 .bit_or => try self.airBinOpSimple(inst, .OpBitwiseOr),
...@@ -678,12 +587,12 @@ pub const DeclGen = struct {...@@ -678,12 +587,12 @@ pub const DeclGen = struct {
678587
679 .not => try self.airNot(inst),588 .not => try self.airNot(inst),
680589
681 .cmp_eq => try self.airCmp(inst, .{.OpFOrdEqual, .OpLogicalEqual, .OpIEqual}),590 .cmp_eq => try self.airCmp(inst, .OpFOrdEqual, .OpLogicalEqual, .OpIEqual),
682 .cmp_neq => try self.airCmp(inst, .{.OpFOrdNotEqual, .OpLogicalNotEqual, .OpINotEqual}),591 .cmp_neq => try self.airCmp(inst, .OpFOrdNotEqual, .OpLogicalNotEqual, .OpINotEqual),
683 .cmp_gt => try self.airCmp(inst, .{.OpFOrdGreaterThan, .OpSGreaterThan, .OpUGreaterThan}),592 .cmp_gt => try self.airCmp(inst, .OpFOrdGreaterThan, .OpSGreaterThan, .OpUGreaterThan),
684 .cmp_gte => try self.airCmp(inst, .{.OpFOrdGreaterThanEqual, .OpSGreaterThanEqual, .OpUGreaterThanEqual}),593 .cmp_gte => try self.airCmp(inst, .OpFOrdGreaterThanEqual, .OpSGreaterThanEqual, .OpUGreaterThanEqual),
685 .cmp_lt => try self.airCmp(inst, .{.OpFOrdLessThan, .OpSLessThan, .OpULessThan}),594 .cmp_lt => try self.airCmp(inst, .OpFOrdLessThan, .OpSLessThan, .OpULessThan),
686 .cmp_lte => try self.airCmp(inst, .{.OpFOrdLessThanEqual, .OpSLessThanEqual, .OpULessThanEqual}),595 .cmp_lte => try self.airCmp(inst, .OpFOrdLessThanEqual, .OpSLessThanEqual, .OpULessThanEqual),
687596
688 .arg => self.airArg(),597 .arg => self.airArg(),
689 .alloc => try self.airAlloc(inst),598 .alloc => try self.airAlloc(inst),
...@@ -701,27 +610,30 @@ pub const DeclGen = struct {...@@ -701,27 +610,30 @@ pub const DeclGen = struct {
701 .unreach => return self.airUnreach(),610 .unreach => return self.airUnreach(),
702 // zig fmt: on611 // zig fmt: on
703612
704 else => |tag| return self.fail("TODO: SPIR-V backend: implement AIR tag {s}", .{613 else => |tag| return self.todo("implement AIR tag {s}", .{
705 @tagName(tag),614 @tagName(tag),
706 }),615 }),
707 };616 };
708617
709 try self.inst_results.putNoClobber(inst, result_id);618 try self.inst_results.putNoClobber(self.spv.gpa, inst, result_id);
710 }619 }
711620
712 fn airBinOpSimple(self: *DeclGen, inst: Air.Inst.Index, opcode: Opcode) !ResultId {621 fn airBinOpSimple(self: *DeclGen, inst: Air.Inst.Index, comptime opcode: Opcode) !IdRef {
713 const bin_op = self.air.instructions.items(.data)[inst].bin_op;622 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
714 const lhs_id = try self.resolve(bin_op.lhs);623 const lhs_id = try self.resolve(bin_op.lhs);
715 const rhs_id = try self.resolve(bin_op.rhs);624 const rhs_id = try self.resolve(bin_op.rhs);
716 const result_id = self.spv.allocResultId();625 const result_id = self.spv.allocId();
717 const result_type_id = try self.genType(self.air.typeOfIndex(inst));626 const result_type_id = try self.resolveTypeId(self.air.typeOfIndex(inst));
718 try writeInstruction(&self.code, opcode, &[_]Word{627 try self.code.emit(self.spv.gpa, opcode, .{
719 result_type_id, result_id, lhs_id, rhs_id,628 .id_result_type = result_type_id,
629 .id_result = result_id,
630 .operand_1 = lhs_id,
631 .operand_2 = rhs_id,
720 });632 });
721 return result_id;633 return result_id.toRef();
722 }634 }
723635
724 fn airArithOp(self: *DeclGen, inst: Air.Inst.Index, ops: [3]Opcode) !ResultId {636 fn airArithOp(self: *DeclGen, inst: Air.Inst.Index, comptime fop: Opcode, comptime sop: Opcode, comptime uop: Opcode) !IdRef {
725 // LHS and RHS are guaranteed to have the same type, and AIR guarantees637 // LHS and RHS are guaranteed to have the same type, and AIR guarantees
726 // the result to be the same as the LHS and RHS, which matches SPIR-V.638 // the result to be the same as the LHS and RHS, which matches SPIR-V.
727 const ty = self.air.typeOfIndex(inst);639 const ty = self.air.typeOfIndex(inst);
...@@ -729,8 +641,8 @@ pub const DeclGen = struct {...@@ -729,8 +641,8 @@ pub const DeclGen = struct {
729 const lhs_id = try self.resolve(bin_op.lhs);641 const lhs_id = try self.resolve(bin_op.lhs);
730 const rhs_id = try self.resolve(bin_op.rhs);642 const rhs_id = try self.resolve(bin_op.rhs);
731643
732 const result_id = self.spv.allocResultId();644 const result_id = self.spv.allocId();
733 const result_type_id = try self.genType(ty);645 const result_type_id = try self.resolveTypeId(ty);
734646
735 assert(self.air.typeOf(bin_op.lhs).eql(ty));647 assert(self.air.typeOf(bin_op.lhs).eql(ty));
736 assert(self.air.typeOf(bin_op.rhs).eql(ty));648 assert(self.air.typeOf(bin_op.rhs).eql(ty));
...@@ -741,10 +653,10 @@ pub const DeclGen = struct {...@@ -741,10 +653,10 @@ pub const DeclGen = struct {
741653
742 const opcode_index: usize = switch (info.class) {654 const opcode_index: usize = switch (info.class) {
743 .composite_integer => {655 .composite_integer => {
744 return self.fail("TODO: SPIR-V backend: binary operations for composite integers", .{});656 return self.todo("binary operations for composite integers", .{});
745 },657 },
746 .strange_integer => {658 .strange_integer => {
747 return self.fail("TODO: SPIR-V backend: binary operations for strange integers", .{});659 return self.todo("binary operations for strange integers", .{});
748 },660 },
749 .integer => switch (info.signedness) {661 .integer => switch (info.signedness) {
750 .signed => @as(usize, 1),662 .signed => @as(usize, 1),
...@@ -753,21 +665,32 @@ pub const DeclGen = struct {...@@ -753,21 +665,32 @@ pub const DeclGen = struct {
753 .float => 0,665 .float => 0,
754 else => unreachable,666 else => unreachable,
755 };667 };
756 const opcode = ops[opcode_index];
757 try writeInstruction(&self.code, opcode, &[_]Word{ result_type_id, result_id, lhs_id, rhs_id });
758668
669 const operands = .{
670 .id_result_type = result_type_id,
671 .id_result = result_id,
672 .operand_1 = lhs_id,
673 .operand_2 = rhs_id,
674 };
675
676 switch (opcode_index) {
677 0 => try self.code.emit(self.spv.gpa, fop, operands),
678 1 => try self.code.emit(self.spv.gpa, sop, operands),
679 2 => try self.code.emit(self.spv.gpa, uop, operands),
680 else => unreachable,
681 }
759 // TODO: Trap on overflow? Probably going to be annoying.682 // TODO: Trap on overflow? Probably going to be annoying.
760 // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap.683 // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap.
761684
762 return result_id;685 return result_id.toRef();
763 }686 }
764687
765 fn airCmp(self: *DeclGen, inst: Air.Inst.Index, ops: [3]Opcode) !ResultId {688 fn airCmp(self: *DeclGen, inst: Air.Inst.Index, comptime fop: Opcode, comptime sop: Opcode, comptime uop: Opcode) !IdRef {
766 const bin_op = self.air.instructions.items(.data)[inst].bin_op;689 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
767 const lhs_id = try self.resolve(bin_op.lhs);690 const lhs_id = try self.resolve(bin_op.lhs);
768 const rhs_id = try self.resolve(bin_op.rhs);691 const rhs_id = try self.resolve(bin_op.rhs);
769 const result_id = self.spv.allocResultId();692 const result_id = self.spv.allocId();
770 const result_type_id = try self.genType(Type.initTag(.bool));693 const result_type_id = try self.resolveTypeId(Type.initTag(.bool));
771 const op_ty = self.air.typeOf(bin_op.lhs);694 const op_ty = self.air.typeOf(bin_op.lhs);
772 assert(op_ty.eql(self.air.typeOf(bin_op.rhs)));695 assert(op_ty.eql(self.air.typeOf(bin_op.rhs)));
773696
...@@ -777,10 +700,10 @@ pub const DeclGen = struct {...@@ -777,10 +700,10 @@ pub const DeclGen = struct {
777700
778 const opcode_index: usize = switch (info.class) {701 const opcode_index: usize = switch (info.class) {
779 .composite_integer => {702 .composite_integer => {
780 return self.fail("TODO: SPIR-V backend: binary operations for composite integers", .{});703 return self.todo("binary operations for composite integers", .{});
781 },704 },
782 .strange_integer => {705 .strange_integer => {
783 return self.fail("TODO: SPIR-V backend: comparison for strange integers", .{});706 return self.todo("comparison for strange integers", .{});
784 },707 },
785 .float => 0,708 .float => 0,
786 .bool => 1,709 .bool => 1,
...@@ -789,53 +712,71 @@ pub const DeclGen = struct {...@@ -789,53 +712,71 @@ pub const DeclGen = struct {
789 .unsigned => @as(usize, 2),712 .unsigned => @as(usize, 2),
790 },713 },
791 };714 };
792 const opcode = ops[opcode_index];
793715
794 try writeInstruction(&self.code, opcode, &[_]Word{ result_type_id, result_id, lhs_id, rhs_id });716 const operands = .{
795 return result_id;717 .id_result_type = result_type_id,
718 .id_result = result_id,
719 .operand_1 = lhs_id,
720 .operand_2 = rhs_id,
721 };
722
723 switch (opcode_index) {
724 0 => try self.code.emit(self.spv.gpa, fop, operands),
725 1 => try self.code.emit(self.spv.gpa, sop, operands),
726 2 => try self.code.emit(self.spv.gpa, uop, operands),
727 else => unreachable,
728 }
729
730 return result_id.toRef();
796 }731 }
797732
798 fn airNot(self: *DeclGen, inst: Air.Inst.Index) !ResultId {733 fn airNot(self: *DeclGen, inst: Air.Inst.Index) !IdRef {
799 const ty_op = self.air.instructions.items(.data)[inst].ty_op;734 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
800 const operand_id = try self.resolve(ty_op.operand);735 const operand_id = try self.resolve(ty_op.operand);
801 const result_id = self.spv.allocResultId();736 const result_id = self.spv.allocId();
802 const result_type_id = try self.genType(Type.initTag(.bool));737 const result_type_id = try self.resolveTypeId(Type.initTag(.bool));
803 const opcode: Opcode = .OpLogicalNot;738 try self.code.emit(self.spv.gpa, .OpLogicalNot, .{
804 try writeInstruction(&self.code, opcode, &[_]Word{ result_type_id, result_id, operand_id });739 .id_result_type = result_type_id,
805 return result_id;740 .id_result = result_id,
741 .operand = operand_id,
742 });
743 return result_id.toRef();
806 }744 }
807745
808 fn airAlloc(self: *DeclGen, inst: Air.Inst.Index) !ResultId {746 fn airAlloc(self: *DeclGen, inst: Air.Inst.Index) !IdRef {
809 const ty = self.air.typeOfIndex(inst);747 const ty = self.air.typeOfIndex(inst);
810 const storage_class = spec.StorageClass.Function;748 const storage_class = spec.StorageClass.Function;
811 const result_type_id = try self.genPointerType(ty, storage_class);749 const result_type_id = try self.genPointerType(ty, storage_class);
812 const result_id = self.spv.allocResultId();750 const result_id = self.spv.allocId();
813751
814 // Rather than generating into code here, we're just going to generate directly into the fn_decls section so that752 // Rather than generating into code here, we're just going to generate directly into the functions section so that
815 // variable declarations appear in the first block of the function.753 // variable declarations appear in the first block of the function.
816 try writeInstruction(&self.spv.binary.fn_decls, .OpVariable, &[_]Word{ result_type_id, result_id, @enumToInt(storage_class) });754 try self.spv.sections.functions.emit(self.spv.gpa, .OpVariable, .{
817755 .id_result_type = result_type_id,
818 return result_id;756 .id_result = result_id,
757 .storage_class = storage_class,
758 });
759 return result_id.toRef();
819 }760 }
820761
821 fn airArg(self: *DeclGen) ResultId {762 fn airArg(self: *DeclGen) IdRef {
822 defer self.next_arg_index += 1;763 defer self.next_arg_index += 1;
823 return self.args.items[self.next_arg_index];764 return self.args.items[self.next_arg_index];
824 }765 }
825766
826 fn airBlock(self: *DeclGen, inst: Air.Inst.Index) !?ResultId {767 fn airBlock(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
827 // In IR, a block doesn't really define an entry point like a block, but more like a scope that breaks can jump out of and768 // In AIR, a block doesn't really define an entry point like a block, but more like a scope that breaks can jump out of and
828 // "return" a value from. This cannot be directly modelled in SPIR-V, so in a block instruction, we're going to split up769 // "return" a value from. This cannot be directly modelled in SPIR-V, so in a block instruction, we're going to split up
829 // the current block by first generating the code of the block, then a label, and then generate the rest of the current770 // the current block by first generating the code of the block, then a label, and then generate the rest of the current
830 // ir.Block in a different SPIR-V block.771 // ir.Block in a different SPIR-V block.
831772
832 const label_id = self.spv.allocResultId();773 const label_id = self.spv.allocId();
833774
834 // 4 chosen as arbitrary initial capacity.775 // 4 chosen as arbitrary initial capacity.
835 var incoming_blocks = try std.ArrayListUnmanaged(IncomingBlock).initCapacity(self.spv.gpa, 4);776 var incoming_blocks = try std.ArrayListUnmanaged(IncomingBlock).initCapacity(self.spv.gpa, 4);
836777
837 try self.blocks.putNoClobber(inst, .{778 try self.blocks.putNoClobber(self.spv.gpa, inst, .{
838 .label_id = label_id,779 .label_id = label_id.toRef(),
839 .incoming_blocks = &incoming_blocks,780 .incoming_blocks = &incoming_blocks,
840 });781 });
841 defer {782 defer {
...@@ -849,7 +790,7 @@ pub const DeclGen = struct {...@@ -849,7 +790,7 @@ pub const DeclGen = struct {
849 const body = self.air.extra[extra.end..][0..extra.data.body_len];790 const body = self.air.extra[extra.end..][0..extra.data.body_len];
850791
851 try self.genBody(body);792 try self.genBody(body);
852 try self.beginSPIRVBlock(label_id);793 try self.beginSpvBlock(label_id);
853794
854 // If this block didn't produce a value, simply return here.795 // If this block didn't produce a value, simply return here.
855 if (!ty.hasRuntimeBits())796 if (!ty.hasRuntimeBits())
...@@ -857,21 +798,21 @@ pub const DeclGen = struct {...@@ -857,21 +798,21 @@ pub const DeclGen = struct {
857798
858 // Combine the result from the blocks using the Phi instruction.799 // Combine the result from the blocks using the Phi instruction.
859800
860 const result_id = self.spv.allocResultId();801 const result_id = self.spv.allocId();
861802
862 // TODO: OpPhi is limited in the types that it may produce, such as pointers. Figure out which other types803 // TODO: OpPhi is limited in the types that it may produce, such as pointers. Figure out which other types
863 // are not allowed to be created from a phi node, and throw an error for those. For now, genType already throws804 // are not allowed to be created from a phi node, and throw an error for those. For now, resolveTypeId already throws
864 // an error for pointers.805 // an error for pointers.
865 const result_type_id = try self.genType(ty);806 const result_type_id = try self.resolveTypeId(ty);
866 _ = result_type_id;807 _ = result_type_id;
867808
868 try writeOpcode(&self.code, .OpPhi, 2 + @intCast(u16, incoming_blocks.items.len * 2)); // result type + result + variable/parent...809 try self.code.emitRaw(self.spv.gpa, .OpPhi, 2 + @intCast(u16, incoming_blocks.items.len * 2)); // result type + result + variable/parent...
869810
870 for (incoming_blocks.items) |incoming| {811 for (incoming_blocks.items) |incoming| {
871 try self.code.appendSlice(&[_]Word{ incoming.break_value_id, incoming.src_label_id });812 self.code.writeOperand(spec.PairIdRefIdRef, .{ incoming.break_value_id, incoming.src_label_id });
872 }813 }
873814
874 return result_id;815 return result_id.toRef();
875 }816 }
876817
877 fn airBr(self: *DeclGen, inst: Air.Inst.Index) !void {818 fn airBr(self: *DeclGen, inst: Air.Inst.Index) !void {
...@@ -885,7 +826,7 @@ pub const DeclGen = struct {...@@ -885,7 +826,7 @@ pub const DeclGen = struct {
885 try block.incoming_blocks.append(self.spv.gpa, .{ .src_label_id = self.current_block_label_id, .break_value_id = operand_id });826 try block.incoming_blocks.append(self.spv.gpa, .{ .src_label_id = self.current_block_label_id, .break_value_id = operand_id });
886 }827 }
887828
888 try writeInstruction(&self.code, .OpBranch, &[_]Word{block.label_id});829 try self.code.emit(self.spv.gpa, .OpBranch, .{ .target_label = block.label_id });
889 }830 }
890831
891 fn airCondBr(self: *DeclGen, inst: Air.Inst.Index) !void {832 fn airCondBr(self: *DeclGen, inst: Air.Inst.Index) !void {
...@@ -896,63 +837,70 @@ pub const DeclGen = struct {...@@ -896,63 +837,70 @@ pub const DeclGen = struct {
896 const condition_id = try self.resolve(pl_op.operand);837 const condition_id = try self.resolve(pl_op.operand);
897838
898 // These will always generate a new SPIR-V block, since they are ir.Body and not ir.Block.839 // These will always generate a new SPIR-V block, since they are ir.Body and not ir.Block.
899 const then_label_id = self.spv.allocResultId();840 const then_label_id = self.spv.allocId();
900 const else_label_id = self.spv.allocResultId();841 const else_label_id = self.spv.allocId();
901842
902 // TODO: We can generate OpSelectionMerge here if we know the target block that both of these will resolve to,843 // TODO: We can generate OpSelectionMerge here if we know the target block that both of these will resolve to,
903 // but i don't know if those will always resolve to the same block.844 // but i don't know if those will always resolve to the same block.
904845
905 try writeInstruction(&self.code, .OpBranchConditional, &[_]Word{846 try self.code.emit(self.spv.gpa, .OpBranchConditional, .{
906 condition_id,847 .condition = condition_id,
907 then_label_id,848 .true_label = then_label_id.toRef(),
908 else_label_id,849 .false_label = else_label_id.toRef(),
909 });850 });
910851
911 try self.beginSPIRVBlock(then_label_id);852 try self.beginSpvBlock(then_label_id);
912 try self.genBody(then_body);853 try self.genBody(then_body);
913 try self.beginSPIRVBlock(else_label_id);854 try self.beginSpvBlock(else_label_id);
914 try self.genBody(else_body);855 try self.genBody(else_body);
915 }856 }
916857
917 fn airDbgStmt(self: *DeclGen, inst: Air.Inst.Index) !void {858 fn airDbgStmt(self: *DeclGen, inst: Air.Inst.Index) !void {
918 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;859 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
919 const src_fname_id = try self.spv.resolveSourceFileName(self.decl);860 const src_fname_id = try self.spv.resolveSourceFileName(self.decl);
920 try writeInstruction(&self.code, .OpLine, &[_]Word{ src_fname_id, dbg_stmt.line, dbg_stmt.column });861 try self.code.emit(self.spv.gpa, .OpLine, .{
862 .file = src_fname_id,
863 .line = dbg_stmt.line,
864 .column = dbg_stmt.column,
865 });
921 }866 }
922867
923 fn airLoad(self: *DeclGen, inst: Air.Inst.Index) !ResultId {868 fn airLoad(self: *DeclGen, inst: Air.Inst.Index) !IdRef {
924 const ty_op = self.air.instructions.items(.data)[inst].ty_op;869 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
925 const operand_id = try self.resolve(ty_op.operand);870 const operand_id = try self.resolve(ty_op.operand);
926 const ty = self.air.typeOfIndex(inst);871 const ty = self.air.typeOfIndex(inst);
927872
928 const result_type_id = try self.genType(ty);873 const result_type_id = try self.resolveTypeId(ty);
929 const result_id = self.spv.allocResultId();874 const result_id = self.spv.allocId();
930875
931 const operands = if (ty.isVolatilePtr())876 const access = spec.MemoryAccess.Extended{
932 &[_]Word{ result_type_id, result_id, operand_id, @bitCast(u32, spec.MemoryAccess{ .Volatile = true }) }877 .Volatile = ty.isVolatilePtr(),
933 else878 };
934 &[_]Word{ result_type_id, result_id, operand_id };
935879
936 try writeInstruction(&self.code, .OpLoad, operands);880 try self.code.emit(self.spv.gpa, .OpLoad, .{
881 .id_result_type = result_type_id,
882 .id_result = result_id,
883 .pointer = operand_id,
884 .memory_access = access,
885 });
937886
938 return result_id;887 return result_id.toRef();
939 }888 }
940889
941 fn airLoop(self: *DeclGen, inst: Air.Inst.Index) !void {890 fn airLoop(self: *DeclGen, inst: Air.Inst.Index) !void {
942 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;891 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
943 const loop = self.air.extraData(Air.Block, ty_pl.payload);892 const loop = self.air.extraData(Air.Block, ty_pl.payload);
944 const body = self.air.extra[loop.end..][0..loop.data.body_len];893 const body = self.air.extra[loop.end..][0..loop.data.body_len];
945 const loop_label_id = self.spv.allocResultId();894 const loop_label_id = self.spv.allocId();
946895
947 // Jump to the loop entry point896 // Jump to the loop entry point
948 try writeInstruction(&self.code, .OpBranch, &[_]Word{loop_label_id});897 try self.code.emit(self.spv.gpa, .OpBranch, .{ .target_label = loop_label_id.toRef() });
949898
950 // TODO: Look into OpLoopMerge.899 // TODO: Look into OpLoopMerge.
951900 try self.beginSpvBlock(loop_label_id);
952 try self.beginSPIRVBlock(loop_label_id);
953 try self.genBody(body);901 try self.genBody(body);
954902
955 try writeInstruction(&self.code, .OpBranch, &[_]Word{loop_label_id});903 try self.code.emit(self.spv.gpa, .OpBranch, .{ .target_label = loop_label_id.toRef() });
956 }904 }
957905
958 fn airRet(self: *DeclGen, inst: Air.Inst.Index) !void {906 fn airRet(self: *DeclGen, inst: Air.Inst.Index) !void {
...@@ -960,9 +908,9 @@ pub const DeclGen = struct {...@@ -960,9 +908,9 @@ pub const DeclGen = struct {
960 const operand_ty = self.air.typeOf(operand);908 const operand_ty = self.air.typeOf(operand);
961 if (operand_ty.hasRuntimeBits()) {909 if (operand_ty.hasRuntimeBits()) {
962 const operand_id = try self.resolve(operand);910 const operand_id = try self.resolve(operand);
963 try writeInstruction(&self.code, .OpReturnValue, &[_]Word{operand_id});911 try self.code.emit(self.spv.gpa, .OpReturnValue, .{ .value = operand_id });
964 } else {912 } else {
965 try writeInstruction(&self.code, .OpReturn, &[_]Word{});913 try self.code.emit(self.spv.gpa, .OpReturn, {});
966 }914 }
967 }915 }
968916
...@@ -972,15 +920,18 @@ pub const DeclGen = struct {...@@ -972,15 +920,18 @@ pub const DeclGen = struct {
972 const src_val_id = try self.resolve(bin_op.rhs);920 const src_val_id = try self.resolve(bin_op.rhs);
973 const lhs_ty = self.air.typeOf(bin_op.lhs);921 const lhs_ty = self.air.typeOf(bin_op.lhs);
974922
975 const operands = if (lhs_ty.isVolatilePtr())923 const access = spec.MemoryAccess.Extended{
976 &[_]Word{ dst_ptr_id, src_val_id, @bitCast(u32, spec.MemoryAccess{ .Volatile = true }) }924 .Volatile = lhs_ty.isVolatilePtr(),
977 else925 };
978 &[_]Word{ dst_ptr_id, src_val_id };
979926
980 try writeInstruction(&self.code, .OpStore, operands);927 try self.code.emit(self.spv.gpa, .OpStore, .{
928 .pointer = dst_ptr_id,
929 .object = src_val_id,
930 .memory_access = access,
931 });
981 }932 }
982933
983 fn airUnreach(self: *DeclGen) !void {934 fn airUnreach(self: *DeclGen) !void {
984 try writeInstruction(&self.code, .OpUnreachable, &[_]Word{});935 try self.code.emit(self.spv.gpa, .OpUnreachable, {});
985 }936 }
986};937};
src/codegen/spirv/Module.zig created+428
...@@ -0,0 +1,428 @@
1//! This structure represents a SPIR-V (sections) module being compiled, and keeps track of all relevant information.
2//! That includes the actual instructions, the current result-id bound, and data structures for querying result-id's
3//! of data which needs to be persistent over different calls to Decl code generation.
4//!
5//! A SPIR-V binary module supports both little- and big endian layout. The layout is detected by the magic word in the
6//! header. Therefore, we can ignore any byte order throughout the implementation, and just use the host byte order,
7//! and make this a problem for the consumer.
8const Module = @This();
9
10const std = @import("std");
11const Allocator = std.mem.Allocator;
12const assert = std.debug.assert;
13
14const ZigDecl = @import("../../Module.zig").Decl;
15
16const spec = @import("spec.zig");
17const Word = spec.Word;
18const IdRef = spec.IdRef;
19const IdResult = spec.IdResult;
20const IdResultType = spec.IdResultType;
21
22const Section = @import("Section.zig");
23const Type = @import("type.zig").Type;
24
25const TypeCache = std.ArrayHashMapUnmanaged(Type, IdResultType, Type.ShallowHashContext32, true);
26
27/// A general-purpose allocator which may be used to allocate resources for this module
28gpa: Allocator,
29
30/// An arena allocator used to store things that have the same lifetime as this module.
31arena: Allocator,
32
33/// Module layout, according to SPIR-V Spec section 2.4, "Logical Layout of a Module".
34sections: struct {
35 /// Capability instructions
36 capabilities: Section = .{},
37 /// OpExtension instructions
38 extensions: Section = .{},
39 // OpExtInstImport instructions - skip for now.
40 // memory model defined by target, not required here.
41 /// OpEntryPoint instructions.
42 entry_points: Section = .{},
43 // OpExecutionMode and OpExecutionModeId instructions - skip for now.
44 /// OpString, OpSourcExtension, OpSource, OpSourceContinued.
45 debug_strings: Section = .{},
46 // OpName, OpMemberName - skip for now.
47 // OpModuleProcessed - skip for now.
48 /// Annotation instructions (OpDecorate etc).
49 annotations: Section = .{},
50 /// Type declarations, constants, global variables
51 /// Below this section, OpLine and OpNoLine is allowed.
52 types_globals_constants: Section = .{},
53 // Functions without a body - skip for now.
54 /// Regular function definitions.
55 functions: Section = .{},
56} = .{},
57
58/// SPIR-V instructions return result-ids. This variable holds the module-wide counter for these.
59next_result_id: Word,
60
61/// Cache for results of OpString instructions for module file names fed to OpSource.
62/// Since OpString is pretty much only used for those, we don't need to keep track of all strings,
63/// just the ones for OpLine. Note that OpLine needs the result of OpString, and not that of OpSource.
64source_file_names: std.StringHashMapUnmanaged(IdRef) = .{},
65
66/// SPIR-V type cache. Note that according to SPIR-V spec section 2.8, Types and Variables, non-pointer
67/// non-aggrerate types (which includes matrices and vectors) must have a _unique_ representation in
68/// the final binary.
69/// Note: Uses ArrayHashMap which is insertion ordered, so that we may refer to other types by index (Type.Ref).
70type_cache: TypeCache = .{},
71
72pub fn init(gpa: Allocator, arena: Allocator) Module {
73 return .{
74 .gpa = gpa,
75 .arena = arena,
76 .next_result_id = 1, // 0 is an invalid SPIR-V result id, so start counting at 1.
77 };
78}
79
80pub fn deinit(self: *Module) void {
81 self.sections.capabilities.deinit(self.gpa);
82 self.sections.extensions.deinit(self.gpa);
83 self.sections.entry_points.deinit(self.gpa);
84 self.sections.debug_strings.deinit(self.gpa);
85 self.sections.annotations.deinit(self.gpa);
86 self.sections.types_globals_constants.deinit(self.gpa);
87 self.sections.functions.deinit(self.gpa);
88
89 self.source_file_names.deinit(self.gpa);
90 self.type_cache.deinit(self.gpa);
91
92 self.* = undefined;
93}
94
95pub fn allocId(self: *Module) spec.IdResult {
96 defer self.next_result_id += 1;
97 return .{ .id = self.next_result_id };
98}
99
100pub fn idBound(self: Module) Word {
101 return self.next_result_id;
102}
103
104/// Emit this module as a spir-v binary.
105pub fn flush(self: Module, file: std.fs.File) !void {
106 // See SPIR-V Spec section 2.3, "Physical Layout of a SPIR-V Module and Instruction"
107
108 const header = [_]Word{
109 spec.magic_number,
110 (spec.version.major << 16) | (spec.version.minor << 8),
111 0, // TODO: Register Zig compiler magic number.
112 self.idBound(),
113 0, // Schema (currently reserved for future use)
114 };
115
116 // Note: needs to be kept in order according to section 2.3!
117 const buffers = &[_][]const Word{
118 &header,
119 self.sections.capabilities.toWords(),
120 self.sections.extensions.toWords(),
121 self.sections.entry_points.toWords(),
122 self.sections.debug_strings.toWords(),
123 self.sections.annotations.toWords(),
124 self.sections.types_globals_constants.toWords(),
125 self.sections.functions.toWords(),
126 };
127
128 var iovc_buffers: [buffers.len]std.os.iovec_const = undefined;
129 var file_size: u64 = 0;
130 for (iovc_buffers) |*iovc, i| {
131 // Note, since spir-v supports both little and big endian we can ignore byte order here and
132 // just treat the words as a sequence of bytes.
133 const bytes = std.mem.sliceAsBytes(buffers[i]);
134 iovc.* = .{ .iov_base = bytes.ptr, .iov_len = bytes.len };
135 file_size += bytes.len;
136 }
137
138 try file.seekTo(0);
139 try file.setEndPos(file_size);
140 try file.pwritevAll(&iovc_buffers, 0);
141}
142
143/// Fetch the result-id of an OpString instruction that encodes the path of the source
144/// file of the decl. This function may also emit an OpSource with source-level information regarding
145/// the decl.
146pub fn resolveSourceFileName(self: *Module, decl: *ZigDecl) !IdRef {
147 const path = decl.getFileScope().sub_file_path;
148 const result = try self.source_file_names.getOrPut(self.gpa, path);
149 if (!result.found_existing) {
150 const file_result_id = self.allocId();
151 result.value_ptr.* = file_result_id.toRef();
152 try self.sections.debug_strings.emit(self.gpa, .OpString, .{
153 .id_result = file_result_id,
154 .string = path,
155 });
156
157 try self.sections.debug_strings.emit(self.gpa, .OpSource, .{
158 .source_language = .Unknown, // TODO: Register Zig source language.
159 .version = 0, // TODO: Zig version as u32?
160 .file = file_result_id.toRef(),
161 .source = null, // TODO: Store actual source also?
162 });
163 }
164
165 return result.value_ptr.*;
166}
167
168/// Fetch a result-id for a spir-v type. This function deduplicates the type as appropriate,
169/// and returns a cached version if that exists.
170/// Note: This function does not attempt to perform any validation on the type.
171/// The type is emitted in a shallow fashion; any child types should already
172/// be emitted at this point.
173pub fn resolveType(self: *Module, ty: Type) !Type.Ref {
174 const result = try self.type_cache.getOrPut(self.gpa, ty);
175 if (!result.found_existing) {
176 result.value_ptr.* = try self.emitType(ty);
177 }
178 return result.index;
179}
180
181pub fn resolveTypeId(self: *Module, ty: Type) !IdRef {
182 return self.typeResultId(try self.resolveType(ty));
183}
184
185/// Get the result-id of a particular type, by reference. Asserts type_ref is valid.
186pub fn typeResultId(self: Module, type_ref: Type.Ref) IdResultType {
187 return self.type_cache.values()[type_ref];
188}
189
190/// Get the result-id of a particular type as IdRef, by Type.Ref. Asserts type_ref is valid.
191pub fn typeRefId(self: Module, type_ref: Type.Ref) IdRef {
192 return self.type_cache.values()[type_ref].toRef();
193}
194
195/// Unconditionally emit a spir-v type into the appropriate section.
196/// Note: If this function is called with a type that is already generated, it may yield an invalid module
197/// as non-pointer non-aggregrate types must me unique!
198/// Note: This function does not attempt to perform any validation on the type.
199/// The type is emitted in a shallow fashion; any child types should already
200/// be emitted at this point.
201pub fn emitType(self: *Module, ty: Type) !IdResultType {
202 const result_id = self.allocId();
203 const ref_id = result_id.toRef();
204 const types = &self.sections.types_globals_constants;
205 const annotations = &self.sections.annotations;
206 const result_id_operand = .{ .id_result = result_id };
207
208 switch (ty.tag()) {
209 .void => try types.emit(self.gpa, .OpTypeVoid, result_id_operand),
210 .bool => try types.emit(self.gpa, .OpTypeBool, result_id_operand),
211 .int => try types.emit(self.gpa, .OpTypeInt, .{
212 .id_result = result_id,
213 .width = ty.payload(.int).width,
214 .signedness = switch (ty.payload(.int).signedness) {
215 .unsigned => @as(spec.LiteralInteger, 0),
216 .signed => 1,
217 },
218 }),
219 .float => try types.emit(self.gpa, .OpTypeFloat, .{
220 .id_result = result_id,
221 .width = ty.payload(.float).width,
222 }),
223 .vector => try types.emit(self.gpa, .OpTypeVector, .{
224 .id_result = result_id,
225 .component_type = self.typeResultId(ty.childType()).toRef(),
226 .component_count = ty.payload(.vector).component_count,
227 }),
228 .matrix => try types.emit(self.gpa, .OpTypeMatrix, .{
229 .id_result = result_id,
230 .column_type = self.typeResultId(ty.childType()).toRef(),
231 .column_count = ty.payload(.matrix).column_count,
232 }),
233 .image => {
234 const info = ty.payload(.image);
235 try types.emit(self.gpa, .OpTypeImage, .{
236 .id_result = result_id,
237 .sampled_type = self.typeResultId(ty.childType()).toRef(),
238 .dim = info.dim,
239 .depth = @enumToInt(info.depth),
240 .arrayed = @boolToInt(info.arrayed),
241 .ms = @boolToInt(info.multisampled),
242 .sampled = @enumToInt(info.sampled),
243 .image_format = info.format,
244 .access_qualifier = info.access_qualifier,
245 });
246 },
247 .sampler => try types.emit(self.gpa, .OpTypeSampler, result_id_operand),
248 .sampled_image => try types.emit(self.gpa, .OpTypeSampledImage, .{
249 .id_result = result_id,
250 .image_type = self.typeResultId(ty.childType()).toRef(),
251 }),
252 .array => {
253 const info = ty.payload(.array);
254 assert(info.length != 0);
255 try types.emit(self.gpa, .OpTypeArray, .{
256 .id_result = result_id,
257 .element_type = self.typeResultId(ty.childType()).toRef(),
258 .length = .{ .id = 0 }, // TODO: info.length must be emitted as constant!
259 });
260 if (info.array_stride != 0) {
261 try annotations.decorate(self.gpa, ref_id, .{ .ArrayStride = .{ .array_stride = info.array_stride } });
262 }
263 },
264 .runtime_array => {
265 const info = ty.payload(.runtime_array);
266 try types.emit(self.gpa, .OpTypeRuntimeArray, .{
267 .id_result = result_id,
268 .element_type = self.typeResultId(ty.childType()).toRef(),
269 });
270 if (info.array_stride != 0) {
271 try annotations.decorate(self.gpa, ref_id, .{ .ArrayStride = .{ .array_stride = info.array_stride } });
272 }
273 },
274 .@"struct" => {
275 const info = ty.payload(.@"struct");
276 try types.emitRaw(self.gpa, .OpTypeStruct, 1 + info.members.len);
277 types.writeOperand(IdResult, result_id);
278 for (info.members) |member| {
279 types.writeOperand(IdRef, self.typeResultId(member.ty).toRef());
280 }
281 try self.decorateStruct(ref_id, info);
282 },
283 .@"opaque" => try types.emit(self.gpa, .OpTypeOpaque, .{
284 .id_result = result_id,
285 .literal_string = ty.payload(.@"opaque").name,
286 }),
287 .pointer => {
288 const info = ty.payload(.pointer);
289 try types.emit(self.gpa, .OpTypePointer, .{
290 .id_result = result_id,
291 .storage_class = info.storage_class,
292 .type = self.typeResultId(ty.childType()).toRef(),
293 });
294 if (info.array_stride != 0) {
295 try annotations.decorate(self.gpa, ref_id, .{ .ArrayStride = .{ .array_stride = info.array_stride } });
296 }
297 if (info.alignment) |alignment| {
298 try annotations.decorate(self.gpa, ref_id, .{ .Alignment = .{ .alignment = alignment } });
299 }
300 if (info.max_byte_offset) |max_byte_offset| {
301 try annotations.decorate(self.gpa, ref_id, .{ .MaxByteOffset = .{ .max_byte_offset = max_byte_offset } });
302 }
303 },
304 .function => {
305 const info = ty.payload(.function);
306 try types.emitRaw(self.gpa, .OpTypeFunction, 2 + info.parameters.len);
307 types.writeOperand(IdResult, result_id);
308 types.writeOperand(IdRef, self.typeResultId(info.return_type).toRef());
309 for (info.parameters) |parameter_type| {
310 types.writeOperand(IdRef, self.typeResultId(parameter_type).toRef());
311 }
312 },
313 .event => try types.emit(self.gpa, .OpTypeEvent, result_id_operand),
314 .device_event => try types.emit(self.gpa, .OpTypeDeviceEvent, result_id_operand),
315 .reserve_id => try types.emit(self.gpa, .OpTypeReserveId, result_id_operand),
316 .queue => try types.emit(self.gpa, .OpTypeQueue, result_id_operand),
317 .pipe => try types.emit(self.gpa, .OpTypePipe, .{
318 .id_result = result_id,
319 .qualifier = ty.payload(.pipe).qualifier,
320 }),
321 .pipe_storage => try types.emit(self.gpa, .OpTypePipeStorage, result_id_operand),
322 .named_barrier => try types.emit(self.gpa, .OpTypeNamedBarrier, result_id_operand),
323 }
324
325 return result_id.toResultType();
326}
327
328fn decorateStruct(self: *Module, target: IdRef, info: *const Type.Payload.Struct) !void {
329 const annotations = &self.sections.annotations;
330
331 // Decorations for the struct type itself.
332 if (info.decorations.block)
333 try annotations.decorate(self.gpa, target, .Block);
334 if (info.decorations.buffer_block)
335 try annotations.decorate(self.gpa, target, .BufferBlock);
336 if (info.decorations.glsl_shared)
337 try annotations.decorate(self.gpa, target, .GLSLShared);
338 if (info.decorations.glsl_packed)
339 try annotations.decorate(self.gpa, target, .GLSLPacked);
340 if (info.decorations.c_packed)
341 try annotations.decorate(self.gpa, target, .CPacked);
342
343 // Decorations for the struct members.
344 const extra = info.member_decoration_extra;
345 var extra_i: u32 = 0;
346 for (info.members) |member, i| {
347 const d = member.decorations;
348 const index = @intCast(Word, i);
349 switch (d.matrix_layout) {
350 .row_major => try annotations.decorateMember(self.gpa, target, index, .RowMajor),
351 .col_major => try annotations.decorateMember(self.gpa, target, index, .ColMajor),
352 .none => {},
353 }
354 if (d.matrix_layout != .none) {
355 try annotations.decorateMember(self.gpa, target, index, .{
356 .MatrixStride = .{ .matrix_stride = extra[extra_i] },
357 });
358 extra_i += 1;
359 }
360
361 if (d.no_perspective)
362 try annotations.decorateMember(self.gpa, target, index, .NoPerspective);
363 if (d.flat)
364 try annotations.decorateMember(self.gpa, target, index, .Flat);
365 if (d.patch)
366 try annotations.decorateMember(self.gpa, target, index, .Patch);
367 if (d.centroid)
368 try annotations.decorateMember(self.gpa, target, index, .Centroid);
369 if (d.sample)
370 try annotations.decorateMember(self.gpa, target, index, .Sample);
371 if (d.invariant)
372 try annotations.decorateMember(self.gpa, target, index, .Invariant);
373 if (d.@"volatile")
374 try annotations.decorateMember(self.gpa, target, index, .Volatile);
375 if (d.coherent)
376 try annotations.decorateMember(self.gpa, target, index, .Coherent);
377 if (d.non_writable)
378 try annotations.decorateMember(self.gpa, target, index, .NonWritable);
379 if (d.non_readable)
380 try annotations.decorateMember(self.gpa, target, index, .NonReadable);
381
382 if (d.builtin) {
383 try annotations.decorateMember(self.gpa, target, index, .{
384 .BuiltIn = .{ .built_in = @intToEnum(spec.BuiltIn, extra[extra_i]) },
385 });
386 extra_i += 1;
387 }
388 if (d.stream) {
389 try annotations.decorateMember(self.gpa, target, index, .{
390 .Stream = .{ .stream_number = extra[extra_i] },
391 });
392 extra_i += 1;
393 }
394 if (d.location) {
395 try annotations.decorateMember(self.gpa, target, index, .{
396 .Location = .{ .location = extra[extra_i] },
397 });
398 extra_i += 1;
399 }
400 if (d.component) {
401 try annotations.decorateMember(self.gpa, target, index, .{
402 .Component = .{ .component = extra[extra_i] },
403 });
404 extra_i += 1;
405 }
406 if (d.xfb_buffer) {
407 try annotations.decorateMember(self.gpa, target, index, .{
408 .XfbBuffer = .{ .xfb_buffer_number = extra[extra_i] },
409 });
410 extra_i += 1;
411 }
412 if (d.xfb_stride) {
413 try annotations.decorateMember(self.gpa, target, index, .{
414 .XfbStride = .{ .xfb_stride = extra[extra_i] },
415 });
416 extra_i += 1;
417 }
418 if (d.user_semantic) {
419 const len = extra[extra_i];
420 extra_i += 1;
421 const semantic = @ptrCast([*]const u8, &extra[extra_i])[0..len];
422 try annotations.decorateMember(self.gpa, target, index, .{
423 .UserSemantic = .{ .semantic = semantic },
424 });
425 extra_i += std.math.divCeil(u32, extra_i, @sizeOf(u32)) catch unreachable;
426 }
427 }
428}
src/codegen/spirv/Section.zig created+423
...@@ -0,0 +1,423 @@
1//! Represents a section or subsection of instructions in a SPIR-V binary. Instructions can be append
2//! to separate sections, which can then later be merged into the final binary.
3const Section = @This();
4
5const std = @import("std");
6const Allocator = std.mem.Allocator;
7const testing = std.testing;
8
9const spec = @import("spec.zig");
10const Word = spec.Word;
11const DoubleWord = std.meta.Int(.unsigned, @bitSizeOf(Word) * 2);
12const Log2Word = std.math.Log2Int(Word);
13
14const Opcode = spec.Opcode;
15
16/// The instructions in this section. Memory is owned by the Module
17/// externally associated to this Section.
18instructions: std.ArrayListUnmanaged(Word) = .{},
19
20pub fn deinit(section: *Section, allocator: Allocator) void {
21 section.instructions.deinit(allocator);
22 section.* = undefined;
23}
24
25/// Clear the instructions in this section
26pub fn reset(section: *Section) void {
27 section.instructions.items.len = 0;
28}
29
30pub fn toWords(section: Section) []Word {
31 return section.instructions.items;
32}
33
34/// Append the instructions from another section into this section.
35pub fn append(section: *Section, allocator: Allocator, other_section: Section) !void {
36 try section.instructions.appendSlice(allocator, other_section.instructions.items);
37}
38
39/// Write an instruction and size, operands are to be inserted manually.
40pub fn emitRaw(
41 section: *Section,
42 allocator: Allocator,
43 opcode: Opcode,
44 operands: usize, // opcode itself not included
45) !void {
46 const word_count = 1 + operands;
47 try section.instructions.ensureUnusedCapacity(allocator, word_count);
48 section.writeWord((@intCast(Word, word_count << 16)) | @enumToInt(opcode));
49}
50
51pub fn emit(
52 section: *Section,
53 allocator: Allocator,
54 comptime opcode: spec.Opcode,
55 operands: opcode.Operands(),
56) !void {
57 const word_count = instructionSize(opcode, operands);
58 try section.instructions.ensureUnusedCapacity(allocator, word_count);
59 section.writeWord(@intCast(Word, word_count << 16) | @enumToInt(opcode));
60 section.writeOperands(opcode.Operands(), operands);
61}
62
63/// Decorate a result-id.
64pub fn decorate(
65 section: *Section,
66 allocator: Allocator,
67 target: spec.IdRef,
68 decoration: spec.Decoration.Extended,
69) !void {
70 try section.emit(allocator, .OpDecorate, .{
71 .target = target,
72 .decoration = decoration,
73 });
74}
75
76/// Decorate a result-id which is a member of some struct.
77pub fn decorateMember(
78 section: *Section,
79 allocator: Allocator,
80 structure_type: spec.IdRef,
81 member: u32,
82 decoration: spec.Decoration.Extended,
83) !void {
84 try section.emit(allocator, .OpMemberDecorate, .{
85 .structure_type = structure_type,
86 .member = member,
87 .decoration = decoration,
88 });
89}
90
91pub fn writeWord(section: *Section, word: Word) void {
92 section.instructions.appendAssumeCapacity(word);
93}
94
95pub fn writeWords(section: *Section, words: []const Word) void {
96 section.instructions.appendSliceAssumeCapacity(words);
97}
98
99fn writeDoubleWord(section: *Section, dword: DoubleWord) void {
100 section.writeWords(&.{
101 @truncate(Word, dword),
102 @truncate(Word, dword >> @bitSizeOf(Word)),
103 });
104}
105
106fn writeOperands(section: *Section, comptime Operands: type, operands: Operands) void {
107 const fields = switch (@typeInfo(Operands)) {
108 .Struct => |info| info.fields,
109 .Void => return,
110 else => unreachable,
111 };
112
113 inline for (fields) |field| {
114 section.writeOperand(field.field_type, @field(operands, field.name));
115 }
116}
117
118pub fn writeOperand(section: *Section, comptime Operand: type, operand: Operand) void {
119 switch (Operand) {
120 spec.IdResultType, spec.IdResult, spec.IdRef => section.writeWord(operand.id),
121
122 spec.LiteralInteger => section.writeWord(operand),
123
124 spec.LiteralString => section.writeString(operand),
125
126 spec.LiteralContextDependentNumber => section.writeContextDependentNumber(operand),
127
128 spec.LiteralExtInstInteger => section.writeWord(operand.inst),
129
130 // TODO: Where this type is used (OpSpecConstantOp) is currently not correct in the spec json,
131 // so it most likely needs to be altered into something that can actually describe the entire
132 // instruction in which it is used.
133 spec.LiteralSpecConstantOpInteger => section.writeWord(@enumToInt(operand.opcode)),
134
135 spec.PairLiteralIntegerIdRef => section.writeWords(&.{ operand.value, operand.label.id }),
136 spec.PairIdRefLiteralInteger => section.writeWords(&.{ operand.target.id, operand.member }),
137 spec.PairIdRefIdRef => section.writeWords(&.{ operand[0].id, operand[1].id }),
138
139 else => switch (@typeInfo(Operand)) {
140 .Enum => section.writeWord(@enumToInt(operand)),
141 .Optional => |info| if (operand) |child| {
142 section.writeOperand(info.child, child);
143 },
144 .Pointer => |info| {
145 std.debug.assert(info.size == .Slice); // Should be no other pointer types in the spec.
146 for (operand) |item| {
147 section.writeOperand(info.child, item);
148 }
149 },
150 .Struct => |info| {
151 if (info.layout == .Packed) {
152 section.writeWord(@bitCast(Word, operand));
153 } else {
154 section.writeExtendedMask(Operand, operand);
155 }
156 },
157 .Union => section.writeExtendedUnion(Operand, operand),
158 else => unreachable,
159 },
160 }
161}
162
163fn writeString(section: *Section, str: []const u8) void {
164 // TODO: Not actually sure whether this is correct for big-endian.
165 // See https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#Literal
166 const zero_terminated_len = str.len + 1;
167 var i: usize = 0;
168 while (i < zero_terminated_len) : (i += @sizeOf(Word)) {
169 var word: Word = 0;
170
171 var j: usize = 0;
172 while (j < @sizeOf(Word) and i + j < str.len) : (j += 1) {
173 word |= @as(Word, str[i + j]) << @intCast(Log2Word, j * std.meta.bitCount(u8));
174 }
175
176 section.instructions.appendAssumeCapacity(word);
177 }
178}
179
180fn writeContextDependentNumber(section: *Section, operand: spec.LiteralContextDependentNumber) void {
181 switch (operand) {
182 .int32 => |int| section.writeWord(@bitCast(Word, int)),
183 .uint32 => |int| section.writeWord(@bitCast(Word, int)),
184 .int64 => |int| section.writeDoubleWord(@bitCast(DoubleWord, int)),
185 .uint64 => |int| section.writeDoubleWord(@bitCast(DoubleWord, int)),
186 .float32 => |float| section.writeWord(@bitCast(Word, float)),
187 .float64 => |float| section.writeDoubleWord(@bitCast(DoubleWord, float)),
188 }
189}
190
191fn writeExtendedMask(section: *Section, comptime Operand: type, operand: Operand) void {
192 var mask: Word = 0;
193 inline for (@typeInfo(Operand).Struct.fields) |field, bit| {
194 switch (@typeInfo(field.field_type)) {
195 .Optional => if (@field(operand, field.name) != null) {
196 mask |= 1 << @intCast(u5, bit);
197 },
198 .Bool => if (@field(operand, field.name)) {
199 mask |= 1 << @intCast(u5, bit);
200 },
201 else => unreachable,
202 }
203 }
204
205 if (mask == 0) {
206 return;
207 }
208
209 section.writeWord(mask);
210
211 inline for (@typeInfo(Operand).Struct.fields) |field| {
212 switch (@typeInfo(field.field_type)) {
213 .Optional => |info| if (@field(operand, field.name)) |child| {
214 section.writeOperands(info.child, child);
215 },
216 .Bool => {},
217 else => unreachable,
218 }
219 }
220}
221
222fn writeExtendedUnion(section: *Section, comptime Operand: type, operand: Operand) void {
223 const tag = std.meta.activeTag(operand);
224 section.writeWord(@enumToInt(tag));
225
226 inline for (@typeInfo(Operand).Union.fields) |field| {
227 if (@field(Operand, field.name) == tag) {
228 section.writeOperands(field.field_type, @field(operand, field.name));
229 return;
230 }
231 }
232 unreachable;
233}
234
235fn instructionSize(comptime opcode: spec.Opcode, operands: opcode.Operands()) usize {
236 return 1 + operandsSize(opcode.Operands(), operands);
237}
238
239fn operandsSize(comptime Operands: type, operands: Operands) usize {
240 const fields = switch (@typeInfo(Operands)) {
241 .Struct => |info| info.fields,
242 .Void => return 0,
243 else => unreachable,
244 };
245
246 var total: usize = 0;
247 inline for (fields) |field| {
248 total += operandSize(field.field_type, @field(operands, field.name));
249 }
250
251 return total;
252}
253
254fn operandSize(comptime Operand: type, operand: Operand) usize {
255 return switch (Operand) {
256 spec.IdResultType,
257 spec.IdResult,
258 spec.IdRef,
259 spec.LiteralInteger,
260 spec.LiteralExtInstInteger,
261 => 1,
262
263 spec.LiteralString => std.math.divCeil(usize, operand.len + 1, @sizeOf(Word)) catch unreachable, // Add one for zero-terminator
264
265 spec.LiteralContextDependentNumber => switch (operand) {
266 .int32, .uint32, .float32 => @as(usize, 1),
267 .int64, .uint64, .float64 => @as(usize, 2),
268 },
269
270 // TODO: Where this type is used (OpSpecConstantOp) is currently not correct in the spec
271 // json, so it most likely needs to be altered into something that can actually
272 // describe the entire insturction in which it is used.
273 spec.LiteralSpecConstantOpInteger => 1,
274
275 spec.PairLiteralIntegerIdRef,
276 spec.PairIdRefLiteralInteger,
277 spec.PairIdRefIdRef,
278 => 2,
279
280 else => switch (@typeInfo(Operand)) {
281 .Enum => 1,
282 .Optional => |info| if (operand) |child| operandSize(info.child, child) else 0,
283 .Pointer => |info| blk: {
284 std.debug.assert(info.size == .Slice); // Should be no other pointer types in the spec.
285 var total: usize = 0;
286 for (operand) |item| {
287 total += operandSize(info.child, item);
288 }
289 break :blk total;
290 },
291 .Struct => |info| if (info.layout == .Packed) 1 else extendedMaskSize(Operand, operand),
292 .Union => extendedUnionSize(Operand, operand),
293 else => unreachable,
294 },
295 };
296}
297
298fn extendedMaskSize(comptime Operand: type, operand: Operand) usize {
299 var total: usize = 0;
300 var any_set = false;
301 inline for (@typeInfo(Operand).Struct.fields) |field| {
302 switch (@typeInfo(field.field_type)) {
303 .Optional => |info| if (@field(operand, field.name)) |child| {
304 total += operandsSize(info.child, child);
305 any_set = true;
306 },
307 .Bool => if (@field(operand, field.name)) {
308 any_set = true;
309 },
310 else => unreachable,
311 }
312 }
313 if (!any_set) {
314 return 0;
315 }
316 return total + 1; // Add one for the mask itself.
317}
318
319fn extendedUnionSize(comptime Operand: type, operand: Operand) usize {
320 const tag = std.meta.activeTag(operand);
321 inline for (@typeInfo(Operand).Union.fields) |field| {
322 if (@field(Operand, field.name) == tag) {
323 // Add one for the tag itself.
324 return 1 + operandsSize(field.field_type, @field(operand, field.name));
325 }
326 }
327 unreachable;
328}
329
330test "SPIR-V Section emit() - no operands" {
331 var section = Section{};
332 defer section.deinit(std.testing.allocator);
333
334 try section.emit(std.testing.allocator, .OpNop, {});
335
336 try testing.expect(section.instructions.items[0] == (@as(Word, 1) << 16) | @enumToInt(Opcode.OpNop));
337}
338
339test "SPIR-V Section emit() - simple" {
340 var section = Section{};
341 defer section.deinit(std.testing.allocator);
342
343 try section.emit(std.testing.allocator, .OpUndef, .{
344 .id_result_type = .{ .id = 0 },
345 .id_result = .{ .id = 1 },
346 });
347
348 try testing.expectEqualSlices(Word, &.{
349 (@as(Word, 3) << 16) | @enumToInt(Opcode.OpUndef),
350 0,
351 1,
352 }, section.instructions.items);
353}
354
355test "SPIR-V Section emit() - string" {
356 var section = Section{};
357 defer section.deinit(std.testing.allocator);
358
359 try section.emit(std.testing.allocator, .OpSource, .{
360 .source_language = .Unknown,
361 .version = 123,
362 .file = .{ .id = 456 },
363 .source = "pub fn main() void {}",
364 });
365
366 try testing.expectEqualSlices(Word, &.{
367 (@as(Word, 10) << 16) | @enumToInt(Opcode.OpSource),
368 @enumToInt(spec.SourceLanguage.Unknown),
369 123,
370 456,
371 std.mem.bytesToValue(Word, "pub "),
372 std.mem.bytesToValue(Word, "fn m"),
373 std.mem.bytesToValue(Word, "ain("),
374 std.mem.bytesToValue(Word, ") vo"),
375 std.mem.bytesToValue(Word, "id {"),
376 std.mem.bytesToValue(Word, "}\x00\x00\x00"),
377 }, section.instructions.items);
378}
379
380test "SPIR-V Section emit()- extended mask" {
381 var section = Section{};
382 defer section.deinit(std.testing.allocator);
383
384 try section.emit(std.testing.allocator, .OpLoopMerge, .{
385 .merge_block = .{ .id = 10 },
386 .continue_target = .{ .id = 20 },
387 .loop_control = .{
388 .Unroll = true,
389 .DependencyLength = .{
390 .literal_integer = 2,
391 },
392 },
393 });
394
395 try testing.expectEqualSlices(Word, &.{
396 (@as(Word, 5) << 16) | @enumToInt(Opcode.OpLoopMerge),
397 10,
398 20,
399 @bitCast(Word, spec.LoopControl{ .Unroll = true, .DependencyLength = true }),
400 2,
401 }, section.instructions.items);
402}
403
404test "SPIR-V Section emit() - extended union" {
405 var section = Section{};
406 defer section.deinit(std.testing.allocator);
407
408 try section.emit(std.testing.allocator, .OpExecutionMode, .{
409 .entry_point = .{ .id = 888 },
410 .mode = .{
411 .LocalSize = .{ .x_size = 4, .y_size = 8, .z_size = 16 },
412 },
413 });
414
415 try testing.expectEqualSlices(Word, &.{
416 (@as(Word, 6) << 16) | @enumToInt(Opcode.OpExecutionMode),
417 888,
418 @enumToInt(spec.ExecutionMode.LocalSize),
419 4,
420 8,
421 16,
422 }, section.instructions.items);
423}
src/codegen/spirv/spec.zig+985-68
...@@ -1,8 +1,46 @@...@@ -1,8 +1,46 @@
1//! This file is auto-generated by tools/gen_spirv_spec.zig.1//! This file is auto-generated by tools/gen_spirv_spec.zig.
22
3const Version = @import("std").builtin.Version;3const Version = @import("std").builtin.Version;
4
5pub const Word = u32;
6pub const IdResultType = struct {
7 id: Word,
8 pub fn toRef(self: IdResultType) IdRef {
9 return .{ .id = self.id };
10 }
11};
12pub const IdResult = struct {
13 id: Word,
14 pub fn toRef(self: IdResult) IdRef {
15 return .{ .id = self.id };
16 }
17 pub fn toResultType(self: IdResult) IdResultType {
18 return .{ .id = self.id };
19 }
20};
21pub const IdRef = struct { id: Word };
22
23pub const IdMemorySemantics = IdRef;
24pub const IdScope = IdRef;
25
26pub const LiteralInteger = Word;
27pub const LiteralString = []const u8;
28pub const LiteralContextDependentNumber = union(enum) {
29 int32: i32,
30 uint32: u32,
31 int64: i64,
32 uint64: u64,
33 float32: f32,
34 float64: f64,
35};
36pub const LiteralExtInstInteger = struct { inst: Word };
37pub const LiteralSpecConstantOpInteger = struct { opcode: Opcode };
38pub const PairLiteralIntegerIdRef = struct { value: LiteralInteger, label: IdRef };
39pub const PairIdRefLiteralInteger = struct { target: IdRef, member: LiteralInteger };
40pub const PairIdRefIdRef = [2]IdRef;
41
4pub const version = Version{ .major = 1, .minor = 5, .patch = 4 };42pub const version = Version{ .major = 1, .minor = 5, .patch = 4 };
5pub const magic_number: u32 = 0x07230203;43pub const magic_number: Word = 0x07230203;
6pub const Opcode = enum(u16) {44pub const Opcode = enum(u16) {
7 OpNop = 0,45 OpNop = 0,
8 OpUndef = 1,46 OpUndef = 1,
...@@ -381,11 +419,11 @@ pub const Opcode = enum(u16) {...@@ -381,11 +419,11 @@ pub const Opcode = enum(u16) {
381 OpImageSampleFootprintNV = 5283,419 OpImageSampleFootprintNV = 5283,
382 OpGroupNonUniformPartitionNV = 5296,420 OpGroupNonUniformPartitionNV = 5296,
383 OpWritePackedPrimitiveIndices4x8NV = 5299,421 OpWritePackedPrimitiveIndices4x8NV = 5299,
384 OpReportIntersectionNV = 5334,422 OpReportIntersectionKHR = 5334,
385 OpIgnoreIntersectionNV = 5335,423 OpIgnoreIntersectionNV = 5335,
386 OpTerminateRayNV = 5336,424 OpTerminateRayNV = 5336,
387 OpTraceNV = 5337,425 OpTraceNV = 5337,
388 OpTypeAccelerationStructureNV = 5341,426 OpTypeAccelerationStructureKHR = 5341,
389 OpExecuteCallableNV = 5344,427 OpExecuteCallableNV = 5344,
390 OpTypeCooperativeMatrixNV = 5358,428 OpTypeCooperativeMatrixNV = 5358,
391 OpCooperativeMatrixLoadNV = 5359,429 OpCooperativeMatrixLoadNV = 5359,
...@@ -580,10 +618,592 @@ pub const Opcode = enum(u16) {...@@ -580,10 +618,592 @@ pub const Opcode = enum(u16) {
580 OpTypeStructContinuedINTEL = 6090,618 OpTypeStructContinuedINTEL = 6090,
581 OpConstantCompositeContinuedINTEL = 6091,619 OpConstantCompositeContinuedINTEL = 6091,
582 OpSpecConstantCompositeContinuedINTEL = 6092,620 OpSpecConstantCompositeContinuedINTEL = 6092,
583 _,
584621
585 const OpReportIntersectionKHR: Opcode = .OpReportIntersectionNV;622 pub const OpReportIntersectionNV = Opcode.OpReportIntersectionKHR;
586 const OpTypeAccelerationStructureKHR: Opcode = .OpTypeAccelerationStructureNV;623 pub const OpTypeAccelerationStructureNV = Opcode.OpTypeAccelerationStructureKHR;
624 pub const OpDecorateStringGOOGLE = Opcode.OpDecorateString;
625 pub const OpMemberDecorateStringGOOGLE = Opcode.OpMemberDecorateString;
626
627 pub fn Operands(comptime self: Opcode) type {
628 return switch (self) {
629 .OpNop => void,
630 .OpUndef => struct { id_result_type: IdResultType, id_result: IdResult },
631 .OpSourceContinued => struct { continued_source: LiteralString },
632 .OpSource => struct { source_language: SourceLanguage, version: LiteralInteger, file: ?IdRef = null, source: ?LiteralString = null },
633 .OpSourceExtension => struct { extension: LiteralString },
634 .OpName => struct { target: IdRef, name: LiteralString },
635 .OpMemberName => struct { type: IdRef, member: LiteralInteger, name: LiteralString },
636 .OpString => struct { id_result: IdResult, string: LiteralString },
637 .OpLine => struct { file: IdRef, line: LiteralInteger, column: LiteralInteger },
638 .OpExtension => struct { name: LiteralString },
639 .OpExtInstImport => struct { id_result: IdResult, name: LiteralString },
640 .OpExtInst => struct { id_result_type: IdResultType, id_result: IdResult, set: IdRef, instruction: LiteralExtInstInteger, id_ref_4: []const IdRef = &.{} },
641 .OpMemoryModel => struct { addressing_model: AddressingModel, memory_model: MemoryModel },
642 .OpEntryPoint => struct { execution_model: ExecutionModel, entry_point: IdRef, name: LiteralString, interface: []const IdRef = &.{} },
643 .OpExecutionMode => struct { entry_point: IdRef, mode: ExecutionMode.Extended },
644 .OpCapability => struct { capability: Capability },
645 .OpTypeVoid => struct { id_result: IdResult },
646 .OpTypeBool => struct { id_result: IdResult },
647 .OpTypeInt => struct { id_result: IdResult, width: LiteralInteger, signedness: LiteralInteger },
648 .OpTypeFloat => struct { id_result: IdResult, width: LiteralInteger },
649 .OpTypeVector => struct { id_result: IdResult, component_type: IdRef, component_count: LiteralInteger },
650 .OpTypeMatrix => struct { id_result: IdResult, column_type: IdRef, column_count: LiteralInteger },
651 .OpTypeImage => struct { id_result: IdResult, sampled_type: IdRef, dim: Dim, depth: LiteralInteger, arrayed: LiteralInteger, ms: LiteralInteger, sampled: LiteralInteger, image_format: ImageFormat, access_qualifier: ?AccessQualifier = null },
652 .OpTypeSampler => struct { id_result: IdResult },
653 .OpTypeSampledImage => struct { id_result: IdResult, image_type: IdRef },
654 .OpTypeArray => struct { id_result: IdResult, element_type: IdRef, length: IdRef },
655 .OpTypeRuntimeArray => struct { id_result: IdResult, element_type: IdRef },
656 .OpTypeStruct => struct { id_result: IdResult, id_ref: []const IdRef = &.{} },
657 .OpTypeOpaque => struct { id_result: IdResult, literal_string: LiteralString },
658 .OpTypePointer => struct { id_result: IdResult, storage_class: StorageClass, type: IdRef },
659 .OpTypeFunction => struct { id_result: IdResult, return_type: IdRef, id_ref_2: []const IdRef = &.{} },
660 .OpTypeEvent => struct { id_result: IdResult },
661 .OpTypeDeviceEvent => struct { id_result: IdResult },
662 .OpTypeReserveId => struct { id_result: IdResult },
663 .OpTypeQueue => struct { id_result: IdResult },
664 .OpTypePipe => struct { id_result: IdResult, qualifier: AccessQualifier },
665 .OpTypeForwardPointer => struct { pointer_type: IdRef, storage_class: StorageClass },
666 .OpConstantTrue => struct { id_result_type: IdResultType, id_result: IdResult },
667 .OpConstantFalse => struct { id_result_type: IdResultType, id_result: IdResult },
668 .OpConstant => struct { id_result_type: IdResultType, id_result: IdResult, value: LiteralContextDependentNumber },
669 .OpConstantComposite => struct { id_result_type: IdResultType, id_result: IdResult, constituents: []const IdRef = &.{} },
670 .OpConstantSampler => struct { id_result_type: IdResultType, id_result: IdResult, sampler_addressing_mode: SamplerAddressingMode, param: LiteralInteger, sampler_filter_mode: SamplerFilterMode },
671 .OpConstantNull => struct { id_result_type: IdResultType, id_result: IdResult },
672 .OpSpecConstantTrue => struct { id_result_type: IdResultType, id_result: IdResult },
673 .OpSpecConstantFalse => struct { id_result_type: IdResultType, id_result: IdResult },
674 .OpSpecConstant => struct { id_result_type: IdResultType, id_result: IdResult, value: LiteralContextDependentNumber },
675 .OpSpecConstantComposite => struct { id_result_type: IdResultType, id_result: IdResult, constituents: []const IdRef = &.{} },
676 .OpSpecConstantOp => struct { id_result_type: IdResultType, id_result: IdResult, opcode: LiteralSpecConstantOpInteger },
677 .OpFunction => struct { id_result_type: IdResultType, id_result: IdResult, function_control: FunctionControl, function_type: IdRef },
678 .OpFunctionParameter => struct { id_result_type: IdResultType, id_result: IdResult },
679 .OpFunctionEnd => void,
680 .OpFunctionCall => struct { id_result_type: IdResultType, id_result: IdResult, function: IdRef, id_ref_3: []const IdRef = &.{} },
681 .OpVariable => struct { id_result_type: IdResultType, id_result: IdResult, storage_class: StorageClass, initializer: ?IdRef = null },
682 .OpImageTexelPointer => struct { id_result_type: IdResultType, id_result: IdResult, image: IdRef, coordinate: IdRef, sample: IdRef },
683 .OpLoad => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef, memory_access: ?MemoryAccess.Extended = null },
684 .OpStore => struct { pointer: IdRef, object: IdRef, memory_access: ?MemoryAccess.Extended = null },
685 .OpCopyMemory => struct { target: IdRef, source: IdRef, memory_access_2: ?MemoryAccess.Extended = null, memory_access_3: ?MemoryAccess.Extended = null },
686 .OpCopyMemorySized => struct { target: IdRef, source: IdRef, size: IdRef, memory_access_3: ?MemoryAccess.Extended = null, memory_access_4: ?MemoryAccess.Extended = null },
687 .OpAccessChain => struct { id_result_type: IdResultType, id_result: IdResult, base: IdRef, indexes: []const IdRef = &.{} },
688 .OpInBoundsAccessChain => struct { id_result_type: IdResultType, id_result: IdResult, base: IdRef, indexes: []const IdRef = &.{} },
689 .OpPtrAccessChain => struct { id_result_type: IdResultType, id_result: IdResult, base: IdRef, element: IdRef, indexes: []const IdRef = &.{} },
690 .OpArrayLength => struct { id_result_type: IdResultType, id_result: IdResult, structure: IdRef, array_member: LiteralInteger },
691 .OpGenericPtrMemSemantics => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef },
692 .OpInBoundsPtrAccessChain => struct { id_result_type: IdResultType, id_result: IdResult, base: IdRef, element: IdRef, indexes: []const IdRef = &.{} },
693 .OpDecorate => struct { target: IdRef, decoration: Decoration.Extended },
694 .OpMemberDecorate => struct { structure_type: IdRef, member: LiteralInteger, decoration: Decoration.Extended },
695 .OpDecorationGroup => struct { id_result: IdResult },
696 .OpGroupDecorate => struct { decoration_group: IdRef, targets: []const IdRef = &.{} },
697 .OpGroupMemberDecorate => struct { decoration_group: IdRef, targets: []const PairIdRefLiteralInteger = &.{} },
698 .OpVectorExtractDynamic => struct { id_result_type: IdResultType, id_result: IdResult, vector: IdRef, index: IdRef },
699 .OpVectorInsertDynamic => struct { id_result_type: IdResultType, id_result: IdResult, vector: IdRef, component: IdRef, index: IdRef },
700 .OpVectorShuffle => struct { id_result_type: IdResultType, id_result: IdResult, vector_1: IdRef, vector_2: IdRef, components: []const LiteralInteger = &.{} },
701 .OpCompositeConstruct => struct { id_result_type: IdResultType, id_result: IdResult, constituents: []const IdRef = &.{} },
702 .OpCompositeExtract => struct { id_result_type: IdResultType, id_result: IdResult, composite: IdRef, indexes: []const LiteralInteger = &.{} },
703 .OpCompositeInsert => struct { id_result_type: IdResultType, id_result: IdResult, object: IdRef, composite: IdRef, indexes: []const LiteralInteger = &.{} },
704 .OpCopyObject => struct { id_result_type: IdResultType, id_result: IdResult, operand: IdRef },
705 .OpTranspose => struct { id_result_type: IdResultType, id_result: IdResult, matrix: IdRef },
706 .OpSampledImage => struct { id_result_type: IdResultType, id_result: IdResult, image: IdRef, sampler: IdRef },
707 .OpImageSampleImplicitLod => struct { id_result_type: IdResultType, id_result: IdResult, sampled_image: IdRef, coordinate: IdRef, image_operands: ?ImageOperands.Extended = null },
708 .OpImageSampleExplicitLod => struct { id_result_type: IdResultType, id_result: IdResult, sampled_image: IdRef, coordinate: IdRef, image_operands: ImageOperands.Extended },
709 .OpImageSampleDrefImplicitLod => struct { id_result_type: IdResultType, id_result: IdResult, sampled_image: IdRef, coordinate: IdRef, d_ref: IdRef, image_operands: ?ImageOperands.Extended = null },
710 .OpImageSampleDrefExplicitLod => struct { id_result_type: IdResultType, id_result: IdResult, sampled_image: IdRef, coordinate: IdRef, d_ref: IdRef, image_operands: ImageOperands.Extended },
711 .OpImageSampleProjImplicitLod => struct { id_result_type: IdResultType, id_result: IdResult, sampled_image: IdRef, coordinate: IdRef, image_operands: ?ImageOperands.Extended = null },
712 .OpImageSampleProjExplicitLod => struct { id_result_type: IdResultType, id_result: IdResult, sampled_image: IdRef, coordinate: IdRef, image_operands: ImageOperands.Extended },
713 .OpImageSampleProjDrefImplicitLod => struct { id_result_type: IdResultType, id_result: IdResult, sampled_image: IdRef, coordinate: IdRef, d_ref: IdRef, image_operands: ?ImageOperands.Extended = null },
714 .OpImageSampleProjDrefExplicitLod => struct { id_result_type: IdResultType, id_result: IdResult, sampled_image: IdRef, coordinate: IdRef, d_ref: IdRef, image_operands: ImageOperands.Extended },
715 .OpImageFetch => struct { id_result_type: IdResultType, id_result: IdResult, image: IdRef, coordinate: IdRef, image_operands: ?ImageOperands.Extended = null },
716 .OpImageGather => struct { id_result_type: IdResultType, id_result: IdResult, sampled_image: IdRef, coordinate: IdRef, component: IdRef, image_operands: ?ImageOperands.Extended = null },
717 .OpImageDrefGather => struct { id_result_type: IdResultType, id_result: IdResult, sampled_image: IdRef, coordinate: IdRef, d_ref: IdRef, image_operands: ?ImageOperands.Extended = null },
718 .OpImageRead => struct { id_result_type: IdResultType, id_result: IdResult, image: IdRef, coordinate: IdRef, image_operands: ?ImageOperands.Extended = null },
719 .OpImageWrite => struct { image: IdRef, coordinate: IdRef, texel: IdRef, image_operands: ?ImageOperands.Extended = null },
720 .OpImage => struct { id_result_type: IdResultType, id_result: IdResult, sampled_image: IdRef },
721 .OpImageQueryFormat => struct { id_result_type: IdResultType, id_result: IdResult, image: IdRef },
722 .OpImageQueryOrder => struct { id_result_type: IdResultType, id_result: IdResult, image: IdRef },
723 .OpImageQuerySizeLod => struct { id_result_type: IdResultType, id_result: IdResult, image: IdRef, level_of_detail: IdRef },
724 .OpImageQuerySize => struct { id_result_type: IdResultType, id_result: IdResult, image: IdRef },
725 .OpImageQueryLod => struct { id_result_type: IdResultType, id_result: IdResult, sampled_image: IdRef, coordinate: IdRef },
726 .OpImageQueryLevels => struct { id_result_type: IdResultType, id_result: IdResult, image: IdRef },
727 .OpImageQuerySamples => struct { id_result_type: IdResultType, id_result: IdResult, image: IdRef },
728 .OpConvertFToU => struct { id_result_type: IdResultType, id_result: IdResult, float_value: IdRef },
729 .OpConvertFToS => struct { id_result_type: IdResultType, id_result: IdResult, float_value: IdRef },
730 .OpConvertSToF => struct { id_result_type: IdResultType, id_result: IdResult, signed_value: IdRef },
731 .OpConvertUToF => struct { id_result_type: IdResultType, id_result: IdResult, unsigned_value: IdRef },
732 .OpUConvert => struct { id_result_type: IdResultType, id_result: IdResult, unsigned_value: IdRef },
733 .OpSConvert => struct { id_result_type: IdResultType, id_result: IdResult, signed_value: IdRef },
734 .OpFConvert => struct { id_result_type: IdResultType, id_result: IdResult, float_value: IdRef },
735 .OpQuantizeToF16 => struct { id_result_type: IdResultType, id_result: IdResult, value: IdRef },
736 .OpConvertPtrToU => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef },
737 .OpSatConvertSToU => struct { id_result_type: IdResultType, id_result: IdResult, signed_value: IdRef },
738 .OpSatConvertUToS => struct { id_result_type: IdResultType, id_result: IdResult, unsigned_value: IdRef },
739 .OpConvertUToPtr => struct { id_result_type: IdResultType, id_result: IdResult, integer_value: IdRef },
740 .OpPtrCastToGeneric => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef },
741 .OpGenericCastToPtr => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef },
742 .OpGenericCastToPtrExplicit => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef, storage: StorageClass },
743 .OpBitcast => struct { id_result_type: IdResultType, id_result: IdResult, operand: IdRef },
744 .OpSNegate => struct { id_result_type: IdResultType, id_result: IdResult, operand: IdRef },
745 .OpFNegate => struct { id_result_type: IdResultType, id_result: IdResult, operand: IdRef },
746 .OpIAdd => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
747 .OpFAdd => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
748 .OpISub => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
749 .OpFSub => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
750 .OpIMul => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
751 .OpFMul => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
752 .OpUDiv => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
753 .OpSDiv => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
754 .OpFDiv => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
755 .OpUMod => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
756 .OpSRem => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
757 .OpSMod => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
758 .OpFRem => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
759 .OpFMod => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
760 .OpVectorTimesScalar => struct { id_result_type: IdResultType, id_result: IdResult, vector: IdRef, scalar: IdRef },
761 .OpMatrixTimesScalar => struct { id_result_type: IdResultType, id_result: IdResult, matrix: IdRef, scalar: IdRef },
762 .OpVectorTimesMatrix => struct { id_result_type: IdResultType, id_result: IdResult, vector: IdRef, matrix: IdRef },
763 .OpMatrixTimesVector => struct { id_result_type: IdResultType, id_result: IdResult, matrix: IdRef, vector: IdRef },
764 .OpMatrixTimesMatrix => struct { id_result_type: IdResultType, id_result: IdResult, leftmatrix: IdRef, rightmatrix: IdRef },
765 .OpOuterProduct => struct { id_result_type: IdResultType, id_result: IdResult, vector_1: IdRef, vector_2: IdRef },
766 .OpDot => struct { id_result_type: IdResultType, id_result: IdResult, vector_1: IdRef, vector_2: IdRef },
767 .OpIAddCarry => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
768 .OpISubBorrow => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
769 .OpUMulExtended => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
770 .OpSMulExtended => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
771 .OpAny => struct { id_result_type: IdResultType, id_result: IdResult, vector: IdRef },
772 .OpAll => struct { id_result_type: IdResultType, id_result: IdResult, vector: IdRef },
773 .OpIsNan => struct { id_result_type: IdResultType, id_result: IdResult, x: IdRef },
774 .OpIsInf => struct { id_result_type: IdResultType, id_result: IdResult, x: IdRef },
775 .OpIsFinite => struct { id_result_type: IdResultType, id_result: IdResult, x: IdRef },
776 .OpIsNormal => struct { id_result_type: IdResultType, id_result: IdResult, x: IdRef },
777 .OpSignBitSet => struct { id_result_type: IdResultType, id_result: IdResult, x: IdRef },
778 .OpLessOrGreater => struct { id_result_type: IdResultType, id_result: IdResult, x: IdRef, y: IdRef },
779 .OpOrdered => struct { id_result_type: IdResultType, id_result: IdResult, x: IdRef, y: IdRef },
780 .OpUnordered => struct { id_result_type: IdResultType, id_result: IdResult, x: IdRef, y: IdRef },
781 .OpLogicalEqual => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
782 .OpLogicalNotEqual => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
783 .OpLogicalOr => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
784 .OpLogicalAnd => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
785 .OpLogicalNot => struct { id_result_type: IdResultType, id_result: IdResult, operand: IdRef },
786 .OpSelect => struct { id_result_type: IdResultType, id_result: IdResult, condition: IdRef, object_1: IdRef, object_2: IdRef },
787 .OpIEqual => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
788 .OpINotEqual => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
789 .OpUGreaterThan => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
790 .OpSGreaterThan => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
791 .OpUGreaterThanEqual => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
792 .OpSGreaterThanEqual => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
793 .OpULessThan => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
794 .OpSLessThan => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
795 .OpULessThanEqual => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
796 .OpSLessThanEqual => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
797 .OpFOrdEqual => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
798 .OpFUnordEqual => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
799 .OpFOrdNotEqual => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
800 .OpFUnordNotEqual => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
801 .OpFOrdLessThan => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
802 .OpFUnordLessThan => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
803 .OpFOrdGreaterThan => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
804 .OpFUnordGreaterThan => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
805 .OpFOrdLessThanEqual => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
806 .OpFUnordLessThanEqual => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
807 .OpFOrdGreaterThanEqual => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
808 .OpFUnordGreaterThanEqual => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
809 .OpShiftRightLogical => struct { id_result_type: IdResultType, id_result: IdResult, base: IdRef, shift: IdRef },
810 .OpShiftRightArithmetic => struct { id_result_type: IdResultType, id_result: IdResult, base: IdRef, shift: IdRef },
811 .OpShiftLeftLogical => struct { id_result_type: IdResultType, id_result: IdResult, base: IdRef, shift: IdRef },
812 .OpBitwiseOr => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
813 .OpBitwiseXor => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
814 .OpBitwiseAnd => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
815 .OpNot => struct { id_result_type: IdResultType, id_result: IdResult, operand: IdRef },
816 .OpBitFieldInsert => struct { id_result_type: IdResultType, id_result: IdResult, base: IdRef, insert: IdRef, offset: IdRef, count: IdRef },
817 .OpBitFieldSExtract => struct { id_result_type: IdResultType, id_result: IdResult, base: IdRef, offset: IdRef, count: IdRef },
818 .OpBitFieldUExtract => struct { id_result_type: IdResultType, id_result: IdResult, base: IdRef, offset: IdRef, count: IdRef },
819 .OpBitReverse => struct { id_result_type: IdResultType, id_result: IdResult, base: IdRef },
820 .OpBitCount => struct { id_result_type: IdResultType, id_result: IdResult, base: IdRef },
821 .OpDPdx => struct { id_result_type: IdResultType, id_result: IdResult, p: IdRef },
822 .OpDPdy => struct { id_result_type: IdResultType, id_result: IdResult, p: IdRef },
823 .OpFwidth => struct { id_result_type: IdResultType, id_result: IdResult, p: IdRef },
824 .OpDPdxFine => struct { id_result_type: IdResultType, id_result: IdResult, p: IdRef },
825 .OpDPdyFine => struct { id_result_type: IdResultType, id_result: IdResult, p: IdRef },
826 .OpFwidthFine => struct { id_result_type: IdResultType, id_result: IdResult, p: IdRef },
827 .OpDPdxCoarse => struct { id_result_type: IdResultType, id_result: IdResult, p: IdRef },
828 .OpDPdyCoarse => struct { id_result_type: IdResultType, id_result: IdResult, p: IdRef },
829 .OpFwidthCoarse => struct { id_result_type: IdResultType, id_result: IdResult, p: IdRef },
830 .OpEmitVertex => void,
831 .OpEndPrimitive => void,
832 .OpEmitStreamVertex => struct { stream: IdRef },
833 .OpEndStreamPrimitive => struct { stream: IdRef },
834 .OpControlBarrier => struct { execution: IdScope, memory: IdScope, semantics: IdMemorySemantics },
835 .OpMemoryBarrier => struct { memory: IdScope, semantics: IdMemorySemantics },
836 .OpAtomicLoad => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef, memory: IdScope, semantics: IdMemorySemantics },
837 .OpAtomicStore => struct { pointer: IdRef, memory: IdScope, semantics: IdMemorySemantics, value: IdRef },
838 .OpAtomicExchange => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef, memory: IdScope, semantics: IdMemorySemantics, value: IdRef },
839 .OpAtomicCompareExchange => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef, memory: IdScope, equal: IdMemorySemantics, unequal: IdMemorySemantics, value: IdRef, comparator: IdRef },
840 .OpAtomicCompareExchangeWeak => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef, memory: IdScope, equal: IdMemorySemantics, unequal: IdMemorySemantics, value: IdRef, comparator: IdRef },
841 .OpAtomicIIncrement => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef, memory: IdScope, semantics: IdMemorySemantics },
842 .OpAtomicIDecrement => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef, memory: IdScope, semantics: IdMemorySemantics },
843 .OpAtomicIAdd => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef, memory: IdScope, semantics: IdMemorySemantics, value: IdRef },
844 .OpAtomicISub => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef, memory: IdScope, semantics: IdMemorySemantics, value: IdRef },
845 .OpAtomicSMin => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef, memory: IdScope, semantics: IdMemorySemantics, value: IdRef },
846 .OpAtomicUMin => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef, memory: IdScope, semantics: IdMemorySemantics, value: IdRef },
847 .OpAtomicSMax => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef, memory: IdScope, semantics: IdMemorySemantics, value: IdRef },
848 .OpAtomicUMax => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef, memory: IdScope, semantics: IdMemorySemantics, value: IdRef },
849 .OpAtomicAnd => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef, memory: IdScope, semantics: IdMemorySemantics, value: IdRef },
850 .OpAtomicOr => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef, memory: IdScope, semantics: IdMemorySemantics, value: IdRef },
851 .OpAtomicXor => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef, memory: IdScope, semantics: IdMemorySemantics, value: IdRef },
852 .OpPhi => struct { id_result_type: IdResultType, id_result: IdResult, pair_id_ref_id_ref: []const PairIdRefIdRef = &.{} },
853 .OpLoopMerge => struct { merge_block: IdRef, continue_target: IdRef, loop_control: LoopControl.Extended },
854 .OpSelectionMerge => struct { merge_block: IdRef, selection_control: SelectionControl },
855 .OpLabel => struct { id_result: IdResult },
856 .OpBranch => struct { target_label: IdRef },
857 .OpBranchConditional => struct { condition: IdRef, true_label: IdRef, false_label: IdRef, branch_weights: []const LiteralInteger = &.{} },
858 .OpSwitch => struct { selector: IdRef, default: IdRef, target: []const PairLiteralIntegerIdRef = &.{} },
859 .OpKill => void,
860 .OpReturn => void,
861 .OpReturnValue => struct { value: IdRef },
862 .OpUnreachable => void,
863 .OpLifetimeStart => struct { pointer: IdRef, size: LiteralInteger },
864 .OpLifetimeStop => struct { pointer: IdRef, size: LiteralInteger },
865 .OpGroupAsyncCopy => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, destination: IdRef, source: IdRef, num_elements: IdRef, stride: IdRef, event: IdRef },
866 .OpGroupWaitEvents => struct { execution: IdScope, num_events: IdRef, events_list: IdRef },
867 .OpGroupAll => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, predicate: IdRef },
868 .OpGroupAny => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, predicate: IdRef },
869 .OpGroupBroadcast => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, value: IdRef, localid: IdRef },
870 .OpGroupIAdd => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, x: IdRef },
871 .OpGroupFAdd => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, x: IdRef },
872 .OpGroupFMin => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, x: IdRef },
873 .OpGroupUMin => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, x: IdRef },
874 .OpGroupSMin => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, x: IdRef },
875 .OpGroupFMax => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, x: IdRef },
876 .OpGroupUMax => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, x: IdRef },
877 .OpGroupSMax => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, x: IdRef },
878 .OpReadPipe => struct { id_result_type: IdResultType, id_result: IdResult, pipe: IdRef, pointer: IdRef, packet_size: IdRef, packet_alignment: IdRef },
879 .OpWritePipe => struct { id_result_type: IdResultType, id_result: IdResult, pipe: IdRef, pointer: IdRef, packet_size: IdRef, packet_alignment: IdRef },
880 .OpReservedReadPipe => struct { id_result_type: IdResultType, id_result: IdResult, pipe: IdRef, reserve_id: IdRef, index: IdRef, pointer: IdRef, packet_size: IdRef, packet_alignment: IdRef },
881 .OpReservedWritePipe => struct { id_result_type: IdResultType, id_result: IdResult, pipe: IdRef, reserve_id: IdRef, index: IdRef, pointer: IdRef, packet_size: IdRef, packet_alignment: IdRef },
882 .OpReserveReadPipePackets => struct { id_result_type: IdResultType, id_result: IdResult, pipe: IdRef, num_packets: IdRef, packet_size: IdRef, packet_alignment: IdRef },
883 .OpReserveWritePipePackets => struct { id_result_type: IdResultType, id_result: IdResult, pipe: IdRef, num_packets: IdRef, packet_size: IdRef, packet_alignment: IdRef },
884 .OpCommitReadPipe => struct { pipe: IdRef, reserve_id: IdRef, packet_size: IdRef, packet_alignment: IdRef },
885 .OpCommitWritePipe => struct { pipe: IdRef, reserve_id: IdRef, packet_size: IdRef, packet_alignment: IdRef },
886 .OpIsValidReserveId => struct { id_result_type: IdResultType, id_result: IdResult, reserve_id: IdRef },
887 .OpGetNumPipePackets => struct { id_result_type: IdResultType, id_result: IdResult, pipe: IdRef, packet_size: IdRef, packet_alignment: IdRef },
888 .OpGetMaxPipePackets => struct { id_result_type: IdResultType, id_result: IdResult, pipe: IdRef, packet_size: IdRef, packet_alignment: IdRef },
889 .OpGroupReserveReadPipePackets => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, pipe: IdRef, num_packets: IdRef, packet_size: IdRef, packet_alignment: IdRef },
890 .OpGroupReserveWritePipePackets => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, pipe: IdRef, num_packets: IdRef, packet_size: IdRef, packet_alignment: IdRef },
891 .OpGroupCommitReadPipe => struct { execution: IdScope, pipe: IdRef, reserve_id: IdRef, packet_size: IdRef, packet_alignment: IdRef },
892 .OpGroupCommitWritePipe => struct { execution: IdScope, pipe: IdRef, reserve_id: IdRef, packet_size: IdRef, packet_alignment: IdRef },
893 .OpEnqueueMarker => struct { id_result_type: IdResultType, id_result: IdResult, queue: IdRef, num_events: IdRef, wait_events: IdRef, ret_event: IdRef },
894 .OpEnqueueKernel => struct { id_result_type: IdResultType, id_result: IdResult, queue: IdRef, flags: IdRef, nd_range: IdRef, num_events: IdRef, wait_events: IdRef, ret_event: IdRef, invoke: IdRef, param: IdRef, param_size: IdRef, param_align: IdRef, local_size: []const IdRef = &.{} },
895 .OpGetKernelNDrangeSubGroupCount => struct { id_result_type: IdResultType, id_result: IdResult, nd_range: IdRef, invoke: IdRef, param: IdRef, param_size: IdRef, param_align: IdRef },
896 .OpGetKernelNDrangeMaxSubGroupSize => struct { id_result_type: IdResultType, id_result: IdResult, nd_range: IdRef, invoke: IdRef, param: IdRef, param_size: IdRef, param_align: IdRef },
897 .OpGetKernelWorkGroupSize => struct { id_result_type: IdResultType, id_result: IdResult, invoke: IdRef, param: IdRef, param_size: IdRef, param_align: IdRef },
898 .OpGetKernelPreferredWorkGroupSizeMultiple => struct { id_result_type: IdResultType, id_result: IdResult, invoke: IdRef, param: IdRef, param_size: IdRef, param_align: IdRef },
899 .OpRetainEvent => struct { event: IdRef },
900 .OpReleaseEvent => struct { event: IdRef },
901 .OpCreateUserEvent => struct { id_result_type: IdResultType, id_result: IdResult },
902 .OpIsValidEvent => struct { id_result_type: IdResultType, id_result: IdResult, event: IdRef },
903 .OpSetUserEventStatus => struct { event: IdRef, status: IdRef },
904 .OpCaptureEventProfilingInfo => struct { event: IdRef, profiling_info: IdRef, value: IdRef },
905 .OpGetDefaultQueue => struct { id_result_type: IdResultType, id_result: IdResult },
906 .OpBuildNDRange => struct { id_result_type: IdResultType, id_result: IdResult, globalworksize: IdRef, localworksize: IdRef, globalworkoffset: IdRef },
907 .OpImageSparseSampleImplicitLod => struct { id_result_type: IdResultType, id_result: IdResult, sampled_image: IdRef, coordinate: IdRef, image_operands: ?ImageOperands.Extended = null },
908 .OpImageSparseSampleExplicitLod => struct { id_result_type: IdResultType, id_result: IdResult, sampled_image: IdRef, coordinate: IdRef, image_operands: ImageOperands.Extended },
909 .OpImageSparseSampleDrefImplicitLod => struct { id_result_type: IdResultType, id_result: IdResult, sampled_image: IdRef, coordinate: IdRef, d_ref: IdRef, image_operands: ?ImageOperands.Extended = null },
910 .OpImageSparseSampleDrefExplicitLod => struct { id_result_type: IdResultType, id_result: IdResult, sampled_image: IdRef, coordinate: IdRef, d_ref: IdRef, image_operands: ImageOperands.Extended },
911 .OpImageSparseSampleProjImplicitLod => struct { id_result_type: IdResultType, id_result: IdResult, sampled_image: IdRef, coordinate: IdRef, image_operands: ?ImageOperands.Extended = null },
912 .OpImageSparseSampleProjExplicitLod => struct { id_result_type: IdResultType, id_result: IdResult, sampled_image: IdRef, coordinate: IdRef, image_operands: ImageOperands.Extended },
913 .OpImageSparseSampleProjDrefImplicitLod => struct { id_result_type: IdResultType, id_result: IdResult, sampled_image: IdRef, coordinate: IdRef, d_ref: IdRef, image_operands: ?ImageOperands.Extended = null },
914 .OpImageSparseSampleProjDrefExplicitLod => struct { id_result_type: IdResultType, id_result: IdResult, sampled_image: IdRef, coordinate: IdRef, d_ref: IdRef, image_operands: ImageOperands.Extended },
915 .OpImageSparseFetch => struct { id_result_type: IdResultType, id_result: IdResult, image: IdRef, coordinate: IdRef, image_operands: ?ImageOperands.Extended = null },
916 .OpImageSparseGather => struct { id_result_type: IdResultType, id_result: IdResult, sampled_image: IdRef, coordinate: IdRef, component: IdRef, image_operands: ?ImageOperands.Extended = null },
917 .OpImageSparseDrefGather => struct { id_result_type: IdResultType, id_result: IdResult, sampled_image: IdRef, coordinate: IdRef, d_ref: IdRef, image_operands: ?ImageOperands.Extended = null },
918 .OpImageSparseTexelsResident => struct { id_result_type: IdResultType, id_result: IdResult, resident_code: IdRef },
919 .OpNoLine => void,
920 .OpAtomicFlagTestAndSet => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef, memory: IdScope, semantics: IdMemorySemantics },
921 .OpAtomicFlagClear => struct { pointer: IdRef, memory: IdScope, semantics: IdMemorySemantics },
922 .OpImageSparseRead => struct { id_result_type: IdResultType, id_result: IdResult, image: IdRef, coordinate: IdRef, image_operands: ?ImageOperands.Extended = null },
923 .OpSizeOf => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef },
924 .OpTypePipeStorage => struct { id_result: IdResult },
925 .OpConstantPipeStorage => struct { id_result_type: IdResultType, id_result: IdResult, packet_size: LiteralInteger, packet_alignment: LiteralInteger, capacity: LiteralInteger },
926 .OpCreatePipeFromPipeStorage => struct { id_result_type: IdResultType, id_result: IdResult, pipe_storage: IdRef },
927 .OpGetKernelLocalSizeForSubgroupCount => struct { id_result_type: IdResultType, id_result: IdResult, subgroup_count: IdRef, invoke: IdRef, param: IdRef, param_size: IdRef, param_align: IdRef },
928 .OpGetKernelMaxNumSubgroups => struct { id_result_type: IdResultType, id_result: IdResult, invoke: IdRef, param: IdRef, param_size: IdRef, param_align: IdRef },
929 .OpTypeNamedBarrier => struct { id_result: IdResult },
930 .OpNamedBarrierInitialize => struct { id_result_type: IdResultType, id_result: IdResult, subgroup_count: IdRef },
931 .OpMemoryNamedBarrier => struct { named_barrier: IdRef, memory: IdScope, semantics: IdMemorySemantics },
932 .OpModuleProcessed => struct { process: LiteralString },
933 .OpExecutionModeId => struct { entry_point: IdRef, mode: ExecutionMode.Extended },
934 .OpDecorateId => struct { target: IdRef, decoration: Decoration.Extended },
935 .OpGroupNonUniformElect => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope },
936 .OpGroupNonUniformAll => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, predicate: IdRef },
937 .OpGroupNonUniformAny => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, predicate: IdRef },
938 .OpGroupNonUniformAllEqual => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, value: IdRef },
939 .OpGroupNonUniformBroadcast => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, value: IdRef, id: IdRef },
940 .OpGroupNonUniformBroadcastFirst => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, value: IdRef },
941 .OpGroupNonUniformBallot => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, predicate: IdRef },
942 .OpGroupNonUniformInverseBallot => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, value: IdRef },
943 .OpGroupNonUniformBallotBitExtract => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, value: IdRef, index: IdRef },
944 .OpGroupNonUniformBallotBitCount => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, value: IdRef },
945 .OpGroupNonUniformBallotFindLSB => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, value: IdRef },
946 .OpGroupNonUniformBallotFindMSB => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, value: IdRef },
947 .OpGroupNonUniformShuffle => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, value: IdRef, id: IdRef },
948 .OpGroupNonUniformShuffleXor => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, value: IdRef, mask: IdRef },
949 .OpGroupNonUniformShuffleUp => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, value: IdRef, delta: IdRef },
950 .OpGroupNonUniformShuffleDown => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, value: IdRef, delta: IdRef },
951 .OpGroupNonUniformIAdd => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, value: IdRef, clustersize: ?IdRef = null },
952 .OpGroupNonUniformFAdd => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, value: IdRef, clustersize: ?IdRef = null },
953 .OpGroupNonUniformIMul => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, value: IdRef, clustersize: ?IdRef = null },
954 .OpGroupNonUniformFMul => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, value: IdRef, clustersize: ?IdRef = null },
955 .OpGroupNonUniformSMin => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, value: IdRef, clustersize: ?IdRef = null },
956 .OpGroupNonUniformUMin => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, value: IdRef, clustersize: ?IdRef = null },
957 .OpGroupNonUniformFMin => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, value: IdRef, clustersize: ?IdRef = null },
958 .OpGroupNonUniformSMax => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, value: IdRef, clustersize: ?IdRef = null },
959 .OpGroupNonUniformUMax => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, value: IdRef, clustersize: ?IdRef = null },
960 .OpGroupNonUniformFMax => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, value: IdRef, clustersize: ?IdRef = null },
961 .OpGroupNonUniformBitwiseAnd => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, value: IdRef, clustersize: ?IdRef = null },
962 .OpGroupNonUniformBitwiseOr => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, value: IdRef, clustersize: ?IdRef = null },
963 .OpGroupNonUniformBitwiseXor => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, value: IdRef, clustersize: ?IdRef = null },
964 .OpGroupNonUniformLogicalAnd => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, value: IdRef, clustersize: ?IdRef = null },
965 .OpGroupNonUniformLogicalOr => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, value: IdRef, clustersize: ?IdRef = null },
966 .OpGroupNonUniformLogicalXor => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, value: IdRef, clustersize: ?IdRef = null },
967 .OpGroupNonUniformQuadBroadcast => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, value: IdRef, index: IdRef },
968 .OpGroupNonUniformQuadSwap => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, value: IdRef, direction: IdRef },
969 .OpCopyLogical => struct { id_result_type: IdResultType, id_result: IdResult, operand: IdRef },
970 .OpPtrEqual => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
971 .OpPtrNotEqual => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
972 .OpPtrDiff => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
973 .OpTerminateInvocation => void,
974 .OpSubgroupBallotKHR => struct { id_result_type: IdResultType, id_result: IdResult, predicate: IdRef },
975 .OpSubgroupFirstInvocationKHR => struct { id_result_type: IdResultType, id_result: IdResult, value: IdRef },
976 .OpSubgroupAllKHR => struct { id_result_type: IdResultType, id_result: IdResult, predicate: IdRef },
977 .OpSubgroupAnyKHR => struct { id_result_type: IdResultType, id_result: IdResult, predicate: IdRef },
978 .OpSubgroupAllEqualKHR => struct { id_result_type: IdResultType, id_result: IdResult, predicate: IdRef },
979 .OpSubgroupReadInvocationKHR => struct { id_result_type: IdResultType, id_result: IdResult, value: IdRef, index: IdRef },
980 .OpTraceRayKHR => struct { accel: IdRef, ray_flags: IdRef, cull_mask: IdRef, sbt_offset: IdRef, sbt_stride: IdRef, miss_index: IdRef, ray_origin: IdRef, ray_tmin: IdRef, ray_direction: IdRef, ray_tmax: IdRef, payload: IdRef },
981 .OpExecuteCallableKHR => struct { sbt_index: IdRef, callable_data: IdRef },
982 .OpConvertUToAccelerationStructureKHR => struct { id_result_type: IdResultType, id_result: IdResult, accel: IdRef },
983 .OpIgnoreIntersectionKHR => void,
984 .OpTerminateRayKHR => void,
985 .OpTypeRayQueryKHR => struct { id_result: IdResult },
986 .OpRayQueryInitializeKHR => struct { rayquery: IdRef, accel: IdRef, rayflags: IdRef, cullmask: IdRef, rayorigin: IdRef, raytmin: IdRef, raydirection: IdRef, raytmax: IdRef },
987 .OpRayQueryTerminateKHR => struct { rayquery: IdRef },
988 .OpRayQueryGenerateIntersectionKHR => struct { rayquery: IdRef, hitt: IdRef },
989 .OpRayQueryConfirmIntersectionKHR => struct { rayquery: IdRef },
990 .OpRayQueryProceedKHR => struct { id_result_type: IdResultType, id_result: IdResult, rayquery: IdRef },
991 .OpRayQueryGetIntersectionTypeKHR => struct { id_result_type: IdResultType, id_result: IdResult, rayquery: IdRef, intersection: IdRef },
992 .OpGroupIAddNonUniformAMD => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, x: IdRef },
993 .OpGroupFAddNonUniformAMD => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, x: IdRef },
994 .OpGroupFMinNonUniformAMD => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, x: IdRef },
995 .OpGroupUMinNonUniformAMD => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, x: IdRef },
996 .OpGroupSMinNonUniformAMD => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, x: IdRef },
997 .OpGroupFMaxNonUniformAMD => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, x: IdRef },
998 .OpGroupUMaxNonUniformAMD => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, x: IdRef },
999 .OpGroupSMaxNonUniformAMD => struct { id_result_type: IdResultType, id_result: IdResult, execution: IdScope, operation: GroupOperation, x: IdRef },
1000 .OpFragmentMaskFetchAMD => struct { id_result_type: IdResultType, id_result: IdResult, image: IdRef, coordinate: IdRef },
1001 .OpFragmentFetchAMD => struct { id_result_type: IdResultType, id_result: IdResult, image: IdRef, coordinate: IdRef, fragment_index: IdRef },
1002 .OpReadClockKHR => struct { id_result_type: IdResultType, id_result: IdResult, scope: IdScope },
1003 .OpImageSampleFootprintNV => struct { id_result_type: IdResultType, id_result: IdResult, sampled_image: IdRef, coordinate: IdRef, granularity: IdRef, coarse: IdRef, image_operands: ?ImageOperands.Extended = null },
1004 .OpGroupNonUniformPartitionNV => struct { id_result_type: IdResultType, id_result: IdResult, value: IdRef },
1005 .OpWritePackedPrimitiveIndices4x8NV => struct { index_offset: IdRef, packed_indices: IdRef },
1006 .OpReportIntersectionKHR => struct { id_result_type: IdResultType, id_result: IdResult, hit: IdRef, hitkind: IdRef },
1007 .OpIgnoreIntersectionNV => void,
1008 .OpTerminateRayNV => void,
1009 .OpTraceNV => struct { accel: IdRef, ray_flags: IdRef, cull_mask: IdRef, sbt_offset: IdRef, sbt_stride: IdRef, miss_index: IdRef, ray_origin: IdRef, ray_tmin: IdRef, ray_direction: IdRef, ray_tmax: IdRef, payloadid: IdRef },
1010 .OpTypeAccelerationStructureKHR => struct { id_result: IdResult },
1011 .OpExecuteCallableNV => struct { sbt_index: IdRef, callable_dataid: IdRef },
1012 .OpTypeCooperativeMatrixNV => struct { id_result: IdResult, component_type: IdRef, execution: IdScope, rows: IdRef, columns: IdRef },
1013 .OpCooperativeMatrixLoadNV => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef, stride: IdRef, column_major: IdRef, memory_access: ?MemoryAccess.Extended = null },
1014 .OpCooperativeMatrixStoreNV => struct { pointer: IdRef, object: IdRef, stride: IdRef, column_major: IdRef, memory_access: ?MemoryAccess.Extended = null },
1015 .OpCooperativeMatrixMulAddNV => struct { id_result_type: IdResultType, id_result: IdResult, a: IdRef, b: IdRef, c: IdRef },
1016 .OpCooperativeMatrixLengthNV => struct { id_result_type: IdResultType, id_result: IdResult, type: IdRef },
1017 .OpBeginInvocationInterlockEXT => void,
1018 .OpEndInvocationInterlockEXT => void,
1019 .OpDemoteToHelperInvocationEXT => void,
1020 .OpIsHelperInvocationEXT => struct { id_result_type: IdResultType, id_result: IdResult },
1021 .OpSubgroupShuffleINTEL => struct { id_result_type: IdResultType, id_result: IdResult, data: IdRef, invocationid: IdRef },
1022 .OpSubgroupShuffleDownINTEL => struct { id_result_type: IdResultType, id_result: IdResult, current: IdRef, next: IdRef, delta: IdRef },
1023 .OpSubgroupShuffleUpINTEL => struct { id_result_type: IdResultType, id_result: IdResult, previous: IdRef, current: IdRef, delta: IdRef },
1024 .OpSubgroupShuffleXorINTEL => struct { id_result_type: IdResultType, id_result: IdResult, data: IdRef, value: IdRef },
1025 .OpSubgroupBlockReadINTEL => struct { id_result_type: IdResultType, id_result: IdResult, ptr: IdRef },
1026 .OpSubgroupBlockWriteINTEL => struct { ptr: IdRef, data: IdRef },
1027 .OpSubgroupImageBlockReadINTEL => struct { id_result_type: IdResultType, id_result: IdResult, image: IdRef, coordinate: IdRef },
1028 .OpSubgroupImageBlockWriteINTEL => struct { image: IdRef, coordinate: IdRef, data: IdRef },
1029 .OpSubgroupImageMediaBlockReadINTEL => struct { id_result_type: IdResultType, id_result: IdResult, image: IdRef, coordinate: IdRef, width: IdRef, height: IdRef },
1030 .OpSubgroupImageMediaBlockWriteINTEL => struct { image: IdRef, coordinate: IdRef, width: IdRef, height: IdRef, data: IdRef },
1031 .OpUCountLeadingZerosINTEL => struct { id_result_type: IdResultType, id_result: IdResult, operand: IdRef },
1032 .OpUCountTrailingZerosINTEL => struct { id_result_type: IdResultType, id_result: IdResult, operand: IdRef },
1033 .OpAbsISubINTEL => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
1034 .OpAbsUSubINTEL => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
1035 .OpIAddSatINTEL => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
1036 .OpUAddSatINTEL => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
1037 .OpIAverageINTEL => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
1038 .OpUAverageINTEL => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
1039 .OpIAverageRoundedINTEL => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
1040 .OpUAverageRoundedINTEL => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
1041 .OpISubSatINTEL => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
1042 .OpUSubSatINTEL => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
1043 .OpIMul32x16INTEL => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
1044 .OpUMul32x16INTEL => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: IdRef, operand_2: IdRef },
1045 .OpConstFunctionPointerINTEL => struct { id_result_type: IdResultType, id_result: IdResult, function: IdRef },
1046 .OpFunctionPointerCallINTEL => struct { id_result_type: IdResultType, id_result: IdResult, operand_1: []const IdRef = &.{} },
1047 .OpAsmTargetINTEL => struct { id_result_type: IdResultType, id_result: IdResult, asm_target: LiteralString },
1048 .OpAsmINTEL => struct { id_result_type: IdResultType, id_result: IdResult, asm_type: IdRef, target: IdRef, asm_instructions: LiteralString, constraints: LiteralString },
1049 .OpAsmCallINTEL => struct { id_result_type: IdResultType, id_result: IdResult, @"asm": IdRef, argument_0: []const IdRef = &.{} },
1050 .OpAtomicFMinEXT => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef, memory: IdScope, semantics: IdMemorySemantics, value: IdRef },
1051 .OpAtomicFMaxEXT => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef, memory: IdScope, semantics: IdMemorySemantics, value: IdRef },
1052 .OpAssumeTrueKHR => struct { condition: IdRef },
1053 .OpExpectKHR => struct { id_result_type: IdResultType, id_result: IdResult, value: IdRef, expectedvalue: IdRef },
1054 .OpDecorateString => struct { target: IdRef, decoration: Decoration.Extended },
1055 .OpMemberDecorateString => struct { struct_type: IdRef, member: LiteralInteger, decoration: Decoration.Extended },
1056 .OpVmeImageINTEL => struct { id_result_type: IdResultType, id_result: IdResult, image_type: IdRef, sampler: IdRef },
1057 .OpTypeVmeImageINTEL => struct { id_result: IdResult, image_type: IdRef },
1058 .OpTypeAvcImePayloadINTEL => struct { id_result: IdResult },
1059 .OpTypeAvcRefPayloadINTEL => struct { id_result: IdResult },
1060 .OpTypeAvcSicPayloadINTEL => struct { id_result: IdResult },
1061 .OpTypeAvcMcePayloadINTEL => struct { id_result: IdResult },
1062 .OpTypeAvcMceResultINTEL => struct { id_result: IdResult },
1063 .OpTypeAvcImeResultINTEL => struct { id_result: IdResult },
1064 .OpTypeAvcImeResultSingleReferenceStreamoutINTEL => struct { id_result: IdResult },
1065 .OpTypeAvcImeResultDualReferenceStreamoutINTEL => struct { id_result: IdResult },
1066 .OpTypeAvcImeSingleReferenceStreaminINTEL => struct { id_result: IdResult },
1067 .OpTypeAvcImeDualReferenceStreaminINTEL => struct { id_result: IdResult },
1068 .OpTypeAvcRefResultINTEL => struct { id_result: IdResult },
1069 .OpTypeAvcSicResultINTEL => struct { id_result: IdResult },
1070 .OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL => struct { id_result_type: IdResultType, id_result: IdResult, slice_type: IdRef, qp: IdRef },
1071 .OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL => struct { id_result_type: IdResultType, id_result: IdResult, reference_base_penalty: IdRef, payload: IdRef },
1072 .OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL => struct { id_result_type: IdResultType, id_result: IdResult, slice_type: IdRef, qp: IdRef },
1073 .OpSubgroupAvcMceSetInterShapePenaltyINTEL => struct { id_result_type: IdResultType, id_result: IdResult, packed_shape_penalty: IdRef, payload: IdRef },
1074 .OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL => struct { id_result_type: IdResultType, id_result: IdResult, slice_type: IdRef, qp: IdRef },
1075 .OpSubgroupAvcMceSetInterDirectionPenaltyINTEL => struct { id_result_type: IdResultType, id_result: IdResult, direction_cost: IdRef, payload: IdRef },
1076 .OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL => struct { id_result_type: IdResultType, id_result: IdResult, slice_type: IdRef, qp: IdRef },
1077 .OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL => struct { id_result_type: IdResultType, id_result: IdResult, slice_type: IdRef, qp: IdRef },
1078 .OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL => struct { id_result_type: IdResultType, id_result: IdResult },
1079 .OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL => struct { id_result_type: IdResultType, id_result: IdResult },
1080 .OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL => struct { id_result_type: IdResultType, id_result: IdResult },
1081 .OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL => struct { id_result_type: IdResultType, id_result: IdResult, packed_cost_center_delta: IdRef, packed_cost_table: IdRef, cost_precision: IdRef, payload: IdRef },
1082 .OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL => struct { id_result_type: IdResultType, id_result: IdResult, slice_type: IdRef, qp: IdRef },
1083 .OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL => struct { id_result_type: IdResultType, id_result: IdResult },
1084 .OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL => struct { id_result_type: IdResultType, id_result: IdResult },
1085 .OpSubgroupAvcMceSetAcOnlyHaarINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1086 .OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL => struct { id_result_type: IdResultType, id_result: IdResult, source_field_polarity: IdRef, payload: IdRef },
1087 .OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL => struct { id_result_type: IdResultType, id_result: IdResult, reference_field_polarity: IdRef, payload: IdRef },
1088 .OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL => struct { id_result_type: IdResultType, id_result: IdResult, forward_reference_field_polarity: IdRef, backward_reference_field_polarity: IdRef, payload: IdRef },
1089 .OpSubgroupAvcMceConvertToImePayloadINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1090 .OpSubgroupAvcMceConvertToImeResultINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1091 .OpSubgroupAvcMceConvertToRefPayloadINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1092 .OpSubgroupAvcMceConvertToRefResultINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1093 .OpSubgroupAvcMceConvertToSicPayloadINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1094 .OpSubgroupAvcMceConvertToSicResultINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1095 .OpSubgroupAvcMceGetMotionVectorsINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1096 .OpSubgroupAvcMceGetInterDistortionsINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1097 .OpSubgroupAvcMceGetBestInterDistortionsINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1098 .OpSubgroupAvcMceGetInterMajorShapeINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1099 .OpSubgroupAvcMceGetInterMinorShapeINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1100 .OpSubgroupAvcMceGetInterDirectionsINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1101 .OpSubgroupAvcMceGetInterMotionVectorCountINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1102 .OpSubgroupAvcMceGetInterReferenceIdsINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1103 .OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL => struct { id_result_type: IdResultType, id_result: IdResult, packed_reference_ids: IdRef, packed_reference_parameter_field_polarities: IdRef, payload: IdRef },
1104 .OpSubgroupAvcImeInitializeINTEL => struct { id_result_type: IdResultType, id_result: IdResult, src_coord: IdRef, partition_mask: IdRef, sad_adjustment: IdRef },
1105 .OpSubgroupAvcImeSetSingleReferenceINTEL => struct { id_result_type: IdResultType, id_result: IdResult, ref_offset: IdRef, search_window_config: IdRef, payload: IdRef },
1106 .OpSubgroupAvcImeSetDualReferenceINTEL => struct { id_result_type: IdResultType, id_result: IdResult, fwd_ref_offset: IdRef, bwd_ref_offset: IdRef, id_ref_4: IdRef, payload: IdRef },
1107 .OpSubgroupAvcImeRefWindowSizeINTEL => struct { id_result_type: IdResultType, id_result: IdResult, search_window_config: IdRef, dual_ref: IdRef },
1108 .OpSubgroupAvcImeAdjustRefOffsetINTEL => struct { id_result_type: IdResultType, id_result: IdResult, ref_offset: IdRef, src_coord: IdRef, ref_window_size: IdRef, image_size: IdRef },
1109 .OpSubgroupAvcImeConvertToMcePayloadINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1110 .OpSubgroupAvcImeSetMaxMotionVectorCountINTEL => struct { id_result_type: IdResultType, id_result: IdResult, max_motion_vector_count: IdRef, payload: IdRef },
1111 .OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1112 .OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL => struct { id_result_type: IdResultType, id_result: IdResult, threshold: IdRef, payload: IdRef },
1113 .OpSubgroupAvcImeSetWeightedSadINTEL => struct { id_result_type: IdResultType, id_result: IdResult, packed_sad_weights: IdRef, payload: IdRef },
1114 .OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL => struct { id_result_type: IdResultType, id_result: IdResult, src_image: IdRef, ref_image: IdRef, payload: IdRef },
1115 .OpSubgroupAvcImeEvaluateWithDualReferenceINTEL => struct { id_result_type: IdResultType, id_result: IdResult, src_image: IdRef, fwd_ref_image: IdRef, bwd_ref_image: IdRef, payload: IdRef },
1116 .OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL => struct { id_result_type: IdResultType, id_result: IdResult, src_image: IdRef, ref_image: IdRef, payload: IdRef, streamin_components: IdRef },
1117 .OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL => struct { id_result_type: IdResultType, id_result: IdResult, src_image: IdRef, fwd_ref_image: IdRef, bwd_ref_image: IdRef, payload: IdRef, streamin_components: IdRef },
1118 .OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL => struct { id_result_type: IdResultType, id_result: IdResult, src_image: IdRef, ref_image: IdRef, payload: IdRef },
1119 .OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL => struct { id_result_type: IdResultType, id_result: IdResult, src_image: IdRef, fwd_ref_image: IdRef, bwd_ref_image: IdRef, payload: IdRef },
1120 .OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL => struct { id_result_type: IdResultType, id_result: IdResult, src_image: IdRef, ref_image: IdRef, payload: IdRef, streamin_components: IdRef },
1121 .OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL => struct { id_result_type: IdResultType, id_result: IdResult, src_image: IdRef, fwd_ref_image: IdRef, bwd_ref_image: IdRef, payload: IdRef, streamin_components: IdRef },
1122 .OpSubgroupAvcImeConvertToMceResultINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1123 .OpSubgroupAvcImeGetSingleReferenceStreaminINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1124 .OpSubgroupAvcImeGetDualReferenceStreaminINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1125 .OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1126 .OpSubgroupAvcImeStripDualReferenceStreamoutINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1127 .OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef, major_shape: IdRef },
1128 .OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef, major_shape: IdRef },
1129 .OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef, major_shape: IdRef },
1130 .OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef, major_shape: IdRef, direction: IdRef },
1131 .OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef, major_shape: IdRef, direction: IdRef },
1132 .OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef, major_shape: IdRef, direction: IdRef },
1133 .OpSubgroupAvcImeGetBorderReachedINTEL => struct { id_result_type: IdResultType, id_result: IdResult, image_select: IdRef, payload: IdRef },
1134 .OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1135 .OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1136 .OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1137 .OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1138 .OpSubgroupAvcFmeInitializeINTEL => struct { id_result_type: IdResultType, id_result: IdResult, src_coord: IdRef, motion_vectors: IdRef, major_shapes: IdRef, minor_shapes: IdRef, direction: IdRef, pixel_resolution: IdRef, sad_adjustment: IdRef },
1139 .OpSubgroupAvcBmeInitializeINTEL => struct { id_result_type: IdResultType, id_result: IdResult, src_coord: IdRef, motion_vectors: IdRef, major_shapes: IdRef, minor_shapes: IdRef, direction: IdRef, pixel_resolution: IdRef, bidirectional_weight: IdRef, sad_adjustment: IdRef },
1140 .OpSubgroupAvcRefConvertToMcePayloadINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1141 .OpSubgroupAvcRefSetBidirectionalMixDisableINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1142 .OpSubgroupAvcRefSetBilinearFilterEnableINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1143 .OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL => struct { id_result_type: IdResultType, id_result: IdResult, src_image: IdRef, ref_image: IdRef, payload: IdRef },
1144 .OpSubgroupAvcRefEvaluateWithDualReferenceINTEL => struct { id_result_type: IdResultType, id_result: IdResult, src_image: IdRef, fwd_ref_image: IdRef, bwd_ref_image: IdRef, payload: IdRef },
1145 .OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL => struct { id_result_type: IdResultType, id_result: IdResult, src_image: IdRef, packed_reference_ids: IdRef, payload: IdRef },
1146 .OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL => struct { id_result_type: IdResultType, id_result: IdResult, src_image: IdRef, packed_reference_ids: IdRef, packed_reference_field_polarities: IdRef, payload: IdRef },
1147 .OpSubgroupAvcRefConvertToMceResultINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1148 .OpSubgroupAvcSicInitializeINTEL => struct { id_result_type: IdResultType, id_result: IdResult, src_coord: IdRef },
1149 .OpSubgroupAvcSicConfigureSkcINTEL => struct { id_result_type: IdResultType, id_result: IdResult, skip_block_partition_type: IdRef, skip_motion_vector_mask: IdRef, motion_vectors: IdRef, bidirectional_weight: IdRef, sad_adjustment: IdRef, payload: IdRef },
1150 .OpSubgroupAvcSicConfigureIpeLumaINTEL => struct { id_result_type: IdResultType, id_result: IdResult, luma_intra_partition_mask: IdRef, intra_neighbour_availabilty: IdRef, left_edge_luma_pixels: IdRef, upper_left_corner_luma_pixel: IdRef, upper_edge_luma_pixels: IdRef, upper_right_edge_luma_pixels: IdRef, sad_adjustment: IdRef, payload: IdRef },
1151 .OpSubgroupAvcSicConfigureIpeLumaChromaINTEL => struct { id_result_type: IdResultType, id_result: IdResult, luma_intra_partition_mask: IdRef, intra_neighbour_availabilty: IdRef, left_edge_luma_pixels: IdRef, upper_left_corner_luma_pixel: IdRef, upper_edge_luma_pixels: IdRef, upper_right_edge_luma_pixels: IdRef, left_edge_chroma_pixels: IdRef, upper_left_corner_chroma_pixel: IdRef, upper_edge_chroma_pixels: IdRef, sad_adjustment: IdRef, payload: IdRef },
1152 .OpSubgroupAvcSicGetMotionVectorMaskINTEL => struct { id_result_type: IdResultType, id_result: IdResult, skip_block_partition_type: IdRef, direction: IdRef },
1153 .OpSubgroupAvcSicConvertToMcePayloadINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1154 .OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL => struct { id_result_type: IdResultType, id_result: IdResult, packed_shape_penalty: IdRef, payload: IdRef },
1155 .OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL => struct { id_result_type: IdResultType, id_result: IdResult, luma_mode_penalty: IdRef, luma_packed_neighbor_modes: IdRef, luma_packed_non_dc_penalty: IdRef, payload: IdRef },
1156 .OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL => struct { id_result_type: IdResultType, id_result: IdResult, chroma_mode_base_penalty: IdRef, payload: IdRef },
1157 .OpSubgroupAvcSicSetBilinearFilterEnableINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1158 .OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL => struct { id_result_type: IdResultType, id_result: IdResult, packed_sad_coefficients: IdRef, payload: IdRef },
1159 .OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL => struct { id_result_type: IdResultType, id_result: IdResult, block_based_skip_type: IdRef, payload: IdRef },
1160 .OpSubgroupAvcSicEvaluateIpeINTEL => struct { id_result_type: IdResultType, id_result: IdResult, src_image: IdRef, payload: IdRef },
1161 .OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL => struct { id_result_type: IdResultType, id_result: IdResult, src_image: IdRef, ref_image: IdRef, payload: IdRef },
1162 .OpSubgroupAvcSicEvaluateWithDualReferenceINTEL => struct { id_result_type: IdResultType, id_result: IdResult, src_image: IdRef, fwd_ref_image: IdRef, bwd_ref_image: IdRef, payload: IdRef },
1163 .OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL => struct { id_result_type: IdResultType, id_result: IdResult, src_image: IdRef, packed_reference_ids: IdRef, payload: IdRef },
1164 .OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL => struct { id_result_type: IdResultType, id_result: IdResult, src_image: IdRef, packed_reference_ids: IdRef, packed_reference_field_polarities: IdRef, payload: IdRef },
1165 .OpSubgroupAvcSicConvertToMceResultINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1166 .OpSubgroupAvcSicGetIpeLumaShapeINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1167 .OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1168 .OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1169 .OpSubgroupAvcSicGetPackedIpeLumaModesINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1170 .OpSubgroupAvcSicGetIpeChromaModeINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1171 .OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1172 .OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1173 .OpSubgroupAvcSicGetInterRawSadsINTEL => struct { id_result_type: IdResultType, id_result: IdResult, payload: IdRef },
1174 .OpVariableLengthArrayINTEL => struct { id_result_type: IdResultType, id_result: IdResult, lenght: IdRef },
1175 .OpSaveMemoryINTEL => struct { id_result_type: IdResultType, id_result: IdResult },
1176 .OpRestoreMemoryINTEL => struct { ptr: IdRef },
1177 .OpLoopControlINTEL => struct { loop_control_parameters: []const LiteralInteger = &.{} },
1178 .OpPtrCastToCrossWorkgroupINTEL => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef },
1179 .OpCrossWorkgroupCastToPtrINTEL => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef },
1180 .OpReadPipeBlockingINTEL => struct { id_result_type: IdResultType, id_result: IdResult, packet_size: IdRef, packet_alignment: IdRef },
1181 .OpWritePipeBlockingINTEL => struct { id_result_type: IdResultType, id_result: IdResult, packet_size: IdRef, packet_alignment: IdRef },
1182 .OpFPGARegINTEL => struct { id_result_type: IdResultType, id_result: IdResult, result: IdRef, input: IdRef },
1183 .OpRayQueryGetRayTMinKHR => struct { id_result_type: IdResultType, id_result: IdResult, rayquery: IdRef },
1184 .OpRayQueryGetRayFlagsKHR => struct { id_result_type: IdResultType, id_result: IdResult, rayquery: IdRef },
1185 .OpRayQueryGetIntersectionTKHR => struct { id_result_type: IdResultType, id_result: IdResult, rayquery: IdRef, intersection: IdRef },
1186 .OpRayQueryGetIntersectionInstanceCustomIndexKHR => struct { id_result_type: IdResultType, id_result: IdResult, rayquery: IdRef, intersection: IdRef },
1187 .OpRayQueryGetIntersectionInstanceIdKHR => struct { id_result_type: IdResultType, id_result: IdResult, rayquery: IdRef, intersection: IdRef },
1188 .OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR => struct { id_result_type: IdResultType, id_result: IdResult, rayquery: IdRef, intersection: IdRef },
1189 .OpRayQueryGetIntersectionGeometryIndexKHR => struct { id_result_type: IdResultType, id_result: IdResult, rayquery: IdRef, intersection: IdRef },
1190 .OpRayQueryGetIntersectionPrimitiveIndexKHR => struct { id_result_type: IdResultType, id_result: IdResult, rayquery: IdRef, intersection: IdRef },
1191 .OpRayQueryGetIntersectionBarycentricsKHR => struct { id_result_type: IdResultType, id_result: IdResult, rayquery: IdRef, intersection: IdRef },
1192 .OpRayQueryGetIntersectionFrontFaceKHR => struct { id_result_type: IdResultType, id_result: IdResult, rayquery: IdRef, intersection: IdRef },
1193 .OpRayQueryGetIntersectionCandidateAABBOpaqueKHR => struct { id_result_type: IdResultType, id_result: IdResult, rayquery: IdRef },
1194 .OpRayQueryGetIntersectionObjectRayDirectionKHR => struct { id_result_type: IdResultType, id_result: IdResult, rayquery: IdRef, intersection: IdRef },
1195 .OpRayQueryGetIntersectionObjectRayOriginKHR => struct { id_result_type: IdResultType, id_result: IdResult, rayquery: IdRef, intersection: IdRef },
1196 .OpRayQueryGetWorldRayDirectionKHR => struct { id_result_type: IdResultType, id_result: IdResult, rayquery: IdRef },
1197 .OpRayQueryGetWorldRayOriginKHR => struct { id_result_type: IdResultType, id_result: IdResult, rayquery: IdRef },
1198 .OpRayQueryGetIntersectionObjectToWorldKHR => struct { id_result_type: IdResultType, id_result: IdResult, rayquery: IdRef, intersection: IdRef },
1199 .OpRayQueryGetIntersectionWorldToObjectKHR => struct { id_result_type: IdResultType, id_result: IdResult, rayquery: IdRef, intersection: IdRef },
1200 .OpAtomicFAddEXT => struct { id_result_type: IdResultType, id_result: IdResult, pointer: IdRef, memory: IdScope, semantics: IdMemorySemantics, value: IdRef },
1201 .OpTypeBufferSurfaceINTEL => struct { id_result: IdResult },
1202 .OpTypeStructContinuedINTEL => struct { id_ref: []const IdRef = &.{} },
1203 .OpConstantCompositeContinuedINTEL => struct { constituents: []const IdRef = &.{} },
1204 .OpSpecConstantCompositeContinuedINTEL => struct { constituents: []const IdRef = &.{} },
1205 };
1206 }
587};1207};
588pub const ImageOperands = packed struct {1208pub const ImageOperands = packed struct {
589 Bias: bool align(@alignOf(u32)) = false,1209 Bias: bool align(@alignOf(u32)) = false,
...@@ -618,6 +1238,46 @@ pub const ImageOperands = packed struct {...@@ -618,6 +1238,46 @@ pub const ImageOperands = packed struct {
618 _reserved_bit_29: bool = false,1238 _reserved_bit_29: bool = false,
619 _reserved_bit_30: bool = false,1239 _reserved_bit_30: bool = false,
620 _reserved_bit_31: bool = false,1240 _reserved_bit_31: bool = false,
1241
1242 pub const MakeTexelAvailableKHR: ImageOperands = .{ .MakeTexelAvailable = true };
1243 pub const MakeTexelVisibleKHR: ImageOperands = .{ .MakeTexelVisible = true };
1244 pub const NonPrivateTexelKHR: ImageOperands = .{ .NonPrivateTexel = true };
1245 pub const VolatileTexelKHR: ImageOperands = .{ .VolatileTexel = true };
1246
1247 pub const Extended = struct {
1248 Bias: ?struct { id_ref: IdRef } = null,
1249 Lod: ?struct { id_ref: IdRef } = null,
1250 Grad: ?struct { id_ref_0: IdRef, id_ref_1: IdRef } = null,
1251 ConstOffset: ?struct { id_ref: IdRef } = null,
1252 Offset: ?struct { id_ref: IdRef } = null,
1253 ConstOffsets: ?struct { id_ref: IdRef } = null,
1254 Sample: ?struct { id_ref: IdRef } = null,
1255 MinLod: ?struct { id_ref: IdRef } = null,
1256 MakeTexelAvailable: ?struct { id_scope: IdScope } = null,
1257 MakeTexelVisible: ?struct { id_scope: IdScope } = null,
1258 NonPrivateTexel: bool = false,
1259 VolatileTexel: bool = false,
1260 SignExtend: bool = false,
1261 ZeroExtend: bool = false,
1262 _reserved_bit_14: bool = false,
1263 _reserved_bit_15: bool = false,
1264 _reserved_bit_16: bool = false,
1265 _reserved_bit_17: bool = false,
1266 _reserved_bit_18: bool = false,
1267 _reserved_bit_19: bool = false,
1268 _reserved_bit_20: bool = false,
1269 _reserved_bit_21: bool = false,
1270 _reserved_bit_22: bool = false,
1271 _reserved_bit_23: bool = false,
1272 _reserved_bit_24: bool = false,
1273 _reserved_bit_25: bool = false,
1274 _reserved_bit_26: bool = false,
1275 _reserved_bit_27: bool = false,
1276 _reserved_bit_28: bool = false,
1277 _reserved_bit_29: bool = false,
1278 _reserved_bit_30: bool = false,
1279 _reserved_bit_31: bool = false,
1280 };
621};1281};
622pub const FPFastMathMode = packed struct {1282pub const FPFastMathMode = packed struct {
623 NotNaN: bool align(@alignOf(u32)) = false,1283 NotNaN: bool align(@alignOf(u32)) = false,
...@@ -720,6 +1380,41 @@ pub const LoopControl = packed struct {...@@ -720,6 +1380,41 @@ pub const LoopControl = packed struct {
720 _reserved_bit_29: bool = false,1380 _reserved_bit_29: bool = false,
721 _reserved_bit_30: bool = false,1381 _reserved_bit_30: bool = false,
722 _reserved_bit_31: bool = false,1382 _reserved_bit_31: bool = false,
1383
1384 pub const Extended = struct {
1385 Unroll: bool = false,
1386 DontUnroll: bool = false,
1387 DependencyInfinite: bool = false,
1388 DependencyLength: ?struct { literal_integer: LiteralInteger } = null,
1389 MinIterations: ?struct { literal_integer: LiteralInteger } = null,
1390 MaxIterations: ?struct { literal_integer: LiteralInteger } = null,
1391 IterationMultiple: ?struct { literal_integer: LiteralInteger } = null,
1392 PeelCount: ?struct { literal_integer: LiteralInteger } = null,
1393 PartialCount: ?struct { literal_integer: LiteralInteger } = null,
1394 _reserved_bit_9: bool = false,
1395 _reserved_bit_10: bool = false,
1396 _reserved_bit_11: bool = false,
1397 _reserved_bit_12: bool = false,
1398 _reserved_bit_13: bool = false,
1399 _reserved_bit_14: bool = false,
1400 _reserved_bit_15: bool = false,
1401 InitiationIntervalINTEL: ?struct { literal_integer: LiteralInteger } = null,
1402 MaxConcurrencyINTEL: ?struct { literal_integer: LiteralInteger } = null,
1403 DependencyArrayINTEL: ?struct { literal_integer: LiteralInteger } = null,
1404 PipelineEnableINTEL: ?struct { literal_integer: LiteralInteger } = null,
1405 LoopCoalesceINTEL: ?struct { literal_integer: LiteralInteger } = null,
1406 MaxInterleavingINTEL: ?struct { literal_integer: LiteralInteger } = null,
1407 SpeculatedIterationsINTEL: ?struct { literal_integer: LiteralInteger } = null,
1408 NoFusionINTEL: ?struct { literal_integer: LiteralInteger } = null,
1409 _reserved_bit_24: bool = false,
1410 _reserved_bit_25: bool = false,
1411 _reserved_bit_26: bool = false,
1412 _reserved_bit_27: bool = false,
1413 _reserved_bit_28: bool = false,
1414 _reserved_bit_29: bool = false,
1415 _reserved_bit_30: bool = false,
1416 _reserved_bit_31: bool = false,
1417 };
723};1418};
724pub const FunctionControl = packed struct {1419pub const FunctionControl = packed struct {
725 Inline: bool align(@alignOf(u32)) = false,1420 Inline: bool align(@alignOf(u32)) = false,
...@@ -788,6 +1483,10 @@ pub const MemorySemantics = packed struct {...@@ -788,6 +1483,10 @@ pub const MemorySemantics = packed struct {
788 _reserved_bit_29: bool = false,1483 _reserved_bit_29: bool = false,
789 _reserved_bit_30: bool = false,1484 _reserved_bit_30: bool = false,
790 _reserved_bit_31: bool = false,1485 _reserved_bit_31: bool = false,
1486
1487 pub const OutputMemoryKHR: MemorySemantics = .{ .OutputMemory = true };
1488 pub const MakeAvailableKHR: MemorySemantics = .{ .MakeAvailable = true };
1489 pub const MakeVisibleKHR: MemorySemantics = .{ .MakeVisible = true };
791};1490};
792pub const MemoryAccess = packed struct {1491pub const MemoryAccess = packed struct {
793 Volatile: bool align(@alignOf(u32)) = false,1492 Volatile: bool align(@alignOf(u32)) = false,
...@@ -822,6 +1521,45 @@ pub const MemoryAccess = packed struct {...@@ -822,6 +1521,45 @@ pub const MemoryAccess = packed struct {
822 _reserved_bit_29: bool = false,1521 _reserved_bit_29: bool = false,
823 _reserved_bit_30: bool = false,1522 _reserved_bit_30: bool = false,
824 _reserved_bit_31: bool = false,1523 _reserved_bit_31: bool = false,
1524
1525 pub const MakePointerAvailableKHR: MemoryAccess = .{ .MakePointerAvailable = true };
1526 pub const MakePointerVisibleKHR: MemoryAccess = .{ .MakePointerVisible = true };
1527 pub const NonPrivatePointerKHR: MemoryAccess = .{ .NonPrivatePointer = true };
1528
1529 pub const Extended = struct {
1530 Volatile: bool = false,
1531 Aligned: ?struct { literal_integer: LiteralInteger } = null,
1532 Nontemporal: bool = false,
1533 MakePointerAvailable: ?struct { id_scope: IdScope } = null,
1534 MakePointerVisible: ?struct { id_scope: IdScope } = null,
1535 NonPrivatePointer: bool = false,
1536 _reserved_bit_6: bool = false,
1537 _reserved_bit_7: bool = false,
1538 _reserved_bit_8: bool = false,
1539 _reserved_bit_9: bool = false,
1540 _reserved_bit_10: bool = false,
1541 _reserved_bit_11: bool = false,
1542 _reserved_bit_12: bool = false,
1543 _reserved_bit_13: bool = false,
1544 _reserved_bit_14: bool = false,
1545 _reserved_bit_15: bool = false,
1546 _reserved_bit_16: bool = false,
1547 _reserved_bit_17: bool = false,
1548 _reserved_bit_18: bool = false,
1549 _reserved_bit_19: bool = false,
1550 _reserved_bit_20: bool = false,
1551 _reserved_bit_21: bool = false,
1552 _reserved_bit_22: bool = false,
1553 _reserved_bit_23: bool = false,
1554 _reserved_bit_24: bool = false,
1555 _reserved_bit_25: bool = false,
1556 _reserved_bit_26: bool = false,
1557 _reserved_bit_27: bool = false,
1558 _reserved_bit_28: bool = false,
1559 _reserved_bit_29: bool = false,
1560 _reserved_bit_30: bool = false,
1561 _reserved_bit_31: bool = false,
1562 };
825};1563};
826pub const KernelProfilingInfo = packed struct {1564pub const KernelProfilingInfo = packed struct {
827 CmdExecTime: bool align(@alignOf(u32)) = false,1565 CmdExecTime: bool align(@alignOf(u32)) = false,
...@@ -932,7 +1670,6 @@ pub const SourceLanguage = enum(u32) {...@@ -932,7 +1670,6 @@ pub const SourceLanguage = enum(u32) {
932 OpenCL_C = 3,1670 OpenCL_C = 3,
933 OpenCL_CPP = 4,1671 OpenCL_CPP = 4,
934 HLSL = 5,1672 HLSL = 5,
935 _,
936};1673};
937pub const ExecutionModel = enum(u32) {1674pub const ExecutionModel = enum(u32) {
938 Vertex = 0,1675 Vertex = 0,
...@@ -944,33 +1681,35 @@ pub const ExecutionModel = enum(u32) {...@@ -944,33 +1681,35 @@ pub const ExecutionModel = enum(u32) {
944 Kernel = 6,1681 Kernel = 6,
945 TaskNV = 5267,1682 TaskNV = 5267,
946 MeshNV = 5268,1683 MeshNV = 5268,
947 RayGenerationNV = 5313,
948 RayGenerationKHR = 5313,1684 RayGenerationKHR = 5313,
949 IntersectionNV = 5314,
950 IntersectionKHR = 5314,1685 IntersectionKHR = 5314,
951 AnyHitNV = 5315,
952 AnyHitKHR = 5315,1686 AnyHitKHR = 5315,
953 ClosestHitNV = 5316,
954 ClosestHitKHR = 5316,1687 ClosestHitKHR = 5316,
955 MissNV = 5317,
956 MissKHR = 5317,1688 MissKHR = 5317,
957 CallableNV = 5318,
958 CallableKHR = 5318,1689 CallableKHR = 5318,
959 _,1690
1691 pub const RayGenerationNV = ExecutionModel.RayGenerationKHR;
1692 pub const IntersectionNV = ExecutionModel.IntersectionKHR;
1693 pub const AnyHitNV = ExecutionModel.AnyHitKHR;
1694 pub const ClosestHitNV = ExecutionModel.ClosestHitKHR;
1695 pub const MissNV = ExecutionModel.MissKHR;
1696 pub const CallableNV = ExecutionModel.CallableKHR;
960};1697};
961pub const AddressingModel = enum(u32) {1698pub const AddressingModel = enum(u32) {
962 Logical = 0,1699 Logical = 0,
963 Physical32 = 1,1700 Physical32 = 1,
964 Physical64 = 2,1701 Physical64 = 2,
965 PhysicalStorageBuffer64 = 5348,1702 PhysicalStorageBuffer64 = 5348,
966 _,1703
1704 pub const PhysicalStorageBuffer64EXT = AddressingModel.PhysicalStorageBuffer64;
967};1705};
968pub const MemoryModel = enum(u32) {1706pub const MemoryModel = enum(u32) {
969 Simple = 0,1707 Simple = 0,
970 GLSL450 = 1,1708 GLSL450 = 1,
971 OpenCL = 2,1709 OpenCL = 2,
972 Vulkan = 3,1710 Vulkan = 3,
973 _,1711
1712 pub const VulkanKHR = MemoryModel.Vulkan;
974};1713};
975pub const ExecutionMode = enum(u32) {1714pub const ExecutionMode = enum(u32) {
976 Invocations = 0,1715 Invocations = 0,
...@@ -1039,7 +1778,75 @@ pub const ExecutionMode = enum(u32) {...@@ -1039,7 +1778,75 @@ pub const ExecutionMode = enum(u32) {
1039 NoGlobalOffsetINTEL = 5895,1778 NoGlobalOffsetINTEL = 5895,
1040 NumSIMDWorkitemsINTEL = 5896,1779 NumSIMDWorkitemsINTEL = 5896,
1041 SchedulerTargetFmaxMhzINTEL = 5903,1780 SchedulerTargetFmaxMhzINTEL = 5903,
1042 _,1781
1782 pub const Extended = union(ExecutionMode) {
1783 Invocations: struct { literal_integer: LiteralInteger },
1784 SpacingEqual,
1785 SpacingFractionalEven,
1786 SpacingFractionalOdd,
1787 VertexOrderCw,
1788 VertexOrderCcw,
1789 PixelCenterInteger,
1790 OriginUpperLeft,
1791 OriginLowerLeft,
1792 EarlyFragmentTests,
1793 PointMode,
1794 Xfb,
1795 DepthReplacing,
1796 DepthGreater,
1797 DepthLess,
1798 DepthUnchanged,
1799 LocalSize: struct { x_size: LiteralInteger, y_size: LiteralInteger, z_size: LiteralInteger },
1800 LocalSizeHint: struct { x_size: LiteralInteger, y_size: LiteralInteger, z_size: LiteralInteger },
1801 InputPoints,
1802 InputLines,
1803 InputLinesAdjacency,
1804 Triangles,
1805 InputTrianglesAdjacency,
1806 Quads,
1807 Isolines,
1808 OutputVertices: struct { vertex_count: LiteralInteger },
1809 OutputPoints,
1810 OutputLineStrip,
1811 OutputTriangleStrip,
1812 VecTypeHint: struct { vector_type: LiteralInteger },
1813 ContractionOff,
1814 Initializer,
1815 Finalizer,
1816 SubgroupSize: struct { subgroup_size: LiteralInteger },
1817 SubgroupsPerWorkgroup: struct { subgroups_per_workgroup: LiteralInteger },
1818 SubgroupsPerWorkgroupId: struct { subgroups_per_workgroup: IdRef },
1819 LocalSizeId: struct { x_size: IdRef, y_size: IdRef, z_size: IdRef },
1820 LocalSizeHintId: struct { local_size_hint: IdRef },
1821 PostDepthCoverage,
1822 DenormPreserve: struct { target_width: LiteralInteger },
1823 DenormFlushToZero: struct { target_width: LiteralInteger },
1824 SignedZeroInfNanPreserve: struct { target_width: LiteralInteger },
1825 RoundingModeRTE: struct { target_width: LiteralInteger },
1826 RoundingModeRTZ: struct { target_width: LiteralInteger },
1827 StencilRefReplacingEXT,
1828 OutputLinesNV,
1829 OutputPrimitivesNV: struct { primitive_count: LiteralInteger },
1830 DerivativeGroupQuadsNV,
1831 DerivativeGroupLinearNV,
1832 OutputTrianglesNV,
1833 PixelInterlockOrderedEXT,
1834 PixelInterlockUnorderedEXT,
1835 SampleInterlockOrderedEXT,
1836 SampleInterlockUnorderedEXT,
1837 ShadingRateInterlockOrderedEXT,
1838 ShadingRateInterlockUnorderedEXT,
1839 SharedLocalMemorySizeINTEL: struct { size: LiteralInteger },
1840 RoundingModeRTPINTEL: struct { target_width: LiteralInteger },
1841 RoundingModeRTNINTEL: struct { target_width: LiteralInteger },
1842 FloatingPointModeALTINTEL: struct { target_width: LiteralInteger },
1843 FloatingPointModeIEEEINTEL: struct { target_width: LiteralInteger },
1844 MaxWorkgroupSizeINTEL: struct { literal_integer_0: LiteralInteger, literal_integer_1: LiteralInteger, literal_integer_2: LiteralInteger },
1845 MaxWorkDimINTEL: struct { literal_integer: LiteralInteger },
1846 NoGlobalOffsetINTEL,
1847 NumSIMDWorkitemsINTEL: struct { literal_integer: LiteralInteger },
1848 SchedulerTargetFmaxMhzINTEL: struct { literal_integer: LiteralInteger },
1849 };
1043};1850};
1044pub const StorageClass = enum(u32) {1851pub const StorageClass = enum(u32) {
1045 UniformConstant = 0,1852 UniformConstant = 0,
...@@ -1065,7 +1872,14 @@ pub const StorageClass = enum(u32) {...@@ -1065,7 +1872,14 @@ pub const StorageClass = enum(u32) {
1065 CodeSectionINTEL = 5605,1872 CodeSectionINTEL = 5605,
1066 DeviceOnlyINTEL = 5936,1873 DeviceOnlyINTEL = 5936,
1067 HostOnlyINTEL = 5937,1874 HostOnlyINTEL = 5937,
1068 _,1875
1876 pub const CallableDataNV = StorageClass.CallableDataKHR;
1877 pub const IncomingCallableDataNV = StorageClass.IncomingCallableDataKHR;
1878 pub const RayPayloadNV = StorageClass.RayPayloadKHR;
1879 pub const HitAttributeNV = StorageClass.HitAttributeKHR;
1880 pub const IncomingRayPayloadNV = StorageClass.IncomingRayPayloadKHR;
1881 pub const ShaderRecordBufferNV = StorageClass.ShaderRecordBufferKHR;
1882 pub const PhysicalStorageBufferEXT = StorageClass.PhysicalStorageBuffer;
1069};1883};
1070pub const Dim = enum(u32) {1884pub const Dim = enum(u32) {
1071 @"1D" = 0,1885 @"1D" = 0,
...@@ -1075,7 +1889,6 @@ pub const Dim = enum(u32) {...@@ -1075,7 +1889,6 @@ pub const Dim = enum(u32) {
1075 Rect = 4,1889 Rect = 4,
1076 Buffer = 5,1890 Buffer = 5,
1077 SubpassData = 6,1891 SubpassData = 6,
1078 _,
1079};1892};
1080pub const SamplerAddressingMode = enum(u32) {1893pub const SamplerAddressingMode = enum(u32) {
1081 None = 0,1894 None = 0,
...@@ -1083,12 +1896,10 @@ pub const SamplerAddressingMode = enum(u32) {...@@ -1083,12 +1896,10 @@ pub const SamplerAddressingMode = enum(u32) {
1083 Clamp = 2,1896 Clamp = 2,
1084 Repeat = 3,1897 Repeat = 3,
1085 RepeatMirrored = 4,1898 RepeatMirrored = 4,
1086 _,
1087};1899};
1088pub const SamplerFilterMode = enum(u32) {1900pub const SamplerFilterMode = enum(u32) {
1089 Nearest = 0,1901 Nearest = 0,
1090 Linear = 1,1902 Linear = 1,
1091 _,
1092};1903};
1093pub const ImageFormat = enum(u32) {1904pub const ImageFormat = enum(u32) {
1094 Unknown = 0,1905 Unknown = 0,
...@@ -1133,7 +1944,6 @@ pub const ImageFormat = enum(u32) {...@@ -1133,7 +1944,6 @@ pub const ImageFormat = enum(u32) {
1133 R8ui = 39,1944 R8ui = 39,
1134 R64ui = 40,1945 R64ui = 40,
1135 R64i = 41,1946 R64i = 41,
1136 _,
1137};1947};
1138pub const ImageChannelOrder = enum(u32) {1948pub const ImageChannelOrder = enum(u32) {
1139 R = 0,1949 R = 0,
...@@ -1156,7 +1966,6 @@ pub const ImageChannelOrder = enum(u32) {...@@ -1156,7 +1966,6 @@ pub const ImageChannelOrder = enum(u32) {
1156 sRGBA = 17,1966 sRGBA = 17,
1157 sBGRA = 18,1967 sBGRA = 18,
1158 ABGR = 19,1968 ABGR = 19,
1159 _,
1160};1969};
1161pub const ImageChannelDataType = enum(u32) {1970pub const ImageChannelDataType = enum(u32) {
1162 SnormInt8 = 0,1971 SnormInt8 = 0,
...@@ -1176,36 +1985,30 @@ pub const ImageChannelDataType = enum(u32) {...@@ -1176,36 +1985,30 @@ pub const ImageChannelDataType = enum(u32) {
1176 Float = 14,1985 Float = 14,
1177 UnormInt24 = 15,1986 UnormInt24 = 15,
1178 UnormInt101010_2 = 16,1987 UnormInt101010_2 = 16,
1179 _,
1180};1988};
1181pub const FPRoundingMode = enum(u32) {1989pub const FPRoundingMode = enum(u32) {
1182 RTE = 0,1990 RTE = 0,
1183 RTZ = 1,1991 RTZ = 1,
1184 RTP = 2,1992 RTP = 2,
1185 RTN = 3,1993 RTN = 3,
1186 _,
1187};1994};
1188pub const FPDenormMode = enum(u32) {1995pub const FPDenormMode = enum(u32) {
1189 Preserve = 0,1996 Preserve = 0,
1190 FlushToZero = 1,1997 FlushToZero = 1,
1191 _,
1192};1998};
1193pub const FPOperationMode = enum(u32) {1999pub const FPOperationMode = enum(u32) {
1194 IEEE = 0,2000 IEEE = 0,
1195 ALT = 1,2001 ALT = 1,
1196 _,
1197};2002};
1198pub const LinkageType = enum(u32) {2003pub const LinkageType = enum(u32) {
1199 Export = 0,2004 Export = 0,
1200 Import = 1,2005 Import = 1,
1201 LinkOnceODR = 2,2006 LinkOnceODR = 2,
1202 _,
1203};2007};
1204pub const AccessQualifier = enum(u32) {2008pub const AccessQualifier = enum(u32) {
1205 ReadOnly = 0,2009 ReadOnly = 0,
1206 WriteOnly = 1,2010 WriteOnly = 1,
1207 ReadWrite = 2,2011 ReadWrite = 2,
1208 _,
1209};2012};
1210pub const FunctionParameterAttribute = enum(u32) {2013pub const FunctionParameterAttribute = enum(u32) {
1211 Zext = 0,2014 Zext = 0,
...@@ -1216,7 +2019,6 @@ pub const FunctionParameterAttribute = enum(u32) {...@@ -1216,7 +2019,6 @@ pub const FunctionParameterAttribute = enum(u32) {
1216 NoCapture = 5,2019 NoCapture = 5,
1217 NoWrite = 6,2020 NoWrite = 6,
1218 NoReadWrite = 7,2021 NoReadWrite = 7,
1219 _,
1220};2022};
1221pub const Decoration = enum(u32) {2023pub const Decoration = enum(u32) {
1222 RelaxedPrecision = 0,2024 RelaxedPrecision = 0,
...@@ -1278,11 +2080,8 @@ pub const Decoration = enum(u32) {...@@ -1278,11 +2080,8 @@ pub const Decoration = enum(u32) {
1278 PerTaskNV = 5273,2080 PerTaskNV = 5273,
1279 PerVertexNV = 5285,2081 PerVertexNV = 5285,
1280 NonUniform = 5300,2082 NonUniform = 5300,
1281 NonUniformEXT = 5300,
1282 RestrictPointer = 5355,2083 RestrictPointer = 5355,
1283 RestrictPointerEXT = 5355,
1284 AliasedPointer = 5356,2084 AliasedPointer = 5356,
1285 AliasedPointerEXT = 5356,
1286 SIMTCallINTEL = 5599,2085 SIMTCallINTEL = 5599,
1287 ReferencedIndirectlyINTEL = 5602,2086 ReferencedIndirectlyINTEL = 5602,
1288 ClobberINTEL = 5607,2087 ClobberINTEL = 5607,
...@@ -1293,9 +2092,7 @@ pub const Decoration = enum(u32) {...@@ -1293,9 +2092,7 @@ pub const Decoration = enum(u32) {
1293 StackCallINTEL = 5627,2092 StackCallINTEL = 5627,
1294 GlobalVariableOffsetINTEL = 5628,2093 GlobalVariableOffsetINTEL = 5628,
1295 CounterBuffer = 5634,2094 CounterBuffer = 5634,
1296 HlslCounterBufferGOOGLE = 5634,
1297 UserSemantic = 5635,2095 UserSemantic = 5635,
1298 HlslSemanticGOOGLE = 5635,
1299 UserTypeGOOGLE = 5636,2096 UserTypeGOOGLE = 5636,
1300 FunctionRoundingModeINTEL = 5822,2097 FunctionRoundingModeINTEL = 5822,
1301 FunctionDenormModeINTEL = 5823,2098 FunctionDenormModeINTEL = 5823,
...@@ -1322,7 +2119,113 @@ pub const Decoration = enum(u32) {...@@ -1322,7 +2119,113 @@ pub const Decoration = enum(u32) {
1322 FunctionFloatingPointModeINTEL = 6080,2119 FunctionFloatingPointModeINTEL = 6080,
1323 SingleElementVectorINTEL = 6085,2120 SingleElementVectorINTEL = 6085,
1324 VectorComputeCallableFunctionINTEL = 6087,2121 VectorComputeCallableFunctionINTEL = 6087,
1325 _,2122
2123 pub const NonUniformEXT = Decoration.NonUniform;
2124 pub const RestrictPointerEXT = Decoration.RestrictPointer;
2125 pub const AliasedPointerEXT = Decoration.AliasedPointer;
2126 pub const HlslCounterBufferGOOGLE = Decoration.CounterBuffer;
2127 pub const HlslSemanticGOOGLE = Decoration.UserSemantic;
2128
2129 pub const Extended = union(Decoration) {
2130 RelaxedPrecision,
2131 SpecId: struct { specialization_constant_id: LiteralInteger },
2132 Block,
2133 BufferBlock,
2134 RowMajor,
2135 ColMajor,
2136 ArrayStride: struct { array_stride: LiteralInteger },
2137 MatrixStride: struct { matrix_stride: LiteralInteger },
2138 GLSLShared,
2139 GLSLPacked,
2140 CPacked,
2141 BuiltIn: struct { built_in: BuiltIn },
2142 NoPerspective,
2143 Flat,
2144 Patch,
2145 Centroid,
2146 Sample,
2147 Invariant,
2148 Restrict,
2149 Aliased,
2150 Volatile,
2151 Constant,
2152 Coherent,
2153 NonWritable,
2154 NonReadable,
2155 Uniform,
2156 UniformId: struct { execution: IdScope },
2157 SaturatedConversion,
2158 Stream: struct { stream_number: LiteralInteger },
2159 Location: struct { location: LiteralInteger },
2160 Component: struct { component: LiteralInteger },
2161 Index: struct { index: LiteralInteger },
2162 Binding: struct { binding_point: LiteralInteger },
2163 DescriptorSet: struct { descriptor_set: LiteralInteger },
2164 Offset: struct { byte_offset: LiteralInteger },
2165 XfbBuffer: struct { xfb_buffer_number: LiteralInteger },
2166 XfbStride: struct { xfb_stride: LiteralInteger },
2167 FuncParamAttr: struct { function_parameter_attribute: FunctionParameterAttribute },
2168 FPRoundingMode: struct { fprounding_mode: FPRoundingMode },
2169 FPFastMathMode: struct { fpfast_math_mode: FPFastMathMode },
2170 LinkageAttributes: struct { name: LiteralString, linkage_type: LinkageType },
2171 NoContraction,
2172 InputAttachmentIndex: struct { attachment_index: LiteralInteger },
2173 Alignment: struct { alignment: LiteralInteger },
2174 MaxByteOffset: struct { max_byte_offset: LiteralInteger },
2175 AlignmentId: struct { alignment: IdRef },
2176 MaxByteOffsetId: struct { max_byte_offset: IdRef },
2177 NoSignedWrap,
2178 NoUnsignedWrap,
2179 ExplicitInterpAMD,
2180 OverrideCoverageNV,
2181 PassthroughNV,
2182 ViewportRelativeNV,
2183 SecondaryViewportRelativeNV: struct { offset: LiteralInteger },
2184 PerPrimitiveNV,
2185 PerViewNV,
2186 PerTaskNV,
2187 PerVertexNV,
2188 NonUniform,
2189 RestrictPointer,
2190 AliasedPointer,
2191 SIMTCallINTEL: struct { n: LiteralInteger },
2192 ReferencedIndirectlyINTEL,
2193 ClobberINTEL: struct { register: LiteralString },
2194 SideEffectsINTEL,
2195 VectorComputeVariableINTEL,
2196 FuncParamIOKindINTEL: struct { kind: LiteralInteger },
2197 VectorComputeFunctionINTEL,
2198 StackCallINTEL,
2199 GlobalVariableOffsetINTEL: struct { offset: LiteralInteger },
2200 CounterBuffer: struct { counter_buffer: IdRef },
2201 UserSemantic: struct { semantic: LiteralString },
2202 UserTypeGOOGLE: struct { user_type: LiteralString },
2203 FunctionRoundingModeINTEL: struct { target_width: LiteralInteger, fp_rounding_mode: FPRoundingMode },
2204 FunctionDenormModeINTEL: struct { target_width: LiteralInteger, fp_denorm_mode: FPDenormMode },
2205 RegisterINTEL,
2206 MemoryINTEL: struct { memory_type: LiteralString },
2207 NumbanksINTEL: struct { banks: LiteralInteger },
2208 BankwidthINTEL: struct { bank_width: LiteralInteger },
2209 MaxPrivateCopiesINTEL: struct { maximum_copies: LiteralInteger },
2210 SinglepumpINTEL,
2211 DoublepumpINTEL,
2212 MaxReplicatesINTEL: struct { maximum_replicates: LiteralInteger },
2213 SimpleDualPortINTEL,
2214 MergeINTEL: struct { merge_key: LiteralString, merge_type: LiteralString },
2215 BankBitsINTEL: struct { bank_bits: []const LiteralInteger = &.{} },
2216 ForcePow2DepthINTEL: struct { force_key: LiteralInteger },
2217 BurstCoalesceINTEL,
2218 CacheSizeINTEL: struct { cache_size_in_bytes: LiteralInteger },
2219 DontStaticallyCoalesceINTEL,
2220 PrefetchINTEL: struct { prefetcher_size_in_bytes: LiteralInteger },
2221 StallEnableINTEL,
2222 FuseLoopsInFunctionINTEL,
2223 BufferLocationINTEL: struct { buffer_location_id: LiteralInteger },
2224 IOPipeStorageINTEL: struct { io_pipe_id: LiteralInteger },
2225 FunctionFloatingPointModeINTEL: struct { target_width: LiteralInteger, fp_operation_mode: FPOperationMode },
2226 SingleElementVectorINTEL,
2227 VectorComputeCallableFunctionINTEL,
2228 };
1326};2229};
1327pub const BuiltIn = enum(u32) {2230pub const BuiltIn = enum(u32) {
1328 Position = 0,2231 Position = 0,
...@@ -1367,15 +2270,10 @@ pub const BuiltIn = enum(u32) {...@@ -1367,15 +2270,10 @@ pub const BuiltIn = enum(u32) {
1367 VertexIndex = 42,2270 VertexIndex = 42,
1368 InstanceIndex = 43,2271 InstanceIndex = 43,
1369 SubgroupEqMask = 4416,2272 SubgroupEqMask = 4416,
1370 SubgroupEqMaskKHR = 4416,
1371 SubgroupGeMask = 4417,2273 SubgroupGeMask = 4417,
1372 SubgroupGeMaskKHR = 4417,
1373 SubgroupGtMask = 4418,2274 SubgroupGtMask = 4418,
1374 SubgroupGtMaskKHR = 4418,
1375 SubgroupLeMask = 4419,2275 SubgroupLeMask = 4419,
1376 SubgroupLeMaskKHR = 4419,
1377 SubgroupLtMask = 4420,2276 SubgroupLtMask = 4420,
1378 SubgroupLtMaskKHR = 4420,
1379 BaseVertex = 4424,2277 BaseVertex = 4424,
1380 BaseInstance = 4425,2278 BaseInstance = 4425,
1381 DrawIndex = 4426,2279 DrawIndex = 4426,
...@@ -1408,42 +2306,47 @@ pub const BuiltIn = enum(u32) {...@@ -1408,42 +2306,47 @@ pub const BuiltIn = enum(u32) {
1408 BaryCoordNV = 5286,2306 BaryCoordNV = 5286,
1409 BaryCoordNoPerspNV = 5287,2307 BaryCoordNoPerspNV = 5287,
1410 FragSizeEXT = 5292,2308 FragSizeEXT = 5292,
1411 FragmentSizeNV = 5292,
1412 FragInvocationCountEXT = 5293,2309 FragInvocationCountEXT = 5293,
1413 InvocationsPerPixelNV = 5293,
1414 LaunchIdNV = 5319,
1415 LaunchIdKHR = 5319,2310 LaunchIdKHR = 5319,
1416 LaunchSizeNV = 5320,
1417 LaunchSizeKHR = 5320,2311 LaunchSizeKHR = 5320,
1418 WorldRayOriginNV = 5321,
1419 WorldRayOriginKHR = 5321,2312 WorldRayOriginKHR = 5321,
1420 WorldRayDirectionNV = 5322,
1421 WorldRayDirectionKHR = 5322,2313 WorldRayDirectionKHR = 5322,
1422 ObjectRayOriginNV = 5323,
1423 ObjectRayOriginKHR = 5323,2314 ObjectRayOriginKHR = 5323,
1424 ObjectRayDirectionNV = 5324,
1425 ObjectRayDirectionKHR = 5324,2315 ObjectRayDirectionKHR = 5324,
1426 RayTminNV = 5325,
1427 RayTminKHR = 5325,2316 RayTminKHR = 5325,
1428 RayTmaxNV = 5326,
1429 RayTmaxKHR = 5326,2317 RayTmaxKHR = 5326,
1430 InstanceCustomIndexNV = 5327,
1431 InstanceCustomIndexKHR = 5327,2318 InstanceCustomIndexKHR = 5327,
1432 ObjectToWorldNV = 5330,
1433 ObjectToWorldKHR = 5330,2319 ObjectToWorldKHR = 5330,
1434 WorldToObjectNV = 5331,
1435 WorldToObjectKHR = 5331,2320 WorldToObjectKHR = 5331,
1436 HitTNV = 5332,2321 HitTNV = 5332,
1437 HitKindNV = 5333,
1438 HitKindKHR = 5333,2322 HitKindKHR = 5333,
1439 IncomingRayFlagsNV = 5351,
1440 IncomingRayFlagsKHR = 5351,2323 IncomingRayFlagsKHR = 5351,
1441 RayGeometryIndexKHR = 5352,2324 RayGeometryIndexKHR = 5352,
1442 WarpsPerSMNV = 5374,2325 WarpsPerSMNV = 5374,
1443 SMCountNV = 5375,2326 SMCountNV = 5375,
1444 WarpIDNV = 5376,2327 WarpIDNV = 5376,
1445 SMIDNV = 5377,2328 SMIDNV = 5377,
1446 _,2329
2330 pub const SubgroupEqMaskKHR = BuiltIn.SubgroupEqMask;
2331 pub const SubgroupGeMaskKHR = BuiltIn.SubgroupGeMask;
2332 pub const SubgroupGtMaskKHR = BuiltIn.SubgroupGtMask;
2333 pub const SubgroupLeMaskKHR = BuiltIn.SubgroupLeMask;
2334 pub const SubgroupLtMaskKHR = BuiltIn.SubgroupLtMask;
2335 pub const FragmentSizeNV = BuiltIn.FragSizeEXT;
2336 pub const InvocationsPerPixelNV = BuiltIn.FragInvocationCountEXT;
2337 pub const LaunchIdNV = BuiltIn.LaunchIdKHR;
2338 pub const LaunchSizeNV = BuiltIn.LaunchSizeKHR;
2339 pub const WorldRayOriginNV = BuiltIn.WorldRayOriginKHR;
2340 pub const WorldRayDirectionNV = BuiltIn.WorldRayDirectionKHR;
2341 pub const ObjectRayOriginNV = BuiltIn.ObjectRayOriginKHR;
2342 pub const ObjectRayDirectionNV = BuiltIn.ObjectRayDirectionKHR;
2343 pub const RayTminNV = BuiltIn.RayTminKHR;
2344 pub const RayTmaxNV = BuiltIn.RayTmaxKHR;
2345 pub const InstanceCustomIndexNV = BuiltIn.InstanceCustomIndexKHR;
2346 pub const ObjectToWorldNV = BuiltIn.ObjectToWorldKHR;
2347 pub const WorldToObjectNV = BuiltIn.WorldToObjectKHR;
2348 pub const HitKindNV = BuiltIn.HitKindKHR;
2349 pub const IncomingRayFlagsNV = BuiltIn.IncomingRayFlagsKHR;
1447};2350};
1448pub const Scope = enum(u32) {2351pub const Scope = enum(u32) {
1449 CrossDevice = 0,2352 CrossDevice = 0,
...@@ -1452,9 +2355,9 @@ pub const Scope = enum(u32) {...@@ -1452,9 +2355,9 @@ pub const Scope = enum(u32) {
1452 Subgroup = 3,2355 Subgroup = 3,
1453 Invocation = 4,2356 Invocation = 4,
1454 QueueFamily = 5,2357 QueueFamily = 5,
1455 QueueFamilyKHR = 5,
1456 ShaderCallKHR = 6,2358 ShaderCallKHR = 6,
1457 _,2359
2360 pub const QueueFamilyKHR = Scope.QueueFamily;
1458};2361};
1459pub const GroupOperation = enum(u32) {2362pub const GroupOperation = enum(u32) {
1460 Reduce = 0,2363 Reduce = 0,
...@@ -1464,13 +2367,11 @@ pub const GroupOperation = enum(u32) {...@@ -1464,13 +2367,11 @@ pub const GroupOperation = enum(u32) {
1464 PartitionedReduceNV = 6,2367 PartitionedReduceNV = 6,
1465 PartitionedInclusiveScanNV = 7,2368 PartitionedInclusiveScanNV = 7,
1466 PartitionedExclusiveScanNV = 8,2369 PartitionedExclusiveScanNV = 8,
1467 _,
1468};2370};
1469pub const KernelEnqueueFlags = enum(u32) {2371pub const KernelEnqueueFlags = enum(u32) {
1470 NoWait = 0,2372 NoWait = 0,
1471 WaitKernel = 1,2373 WaitKernel = 1,
1472 WaitWorkGroup = 2,2374 WaitWorkGroup = 2,
1473 _,
1474};2375};
1475pub const Capability = enum(u32) {2376pub const Capability = enum(u32) {
1476 Matrix = 0,2377 Matrix = 0,
...@@ -1550,7 +2451,7 @@ pub const Capability = enum(u32) {...@@ -1550,7 +2451,7 @@ pub const Capability = enum(u32) {
1550 WorkgroupMemoryExplicitLayout16BitAccessKHR = 4430,2451 WorkgroupMemoryExplicitLayout16BitAccessKHR = 4430,
1551 SubgroupVoteKHR = 4431,2452 SubgroupVoteKHR = 4431,
1552 StorageBuffer16BitAccess = 4433,2453 StorageBuffer16BitAccess = 4433,
1553 StorageUniform16 = 4434,2454 UniformAndStorageBuffer16BitAccess = 4434,
1554 StoragePushConstant16 = 4435,2455 StoragePushConstant16 = 4435,
1555 StorageInputOutput16 = 4436,2456 StorageInputOutput16 = 4436,
1556 DeviceGroup = 4437,2457 DeviceGroup = 4437,
...@@ -1580,7 +2481,7 @@ pub const Capability = enum(u32) {...@@ -1580,7 +2481,7 @@ pub const Capability = enum(u32) {
1580 ShaderClockKHR = 5055,2481 ShaderClockKHR = 5055,
1581 SampleMaskOverrideCoverageNV = 5249,2482 SampleMaskOverrideCoverageNV = 5249,
1582 GeometryShaderPassthroughNV = 5251,2483 GeometryShaderPassthroughNV = 5251,
1583 ShaderViewportIndexLayerNV = 5254,2484 ShaderViewportIndexLayerEXT = 5254,
1584 ShaderViewportMaskNV = 5255,2485 ShaderViewportMaskNV = 5255,
1585 ShaderStereoViewNV = 5259,2486 ShaderStereoViewNV = 5259,
1586 PerViewAttributesNV = 5260,2487 PerViewAttributesNV = 5260,
...@@ -1589,7 +2490,7 @@ pub const Capability = enum(u32) {...@@ -1589,7 +2490,7 @@ pub const Capability = enum(u32) {
1589 ImageFootprintNV = 5282,2490 ImageFootprintNV = 5282,
1590 FragmentBarycentricNV = 5284,2491 FragmentBarycentricNV = 5284,
1591 ComputeDerivativeGroupQuadsNV = 5288,2492 ComputeDerivativeGroupQuadsNV = 5288,
1592 ShadingRateNV = 5291,2493 FragmentDensityEXT = 5291,
1593 GroupNonUniformPartitionedNV = 5297,2494 GroupNonUniformPartitionedNV = 5297,
1594 ShaderNonUniform = 5301,2495 ShaderNonUniform = 5301,
1595 RuntimeDescriptorArray = 5302,2496 RuntimeDescriptorArray = 5302,
...@@ -1654,21 +2555,37 @@ pub const Capability = enum(u32) {...@@ -1654,21 +2555,37 @@ pub const Capability = enum(u32) {
1654 AtomicFloat32AddEXT = 6033,2555 AtomicFloat32AddEXT = 6033,
1655 AtomicFloat64AddEXT = 6034,2556 AtomicFloat64AddEXT = 6034,
1656 LongConstantCompositeINTEL = 6089,2557 LongConstantCompositeINTEL = 6089,
1657 _,2558
2559 pub const StorageUniformBufferBlock16 = Capability.StorageBuffer16BitAccess;
2560 pub const StorageUniform16 = Capability.UniformAndStorageBuffer16BitAccess;
2561 pub const ShaderViewportIndexLayerNV = Capability.ShaderViewportIndexLayerEXT;
2562 pub const ShadingRateNV = Capability.FragmentDensityEXT;
2563 pub const ShaderNonUniformEXT = Capability.ShaderNonUniform;
2564 pub const RuntimeDescriptorArrayEXT = Capability.RuntimeDescriptorArray;
2565 pub const InputAttachmentArrayDynamicIndexingEXT = Capability.InputAttachmentArrayDynamicIndexing;
2566 pub const UniformTexelBufferArrayDynamicIndexingEXT = Capability.UniformTexelBufferArrayDynamicIndexing;
2567 pub const StorageTexelBufferArrayDynamicIndexingEXT = Capability.StorageTexelBufferArrayDynamicIndexing;
2568 pub const UniformBufferArrayNonUniformIndexingEXT = Capability.UniformBufferArrayNonUniformIndexing;
2569 pub const SampledImageArrayNonUniformIndexingEXT = Capability.SampledImageArrayNonUniformIndexing;
2570 pub const StorageBufferArrayNonUniformIndexingEXT = Capability.StorageBufferArrayNonUniformIndexing;
2571 pub const StorageImageArrayNonUniformIndexingEXT = Capability.StorageImageArrayNonUniformIndexing;
2572 pub const InputAttachmentArrayNonUniformIndexingEXT = Capability.InputAttachmentArrayNonUniformIndexing;
2573 pub const UniformTexelBufferArrayNonUniformIndexingEXT = Capability.UniformTexelBufferArrayNonUniformIndexing;
2574 pub const StorageTexelBufferArrayNonUniformIndexingEXT = Capability.StorageTexelBufferArrayNonUniformIndexing;
2575 pub const VulkanMemoryModelKHR = Capability.VulkanMemoryModel;
2576 pub const VulkanMemoryModelDeviceScopeKHR = Capability.VulkanMemoryModelDeviceScope;
2577 pub const PhysicalStorageBufferAddressesEXT = Capability.PhysicalStorageBufferAddresses;
1658};2578};
1659pub const RayQueryIntersection = enum(u32) {2579pub const RayQueryIntersection = enum(u32) {
1660 RayQueryCandidateIntersectionKHR = 0,2580 RayQueryCandidateIntersectionKHR = 0,
1661 RayQueryCommittedIntersectionKHR = 1,2581 RayQueryCommittedIntersectionKHR = 1,
1662 _,
1663};2582};
1664pub const RayQueryCommittedIntersectionType = enum(u32) {2583pub const RayQueryCommittedIntersectionType = enum(u32) {
1665 RayQueryCommittedIntersectionNoneKHR = 0,2584 RayQueryCommittedIntersectionNoneKHR = 0,
1666 RayQueryCommittedIntersectionTriangleKHR = 1,2585 RayQueryCommittedIntersectionTriangleKHR = 1,
1667 RayQueryCommittedIntersectionGeneratedKHR = 2,2586 RayQueryCommittedIntersectionGeneratedKHR = 2,
1668 _,
1669};2587};
1670pub const RayQueryCandidateIntersectionType = enum(u32) {2588pub const RayQueryCandidateIntersectionType = enum(u32) {
1671 RayQueryCandidateIntersectionTriangleKHR = 0,2589 RayQueryCandidateIntersectionTriangleKHR = 0,
1672 RayQueryCandidateIntersectionAABBKHR = 1,2590 RayQueryCandidateIntersectionAABBKHR = 1,
1673 _,
1674};2591};
src/codegen/spirv/type.zig created+433
...@@ -0,0 +1,433 @@
1//! This module models a SPIR-V Type. These are distinct from Zig types, with some types
2//! which are not representable by Zig directly.
3
4const std = @import("std");
5const assert = std.debug.assert;
6
7const spec = @import("spec.zig");
8
9pub const Type = extern union {
10 tag_if_small_enough: Tag,
11 ptr_otherwise: *Payload,
12
13 /// A reference to another SPIR-V type.
14 pub const Ref = usize;
15
16 pub fn initTag(comptime small_tag: Tag) Type {
17 comptime assert(@enumToInt(small_tag) < Tag.no_payload_count);
18 return .{ .tag_if_small_enough = small_tag };
19 }
20
21 pub fn initPayload(pl: *Payload) Type {
22 assert(@enumToInt(pl.tag) >= Tag.no_payload_count);
23 return .{ .ptr_otherwise = pl };
24 }
25
26 pub fn tag(self: Type) Tag {
27 if (@enumToInt(self.tag_if_small_enough) < Tag.no_payload_count) {
28 return self.tag_if_small_enough;
29 } else {
30 return self.ptr_otherwise.tag;
31 }
32 }
33
34 pub fn castTag(self: Type, comptime t: Tag) ?*t.Type() {
35 if (@enumToInt(self.tag_if_small_enough) < Tag.no_payload_count)
36 return null;
37
38 if (self.ptr_otherwise.tag == t)
39 return self.payload(t);
40
41 return null;
42 }
43
44 /// Access the payload of a type directly.
45 pub fn payload(self: Type, comptime t: Tag) *t.Type() {
46 assert(self.tag() == t);
47 return @fieldParentPtr(t.Type(), "base", self.ptr_otherwise);
48 }
49
50 /// Perform a shallow equality test, comparing two types while assuming that any child types
51 /// are equal only if their references are equal.
52 pub fn eqlShallow(a: Type, b: Type) bool {
53 if (a.tag_if_small_enough == b.tag_if_small_enough)
54 return true;
55
56 const tag_a = a.tag();
57 const tag_b = b.tag();
58 if (tag_a != tag_b)
59 return false;
60
61 inline for (@typeInfo(Tag).Enum.fields) |field| {
62 const t = @field(Tag, field.name);
63 if (t == tag_a) {
64 return eqlPayloads(t, a, b);
65 }
66 }
67
68 unreachable;
69 }
70
71 /// Compare the payload of two compatible tags, given that we already know the tag of both types.
72 fn eqlPayloads(comptime t: Tag, a: Type, b: Type) bool {
73 switch (t) {
74 .void,
75 .bool,
76 .sampler,
77 .event,
78 .device_event,
79 .reserve_id,
80 .queue,
81 .pipe_storage,
82 .named_barrier,
83 => return true,
84 .int,
85 .float,
86 .vector,
87 .matrix,
88 .sampled_image,
89 .array,
90 .runtime_array,
91 .@"opaque",
92 .pointer,
93 .pipe,
94 .image,
95 => return std.meta.eql(a.payload(t).*, b.payload(t).*),
96 .@"struct" => {
97 const struct_a = a.payload(.@"struct");
98 const struct_b = b.payload(.@"struct");
99 if (struct_a.members.len != struct_b.members.len)
100 return false;
101 for (struct_a.members) |mem_a, i| {
102 if (!std.meta.eql(mem_a, struct_b.members[i]))
103 return false;
104 }
105 return true;
106 },
107 .@"function" => {
108 const fn_a = a.payload(.function);
109 const fn_b = b.payload(.function);
110 if (fn_a.return_type != fn_b.return_type)
111 return false;
112 return std.mem.eql(Ref, fn_a.parameters, fn_b.parameters);
113 },
114 }
115 }
116
117 /// Perform a shallow hash, which hashes the reference value of child types instead of recursing.
118 pub fn hashShallow(self: Type) u64 {
119 var hasher = std.hash.Wyhash.init(0);
120 const t = self.tag();
121 std.hash.autoHash(&hasher, t);
122
123 inline for (@typeInfo(Tag).Enum.fields) |field| {
124 if (@field(Tag, field.name) == t) {
125 switch (@field(Tag, field.name)) {
126 .void,
127 .bool,
128 .sampler,
129 .event,
130 .device_event,
131 .reserve_id,
132 .queue,
133 .pipe_storage,
134 .named_barrier,
135 => {},
136 else => self.hashPayload(@field(Tag, field.name), &hasher),
137 }
138 }
139 }
140
141 return hasher.final();
142 }
143
144 /// Perform a shallow hash, given that we know the tag of the field ahead of time.
145 fn hashPayload(self: Type, comptime t: Tag, hasher: *std.hash.Wyhash) void {
146 const fields = @typeInfo(t.Type()).Struct.fields;
147 const pl = self.payload(t);
148 comptime assert(std.mem.eql(u8, fields[0].name, "base"));
149 inline for (fields[1..]) |field| { // Skip the 'base' field.
150 std.hash.autoHashStrat(hasher, @field(pl, field.name), .DeepRecursive);
151 }
152 }
153
154 /// Hash context that hashes and compares types in a shallow fashion, useful for type caches.
155 pub const ShallowHashContext32 = struct {
156 pub fn hash(self: @This(), t: Type) u32 {
157 _ = self;
158 return @truncate(u32, t.hashShallow());
159 }
160 pub fn eql(self: @This(), a: Type, b: Type) bool {
161 _ = self;
162 return a.eqlShallow(b);
163 }
164 };
165
166 /// Return the reference to any child type. Asserts the type is one of:
167 /// - Vectors
168 /// - Matrices
169 /// - Images
170 /// - SampledImages,
171 /// - Arrays
172 /// - RuntimeArrays
173 /// - Pointers
174 pub fn childType(self: Type) Ref {
175 return switch (self.tag()) {
176 .vector => self.payload(.vector).component_type,
177 .matrix => self.payload(.matrix).column_type,
178 .image => self.payload(.image).sampled_type,
179 .sampled_image => self.payload(.sampled_image).image_type,
180 .array => self.payload(.array).element_type,
181 .runtime_array => self.payload(.runtime_array).element_type,
182 .pointer => self.payload(.pointer).child_type,
183 else => unreachable,
184 };
185 }
186
187 pub const Tag = enum(usize) {
188 void,
189 bool,
190 sampler,
191 event,
192 device_event,
193 reserve_id,
194 queue,
195 pipe_storage,
196 named_barrier,
197
198 // After this, the tag requires a payload.
199 int,
200 float,
201 vector,
202 matrix,
203 image,
204 sampled_image,
205 array,
206 runtime_array,
207 @"struct",
208 @"opaque",
209 pointer,
210 function,
211 pipe,
212
213 pub const last_no_payload_tag = Tag.named_barrier;
214 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
215
216 pub fn Type(comptime t: Tag) type {
217 return switch (t) {
218 .void, .bool, .sampler, .event, .device_event, .reserve_id, .queue, .pipe_storage, .named_barrier => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
219 .int => Payload.Int,
220 .float => Payload.Float,
221 .vector => Payload.Vector,
222 .matrix => Payload.Matrix,
223 .image => Payload.Image,
224 .sampled_image => Payload.SampledImage,
225 .array => Payload.Array,
226 .runtime_array => Payload.RuntimeArray,
227 .@"struct" => Payload.Struct,
228 .@"opaque" => Payload.Opaque,
229 .pointer => Payload.Pointer,
230 .function => Payload.Function,
231 .pipe => Payload.Pipe,
232 };
233 }
234 };
235
236 pub const Payload = struct {
237 tag: Tag,
238
239 pub const Int = struct {
240 base: Payload = .{ .tag = .int },
241 width: u32,
242 signedness: std.builtin.Signedness,
243 };
244
245 pub const Float = struct {
246 base: Payload = .{ .tag = .float },
247 width: u32,
248 };
249
250 pub const Vector = struct {
251 base: Payload = .{ .tag = .vector },
252 component_type: Ref,
253 component_count: u32,
254 };
255
256 pub const Matrix = struct {
257 base: Payload = .{ .tag = .matrix },
258 column_type: Ref,
259 column_count: u32,
260 };
261
262 pub const Image = struct {
263 base: Payload = .{ .tag = .image },
264 sampled_type: Ref,
265 dim: spec.Dim,
266 depth: enum(u2) {
267 no = 0,
268 yes = 1,
269 maybe = 2,
270 },
271 arrayed: bool,
272 multisampled: bool,
273 sampled: enum(u2) {
274 known_at_runtime = 0,
275 with_sampler = 1,
276 without_sampler = 2,
277 },
278 format: spec.ImageFormat,
279 access_qualifier: ?spec.AccessQualifier,
280 };
281
282 pub const SampledImage = struct {
283 base: Payload = .{ .tag = .sampled_image },
284 image_type: Ref,
285 };
286
287 pub const Array = struct {
288 base: Payload = .{ .tag = .array },
289 element_type: Ref,
290 /// Note: Must be emitted as constant, not as literal!
291 length: u32,
292 /// Type has the 'ArrayStride' decoration.
293 /// If zero, no stride is present.
294 array_stride: u32,
295 };
296
297 pub const RuntimeArray = struct {
298 base: Payload = .{ .tag = .runtime_array },
299 element_type: Ref,
300 /// Type has the 'ArrayStride' decoration.
301 /// If zero, no stride is present.
302 array_stride: u32,
303 };
304
305 pub const Struct = struct {
306 base: Payload = .{ .tag = .@"struct" },
307 members: []Member,
308 decorations: StructDecorations,
309
310 /// Extra information for decorations, packed for efficiency. Fields are stored sequentially by
311 /// order of the `members` slice and `MemberDecorations` struct.
312 member_decoration_extra: []u32,
313
314 pub const Member = struct {
315 ty: Ref,
316 offset: u32,
317 decorations: MemberDecorations,
318 };
319
320 pub const StructDecorations = packed struct {
321 /// Type has the 'Block' decoration.
322 block: bool,
323 /// Type has the 'BufferBlock' decoration.
324 buffer_block: bool,
325 /// Type has the 'GLSLShared' decoration.
326 glsl_shared: bool,
327 /// Type has the 'GLSLPacked' decoration.
328 glsl_packed: bool,
329 /// Type has the 'CPacked' decoration.
330 c_packed: bool,
331 };
332
333 pub const MemberDecorations = packed struct {
334 /// Matrix layout for (arrays of) matrices. If this field is not .none,
335 /// then there is also an extra field containing the matrix stride corresponding
336 /// to the 'MatrixStride' decoration.
337 matrix_layout: enum(u2) {
338 /// Member has the 'RowMajor' decoration. The member type
339 /// must be a matrix or an array of matrices.
340 row_major,
341 /// Member has the 'ColMajor' decoration. The member type
342 /// must be a matrix or an array of matrices.
343 col_major,
344 /// Member is not a matrix or array of matrices.
345 none,
346 },
347
348 // Regular decorations, these do not imply extra fields.
349
350 /// Member has the 'NoPerspective' decoration.
351 no_perspective: bool,
352 /// Member has the 'Flat' decoration.
353 flat: bool,
354 /// Member has the 'Patch' decoration.
355 patch: bool,
356 /// Member has the 'Centroid' decoration.
357 centroid: bool,
358 /// Member has the 'Sample' decoration.
359 sample: bool,
360 /// Member has the 'Invariant' decoration.
361 /// Note: requires parent struct to have 'Block'.
362 invariant: bool,
363 /// Member has the 'Volatile' decoration.
364 @"volatile": bool,
365 /// Member has the 'Coherent' decoration.
366 coherent: bool,
367 /// Member has the 'NonWritable' decoration.
368 non_writable: bool,
369 /// Member has the 'NonReadable' decoration.
370 non_readable: bool,
371
372 // The following decorations all imply extra field(s).
373
374 /// Member has the 'BuiltIn' decoration.
375 /// This decoration has an extra field of type `spec.BuiltIn`.
376 /// Note: If any member of a struct has the BuiltIn decoration, all members must have one.
377 /// Note: Each builtin may only be reachable once for a particular entry point.
378 /// Note: The member type may be constrained by a particular built-in, defined in the client API specification.
379 builtin: bool,
380 /// Member has the 'Stream' decoration.
381 /// This member has an extra field of type `u32`.
382 stream: bool,
383 /// Member has the 'Location' decoration.
384 /// This member has an extra field of type `u32`.
385 location: bool,
386 /// Member has the 'Component' decoration.
387 /// This member has an extra field of type `u32`.
388 component: bool,
389 /// Member has the 'XfbBuffer' decoration.
390 /// This member has an extra field of type `u32`.
391 xfb_buffer: bool,
392 /// Member has the 'XfbStride' decoration.
393 /// This member has an extra field of type `u32`.
394 xfb_stride: bool,
395 /// Member has the 'UserSemantic' decoration.
396 /// This member has an extra field of type `[]u8`, which is encoded
397 /// by an `u32` containing the number of chars exactly, and then the string padded to
398 /// a multiple of 4 bytes with zeroes.
399 user_semantic: bool,
400 };
401 };
402
403 pub const Opaque = struct {
404 base: Payload = .{ .tag = .@"opaque" },
405 name: []u8,
406 };
407
408 pub const Pointer = struct {
409 base: Payload = .{ .tag = .pointer },
410 storage_class: spec.StorageClass,
411 child_type: Ref,
412 /// Type has the 'ArrayStride' decoration.
413 /// This is valid for pointers to elements of an array.
414 /// If zero, no stride is present.
415 array_stride: u32,
416 /// Type has the 'Alignment' decoration.
417 alignment: ?u32,
418 /// Type has the 'MaxByteOffset' decoration.
419 max_byte_offset: ?u32,
420 };
421
422 pub const Function = struct {
423 base: Payload = .{ .tag = .function },
424 return_type: Ref,
425 parameters: []Ref,
426 };
427
428 pub const Pipe = struct {
429 base: Payload = .{ .tag = .pipe },
430 qualifier: spec.AccessQualifier,
431 };
432 };
433};
src/link/SpirV.zig+103-69
...@@ -24,6 +24,7 @@ const SpirV = @This();...@@ -24,6 +24,7 @@ const SpirV = @This();
2424
25const std = @import("std");25const std = @import("std");
26const Allocator = std.mem.Allocator;26const Allocator = std.mem.Allocator;
27const ArenaAllocator = std.heap.ArenaAllocator;
27const assert = std.debug.assert;28const assert = std.debug.assert;
28const log = std.log.scoped(.link);29const log = std.log.scoped(.link);
2930
...@@ -31,19 +32,21 @@ const Module = @import("../Module.zig");...@@ -31,19 +32,21 @@ const Module = @import("../Module.zig");
31const Compilation = @import("../Compilation.zig");32const Compilation = @import("../Compilation.zig");
32const link = @import("../link.zig");33const link = @import("../link.zig");
33const codegen = @import("../codegen/spirv.zig");34const codegen = @import("../codegen/spirv.zig");
34const Word = codegen.Word;
35const ResultId = codegen.ResultId;
36const trace = @import("../tracy.zig").trace;35const trace = @import("../tracy.zig").trace;
37const build_options = @import("build_options");36const build_options = @import("build_options");
38const spec = @import("../codegen/spirv/spec.zig");
39const Air = @import("../Air.zig");37const Air = @import("../Air.zig");
40const Liveness = @import("../Liveness.zig");38const Liveness = @import("../Liveness.zig");
39const Value = @import("../value.zig").Value;
40
41const SpvModule = @import("../codegen/spirv/Module.zig");
42const spec = @import("../codegen/spirv/spec.zig");
43const IdResult = spec.IdResult;
4144
42// TODO: Should this struct be used at all rather than just a hashmap of aux data for every decl?45// TODO: Should this struct be used at all rather than just a hashmap of aux data for every decl?
43pub const FnData = struct {46pub const FnData = struct {
44 // We're going to fill these in flushModule, and we're going to fill them unconditionally,47 // We're going to fill these in flushModule, and we're going to fill them unconditionally,
45 // so just set it to undefined.48 // so just set it to undefined.
46 id: ResultId = undefined,49 id: IdResult = undefined,
47};50};
4851
49base: link.File,52base: link.File,
...@@ -55,7 +58,15 @@ decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, DeclGenContext) = .{},...@@ -55,7 +58,15 @@ decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, DeclGenContext) = .{},
5558
56const DeclGenContext = struct {59const DeclGenContext = struct {
57 air: Air,60 air: Air,
61 air_value_arena: ArenaAllocator.State,
58 liveness: Liveness,62 liveness: Liveness,
63
64 fn deinit(self: *DeclGenContext, gpa: Allocator) void {
65 self.air.deinit(gpa);
66 self.liveness.deinit(gpa);
67 self.air_value_arena.promote(gpa).deinit();
68 self.* = undefined;
69 }
59};70};
6071
61pub fn createEmpty(gpa: Allocator, options: link.Options) !*SpirV {72pub fn createEmpty(gpa: Allocator, options: link.Options) !*SpirV {
...@@ -113,12 +124,27 @@ pub fn updateFunc(self: *SpirV, module: *Module, func: *Module.Fn, air: Air, liv...@@ -113,12 +124,27 @@ pub fn updateFunc(self: *SpirV, module: *Module, func: *Module.Fn, air: Air, liv
113 @panic("Attempted to compile for architecture that was disabled by build configuration");124 @panic("Attempted to compile for architecture that was disabled by build configuration");
114 }125 }
115 _ = module;126 _ = module;
127
116 // Keep track of all decls so we can iterate over them on flush().128 // Keep track of all decls so we can iterate over them on flush().
117 _ = try self.decl_table.getOrPut(self.base.allocator, func.owner_decl);129 const result = try self.decl_table.getOrPut(self.base.allocator, func.owner_decl);
130 if (result.found_existing) {
131 result.value_ptr.deinit(self.base.allocator);
132 }
133
134 var arena = ArenaAllocator.init(self.base.allocator);
135 errdefer arena.deinit();
136
137 var new_air = try cloneAir(air, self.base.allocator, arena.allocator());
138 errdefer new_air.deinit(self.base.allocator);
118139
119 _ = air;140 var new_liveness = try cloneLiveness(liveness, self.base.allocator);
120 _ = liveness;141 errdefer new_liveness.deinit(self.base.allocator);
121 @panic("TODO SPIR-V needs to keep track of Air and Liveness so it can use them later");142
143 result.value_ptr.* = .{
144 .air = new_air,
145 .air_value_arena = arena.state,
146 .liveness = new_liveness,
147 };
122}148}
123149
124pub fn updateDecl(self: *SpirV, module: *Module, decl: *Module.Decl) !void {150pub fn updateDecl(self: *SpirV, module: *Module, decl: *Module.Decl) !void {
...@@ -143,7 +169,11 @@ pub fn updateDeclExports(...@@ -143,7 +169,11 @@ pub fn updateDeclExports(
143}169}
144170
145pub fn freeDecl(self: *SpirV, decl: *Module.Decl) void {171pub fn freeDecl(self: *SpirV, decl: *Module.Decl) void {
146 assert(self.decl_table.swapRemove(decl));172 const index = self.decl_table.getIndex(decl).?;
173 if (decl.val.tag() == .function) {
174 self.decl_table.values()[index].deinit(self.base.allocator);
175 }
176 self.decl_table.swapRemoveAt(index);
147}177}
148178
149pub fn flush(self: *SpirV, comp: *Compilation) !void {179pub fn flush(self: *SpirV, comp: *Compilation) !void {
...@@ -165,7 +195,10 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {...@@ -165,7 +195,10 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
165 const module = self.base.options.module.?;195 const module = self.base.options.module.?;
166 const target = comp.getTarget();196 const target = comp.getTarget();
167197
168 var spv = codegen.SPIRVModule.init(self.base.allocator, module);198 var arena = std.heap.ArenaAllocator.init(self.base.allocator);
199 defer arena.deinit();
200
201 var spv = SpvModule.init(self.base.allocator, arena.allocator());
169 defer spv.deinit();202 defer spv.deinit();
170203
171 // Allocate an ID for every declaration before generating code,204 // Allocate an ID for every declaration before generating code,
...@@ -173,73 +206,38 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {...@@ -173,73 +206,38 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
173 // TODO: We're allocating an ID unconditionally now, are there206 // TODO: We're allocating an ID unconditionally now, are there
174 // declarations which don't generate a result?207 // declarations which don't generate a result?
175 // TODO: fn_link is used here, but thats probably not the right field. It will work anyway though.208 // TODO: fn_link is used here, but thats probably not the right field. It will work anyway though.
176 {209 for (self.decl_table.keys()) |decl| {
177 for (self.decl_table.keys()) |decl| {210 if (decl.has_tv) {
178 if (!decl.has_tv) continue;211 decl.fn_link.spirv.id = spv.allocId();
179
180 decl.fn_link.spirv.id = spv.allocResultId();
181 }212 }
182 }213 }
183214
184 // Now, actually generate the code for all declarations.215 // Now, actually generate the code for all declarations.
185 {216 var decl_gen = codegen.DeclGen.init(module, &spv);
186 var decl_gen = codegen.DeclGen.init(&spv);217 defer decl_gen.deinit();
187 defer decl_gen.deinit();
188
189 var it = self.decl_table.iterator();
190 while (it.next()) |entry| {
191 const decl = entry.key_ptr.*;
192 if (!decl.has_tv) continue;
193
194 const air = entry.value_ptr.air;
195 const liveness = entry.value_ptr.liveness;
196
197 if (try decl_gen.gen(decl, air, liveness)) |msg| {
198 try module.failed_decls.put(module.gpa, decl, msg);
199 return; // TODO: Attempt to generate more decls?
200 }
201 }
202 }
203
204 try writeCapabilities(&spv.binary.capabilities_and_extensions, target);
205 try writeMemoryModel(&spv.binary.capabilities_and_extensions, target);
206218
207 const header = [_]Word{219 var it = self.decl_table.iterator();
208 spec.magic_number,220 while (it.next()) |entry| {
209 (spec.version.major << 16) | (spec.version.minor << 8),221 const decl = entry.key_ptr.*;
210 0, // TODO: Register Zig compiler magic number.222 if (!decl.has_tv) continue;
211 spv.resultIdBound(),
212 0, // Schema (currently reserved for future use in the SPIR-V spec).
213 };
214223
215 // Note: The order of adding sections to the final binary224 const air = entry.value_ptr.air;
216 // follows the SPIR-V logical module format!225 const liveness = entry.value_ptr.liveness;
217 const buffers = &[_][]const Word{
218 &header,
219 spv.binary.capabilities_and_extensions.items,
220 spv.binary.debug_strings.items,
221 spv.binary.types_globals_constants.items,
222 spv.binary.fn_decls.items,
223 };
224226
225 var iovc_buffers: [buffers.len]std.os.iovec_const = undefined;227 // Note, if `decl` is not a function, air/liveness may be undefined.
226 for (iovc_buffers) |*iovc, i| {228 if (try decl_gen.gen(decl, air, liveness)) |msg| {
227 const bytes = std.mem.sliceAsBytes(buffers[i]);229 try module.failed_decls.put(module.gpa, decl, msg);
228 iovc.* = .{ .iov_base = bytes.ptr, .iov_len = bytes.len };230 return; // TODO: Attempt to generate more decls?
231 }
229 }232 }
230233
231 var file_size: u64 = 0;234 try writeCapabilities(&spv, target);
232 for (iovc_buffers) |iov| {235 try writeMemoryModel(&spv, target);
233 file_size += iov.iov_len;
234 }
235236
236 const file = self.base.file.?;237 try spv.flush(self.base.file.?);
237 try file.seekTo(0);
238 try file.setEndPos(file_size);
239 try file.pwritevAll(&iovc_buffers, 0);
240}238}
241239
242fn writeCapabilities(binary: *std.ArrayList(Word), target: std.Target) !void {240fn writeCapabilities(spv: *SpvModule, target: std.Target) !void {
243 // TODO: Integrate with a hypothetical feature system241 // TODO: Integrate with a hypothetical feature system
244 const cap: spec.Capability = switch (target.os.tag) {242 const cap: spec.Capability = switch (target.os.tag) {
245 .opencl => .Kernel,243 .opencl => .Kernel,
...@@ -248,10 +246,12 @@ fn writeCapabilities(binary: *std.ArrayList(Word), target: std.Target) !void {...@@ -248,10 +246,12 @@ fn writeCapabilities(binary: *std.ArrayList(Word), target: std.Target) !void {
248 else => unreachable, // TODO246 else => unreachable, // TODO
249 };247 };
250248
251 try codegen.writeInstruction(binary, .OpCapability, &[_]Word{@enumToInt(cap)});249 try spv.sections.capabilities.emit(spv.gpa, .OpCapability, .{
250 .capability = cap,
251 });
252}252}
253253
254fn writeMemoryModel(binary: *std.ArrayList(Word), target: std.Target) !void {254fn writeMemoryModel(spv: *SpvModule, target: std.Target) !void {
255 const addressing_model = switch (target.os.tag) {255 const addressing_model = switch (target.os.tag) {
256 .opencl => switch (target.cpu.arch) {256 .opencl => switch (target.cpu.arch) {
257 .spirv32 => spec.AddressingModel.Physical32,257 .spirv32 => spec.AddressingModel.Physical32,
...@@ -269,7 +269,41 @@ fn writeMemoryModel(binary: *std.ArrayList(Word), target: std.Target) !void {...@@ -269,7 +269,41 @@ fn writeMemoryModel(binary: *std.ArrayList(Word), target: std.Target) !void {
269 else => unreachable,269 else => unreachable,
270 };270 };
271271
272 try codegen.writeInstruction(binary, .OpMemoryModel, &[_]Word{272 // TODO: Put this in a proper section.
273 @enumToInt(addressing_model), @enumToInt(memory_model),273 try spv.sections.capabilities.emit(spv.gpa, .OpMemoryModel, .{
274 .addressing_model = addressing_model,
275 .memory_model = memory_model,
274 });276 });
275}277}
278
279fn cloneLiveness(l: Liveness, gpa: Allocator) !Liveness {
280 const tomb_bits = try gpa.dupe(usize, l.tomb_bits);
281 errdefer gpa.free(tomb_bits);
282
283 const extra = try gpa.dupe(u32, l.extra);
284 errdefer gpa.free(extra);
285
286 return Liveness{
287 .tomb_bits = tomb_bits,
288 .extra = extra,
289 .special = try l.special.clone(gpa),
290 };
291}
292
293fn cloneAir(air: Air, gpa: Allocator, value_arena: Allocator) !Air {
294 const values = try gpa.alloc(Value, air.values.len);
295 errdefer gpa.free(values);
296
297 for (values) |*value, i| {
298 value.* = try air.values[i].copy(value_arena);
299 }
300
301 var instructions = try air.instructions.toMultiArrayList().clone(gpa);
302 errdefer instructions.deinit(gpa);
303
304 return Air{
305 .instructions = instructions.slice(),
306 .extra = try gpa.dupe(u32, air.extra),
307 .values = values,
308 };
309}
tools/gen_spirv_spec.zig+427-55
...@@ -1,5 +1,8 @@...@@ -1,5 +1,8 @@
1const std = @import("std");1const std = @import("std");
2const g = @import("spirv/grammar.zig");2const g = @import("spirv/grammar.zig");
3const Allocator = std.mem.Allocator;
4
5const ExtendedStructSet = std.StringHashMap(void);
36
4pub fn main() !void {7pub fn main() !void {
5 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);8 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
...@@ -20,101 +23,308 @@ pub fn main() !void {...@@ -20,101 +23,308 @@ pub fn main() !void {
20 var tokens = std.json.TokenStream.init(spec);23 var tokens = std.json.TokenStream.init(spec);
21 var registry = try std.json.parse(g.Registry, &tokens, .{ .allocator = allocator });24 var registry = try std.json.parse(g.Registry, &tokens, .{ .allocator = allocator });
2225
26 const core_reg = switch (registry) {
27 .core => |core_reg| core_reg,
28 .extension => return error.TODOSpirVExtensionSpec,
29 };
30
23 var bw = std.io.bufferedWriter(std.io.getStdOut().writer());31 var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
24 try render(bw.writer(), registry);32 try render(bw.writer(), allocator, core_reg);
25 try bw.flush();33 try bw.flush();
26}34}
2735
28fn render(writer: anytype, registry: g.Registry) !void {36/// Returns a set with types that require an extra struct for the `Instruction` interface
37/// to the spir-v spec, or whether the original type can be used.
38fn extendedStructs(
39 arena: Allocator,
40 kinds: []const g.OperandKind,
41) !ExtendedStructSet {
42 var map = ExtendedStructSet.init(arena);
43 try map.ensureTotalCapacity(@intCast(u32, kinds.len));
44
45 for (kinds) |kind| {
46 const enumerants = kind.enumerants orelse continue;
47
48 for (enumerants) |enumerant| {
49 if (enumerant.parameters.len > 0) {
50 break;
51 }
52 } else continue;
53
54 map.putAssumeCapacity(kind.kind, {});
55 }
56
57 return map;
58}
59
60// Return a score for a particular priority. Duplicate instruction/operand enum values are
61// removed by picking the tag with the lowest score to keep, and by making an alias for the
62// other. Note that the tag does not need to be just a tag at this point, in which case it
63// gets the lowest score automatically anyway.
64fn tagPriorityScore(tag: []const u8) usize {
65 if (tag.len == 0) {
66 return 1;
67 } else if (std.mem.eql(u8, tag, "EXT")) {
68 return 2;
69 } else if (std.mem.eql(u8, tag, "KHR")) {
70 return 3;
71 } else {
72 return 4;
73 }
74}
75
76fn render(writer: anytype, allocator: Allocator, registry: g.CoreRegistry) !void {
29 try writer.writeAll(77 try writer.writeAll(
30 \\//! This file is auto-generated by tools/gen_spirv_spec.zig.78 \\//! This file is auto-generated by tools/gen_spirv_spec.zig.
31 \\79 \\
32 \\const Version = @import("std").builtin.Version;80 \\const Version = @import("std").builtin.Version;
33 \\81 \\
82 \\pub const Word = u32;
83 \\pub const IdResultType = struct{
84 \\ id: Word,
85 \\ pub fn toRef(self: IdResultType) IdRef {
86 \\ return .{.id = self.id};
87 \\ }
88 \\};
89 \\pub const IdResult = struct{
90 \\ id: Word,
91 \\ pub fn toRef(self: IdResult) IdRef {
92 \\ return .{.id = self.id};
93 \\ }
94 \\ pub fn toResultType(self: IdResult) IdResultType {
95 \\ return .{.id = self.id};
96 \\ }
97 \\};
98 \\pub const IdRef = struct{ id: Word };
99 \\
100 \\pub const IdMemorySemantics = IdRef;
101 \\pub const IdScope = IdRef;
102 \\
103 \\pub const LiteralInteger = Word;
104 \\pub const LiteralString = []const u8;
105 \\pub const LiteralContextDependentNumber = union(enum) {
106 \\ int32: i32,
107 \\ uint32: u32,
108 \\ int64: i64,
109 \\ uint64: u64,
110 \\ float32: f32,
111 \\ float64: f64,
112 \\};
113 \\pub const LiteralExtInstInteger = struct{ inst: Word };
114 \\pub const LiteralSpecConstantOpInteger = struct { opcode: Opcode };
115 \\pub const PairLiteralIntegerIdRef = struct { value: LiteralInteger, label: IdRef };
116 \\pub const PairIdRefLiteralInteger = struct { target: IdRef, member: LiteralInteger };
117 \\pub const PairIdRefIdRef = [2]IdRef;
118 \\
119 \\
34 );120 );
35121
36 switch (registry) {122 try writer.print(
37 .core => |core_reg| {123 \\pub const version = Version{{ .major = {}, .minor = {}, .patch = {} }};
38 try writer.print(124 \\pub const magic_number: Word = {s};
39 \\pub const version = Version{{ .major = {}, .minor = {}, .patch = {} }};125 \\
40 \\pub const magic_number: u32 = {s};126 ,
41 \\127 .{ registry.major_version, registry.minor_version, registry.revision, registry.magic_number },
42 ,128 );
43 .{ core_reg.major_version, core_reg.minor_version, core_reg.revision, core_reg.magic_number },129 const extended_structs = try extendedStructs(allocator, registry.operand_kinds);
44 );130 try renderOpcodes(writer, allocator, registry.instructions, extended_structs);
45 try renderOpcodes(writer, core_reg.instructions);131 try renderOperandKinds(writer, allocator, registry.operand_kinds, extended_structs);
46 try renderOperandKinds(writer, core_reg.operand_kinds);
47 },
48 .extension => |ext_reg| {
49 try writer.print(
50 \\pub const version = Version{{ .major = {}, .minor = 0, .patch = {} }};
51 \\
52 ,
53 .{ ext_reg.version, ext_reg.revision },
54 );
55 try renderOpcodes(writer, ext_reg.instructions);
56 try renderOperandKinds(writer, ext_reg.operand_kinds);
57 },
58 }
59}132}
60133
61fn renderOpcodes(writer: anytype, instructions: []const g.Instruction) !void {134fn renderOpcodes(
62 try writer.writeAll("pub const Opcode = extern enum(u16) {\n");135 writer: anytype,
63 for (instructions) |instr| {136 allocator: Allocator,
64 try writer.print(" {} = {},\n", .{ std.zig.fmtId(instr.opname), instr.opcode });137 instructions: []const g.Instruction,
138 extended_structs: ExtendedStructSet,
139) !void {
140 var inst_map = std.AutoArrayHashMap(u32, usize).init(allocator);
141 try inst_map.ensureTotalCapacity(instructions.len);
142
143 var aliases = std.ArrayList(struct { inst: usize, alias: usize }).init(allocator);
144 try aliases.ensureTotalCapacity(instructions.len);
145
146 for (instructions) |inst, i| {
147 const result = inst_map.getOrPutAssumeCapacity(inst.opcode);
148 if (!result.found_existing) {
149 result.value_ptr.* = i;
150 continue;
151 }
152
153 const existing = instructions[result.value_ptr.*];
154
155 const tag_index = std.mem.indexOfDiff(u8, inst.opname, existing.opname).?;
156 const inst_priority = tagPriorityScore(inst.opname[tag_index..]);
157 const existing_priority = tagPriorityScore(existing.opname[tag_index..]);
158
159 if (inst_priority < existing_priority) {
160 aliases.appendAssumeCapacity(.{ .inst = result.value_ptr.*, .alias = i });
161 result.value_ptr.* = i;
162 } else {
163 aliases.appendAssumeCapacity(.{ .inst = i, .alias = result.value_ptr.* });
164 }
65 }165 }
66 try writer.writeAll(" _,\n};\n");166
167 const instructions_indices = inst_map.values();
168
169 try writer.writeAll("pub const Opcode = enum(u16) {\n");
170 for (instructions_indices) |i| {
171 const inst = instructions[i];
172 try writer.print("{} = {},\n", .{ std.zig.fmtId(inst.opname), inst.opcode });
173 }
174
175 try writer.writeByte('\n');
176
177 for (aliases.items) |alias| {
178 try writer.print("pub const {} = Opcode.{};\n", .{
179 std.zig.fmtId(instructions[alias.inst].opname),
180 std.zig.fmtId(instructions[alias.alias].opname),
181 });
182 }
183
184 try writer.writeAll(
185 \\
186 \\pub fn Operands(comptime self: Opcode) type {
187 \\return switch (self) {
188 \\
189 );
190
191 for (instructions_indices) |i| {
192 const inst = instructions[i];
193 try renderOperand(writer, .instruction, inst.opname, inst.operands, extended_structs);
194 }
195 try writer.writeAll("};\n}\n};\n");
196 _ = extended_structs;
67}197}
68198
69fn renderOperandKinds(writer: anytype, kinds: []const g.OperandKind) !void {199fn renderOperandKinds(
200 writer: anytype,
201 allocator: Allocator,
202 kinds: []const g.OperandKind,
203 extended_structs: ExtendedStructSet,
204) !void {
70 for (kinds) |kind| {205 for (kinds) |kind| {
71 switch (kind.category) {206 switch (kind.category) {
72 .ValueEnum => try renderValueEnum(writer, kind),207 .ValueEnum => try renderValueEnum(writer, allocator, kind, extended_structs),
73 .BitEnum => try renderBitEnum(writer, kind),208 .BitEnum => try renderBitEnum(writer, allocator, kind, extended_structs),
74 else => {},209 else => {},
75 }210 }
76 }211 }
77}212}
78213
79fn renderValueEnum(writer: anytype, enumeration: g.OperandKind) !void {214fn renderValueEnum(
80 try writer.print("pub const {s} = extern enum(u32) {{\n", .{enumeration.kind});215 writer: anytype,
81216 allocator: Allocator,
217 enumeration: g.OperandKind,
218 extended_structs: ExtendedStructSet,
219) !void {
82 const enumerants = enumeration.enumerants orelse return error.InvalidRegistry;220 const enumerants = enumeration.enumerants orelse return error.InvalidRegistry;
83 for (enumerants) |enumerant| {221
222 var enum_map = std.AutoArrayHashMap(u32, usize).init(allocator);
223 try enum_map.ensureTotalCapacity(enumerants.len);
224
225 var aliases = std.ArrayList(struct { enumerant: usize, alias: usize }).init(allocator);
226 try aliases.ensureTotalCapacity(enumerants.len);
227
228 for (enumerants) |enumerant, i| {
229 const result = enum_map.getOrPutAssumeCapacity(enumerant.value.int);
230 if (!result.found_existing) {
231 result.value_ptr.* = i;
232 continue;
233 }
234
235 const existing = enumerants[result.value_ptr.*];
236
237 const tag_index = std.mem.indexOfDiff(u8, enumerant.enumerant, existing.enumerant).?;
238 const enum_priority = tagPriorityScore(enumerant.enumerant[tag_index..]);
239 const existing_priority = tagPriorityScore(existing.enumerant[tag_index..]);
240
241 if (enum_priority < existing_priority) {
242 aliases.appendAssumeCapacity(.{ .enumerant = result.value_ptr.*, .alias = i });
243 result.value_ptr.* = i;
244 } else {
245 aliases.appendAssumeCapacity(.{ .enumerant = i, .alias = result.value_ptr.* });
246 }
247 }
248
249 const enum_indices = enum_map.values();
250
251 try writer.print("pub const {s} = enum(u32) {{\n", .{std.zig.fmtId(enumeration.kind)});
252
253 for (enum_indices) |i| {
254 const enumerant = enumerants[i];
84 if (enumerant.value != .int) return error.InvalidRegistry;255 if (enumerant.value != .int) return error.InvalidRegistry;
85256
86 try writer.print(" {} = {},\n", .{ std.zig.fmtId(enumerant.enumerant), enumerant.value.int });257 try writer.print("{} = {},\n", .{ std.zig.fmtId(enumerant.enumerant), enumerant.value.int });
258 }
259
260 try writer.writeByte('\n');
261
262 for (aliases.items) |alias| {
263 try writer.print("pub const {} = {}.{};\n", .{
264 std.zig.fmtId(enumerants[alias.enumerant].enumerant),
265 std.zig.fmtId(enumeration.kind),
266 std.zig.fmtId(enumerants[alias.alias].enumerant),
267 });
268 }
269
270 if (!extended_structs.contains(enumeration.kind)) {
271 try writer.writeAll("};\n");
272 return;
273 }
274
275 try writer.print("\npub const Extended = union({}) {{\n", .{std.zig.fmtId(enumeration.kind)});
276
277 for (enum_indices) |i| {
278 const enumerant = enumerants[i];
279 try renderOperand(writer, .@"union", enumerant.enumerant, enumerant.parameters, extended_structs);
87 }280 }
88281
89 try writer.writeAll(" _,\n};\n");282 try writer.writeAll("};\n};\n");
90}283}
91284
92fn renderBitEnum(writer: anytype, enumeration: g.OperandKind) !void {285fn renderBitEnum(
93 try writer.print("pub const {s} = packed struct {{\n", .{enumeration.kind});286 writer: anytype,
287 allocator: Allocator,
288 enumeration: g.OperandKind,
289 extended_structs: ExtendedStructSet,
290) !void {
291 try writer.print("pub const {s} = packed struct {{\n", .{std.zig.fmtId(enumeration.kind)});
94292
95 var flags_by_bitpos = [_]?[]const u8{null} ** 32;293 var flags_by_bitpos = [_]?usize{null} ** 32;
96 const enumerants = enumeration.enumerants orelse return error.InvalidRegistry;294 const enumerants = enumeration.enumerants orelse return error.InvalidRegistry;
97 for (enumerants) |enumerant| {295
296 var aliases = std.ArrayList(struct { flag: usize, alias: u5 }).init(allocator);
297 try aliases.ensureTotalCapacity(enumerants.len);
298
299 for (enumerants) |enumerant, i| {
98 if (enumerant.value != .bitflag) return error.InvalidRegistry;300 if (enumerant.value != .bitflag) return error.InvalidRegistry;
99 const value = try parseHexInt(enumerant.value.bitflag);301 const value = try parseHexInt(enumerant.value.bitflag);
100 if (@popCount(u32, value) != 1) {302 if (@popCount(u32, value) == 0) {
101 continue; // Skip combinations and 'none' items303 continue; // Skip 'none' items
102 }304 }
103305
306 std.debug.assert(@popCount(u32, value) == 1);
307
104 var bitpos = std.math.log2_int(u32, value);308 var bitpos = std.math.log2_int(u32, value);
105 if (flags_by_bitpos[bitpos]) |*existing| {309 if (flags_by_bitpos[bitpos]) |*existing| {
106 // Keep the shortest310 const tag_index = std.mem.indexOfDiff(u8, enumerant.enumerant, enumerants[existing.*].enumerant).?;
107 if (enumerant.enumerant.len < existing.len)311 const enum_priority = tagPriorityScore(enumerant.enumerant[tag_index..]);
108 existing.* = enumerant.enumerant;312 const existing_priority = tagPriorityScore(enumerants[existing.*].enumerant[tag_index..]);
313
314 if (enum_priority < existing_priority) {
315 aliases.appendAssumeCapacity(.{ .flag = existing.*, .alias = bitpos });
316 existing.* = i;
317 } else {
318 aliases.appendAssumeCapacity(.{ .flag = i, .alias = bitpos });
319 }
109 } else {320 } else {
110 flags_by_bitpos[bitpos] = enumerant.enumerant;321 flags_by_bitpos[bitpos] = i;
111 }322 }
112 }323 }
113324
114 for (flags_by_bitpos) |maybe_flag_name, bitpos| {325 for (flags_by_bitpos) |maybe_flag_index, bitpos| {
115 try writer.writeAll(" ");326 if (maybe_flag_index) |flag_index| {
116 if (maybe_flag_name) |flag_name| {327 try writer.print("{}", .{std.zig.fmtId(enumerants[flag_index].enumerant)});
117 try writer.writeAll(flag_name);
118 } else {328 } else {
119 try writer.print("_reserved_bit_{}", .{bitpos});329 try writer.print("_reserved_bit_{}", .{bitpos});
120 }330 }
...@@ -126,7 +336,169 @@ fn renderBitEnum(writer: anytype, enumeration: g.OperandKind) !void {...@@ -126,7 +336,169 @@ fn renderBitEnum(writer: anytype, enumeration: g.OperandKind) !void {
126 try writer.writeAll("= false,\n");336 try writer.writeAll("= false,\n");
127 }337 }
128338
129 try writer.writeAll("};\n");339 try writer.writeByte('\n');
340
341 for (aliases.items) |alias| {
342 try writer.print("pub const {}: {} = .{{.{} = true}};\n", .{
343 std.zig.fmtId(enumerants[alias.flag].enumerant),
344 std.zig.fmtId(enumeration.kind),
345 std.zig.fmtId(enumerants[flags_by_bitpos[alias.alias].?].enumerant),
346 });
347 }
348
349 if (!extended_structs.contains(enumeration.kind)) {
350 try writer.writeAll("};\n");
351 return;
352 }
353
354 try writer.print("\npub const Extended = struct {{\n", .{});
355
356 for (flags_by_bitpos) |maybe_flag_index, bitpos| {
357 const flag_index = maybe_flag_index orelse {
358 try writer.print("_reserved_bit_{}: bool = false,\n", .{bitpos});
359 continue;
360 };
361 const enumerant = enumerants[flag_index];
362
363 try renderOperand(writer, .mask, enumerant.enumerant, enumerant.parameters, extended_structs);
364 }
365
366 try writer.writeAll("};\n};\n");
367}
368
369fn renderOperand(
370 writer: anytype,
371 kind: enum {
372 @"union",
373 instruction,
374 mask,
375 },
376 field_name: []const u8,
377 parameters: []const g.Operand,
378 extended_structs: ExtendedStructSet,
379) !void {
380 if (kind == .instruction) {
381 try writer.writeByte('.');
382 }
383 try writer.print("{}", .{std.zig.fmtId(field_name)});
384 if (parameters.len == 0) {
385 switch (kind) {
386 .@"union" => try writer.writeAll(",\n"),
387 .instruction => try writer.writeAll(" => void,\n"),
388 .mask => try writer.writeAll(": bool = false,\n"),
389 }
390 return;
391 }
392
393 if (kind == .instruction) {
394 try writer.writeAll(" => ");
395 } else {
396 try writer.writeAll(": ");
397 }
398
399 if (kind == .mask) {
400 try writer.writeByte('?');
401 }
402
403 try writer.writeAll("struct{");
404
405 for (parameters) |param, j| {
406 if (j != 0) {
407 try writer.writeAll(", ");
408 }
409
410 try renderFieldName(writer, parameters, j);
411 try writer.writeAll(": ");
412
413 if (param.quantifier) |q| {
414 switch (q) {
415 .@"?" => try writer.writeByte('?'),
416 .@"*" => try writer.writeAll("[]const "),
417 }
418 }
419
420 try writer.print("{}", .{std.zig.fmtId(param.kind)});
421
422 if (extended_structs.contains(param.kind)) {
423 try writer.writeAll(".Extended");
424 }
425
426 if (param.quantifier) |q| {
427 switch (q) {
428 .@"?" => try writer.writeAll(" = null"),
429 .@"*" => try writer.writeAll(" = &.{}"),
430 }
431 }
432 }
433
434 try writer.writeAll("}");
435
436 if (kind == .mask) {
437 try writer.writeAll(" = null");
438 }
439
440 try writer.writeAll(",\n");
441}
442
443fn renderFieldName(writer: anytype, operands: []const g.Operand, field_index: usize) !void {
444 const operand = operands[field_index];
445
446 // Should be enough for all names - adjust as needed.
447 var name_buffer = std.BoundedArray(u8, 64){
448 .buffer = undefined,
449 };
450
451 derive_from_kind: {
452 // Operand names are often in the json encoded as "'Name'" (with two sets of quotes).
453 // Additionally, some operands have ~ in them at the end (D~ref~).
454 const name = std.mem.trim(u8, operand.name, "'~");
455 if (name.len == 0) {
456 break :derive_from_kind;
457 }
458
459 // Some names have weird characters in them (like newlines) - skip any such ones.
460 // Use the same loop to transform to snake-case.
461 for (name) |c| {
462 switch (c) {
463 'a'...'z', '0'...'9' => try name_buffer.append(c),
464 'A'...'Z' => try name_buffer.append(std.ascii.toLower(c)),
465 ' ', '~' => try name_buffer.append('_'),
466 else => break :derive_from_kind,
467 }
468 }
469
470 // Assume there are no duplicate 'name' fields.
471 try writer.print("{}", .{std.zig.fmtId(name_buffer.slice())});
472 return;
473 }
474
475 // Translate to snake case.
476 name_buffer.len = 0;
477 for (operand.kind) |c, i| {
478 switch (c) {
479 'a'...'z', '0'...'9' => try name_buffer.append(c),
480 'A'...'Z' => if (i > 0 and std.ascii.isLower(operand.kind[i - 1])) {
481 try name_buffer.appendSlice(&[_]u8{ '_', std.ascii.toLower(c) });
482 } else {
483 try name_buffer.append(std.ascii.toLower(c));
484 },
485 else => unreachable, // Assume that the name is valid C-syntax (and contains no underscores).
486 }
487 }
488
489 try writer.print("{}", .{std.zig.fmtId(name_buffer.slice())});
490
491 // For fields derived from type name, there could be any amount.
492 // Simply check against all other fields, and if another similar one exists, add a number.
493 const need_extra_index = for (operands) |other_operand, i| {
494 if (i != field_index and std.mem.eql(u8, operand.kind, other_operand.kind)) {
495 break true;
496 }
497 } else false;
498
499 if (need_extra_index) {
500 try writer.print("_{}", .{field_index});
501 }
130}502}
131503
132fn parseHexInt(text: []const u8) !u31 {504fn parseHexInt(text: []const u8) !u31 {
...@@ -142,7 +514,7 @@ fn usageAndExit(file: std.fs.File, arg0: []const u8, code: u8) noreturn {...@@ -142,7 +514,7 @@ fn usageAndExit(file: std.fs.File, arg0: []const u8, code: u8) noreturn {
142 \\514 \\
143 \\Generates Zig bindings for a SPIR-V specification .json (either core or515 \\Generates Zig bindings for a SPIR-V specification .json (either core or
144 \\extinst versions). The result, printed to stdout, should be used to update516 \\extinst versions). The result, printed to stdout, should be used to update
145 \\files in src/codegen/spirv.517 \\files in src/codegen/spirv. Don't forget to format the output.
146 \\518 \\
147 \\The relevant specifications can be obtained from the SPIR-V registry:519 \\The relevant specifications can be obtained from the SPIR-V registry:
148 \\https://github.com/KhronosGroup/SPIRV-Headers/blob/master/include/spirv/unified1/520 \\https://github.com/KhronosGroup/SPIRV-Headers/blob/master/include/spirv/unified1/