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;
44const log = std.log.scoped(.codegen);
55const assert = std.debug.assert;
66
7const spec = @import("spirv/spec.zig");
8const Opcode = spec.Opcode;
9
107const Module = @import("../Module.zig");
118const Decl = Module.Decl;
129const Type = @import("../type.zig").Type;
......@@ -15,187 +12,78 @@ const LazySrcLoc = Module.LazySrcLoc;
1512const Air = @import("../Air.zig");
1613const Liveness = @import("../Liveness.zig");
1714
18pub const Word = u32;
19pub const ResultId = u32;
15const spec = @import("spirv/spec.zig");
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);
22pub const InstMap = std.AutoHashMap(Air.Inst.Index, ResultId);
26const InstMap = std.AutoHashMapUnmanaged(Air.Inst.Index, IdRef);
2327
2428const IncomingBlock = struct {
25 src_label_id: ResultId,
26 break_value_id: ResultId,
29 src_label_id: IdRef,
30 break_value_id: IdRef,
2731};
2832
29pub const BlockMap = std.AutoHashMap(Air.Inst.Index, struct {
30 label_id: ResultId,
33pub const BlockMap = std.AutoHashMapUnmanaged(Air.Inst.Index, struct {
34 label_id: IdRef,
3135 incoming_blocks: *std.ArrayListUnmanaged(IncomingBlock),
3236});
3337
34pub fn writeOpcode(code: *std.ArrayList(Word), opcode: Opcode, arg_count: u16) !void {
35 const word_count: Word = arg_count + 1;
36 try code.append((word_count << 16) | @enumToInt(opcode));
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.
38/// This structure is used to compile a declaration, and contains all relevant meta-information to deal with that.
39pub const DeclGen = struct {
40 /// The Zig module that we are generating decls for.
7641 module: *Module,
7742
78 /// SPIR-V instructions return result-ids. This variable holds the module-wide counter for these.
79 next_result_id: ResultId,
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 }
43 /// The SPIR-V module code should be put in.
44 spv: *SpvModule,
15345
154 return result.value_ptr.*;
155 }
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,
46 /// The decl we are currently generating code for.
47 decl: *Decl,
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!
16351 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!
16455 liveness: Liveness,
16556
16657 /// An array of function argument result-ids. Each index corresponds with the
16758 /// function argument of the same index.
168 args: std.ArrayList(ResultId),
59 args: std.ArrayListUnmanaged(IdRef) = .{},
16960
17061 /// A counter to keep track of how many `arg` instructions we've seen yet.
17162 next_arg_index: u32,
17263
17364 /// A map keeping track of which instruction generated which result-id.
174 inst_results: InstMap,
65 inst_results: InstMap = .{},
17566
17667 /// We need to keep track of result ids for block labels, as well as the 'incoming'
17768 /// blocks for a block.
178 blocks: BlockMap,
69 blocks: BlockMap = .{},
17970
18071 /// The label of the SPIR-V block we are currently generating.
181 current_block_label_id: ResultId,
72 current_block_label_id: IdRef,
18273
18374 /// The actual instructions for this function. We need to declare all locals in
18475 /// the first block, and because we don't know which locals there are going to be,
18576 /// we're just going to generate everything after the locals-section in this array.
18677 /// Note: It will not contain OpFunction, OpFunctionParameter, OpVariable and the
187 /// initial OpLabel. These will be generated into spv.binary.fn_decls directly.
188 code: std.ArrayList(Word),
78 /// initial OpLabel. These will be generated into spv.sections.functions directly.
79 code: SpvSection = .{},
18980
190 /// The decl we are currently generating code for.
191 decl: *Decl,
192
193 /// If `gen` returned `Error.AnalysisFail`, this contains an explanatory message.
81 /// If `gen` returned `Error.CodegenFail`, this contains an explanatory message.
19482 /// Memory is owned by `module.gpa`.
19583 error_msg: ?*Module.ErrorMsg,
19684
19785 /// Possible errors the `gen` function may return.
198 const Error = error{ AnalysisFail, OutOfMemory };
86 const Error = error{ CodegenFail, OutOfMemory };
19987
20088 /// This structure is used to return information about a type typically used for
20189 /// arithmetic operations. These types may either be integers, floats, or a vector
......@@ -244,18 +132,15 @@ pub const DeclGen = struct {
244132
245133 /// Initialize the common resources of a DeclGen. Some fields are left uninitialized,
246134 /// only set when `gen` is called.
247 pub fn init(spv: *SPIRVModule) DeclGen {
135 pub fn init(module: *Module, spv: *SpvModule) DeclGen {
248136 return .{
137 .module = module,
249138 .spv = spv,
139 .decl = undefined,
250140 .air = undefined,
251141 .liveness = undefined,
252 .args = std.ArrayList(ResultId).init(spv.gpa),
253142 .next_arg_index = undefined,
254 .inst_results = InstMap.init(spv.gpa),
255 .blocks = BlockMap.init(spv.gpa),
256143 .current_block_label_id = undefined,
257 .code = std.ArrayList(Word).init(spv.gpa),
258 .decl = undefined,
259144 .error_msg = undefined,
260145 };
261146 }
......@@ -265,6 +150,7 @@ pub const DeclGen = struct {
265150 /// returns such a reportable error, it is valid to be called again for a different decl.
266151 pub fn gen(self: *DeclGen, decl: *Decl, air: Air, liveness: Liveness) !?*Module.ErrorMsg {
267152 // Reset internal resources, we don't want to re-allocate these.
153 self.decl = decl;
268154 self.air = air;
269155 self.liveness = liveness;
270156 self.args.items.len = 0;
......@@ -272,35 +158,50 @@ pub const DeclGen = struct {
272158 self.inst_results.clearRetainingCapacity();
273159 self.blocks.clearRetainingCapacity();
274160 self.current_block_label_id = undefined;
275 self.code.items.len = 0;
276 self.decl = decl;
161 self.code.reset();
277162 self.error_msg = null;
278163
279 try self.genDecl();
280 return self.error_msg;
164 self.genDecl() catch |err| switch (err) {
165 error.CodegenFail => return self.error_msg,
166 else => |others| return others,
167 };
168
169 return null;
281170 }
282171
283172 /// Free resources owned by the DeclGen.
284173 pub fn deinit(self: *DeclGen) void {
285 self.args.deinit();
286 self.inst_results.deinit();
287 self.blocks.deinit();
288 self.code.deinit();
174 self.args.deinit(self.spv.gpa);
175 self.inst_results.deinit(self.spv.gpa);
176 self.blocks.deinit(self.spv.gpa);
177 self.code.deinit(self.spv.gpa);
289178 }
290179
180 /// Return the target which we are currently compiling for.
291181 fn getTarget(self: *DeclGen) std.Target {
292 return self.spv.module.getTarget();
182 return self.module.getTarget();
293183 }
294184
295185 fn fail(self: *DeclGen, comptime format: []const u8, args: anytype) Error {
296186 @setCold(true);
297187 const src: LazySrcLoc = .{ .node_offset = 0 };
298188 const src_loc = src.toSrcLoc(self.decl);
299 self.error_msg = try Module.ErrorMsg.create(self.spv.module.gpa, src_loc, format, args);
300 return error.AnalysisFail;
189 assert(self.error_msg == null);
190 self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, format, args);
191 return error.CodegenFail;
301192 }
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 {
304205 if (self.air.value(inst)) |val| {
305206 return self.genConstant(self.air.typeOf(inst), val);
306207 }
......@@ -308,9 +209,13 @@ pub const DeclGen = struct {
308209 return self.inst_results.get(index).?; // Assertion means instruction does not dominate usage.
309210 }
310211
311 fn beginSPIRVBlock(self: *DeclGen, label_id: ResultId) !void {
312 try writeInstruction(&self.code, .OpLabel, &[_]Word{label_id});
313 self.current_block_label_id = label_id;
212 /// Start a new SPIR-V block, Emits the label of the new block, and stores which
213 /// block we are currently generating.
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();
314219 }
315220
316221 /// 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 {
392297 const int_info = ty.intInfo(target);
393298 // TODO: Maybe it's useful to also return this value.
394299 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|
396 if (backing_bits == int_info.bits)
397 ArithmeticTypeInfo.Class.integer
300 break :blk ArithmeticTypeInfo{
301 .bits = int_info.bits,
302 .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
398309 else
399 ArithmeticTypeInfo.Class.strange_integer
400 else
401 .composite_integer };
310 .composite_integer,
311 };
402312 },
403313 // 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", .{}),
405315 // 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}),
407317 };
408318 }
409319
410320 /// Generate a constant representing `val`.
411321 /// TODO: Deduplication?
412 fn genConstant(self: *DeclGen, ty: Type, val: Value) Error!ResultId {
322 fn genConstant(self: *DeclGen, ty: Type, val: Value) Error!IdRef {
413323 const target = self.getTarget();
414 const code = &self.spv.binary.types_globals_constants;
415 const result_id = self.spv.allocResultId();
416 const result_type_id = try self.genType(ty);
324 const section = &self.spv.sections.types_globals_constants;
325 const result_id = self.spv.allocId();
326 const result_type_id = try self.resolveTypeId(ty);
417327
418328 if (val.isUndef()) {
419 try writeInstruction(code, .OpUndef, &[_]Word{ result_type_id, result_id });
420 return result_id;
329 try section.emit(self.spv.gpa, .OpUndef, .{ .id_result_type = result_type_id, .id_result = result_id });
330 return result_id.toRef();
421331 }
422332
423333 switch (ty.zigTypeTag()) {
......@@ -425,101 +335,96 @@ pub const DeclGen = struct {
425335 const int_info = ty.intInfo(target);
426336 const backing_bits = self.backingIntBits(int_info.bits) orelse {
427337 // 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});
429339 };
430340
431341 // We can just use toSignedInt/toUnsignedInt here as it returns u64 - a type large enough to hold any
432342 // SPIR-V native type (up to i/u64 with Int64). If SPIR-V ever supports native ints of a larger size, this
433343 // might need to be updated.
434344 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
435348 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 values
438 // only use the actual bits of the type.
439 // TODO: Should this be the backing type bits or the actual type bits?
440 int_bits &= (@as(u64, 1) << @intCast(u6, backing_bits)) - 1;
441
442 switch (backing_bits) {
443 0 => unreachable,
444 1...32 => try writeInstruction(code, .OpConstant, &[_]Word{
445 result_type_id,
446 result_id,
447 @truncate(u32, int_bits),
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 }
350 const value: spec.LiteralContextDependentNumber = switch (backing_bits) {
351 1...32 => .{ .uint32 = @truncate(u32, int_bits) },
352 33...64 => .{ .uint64 = int_bits },
353 else => unreachable,
354 };
355
356 try section.emit(self.spv.gpa, .OpConstant, .{
357 .id_result_type = result_type_id,
358 .id_result = result_id,
359 .value = value,
360 });
457361 },
458362 .Bool => {
459 const opcode: Opcode = if (val.toBool()) .OpConstantTrue else .OpConstantFalse;
460 try writeInstruction(code, opcode, &[_]Word{ result_type_id, result_id });
363 const operands = .{ .id_result_type = result_type_id, .id_result = 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 }
461369 },
462370 .Float => {
463371 // At this point we are guaranteed that the target floating point type is supported, otherwise the function
464 // would have exited at genType(ty).
465
466 // f16 and f32 require one word of storage. f64 requires 2, low-order first.
467
468 switch (ty.floatBits(target)) {
469 16 => try writeInstruction(code, .OpConstant, &[_]Word{ result_type_id, result_id, @bitCast(u16, val.toFloat(f16)) }),
470 32 => try writeInstruction(code, .OpConstant, &[_]Word{ result_type_id, result_id, @bitCast(u32, val.toFloat(f32)) }),
471 64 => {
472 const float_bits = @bitCast(u64, val.toFloat(f64));
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.
372 // would have exited at resolveTypeId(ty).
373
374 const value: spec.LiteralContextDependentNumber = switch (ty.floatBits(target)) {
375 // Prevent upcasting to f32 by bitcasting and writing as a uint32.
376 16 => .{ .uint32 = @bitCast(u16, val.toFloat(f16)) },
377 32 => .{ .float32 = val.toFloat(f32) },
378 64 => .{ .float64 = val.toFloat(f64) },
379 128 => unreachable, // Filtered out in the call to resolveTypeId.
380 // TODO: Insert case for long double when the layout for that is determined?
482381 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 });
484389 },
485390 .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}),
487392 }
488393
489 return result_id;
394 return result_id.toRef();
490395 }
491396
492 fn genType(self: *DeclGen, ty: Type) Error!ResultId {
493 // We can't use getOrPut here so we can recursively generate types.
494 if (self.spv.types.get(ty)) |already_generated| {
495 return already_generated;
496 }
397 /// Turn a Zig type into a SPIR-V Type, and return its type result-id.
398 fn resolveTypeId(self: *DeclGen, ty: Type) !IdResultType {
399 return self.spv.typeResultId(try self.resolveType(ty));
400 }
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 {
498404 const target = self.getTarget();
499 const code = &self.spv.binary.types_globals_constants;
500 const result_id = self.spv.allocResultId();
501
502 switch (ty.zigTypeTag()) {
503 .Void => try writeInstruction(code, .OpTypeVoid, &[_]Word{result_id}),
504 .Bool => try writeInstruction(code, .OpTypeBool, &[_]Word{result_id}),
505 .Int => {
405 return switch (ty.zigTypeTag()) {
406 .Void => try self.spv.resolveType(SpvType.initTag(.void)),
407 .Bool => blk: {
408 // TODO: SPIR-V booleans are opaque. For local variables this is fine, but for structs
409 // members we want to use integer types instead.
410 break :blk try self.spv.resolveType(SpvType.initTag(.bool));
411 },
412 .Int => blk: {
506413 const int_info = ty.intInfo(target);
507414 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.
509 return self.fail("TODO: SPIR-V backend: implement composite int {}", .{ty});
415 // TODO: Integers too big for any native type are represented as "composite integers":
416 // An array of largestSupportedIntBits.
417 return self.todo("Implement composite int type {}", .{ty});
510418 };
511419
512 // TODO: If backing_bits != int_info.bits, a duplicate type might be generated here.
513 try writeInstruction(code, .OpTypeInt, &[_]Word{
514 result_id,
515 backing_bits,
516 switch (int_info.signedness) {
517 .unsigned => 0,
518 .signed => 1,
519 },
520 });
420 const payload = try self.spv.arena.create(SpvType.Payload.Int);
421 payload.* = .{
422 .width = backing_bits,
423 .signedness = int_info.signedness,
424 };
425 break :blk try self.spv.resolveType(SpvType.initPayload(&payload.base));
521426 },
522 .Float => {
427 .Float => blk: {
523428 // We can (and want) not really emulate floating points with other floating point types like with the integer types,
524429 // so if the float is not supported, just return an error.
525430 const bits = ty.floatBits(target);
......@@ -535,37 +440,34 @@ pub const DeclGen = struct {
535440 return self.fail("Floating point width of {} bits is not supported for the current SPIR-V feature set", .{bits});
536441 }
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));
539448 },
540 .Fn => {
449 .Fn => blk: {
541450 // We only support zig-calling-convention functions, no varargs.
542451 if (ty.fnCallingConvention() != .Unspecified)
543452 return self.fail("Unsupported calling convention for SPIR-V", .{});
544453 if (ty.fnIsVarArgs())
545 return self.fail("VarArgs 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 }
454 return self.fail("VarArgs functions are unsupported for SPIR-V", .{});
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.
558 try writeOpcode(code, .OpTypeFunction, 2 + @intCast(u16, ty.fnParamLen()));
559 try code.appendSlice(&.{ result_id, return_type_id });
461 const return_type = try self.resolveType(ty.fnReturnType());
560462
561 i = 0;
562 while (i < params) : (i += 1) {
563 const param_type_id = self.spv.types.get(ty.fnParamType(i)).?;
564 try code.append(param_type_id);
565 }
463 const payload = try self.spv.arena.create(SpvType.Payload.Function);
464 payload.* = .{ .return_type = return_type, .parameters = param_types };
465 break :blk try self.spv.resolveType(SpvType.initPayload(&payload.base));
466 },
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", .{});
566470 },
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", .{}),
569471 .Vector => {
570472 // Although not 100% the same, Zig vectors map quite neatly to SPIR-V vectors (including many integer and float operations
571473 // which work on them), so simply use those.
......@@ -575,41 +477,42 @@ pub const DeclGen = struct {
575477 // is adequate at all for this.
576478
577479 // 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", .{});
579481 },
482
580483 .Null,
581484 .Undefined,
582485 .EnumLiteral,
583486 .ComptimeFloat,
584487 .ComptimeInt,
585488 .Type,
586 => unreachable, // Must be const or comptime.
489 => unreachable, // Must be comptime.
587490
588491 .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}),
591 }
592
593 try self.spv.types.putNoClobber(ty, result_id);
594 return result_id;
493 else => |tag| return self.todo("Implement zig type '{}'", .{tag}),
494 };
595495 }
596496
597497 /// SPIR-V requires pointers to have a storage class (address space), and so we have a special function for that.
598498 /// 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 {
600500 assert(ty.zigTypeTag() == .Pointer);
601501
602 const code = &self.spv.binary.types_globals_constants;
603 const result_id = self.spv.allocResultId();
502 const result_id = self.spv.allocId();
604503
605504 // TODO: There are many constraints which are ignored for now: We may only create pointers to certain types, and to other types
606505 // if more capabilities are enabled. For example, we may only create pointers to f16 if Float16Buffer is enabled.
607506 // 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();
613516 }
614517
615518 fn genDecl(self: *DeclGen) !void {
......@@ -618,41 +521,47 @@ pub const DeclGen = struct {
618521
619522 if (decl.val.castTag(.function)) |_| {
620523 assert(decl.ty.zigTypeTag() == .Fn);
621 const prototype_id = try self.genType(decl.ty);
622 try writeInstruction(&self.spv.binary.fn_decls, .OpFunction, &[_]Word{
623 self.spv.types.get(decl.ty.fnReturnType()).?, // This type should be generated along with the prototype.
624 result_id,
625 @bitCast(Word, spec.FunctionControl{}), // TODO: We can set inline here if the type requires it.
626 prototype_id,
524 const prototype_id = try self.resolveTypeId(decl.ty);
525 try self.spv.sections.functions.emit(self.spv.gpa, .OpFunction, .{
526 .id_result_type = try self.resolveTypeId(decl.ty.fnReturnType()),
527 .id_result = result_id,
528 .function_control = .{}, // TODO: We can set inline here if the type requires it.
529 .function_type = prototype_id.toRef(),
627530 });
628531
629532 const params = decl.ty.fnParamLen();
630533 var i: usize = 0;
631534
632 try self.args.ensureUnusedCapacity(params);
535 try self.args.ensureUnusedCapacity(self.spv.gpa, params);
633536 while (i < params) : (i += 1) {
634 const param_type_id = self.spv.types.get(decl.ty.fnParamType(i)).?;
635 const arg_result_id = self.spv.allocResultId();
636 try writeInstruction(&self.spv.binary.fn_decls, .OpFunctionParameter, &[_]Word{ param_type_id, arg_result_id });
637 self.args.appendAssumeCapacity(arg_result_id);
537 const param_type_id = try self.resolveTypeId(decl.ty.fnParamType(i));
538 const arg_result_id = self.spv.allocId();
539 try self.spv.sections.functions.emit(self.spv.gpa, .OpFunctionParameter, .{
540 .id_result_type = param_type_id,
541 .id_result = arg_result_id,
542 });
543 self.args.appendAssumeCapacity(arg_result_id.toRef());
638544 }
639545
640546 // 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 after
644 // here. Since we're not generating in self.code, we're just going to bypass self.beginSPIRVBlock here.
645 try writeInstruction(&self.spv.binary.fn_decls, .OpLabel, &[_]Word{root_block_id});
646 self.current_block_label_id = root_block_id;
549 // We need to generate the label directly in the functions section here because we're going to write the local variables after
550 // here. Since we're not generating in self.code, we're just going to bypass self.beginSpvBlock here.
551 try self.spv.sections.functions.emit(self.spv.gpa, .OpLabel, .{
552 .id_result = root_block_id,
553 });
554 self.current_block_label_id = root_block_id.toRef();
647555
648556 const main_body = self.air.getMainBody();
649557 try self.genBody(main_body);
650558
651 // Append the actual code into the fn_decls section.
652 try self.spv.binary.fn_decls.appendSlice(self.code.items);
653 try writeInstruction(&self.spv.binary.fn_decls, .OpFunctionEnd, &[_]Word{});
559 // Append the actual code into the functions section.
560 try self.spv.sections.functions.append(self.spv.gpa, self.code);
561 try self.spv.sections.functions.emit(self.spv.gpa, .OpFunctionEnd, {});
654562 } 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()});
656565 }
657566 }
658567
......@@ -666,9 +575,9 @@ pub const DeclGen = struct {
666575 const air_tags = self.air.instructions.items(.tag);
667576 const result_id = switch (air_tags[inst]) {
668577 // zig fmt: off
669 .add, .addwrap => try self.airArithOp(inst, .{.OpFAdd, .OpIAdd, .OpIAdd}),
670 .sub, .subwrap => try self.airArithOp(inst, .{.OpFSub, .OpISub, .OpISub}),
671 .mul, .mulwrap => try self.airArithOp(inst, .{.OpFMul, .OpIMul, .OpIMul}),
578 .add, .addwrap => try self.airArithOp(inst, .OpFAdd, .OpIAdd, .OpIAdd),
579 .sub, .subwrap => try self.airArithOp(inst, .OpFSub, .OpISub, .OpISub),
580 .mul, .mulwrap => try self.airArithOp(inst, .OpFMul, .OpIMul, .OpIMul),
672581
673582 .bit_and => try self.airBinOpSimple(inst, .OpBitwiseAnd),
674583 .bit_or => try self.airBinOpSimple(inst, .OpBitwiseOr),
......@@ -678,12 +587,12 @@ pub const DeclGen = struct {
678587
679588 .not => try self.airNot(inst),
680589
681 .cmp_eq => try self.airCmp(inst, .{.OpFOrdEqual, .OpLogicalEqual, .OpIEqual}),
682 .cmp_neq => try self.airCmp(inst, .{.OpFOrdNotEqual, .OpLogicalNotEqual, .OpINotEqual}),
683 .cmp_gt => try self.airCmp(inst, .{.OpFOrdGreaterThan, .OpSGreaterThan, .OpUGreaterThan}),
684 .cmp_gte => try self.airCmp(inst, .{.OpFOrdGreaterThanEqual, .OpSGreaterThanEqual, .OpUGreaterThanEqual}),
685 .cmp_lt => try self.airCmp(inst, .{.OpFOrdLessThan, .OpSLessThan, .OpULessThan}),
686 .cmp_lte => try self.airCmp(inst, .{.OpFOrdLessThanEqual, .OpSLessThanEqual, .OpULessThanEqual}),
590 .cmp_eq => try self.airCmp(inst, .OpFOrdEqual, .OpLogicalEqual, .OpIEqual),
591 .cmp_neq => try self.airCmp(inst, .OpFOrdNotEqual, .OpLogicalNotEqual, .OpINotEqual),
592 .cmp_gt => try self.airCmp(inst, .OpFOrdGreaterThan, .OpSGreaterThan, .OpUGreaterThan),
593 .cmp_gte => try self.airCmp(inst, .OpFOrdGreaterThanEqual, .OpSGreaterThanEqual, .OpUGreaterThanEqual),
594 .cmp_lt => try self.airCmp(inst, .OpFOrdLessThan, .OpSLessThan, .OpULessThan),
595 .cmp_lte => try self.airCmp(inst, .OpFOrdLessThanEqual, .OpSLessThanEqual, .OpULessThanEqual),
687596
688597 .arg => self.airArg(),
689598 .alloc => try self.airAlloc(inst),
......@@ -701,27 +610,30 @@ pub const DeclGen = struct {
701610 .unreach => return self.airUnreach(),
702611 // 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}", .{
705614 @tagName(tag),
706615 }),
707616 };
708617
709 try self.inst_results.putNoClobber(inst, result_id);
618 try self.inst_results.putNoClobber(self.spv.gpa, inst, result_id);
710619 }
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 {
713622 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
714623 const lhs_id = try self.resolve(bin_op.lhs);
715624 const rhs_id = try self.resolve(bin_op.rhs);
716 const result_id = self.spv.allocResultId();
717 const result_type_id = try self.genType(self.air.typeOfIndex(inst));
718 try writeInstruction(&self.code, opcode, &[_]Word{
719 result_type_id, result_id, lhs_id, rhs_id,
625 const result_id = self.spv.allocId();
626 const result_type_id = try self.resolveTypeId(self.air.typeOfIndex(inst));
627 try self.code.emit(self.spv.gpa, opcode, .{
628 .id_result_type = result_type_id,
629 .id_result = result_id,
630 .operand_1 = lhs_id,
631 .operand_2 = rhs_id,
720632 });
721 return result_id;
633 return result_id.toRef();
722634 }
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 {
725637 // LHS and RHS are guaranteed to have the same type, and AIR guarantees
726638 // the result to be the same as the LHS and RHS, which matches SPIR-V.
727639 const ty = self.air.typeOfIndex(inst);
......@@ -729,8 +641,8 @@ pub const DeclGen = struct {
729641 const lhs_id = try self.resolve(bin_op.lhs);
730642 const rhs_id = try self.resolve(bin_op.rhs);
731643
732 const result_id = self.spv.allocResultId();
733 const result_type_id = try self.genType(ty);
644 const result_id = self.spv.allocId();
645 const result_type_id = try self.resolveTypeId(ty);
734646
735647 assert(self.air.typeOf(bin_op.lhs).eql(ty));
736648 assert(self.air.typeOf(bin_op.rhs).eql(ty));
......@@ -741,10 +653,10 @@ pub const DeclGen = struct {
741653
742654 const opcode_index: usize = switch (info.class) {
743655 .composite_integer => {
744 return self.fail("TODO: SPIR-V backend: binary operations for composite integers", .{});
656 return self.todo("binary operations for composite integers", .{});
745657 },
746658 .strange_integer => {
747 return self.fail("TODO: SPIR-V backend: binary operations for strange integers", .{});
659 return self.todo("binary operations for strange integers", .{});
748660 },
749661 .integer => switch (info.signedness) {
750662 .signed => @as(usize, 1),
......@@ -753,21 +665,32 @@ pub const DeclGen = struct {
753665 .float => 0,
754666 else => unreachable,
755667 };
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 }
759682 // TODO: Trap on overflow? Probably going to be annoying.
760683 // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap.
761684
762 return result_id;
685 return result_id.toRef();
763686 }
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 {
766689 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
767690 const lhs_id = try self.resolve(bin_op.lhs);
768691 const rhs_id = try self.resolve(bin_op.rhs);
769 const result_id = self.spv.allocResultId();
770 const result_type_id = try self.genType(Type.initTag(.bool));
692 const result_id = self.spv.allocId();
693 const result_type_id = try self.resolveTypeId(Type.initTag(.bool));
771694 const op_ty = self.air.typeOf(bin_op.lhs);
772695 assert(op_ty.eql(self.air.typeOf(bin_op.rhs)));
773696
......@@ -777,10 +700,10 @@ pub const DeclGen = struct {
777700
778701 const opcode_index: usize = switch (info.class) {
779702 .composite_integer => {
780 return self.fail("TODO: SPIR-V backend: binary operations for composite integers", .{});
703 return self.todo("binary operations for composite integers", .{});
781704 },
782705 .strange_integer => {
783 return self.fail("TODO: SPIR-V backend: comparison for strange integers", .{});
706 return self.todo("comparison for strange integers", .{});
784707 },
785708 .float => 0,
786709 .bool => 1,
......@@ -789,53 +712,71 @@ pub const DeclGen = struct {
789712 .unsigned => @as(usize, 2),
790713 },
791714 };
792 const opcode = ops[opcode_index];
793715
794 try writeInstruction(&self.code, opcode, &[_]Word{ result_type_id, result_id, lhs_id, rhs_id });
795 return result_id;
716 const operands = .{
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();
796731 }
797732
798 fn airNot(self: *DeclGen, inst: Air.Inst.Index) !ResultId {
733 fn airNot(self: *DeclGen, inst: Air.Inst.Index) !IdRef {
799734 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
800735 const operand_id = try self.resolve(ty_op.operand);
801 const result_id = self.spv.allocResultId();
802 const result_type_id = try self.genType(Type.initTag(.bool));
803 const opcode: Opcode = .OpLogicalNot;
804 try writeInstruction(&self.code, opcode, &[_]Word{ result_type_id, result_id, operand_id });
805 return result_id;
736 const result_id = self.spv.allocId();
737 const result_type_id = try self.resolveTypeId(Type.initTag(.bool));
738 try self.code.emit(self.spv.gpa, .OpLogicalNot, .{
739 .id_result_type = result_type_id,
740 .id_result = result_id,
741 .operand = operand_id,
742 });
743 return result_id.toRef();
806744 }
807745
808 fn airAlloc(self: *DeclGen, inst: Air.Inst.Index) !ResultId {
746 fn airAlloc(self: *DeclGen, inst: Air.Inst.Index) !IdRef {
809747 const ty = self.air.typeOfIndex(inst);
810748 const storage_class = spec.StorageClass.Function;
811749 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 that
752 // Rather than generating into code here, we're just going to generate directly into the functions section so that
815753 // 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) });
817
818 return result_id;
754 try self.spv.sections.functions.emit(self.spv.gpa, .OpVariable, .{
755 .id_result_type = result_type_id,
756 .id_result = result_id,
757 .storage_class = storage_class,
758 });
759 return result_id.toRef();
819760 }
820761
821 fn airArg(self: *DeclGen) ResultId {
762 fn airArg(self: *DeclGen) IdRef {
822763 defer self.next_arg_index += 1;
823764 return self.args.items[self.next_arg_index];
824765 }
825766
826 fn airBlock(self: *DeclGen, inst: Air.Inst.Index) !?ResultId {
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 and
767 fn airBlock(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
768 // 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
828769 // "return" a value from. This cannot be directly modelled in SPIR-V, so in a block instruction, we're going to split up
829770 // the current block by first generating the code of the block, then a label, and then generate the rest of the current
830771 // ir.Block in a different SPIR-V block.
831772
832 const label_id = self.spv.allocResultId();
773 const label_id = self.spv.allocId();
833774
834775 // 4 chosen as arbitrary initial capacity.
835776 var incoming_blocks = try std.ArrayListUnmanaged(IncomingBlock).initCapacity(self.spv.gpa, 4);
836777
837 try self.blocks.putNoClobber(inst, .{
838 .label_id = label_id,
778 try self.blocks.putNoClobber(self.spv.gpa, inst, .{
779 .label_id = label_id.toRef(),
839780 .incoming_blocks = &incoming_blocks,
840781 });
841782 defer {
......@@ -849,7 +790,7 @@ pub const DeclGen = struct {
849790 const body = self.air.extra[extra.end..][0..extra.data.body_len];
850791
851792 try self.genBody(body);
852 try self.beginSPIRVBlock(label_id);
793 try self.beginSpvBlock(label_id);
853794
854795 // If this block didn't produce a value, simply return here.
855796 if (!ty.hasRuntimeBits())
......@@ -857,21 +798,21 @@ pub const DeclGen = struct {
857798
858799 // 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
862803 // 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 throws
804 // are not allowed to be created from a phi node, and throw an error for those. For now, resolveTypeId already throws
864805 // an error for pointers.
865 const result_type_id = try self.genType(ty);
806 const result_type_id = try self.resolveTypeId(ty);
866807 _ = 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
870811 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 });
872813 }
873814
874 return result_id;
815 return result_id.toRef();
875816 }
876817
877818 fn airBr(self: *DeclGen, inst: Air.Inst.Index) !void {
......@@ -885,7 +826,7 @@ pub const DeclGen = struct {
885826 try block.incoming_blocks.append(self.spv.gpa, .{ .src_label_id = self.current_block_label_id, .break_value_id = operand_id });
886827 }
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 });
889830 }
890831
891832 fn airCondBr(self: *DeclGen, inst: Air.Inst.Index) !void {
......@@ -896,63 +837,70 @@ pub const DeclGen = struct {
896837 const condition_id = try self.resolve(pl_op.operand);
897838
898839 // 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();
900 const else_label_id = self.spv.allocResultId();
840 const then_label_id = self.spv.allocId();
841 const else_label_id = self.spv.allocId();
901842
902843 // TODO: We can generate OpSelectionMerge here if we know the target block that both of these will resolve to,
903844 // but i don't know if those will always resolve to the same block.
904845
905 try writeInstruction(&self.code, .OpBranchConditional, &[_]Word{
906 condition_id,
907 then_label_id,
908 else_label_id,
846 try self.code.emit(self.spv.gpa, .OpBranchConditional, .{
847 .condition = condition_id,
848 .true_label = then_label_id.toRef(),
849 .false_label = else_label_id.toRef(),
909850 });
910851
911 try self.beginSPIRVBlock(then_label_id);
852 try self.beginSpvBlock(then_label_id);
912853 try self.genBody(then_body);
913 try self.beginSPIRVBlock(else_label_id);
854 try self.beginSpvBlock(else_label_id);
914855 try self.genBody(else_body);
915856 }
916857
917858 fn airDbgStmt(self: *DeclGen, inst: Air.Inst.Index) !void {
918859 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
919860 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 });
921866 }
922867
923 fn airLoad(self: *DeclGen, inst: Air.Inst.Index) !ResultId {
868 fn airLoad(self: *DeclGen, inst: Air.Inst.Index) !IdRef {
924869 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
925870 const operand_id = try self.resolve(ty_op.operand);
926871 const ty = self.air.typeOfIndex(inst);
927872
928 const result_type_id = try self.genType(ty);
929 const result_id = self.spv.allocResultId();
873 const result_type_id = try self.resolveTypeId(ty);
874 const result_id = self.spv.allocId();
930875
931 const operands = if (ty.isVolatilePtr())
932 &[_]Word{ result_type_id, result_id, operand_id, @bitCast(u32, spec.MemoryAccess{ .Volatile = true }) }
933 else
934 &[_]Word{ result_type_id, result_id, operand_id };
876 const access = spec.MemoryAccess.Extended{
877 .Volatile = ty.isVolatilePtr(),
878 };
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();
939888 }
940889
941890 fn airLoop(self: *DeclGen, inst: Air.Inst.Index) !void {
942891 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
943892 const loop = self.air.extraData(Air.Block, ty_pl.payload);
944893 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
947896 // 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
950899 // TODO: Look into OpLoopMerge.
951
952 try self.beginSPIRVBlock(loop_label_id);
900 try self.beginSpvBlock(loop_label_id);
953901 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() });
956904 }
957905
958906 fn airRet(self: *DeclGen, inst: Air.Inst.Index) !void {
......@@ -960,9 +908,9 @@ pub const DeclGen = struct {
960908 const operand_ty = self.air.typeOf(operand);
961909 if (operand_ty.hasRuntimeBits()) {
962910 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 });
964912 } else {
965 try writeInstruction(&self.code, .OpReturn, &[_]Word{});
913 try self.code.emit(self.spv.gpa, .OpReturn, {});
966914 }
967915 }
968916
......@@ -972,15 +920,18 @@ pub const DeclGen = struct {
972920 const src_val_id = try self.resolve(bin_op.rhs);
973921 const lhs_ty = self.air.typeOf(bin_op.lhs);
974922
975 const operands = if (lhs_ty.isVolatilePtr())
976 &[_]Word{ dst_ptr_id, src_val_id, @bitCast(u32, spec.MemoryAccess{ .Volatile = true }) }
977 else
978 &[_]Word{ dst_ptr_id, src_val_id };
923 const access = spec.MemoryAccess.Extended{
924 .Volatile = lhs_ty.isVolatilePtr(),
925 };
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 });
981932 }
982933
983934 fn airUnreach(self: *DeclGen) !void {
984 try writeInstruction(&self.code, .OpUnreachable, &[_]Word{});
935 try self.code.emit(self.spv.gpa, .OpUnreachable, {});
985936 }
986937};
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 @@
11//! This file is auto-generated by tools/gen_spirv_spec.zig.
22
33const 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
442pub const version = Version{ .major = 1, .minor = 5, .patch = 4 };
5pub const magic_number: u32 = 0x07230203;
43pub const magic_number: Word = 0x07230203;
644pub const Opcode = enum(u16) {
745 OpNop = 0,
846 OpUndef = 1,
......@@ -381,11 +419,11 @@ pub const Opcode = enum(u16) {
381419 OpImageSampleFootprintNV = 5283,
382420 OpGroupNonUniformPartitionNV = 5296,
383421 OpWritePackedPrimitiveIndices4x8NV = 5299,
384 OpReportIntersectionNV = 5334,
422 OpReportIntersectionKHR = 5334,
385423 OpIgnoreIntersectionNV = 5335,
386424 OpTerminateRayNV = 5336,
387425 OpTraceNV = 5337,
388 OpTypeAccelerationStructureNV = 5341,
426 OpTypeAccelerationStructureKHR = 5341,
389427 OpExecuteCallableNV = 5344,
390428 OpTypeCooperativeMatrixNV = 5358,
391429 OpCooperativeMatrixLoadNV = 5359,
......@@ -580,10 +618,592 @@ pub const Opcode = enum(u16) {
580618 OpTypeStructContinuedINTEL = 6090,
581619 OpConstantCompositeContinuedINTEL = 6091,
582620 OpSpecConstantCompositeContinuedINTEL = 6092,
583 _,
584621
585 const OpReportIntersectionKHR: Opcode = .OpReportIntersectionNV;
586 const OpTypeAccelerationStructureKHR: Opcode = .OpTypeAccelerationStructureNV;
622 pub const OpReportIntersectionNV = Opcode.OpReportIntersectionKHR;
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 }
5871207};
5881208pub const ImageOperands = packed struct {
5891209 Bias: bool align(@alignOf(u32)) = false,
......@@ -618,6 +1238,46 @@ pub const ImageOperands = packed struct {
6181238 _reserved_bit_29: bool = false,
6191239 _reserved_bit_30: bool = false,
6201240 _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 };
6211281};
6221282pub const FPFastMathMode = packed struct {
6231283 NotNaN: bool align(@alignOf(u32)) = false,
......@@ -720,6 +1380,41 @@ pub const LoopControl = packed struct {
7201380 _reserved_bit_29: bool = false,
7211381 _reserved_bit_30: bool = false,
7221382 _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 };
7231418};
7241419pub const FunctionControl = packed struct {
7251420 Inline: bool align(@alignOf(u32)) = false,
......@@ -788,6 +1483,10 @@ pub const MemorySemantics = packed struct {
7881483 _reserved_bit_29: bool = false,
7891484 _reserved_bit_30: bool = false,
7901485 _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 };
7911490};
7921491pub const MemoryAccess = packed struct {
7931492 Volatile: bool align(@alignOf(u32)) = false,
......@@ -822,6 +1521,45 @@ pub const MemoryAccess = packed struct {
8221521 _reserved_bit_29: bool = false,
8231522 _reserved_bit_30: bool = false,
8241523 _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 };
8251563};
8261564pub const KernelProfilingInfo = packed struct {
8271565 CmdExecTime: bool align(@alignOf(u32)) = false,
......@@ -932,7 +1670,6 @@ pub const SourceLanguage = enum(u32) {
9321670 OpenCL_C = 3,
9331671 OpenCL_CPP = 4,
9341672 HLSL = 5,
935 _,
9361673};
9371674pub const ExecutionModel = enum(u32) {
9381675 Vertex = 0,
......@@ -944,33 +1681,35 @@ pub const ExecutionModel = enum(u32) {
9441681 Kernel = 6,
9451682 TaskNV = 5267,
9461683 MeshNV = 5268,
947 RayGenerationNV = 5313,
9481684 RayGenerationKHR = 5313,
949 IntersectionNV = 5314,
9501685 IntersectionKHR = 5314,
951 AnyHitNV = 5315,
9521686 AnyHitKHR = 5315,
953 ClosestHitNV = 5316,
9541687 ClosestHitKHR = 5316,
955 MissNV = 5317,
9561688 MissKHR = 5317,
957 CallableNV = 5318,
9581689 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;
9601697};
9611698pub const AddressingModel = enum(u32) {
9621699 Logical = 0,
9631700 Physical32 = 1,
9641701 Physical64 = 2,
9651702 PhysicalStorageBuffer64 = 5348,
966 _,
1703
1704 pub const PhysicalStorageBuffer64EXT = AddressingModel.PhysicalStorageBuffer64;
9671705};
9681706pub const MemoryModel = enum(u32) {
9691707 Simple = 0,
9701708 GLSL450 = 1,
9711709 OpenCL = 2,
9721710 Vulkan = 3,
973 _,
1711
1712 pub const VulkanKHR = MemoryModel.Vulkan;
9741713};
9751714pub const ExecutionMode = enum(u32) {
9761715 Invocations = 0,
......@@ -1039,7 +1778,75 @@ pub const ExecutionMode = enum(u32) {
10391778 NoGlobalOffsetINTEL = 5895,
10401779 NumSIMDWorkitemsINTEL = 5896,
10411780 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 };
10431850};
10441851pub const StorageClass = enum(u32) {
10451852 UniformConstant = 0,
......@@ -1065,7 +1872,14 @@ pub const StorageClass = enum(u32) {
10651872 CodeSectionINTEL = 5605,
10661873 DeviceOnlyINTEL = 5936,
10671874 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;
10691883};
10701884pub const Dim = enum(u32) {
10711885 @"1D" = 0,
......@@ -1075,7 +1889,6 @@ pub const Dim = enum(u32) {
10751889 Rect = 4,
10761890 Buffer = 5,
10771891 SubpassData = 6,
1078 _,
10791892};
10801893pub const SamplerAddressingMode = enum(u32) {
10811894 None = 0,
......@@ -1083,12 +1896,10 @@ pub const SamplerAddressingMode = enum(u32) {
10831896 Clamp = 2,
10841897 Repeat = 3,
10851898 RepeatMirrored = 4,
1086 _,
10871899};
10881900pub const SamplerFilterMode = enum(u32) {
10891901 Nearest = 0,
10901902 Linear = 1,
1091 _,
10921903};
10931904pub const ImageFormat = enum(u32) {
10941905 Unknown = 0,
......@@ -1133,7 +1944,6 @@ pub const ImageFormat = enum(u32) {
11331944 R8ui = 39,
11341945 R64ui = 40,
11351946 R64i = 41,
1136 _,
11371947};
11381948pub const ImageChannelOrder = enum(u32) {
11391949 R = 0,
......@@ -1156,7 +1966,6 @@ pub const ImageChannelOrder = enum(u32) {
11561966 sRGBA = 17,
11571967 sBGRA = 18,
11581968 ABGR = 19,
1159 _,
11601969};
11611970pub const ImageChannelDataType = enum(u32) {
11621971 SnormInt8 = 0,
......@@ -1176,36 +1985,30 @@ pub const ImageChannelDataType = enum(u32) {
11761985 Float = 14,
11771986 UnormInt24 = 15,
11781987 UnormInt101010_2 = 16,
1179 _,
11801988};
11811989pub const FPRoundingMode = enum(u32) {
11821990 RTE = 0,
11831991 RTZ = 1,
11841992 RTP = 2,
11851993 RTN = 3,
1186 _,
11871994};
11881995pub const FPDenormMode = enum(u32) {
11891996 Preserve = 0,
11901997 FlushToZero = 1,
1191 _,
11921998};
11931999pub const FPOperationMode = enum(u32) {
11942000 IEEE = 0,
11952001 ALT = 1,
1196 _,
11972002};
11982003pub const LinkageType = enum(u32) {
11992004 Export = 0,
12002005 Import = 1,
12012006 LinkOnceODR = 2,
1202 _,
12032007};
12042008pub const AccessQualifier = enum(u32) {
12052009 ReadOnly = 0,
12062010 WriteOnly = 1,
12072011 ReadWrite = 2,
1208 _,
12092012};
12102013pub const FunctionParameterAttribute = enum(u32) {
12112014 Zext = 0,
......@@ -1216,7 +2019,6 @@ pub const FunctionParameterAttribute = enum(u32) {
12162019 NoCapture = 5,
12172020 NoWrite = 6,
12182021 NoReadWrite = 7,
1219 _,
12202022};
12212023pub const Decoration = enum(u32) {
12222024 RelaxedPrecision = 0,
......@@ -1278,11 +2080,8 @@ pub const Decoration = enum(u32) {
12782080 PerTaskNV = 5273,
12792081 PerVertexNV = 5285,
12802082 NonUniform = 5300,
1281 NonUniformEXT = 5300,
12822083 RestrictPointer = 5355,
1283 RestrictPointerEXT = 5355,
12842084 AliasedPointer = 5356,
1285 AliasedPointerEXT = 5356,
12862085 SIMTCallINTEL = 5599,
12872086 ReferencedIndirectlyINTEL = 5602,
12882087 ClobberINTEL = 5607,
......@@ -1293,9 +2092,7 @@ pub const Decoration = enum(u32) {
12932092 StackCallINTEL = 5627,
12942093 GlobalVariableOffsetINTEL = 5628,
12952094 CounterBuffer = 5634,
1296 HlslCounterBufferGOOGLE = 5634,
12972095 UserSemantic = 5635,
1298 HlslSemanticGOOGLE = 5635,
12992096 UserTypeGOOGLE = 5636,
13002097 FunctionRoundingModeINTEL = 5822,
13012098 FunctionDenormModeINTEL = 5823,
......@@ -1322,7 +2119,113 @@ pub const Decoration = enum(u32) {
13222119 FunctionFloatingPointModeINTEL = 6080,
13232120 SingleElementVectorINTEL = 6085,
13242121 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 };
13262229};
13272230pub const BuiltIn = enum(u32) {
13282231 Position = 0,
......@@ -1367,15 +2270,10 @@ pub const BuiltIn = enum(u32) {
13672270 VertexIndex = 42,
13682271 InstanceIndex = 43,
13692272 SubgroupEqMask = 4416,
1370 SubgroupEqMaskKHR = 4416,
13712273 SubgroupGeMask = 4417,
1372 SubgroupGeMaskKHR = 4417,
13732274 SubgroupGtMask = 4418,
1374 SubgroupGtMaskKHR = 4418,
13752275 SubgroupLeMask = 4419,
1376 SubgroupLeMaskKHR = 4419,
13772276 SubgroupLtMask = 4420,
1378 SubgroupLtMaskKHR = 4420,
13792277 BaseVertex = 4424,
13802278 BaseInstance = 4425,
13812279 DrawIndex = 4426,
......@@ -1408,42 +2306,47 @@ pub const BuiltIn = enum(u32) {
14082306 BaryCoordNV = 5286,
14092307 BaryCoordNoPerspNV = 5287,
14102308 FragSizeEXT = 5292,
1411 FragmentSizeNV = 5292,
14122309 FragInvocationCountEXT = 5293,
1413 InvocationsPerPixelNV = 5293,
1414 LaunchIdNV = 5319,
14152310 LaunchIdKHR = 5319,
1416 LaunchSizeNV = 5320,
14172311 LaunchSizeKHR = 5320,
1418 WorldRayOriginNV = 5321,
14192312 WorldRayOriginKHR = 5321,
1420 WorldRayDirectionNV = 5322,
14212313 WorldRayDirectionKHR = 5322,
1422 ObjectRayOriginNV = 5323,
14232314 ObjectRayOriginKHR = 5323,
1424 ObjectRayDirectionNV = 5324,
14252315 ObjectRayDirectionKHR = 5324,
1426 RayTminNV = 5325,
14272316 RayTminKHR = 5325,
1428 RayTmaxNV = 5326,
14292317 RayTmaxKHR = 5326,
1430 InstanceCustomIndexNV = 5327,
14312318 InstanceCustomIndexKHR = 5327,
1432 ObjectToWorldNV = 5330,
14332319 ObjectToWorldKHR = 5330,
1434 WorldToObjectNV = 5331,
14352320 WorldToObjectKHR = 5331,
14362321 HitTNV = 5332,
1437 HitKindNV = 5333,
14382322 HitKindKHR = 5333,
1439 IncomingRayFlagsNV = 5351,
14402323 IncomingRayFlagsKHR = 5351,
14412324 RayGeometryIndexKHR = 5352,
14422325 WarpsPerSMNV = 5374,
14432326 SMCountNV = 5375,
14442327 WarpIDNV = 5376,
14452328 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;
14472350};
14482351pub const Scope = enum(u32) {
14492352 CrossDevice = 0,
......@@ -1452,9 +2355,9 @@ pub const Scope = enum(u32) {
14522355 Subgroup = 3,
14532356 Invocation = 4,
14542357 QueueFamily = 5,
1455 QueueFamilyKHR = 5,
14562358 ShaderCallKHR = 6,
1457 _,
2359
2360 pub const QueueFamilyKHR = Scope.QueueFamily;
14582361};
14592362pub const GroupOperation = enum(u32) {
14602363 Reduce = 0,
......@@ -1464,13 +2367,11 @@ pub const GroupOperation = enum(u32) {
14642367 PartitionedReduceNV = 6,
14652368 PartitionedInclusiveScanNV = 7,
14662369 PartitionedExclusiveScanNV = 8,
1467 _,
14682370};
14692371pub const KernelEnqueueFlags = enum(u32) {
14702372 NoWait = 0,
14712373 WaitKernel = 1,
14722374 WaitWorkGroup = 2,
1473 _,
14742375};
14752376pub const Capability = enum(u32) {
14762377 Matrix = 0,
......@@ -1550,7 +2451,7 @@ pub const Capability = enum(u32) {
15502451 WorkgroupMemoryExplicitLayout16BitAccessKHR = 4430,
15512452 SubgroupVoteKHR = 4431,
15522453 StorageBuffer16BitAccess = 4433,
1553 StorageUniform16 = 4434,
2454 UniformAndStorageBuffer16BitAccess = 4434,
15542455 StoragePushConstant16 = 4435,
15552456 StorageInputOutput16 = 4436,
15562457 DeviceGroup = 4437,
......@@ -1580,7 +2481,7 @@ pub const Capability = enum(u32) {
15802481 ShaderClockKHR = 5055,
15812482 SampleMaskOverrideCoverageNV = 5249,
15822483 GeometryShaderPassthroughNV = 5251,
1583 ShaderViewportIndexLayerNV = 5254,
2484 ShaderViewportIndexLayerEXT = 5254,
15842485 ShaderViewportMaskNV = 5255,
15852486 ShaderStereoViewNV = 5259,
15862487 PerViewAttributesNV = 5260,
......@@ -1589,7 +2490,7 @@ pub const Capability = enum(u32) {
15892490 ImageFootprintNV = 5282,
15902491 FragmentBarycentricNV = 5284,
15912492 ComputeDerivativeGroupQuadsNV = 5288,
1592 ShadingRateNV = 5291,
2493 FragmentDensityEXT = 5291,
15932494 GroupNonUniformPartitionedNV = 5297,
15942495 ShaderNonUniform = 5301,
15952496 RuntimeDescriptorArray = 5302,
......@@ -1654,21 +2555,37 @@ pub const Capability = enum(u32) {
16542555 AtomicFloat32AddEXT = 6033,
16552556 AtomicFloat64AddEXT = 6034,
16562557 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;
16582578};
16592579pub const RayQueryIntersection = enum(u32) {
16602580 RayQueryCandidateIntersectionKHR = 0,
16612581 RayQueryCommittedIntersectionKHR = 1,
1662 _,
16632582};
16642583pub const RayQueryCommittedIntersectionType = enum(u32) {
16652584 RayQueryCommittedIntersectionNoneKHR = 0,
16662585 RayQueryCommittedIntersectionTriangleKHR = 1,
16672586 RayQueryCommittedIntersectionGeneratedKHR = 2,
1668 _,
16692587};
16702588pub const RayQueryCandidateIntersectionType = enum(u32) {
16712589 RayQueryCandidateIntersectionTriangleKHR = 0,
16722590 RayQueryCandidateIntersectionAABBKHR = 1,
1673 _,
16742591};
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();
2424
2525const std = @import("std");
2626const Allocator = std.mem.Allocator;
27const ArenaAllocator = std.heap.ArenaAllocator;
2728const assert = std.debug.assert;
2829const log = std.log.scoped(.link);
2930
......@@ -31,19 +32,21 @@ const Module = @import("../Module.zig");
3132const Compilation = @import("../Compilation.zig");
3233const link = @import("../link.zig");
3334const codegen = @import("../codegen/spirv.zig");
34const Word = codegen.Word;
35const ResultId = codegen.ResultId;
3635const trace = @import("../tracy.zig").trace;
3736const build_options = @import("build_options");
38const spec = @import("../codegen/spirv/spec.zig");
3937const Air = @import("../Air.zig");
4038const 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
4245// TODO: Should this struct be used at all rather than just a hashmap of aux data for every decl?
4346pub const FnData = struct {
4447 // We're going to fill these in flushModule, and we're going to fill them unconditionally,
4548 // so just set it to undefined.
46 id: ResultId = undefined,
49 id: IdResult = undefined,
4750};
4851
4952base: link.File,
......@@ -55,7 +58,15 @@ decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, DeclGenContext) = .{},
5558
5659const DeclGenContext = struct {
5760 air: Air,
61 air_value_arena: ArenaAllocator.State,
5862 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 }
5970};
6071
6172pub 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
113124 @panic("Attempted to compile for architecture that was disabled by build configuration");
114125 }
115126 _ = module;
127
116128 // 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;
120 _ = liveness;
121 @panic("TODO SPIR-V needs to keep track of Air and Liveness so it can use them later");
140 var new_liveness = try cloneLiveness(liveness, self.base.allocator);
141 errdefer new_liveness.deinit(self.base.allocator);
142
143 result.value_ptr.* = .{
144 .air = new_air,
145 .air_value_arena = arena.state,
146 .liveness = new_liveness,
147 };
122148}
123149
124150pub fn updateDecl(self: *SpirV, module: *Module, decl: *Module.Decl) !void {
......@@ -143,7 +169,11 @@ pub fn updateDeclExports(
143169}
144170
145171pub 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);
147177}
148178
149179pub fn flush(self: *SpirV, comp: *Compilation) !void {
......@@ -165,7 +195,10 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
165195 const module = self.base.options.module.?;
166196 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());
169202 defer spv.deinit();
170203
171204 // Allocate an ID for every declaration before generating code,
......@@ -173,73 +206,38 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
173206 // TODO: We're allocating an ID unconditionally now, are there
174207 // declarations which don't generate a result?
175208 // TODO: fn_link is used here, but thats probably not the right field. It will work anyway though.
176 {
177 for (self.decl_table.keys()) |decl| {
178 if (!decl.has_tv) continue;
179
180 decl.fn_link.spirv.id = spv.allocResultId();
209 for (self.decl_table.keys()) |decl| {
210 if (decl.has_tv) {
211 decl.fn_link.spirv.id = spv.allocId();
181212 }
182213 }
183214
184215 // Now, actually generate the code for all declarations.
185 {
186 var decl_gen = codegen.DeclGen.init(&spv);
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);
216 var decl_gen = codegen.DeclGen.init(module, &spv);
217 defer decl_gen.deinit();
206218
207 const header = [_]Word{
208 spec.magic_number,
209 (spec.version.major << 16) | (spec.version.minor << 8),
210 0, // TODO: Register Zig compiler magic number.
211 spv.resultIdBound(),
212 0, // Schema (currently reserved for future use in the SPIR-V spec).
213 };
219 var it = self.decl_table.iterator();
220 while (it.next()) |entry| {
221 const decl = entry.key_ptr.*;
222 if (!decl.has_tv) continue;
214223
215 // Note: The order of adding sections to the final binary
216 // follows the SPIR-V logical module format!
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 };
224 const air = entry.value_ptr.air;
225 const liveness = entry.value_ptr.liveness;
224226
225 var iovc_buffers: [buffers.len]std.os.iovec_const = undefined;
226 for (iovc_buffers) |*iovc, i| {
227 const bytes = std.mem.sliceAsBytes(buffers[i]);
228 iovc.* = .{ .iov_base = bytes.ptr, .iov_len = bytes.len };
227 // Note, if `decl` is not a function, air/liveness may be undefined.
228 if (try decl_gen.gen(decl, air, liveness)) |msg| {
229 try module.failed_decls.put(module.gpa, decl, msg);
230 return; // TODO: Attempt to generate more decls?
231 }
229232 }
230233
231 var file_size: u64 = 0;
232 for (iovc_buffers) |iov| {
233 file_size += iov.iov_len;
234 }
234 try writeCapabilities(&spv, target);
235 try writeMemoryModel(&spv, target);
235236
236 const file = self.base.file.?;
237 try file.seekTo(0);
238 try file.setEndPos(file_size);
239 try file.pwritevAll(&iovc_buffers, 0);
237 try spv.flush(self.base.file.?);
240238}
241239
242fn writeCapabilities(binary: *std.ArrayList(Word), target: std.Target) !void {
240fn writeCapabilities(spv: *SpvModule, target: std.Target) !void {
243241 // TODO: Integrate with a hypothetical feature system
244242 const cap: spec.Capability = switch (target.os.tag) {
245243 .opencl => .Kernel,
......@@ -248,10 +246,12 @@ fn writeCapabilities(binary: *std.ArrayList(Word), target: std.Target) !void {
248246 else => unreachable, // TODO
249247 };
250248
251 try codegen.writeInstruction(binary, .OpCapability, &[_]Word{@enumToInt(cap)});
249 try spv.sections.capabilities.emit(spv.gpa, .OpCapability, .{
250 .capability = cap,
251 });
252252}
253253
254fn writeMemoryModel(binary: *std.ArrayList(Word), target: std.Target) !void {
254fn writeMemoryModel(spv: *SpvModule, target: std.Target) !void {
255255 const addressing_model = switch (target.os.tag) {
256256 .opencl => switch (target.cpu.arch) {
257257 .spirv32 => spec.AddressingModel.Physical32,
......@@ -269,7 +269,41 @@ fn writeMemoryModel(binary: *std.ArrayList(Word), target: std.Target) !void {
269269 else => unreachable,
270270 };
271271
272 try codegen.writeInstruction(binary, .OpMemoryModel, &[_]Word{
273 @enumToInt(addressing_model), @enumToInt(memory_model),
272 // TODO: Put this in a proper section.
273 try spv.sections.capabilities.emit(spv.gpa, .OpMemoryModel, .{
274 .addressing_model = addressing_model,
275 .memory_model = memory_model,
274276 });
275277}
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 @@
11const std = @import("std");
22const g = @import("spirv/grammar.zig");
3const Allocator = std.mem.Allocator;
4
5const ExtendedStructSet = std.StringHashMap(void);
36
47pub fn main() !void {
58 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
......@@ -20,101 +23,308 @@ pub fn main() !void {
2023 var tokens = std.json.TokenStream.init(spec);
2124 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
2331 var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
24 try render(bw.writer(), registry);
32 try render(bw.writer(), allocator, core_reg);
2533 try bw.flush();
2634}
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 {
2977 try writer.writeAll(
3078 \\//! This file is auto-generated by tools/gen_spirv_spec.zig.
3179 \\
3280 \\const Version = @import("std").builtin.Version;
3381 \\
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 \\
34120 );
35121
36 switch (registry) {
37 .core => |core_reg| {
38 try writer.print(
39 \\pub const version = Version{{ .major = {}, .minor = {}, .patch = {} }};
40 \\pub const magic_number: u32 = {s};
41 \\
42 ,
43 .{ core_reg.major_version, core_reg.minor_version, core_reg.revision, core_reg.magic_number },
44 );
45 try renderOpcodes(writer, core_reg.instructions);
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 }
122 try writer.print(
123 \\pub const version = Version{{ .major = {}, .minor = {}, .patch = {} }};
124 \\pub const magic_number: Word = {s};
125 \\
126 ,
127 .{ registry.major_version, registry.minor_version, registry.revision, registry.magic_number },
128 );
129 const extended_structs = try extendedStructs(allocator, registry.operand_kinds);
130 try renderOpcodes(writer, allocator, registry.instructions, extended_structs);
131 try renderOperandKinds(writer, allocator, registry.operand_kinds, extended_structs);
59132}
60133
61fn renderOpcodes(writer: anytype, instructions: []const g.Instruction) !void {
62 try writer.writeAll("pub const Opcode = extern enum(u16) {\n");
63 for (instructions) |instr| {
64 try writer.print(" {} = {},\n", .{ std.zig.fmtId(instr.opname), instr.opcode });
134fn renderOpcodes(
135 writer: anytype,
136 allocator: Allocator,
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 }
65165 }
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;
67197}
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 {
70205 for (kinds) |kind| {
71206 switch (kind.category) {
72 .ValueEnum => try renderValueEnum(writer, kind),
73 .BitEnum => try renderBitEnum(writer, kind),
207 .ValueEnum => try renderValueEnum(writer, allocator, kind, extended_structs),
208 .BitEnum => try renderBitEnum(writer, allocator, kind, extended_structs),
74209 else => {},
75210 }
76211 }
77212}
78213
79fn renderValueEnum(writer: anytype, enumeration: g.OperandKind) !void {
80 try writer.print("pub const {s} = extern enum(u32) {{\n", .{enumeration.kind});
81
214fn renderValueEnum(
215 writer: anytype,
216 allocator: Allocator,
217 enumeration: g.OperandKind,
218 extended_structs: ExtendedStructSet,
219) !void {
82220 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];
84255 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);
87280 }
88281
89 try writer.writeAll(" _,\n};\n");
282 try writer.writeAll("};\n};\n");
90283}
91284
92fn renderBitEnum(writer: anytype, enumeration: g.OperandKind) !void {
93 try writer.print("pub const {s} = packed struct {{\n", .{enumeration.kind});
285fn renderBitEnum(
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;
96294 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| {
98300 if (enumerant.value != .bitflag) return error.InvalidRegistry;
99301 const value = try parseHexInt(enumerant.value.bitflag);
100 if (@popCount(u32, value) != 1) {
101 continue; // Skip combinations and 'none' items
302 if (@popCount(u32, value) == 0) {
303 continue; // Skip 'none' items
102304 }
103305
306 std.debug.assert(@popCount(u32, value) == 1);
307
104308 var bitpos = std.math.log2_int(u32, value);
105309 if (flags_by_bitpos[bitpos]) |*existing| {
106 // Keep the shortest
107 if (enumerant.enumerant.len < existing.len)
108 existing.* = enumerant.enumerant;
310 const tag_index = std.mem.indexOfDiff(u8, enumerant.enumerant, enumerants[existing.*].enumerant).?;
311 const enum_priority = tagPriorityScore(enumerant.enumerant[tag_index..]);
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 }
109320 } else {
110 flags_by_bitpos[bitpos] = enumerant.enumerant;
321 flags_by_bitpos[bitpos] = i;
111322 }
112323 }
113324
114 for (flags_by_bitpos) |maybe_flag_name, bitpos| {
115 try writer.writeAll(" ");
116 if (maybe_flag_name) |flag_name| {
117 try writer.writeAll(flag_name);
325 for (flags_by_bitpos) |maybe_flag_index, bitpos| {
326 if (maybe_flag_index) |flag_index| {
327 try writer.print("{}", .{std.zig.fmtId(enumerants[flag_index].enumerant)});
118328 } else {
119329 try writer.print("_reserved_bit_{}", .{bitpos});
120330 }
......@@ -126,7 +336,169 @@ fn renderBitEnum(writer: anytype, enumeration: g.OperandKind) !void {
126336 try writer.writeAll("= false,\n");
127337 }
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 }
130502}
131503
132504fn parseHexInt(text: []const u8) !u31 {
......@@ -142,7 +514,7 @@ fn usageAndExit(file: std.fs.File, arg0: []const u8, code: u8) noreturn {
142514 \\
143515 \\Generates Zig bindings for a SPIR-V specification .json (either core or
144516 \\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.
146518 \\
147519 \\The relevant specifications can be obtained from the SPIR-V registry:
148520 \\https://github.com/KhronosGroup/SPIRV-Headers/blob/master/include/spirv/unified1/