| author | |
| committer | |
| log | 9f16d9ed07275209946b9e733c30be1bb0a1ae33 |
| tree | b4a226f51cb417231dd8e8835173210abc4e7b94 |
| parent | e288148f60770a2cfa4c64f832b599172c383d36 |
| parent | 98ee39d1b0ed516428c611d8dc1e52d21c786f97 |
| signature |
spir-v improvements7 files changed, 3145 insertions(+), 587 deletions(-)
src/codegen/spirv.zig+346-395| ... | ... | @@ -4,9 +4,6 @@ const Target = std.Target; |
| 4 | 4 | const log = std.log.scoped(.codegen); |
| 5 | 5 | const assert = std.debug.assert; |
| 6 | 6 | |
| 7 | const spec = @import("spirv/spec.zig"); | |
| 8 | const Opcode = spec.Opcode; | |
| 9 | ||
| 10 | 7 | const Module = @import("../Module.zig"); |
| 11 | 8 | const Decl = Module.Decl; |
| 12 | 9 | const Type = @import("../type.zig").Type; |
| ... | ... | @@ -15,187 +12,78 @@ const LazySrcLoc = Module.LazySrcLoc; |
| 15 | 12 | const Air = @import("../Air.zig"); |
| 16 | 13 | const Liveness = @import("../Liveness.zig"); |
| 17 | 14 | |
| 18 | pub const Word = u32; | |
| 19 | pub const ResultId = u32; | |
| 15 | const spec = @import("spirv/spec.zig"); | |
| 16 | const Opcode = spec.Opcode; | |
| 17 | const Word = spec.Word; | |
| 18 | const IdRef = spec.IdRef; | |
| 19 | const IdResult = spec.IdResult; | |
| 20 | const IdResultType = spec.IdResultType; | |
| 21 | ||
| 22 | const SpvModule = @import("spirv/Module.zig"); | |
| 23 | const SpvSection = @import("spirv/Section.zig"); | |
| 24 | const SpvType = @import("spirv/type.zig").Type; | |
| 20 | 25 | |
| 21 | pub const TypeMap = std.HashMap(Type, u32, Type.HashContext64, std.hash_map.default_max_load_percentage); | |
| 22 | pub const InstMap = std.AutoHashMap(Air.Inst.Index, ResultId); | |
| 26 | const InstMap = std.AutoHashMapUnmanaged(Air.Inst.Index, IdRef); | |
| 23 | 27 | |
| 24 | 28 | const IncomingBlock = struct { |
| 25 | src_label_id: ResultId, | |
| 26 | break_value_id: ResultId, | |
| 29 | src_label_id: IdRef, | |
| 30 | break_value_id: IdRef, | |
| 27 | 31 | }; |
| 28 | 32 | |
| 29 | pub const BlockMap = std.AutoHashMap(Air.Inst.Index, struct { | |
| 30 | label_id: ResultId, | |
| 33 | pub const BlockMap = std.AutoHashMapUnmanaged(Air.Inst.Index, struct { | |
| 34 | label_id: IdRef, | |
| 31 | 35 | incoming_blocks: *std.ArrayListUnmanaged(IncomingBlock), |
| 32 | 36 | }); |
| 33 | 37 | |
| 34 | pub 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 | ||
| 39 | pub 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 | ||
| 44 | pub 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. | |
| 71 | pub 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. | |
| 39 | pub const DeclGen = struct { | |
| 40 | /// The Zig module that we are generating decls for. | |
| 76 | 41 | module: *Module, |
| 77 | 42 | |
| 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, | |
| 153 | 45 | |
| 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. | |
| 159 | pub 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, | |
| 162 | 48 | |
| 49 | /// The intermediate code of the declaration we are currently generating. Note: If | |
| 50 | /// the declaration is not a function, this value will be undefined! | |
| 163 | 51 | air: Air, |
| 52 | ||
| 53 | /// The liveness analysis of the intermediate code for the declaration we are currently generating. | |
| 54 | /// Note: If the declaration is not a function, this value will be undefined! | |
| 164 | 55 | liveness: Liveness, |
| 165 | 56 | |
| 166 | 57 | /// An array of function argument result-ids. Each index corresponds with the |
| 167 | 58 | /// function argument of the same index. |
| 168 | args: std.ArrayList(ResultId), | |
| 59 | args: std.ArrayListUnmanaged(IdRef) = .{}, | |
| 169 | 60 | |
| 170 | 61 | /// A counter to keep track of how many `arg` instructions we've seen yet. |
| 171 | 62 | next_arg_index: u32, |
| 172 | 63 | |
| 173 | 64 | /// A map keeping track of which instruction generated which result-id. |
| 174 | inst_results: InstMap, | |
| 65 | inst_results: InstMap = .{}, | |
| 175 | 66 | |
| 176 | 67 | /// We need to keep track of result ids for block labels, as well as the 'incoming' |
| 177 | 68 | /// blocks for a block. |
| 178 | blocks: BlockMap, | |
| 69 | blocks: BlockMap = .{}, | |
| 179 | 70 | |
| 180 | 71 | /// The label of the SPIR-V block we are currently generating. |
| 181 | current_block_label_id: ResultId, | |
| 72 | current_block_label_id: IdRef, | |
| 182 | 73 | |
| 183 | 74 | /// The actual instructions for this function. We need to declare all locals in |
| 184 | 75 | /// the first block, and because we don't know which locals there are going to be, |
| 185 | 76 | /// we're just going to generate everything after the locals-section in this array. |
| 186 | 77 | /// 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 = .{}, | |
| 189 | 80 | |
| 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. | |
| 194 | 82 | /// Memory is owned by `module.gpa`. |
| 195 | 83 | error_msg: ?*Module.ErrorMsg, |
| 196 | 84 | |
| 197 | 85 | /// Possible errors the `gen` function may return. |
| 198 | const Error = error{ AnalysisFail, OutOfMemory }; | |
| 86 | const Error = error{ CodegenFail, OutOfMemory }; | |
| 199 | 87 | |
| 200 | 88 | /// This structure is used to return information about a type typically used for |
| 201 | 89 | /// arithmetic operations. These types may either be integers, floats, or a vector |
| ... | ... | @@ -244,18 +132,15 @@ pub const DeclGen = struct { |
| 244 | 132 | |
| 245 | 133 | /// Initialize the common resources of a DeclGen. Some fields are left uninitialized, |
| 246 | 134 | /// only set when `gen` is called. |
| 247 | pub fn init(spv: *SPIRVModule) DeclGen { | |
| 135 | pub fn init(module: *Module, spv: *SpvModule) DeclGen { | |
| 248 | 136 | return .{ |
| 137 | .module = module, | |
| 249 | 138 | .spv = spv, |
| 139 | .decl = undefined, | |
| 250 | 140 | .air = undefined, |
| 251 | 141 | .liveness = undefined, |
| 252 | .args = std.ArrayList(ResultId).init(spv.gpa), | |
| 253 | 142 | .next_arg_index = undefined, |
| 254 | .inst_results = InstMap.init(spv.gpa), | |
| 255 | .blocks = BlockMap.init(spv.gpa), | |
| 256 | 143 | .current_block_label_id = undefined, |
| 257 | .code = std.ArrayList(Word).init(spv.gpa), | |
| 258 | .decl = undefined, | |
| 259 | 144 | .error_msg = undefined, |
| 260 | 145 | }; |
| 261 | 146 | } |
| ... | ... | @@ -265,6 +150,7 @@ pub const DeclGen = struct { |
| 265 | 150 | /// returns such a reportable error, it is valid to be called again for a different decl. |
| 266 | 151 | pub fn gen(self: *DeclGen, decl: *Decl, air: Air, liveness: Liveness) !?*Module.ErrorMsg { |
| 267 | 152 | // Reset internal resources, we don't want to re-allocate these. |
| 153 | self.decl = decl; | |
| 268 | 154 | self.air = air; |
| 269 | 155 | self.liveness = liveness; |
| 270 | 156 | self.args.items.len = 0; |
| ... | ... | @@ -272,35 +158,50 @@ pub const DeclGen = struct { |
| 272 | 158 | self.inst_results.clearRetainingCapacity(); |
| 273 | 159 | self.blocks.clearRetainingCapacity(); |
| 274 | 160 | self.current_block_label_id = undefined; |
| 275 | self.code.items.len = 0; | |
| 276 | self.decl = decl; | |
| 161 | self.code.reset(); | |
| 277 | 162 | self.error_msg = null; |
| 278 | 163 | |
| 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; | |
| 281 | 170 | } |
| 282 | 171 | |
| 283 | 172 | /// Free resources owned by the DeclGen. |
| 284 | 173 | 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); | |
| 289 | 178 | } |
| 290 | 179 | |
| 180 | /// Return the target which we are currently compiling for. | |
| 291 | 181 | fn getTarget(self: *DeclGen) std.Target { |
| 292 | return self.spv.module.getTarget(); | |
| 182 | return self.module.getTarget(); | |
| 293 | 183 | } |
| 294 | 184 | |
| 295 | 185 | fn fail(self: *DeclGen, comptime format: []const u8, args: anytype) Error { |
| 296 | 186 | @setCold(true); |
| 297 | 187 | const src: LazySrcLoc = .{ .node_offset = 0 }; |
| 298 | 188 | 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; | |
| 301 | 192 | } |
| 302 | 193 | |
| 303 | fn resolve(self: *DeclGen, inst: Air.Inst.Ref) !ResultId { | |
| 194 | fn todo(self: *DeclGen, comptime format: []const u8, args: anytype) Error { | |
| 195 | @setCold(true); | |
| 196 | const src: LazySrcLoc = .{ .node_offset = 0 }; | |
| 197 | const src_loc = src.toSrcLoc(self.decl); | |
| 198 | assert(self.error_msg == null); | |
| 199 | self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, "TODO (SPIR-V): " ++ format, args); | |
| 200 | return error.CodegenFail; | |
| 201 | } | |
| 202 | ||
| 203 | /// Fetch the result-id for a previously generated instruction or constant. | |
| 204 | fn resolve(self: *DeclGen, inst: Air.Inst.Ref) !IdRef { | |
| 304 | 205 | if (self.air.value(inst)) |val| { |
| 305 | 206 | return self.genConstant(self.air.typeOf(inst), val); |
| 306 | 207 | } |
| ... | ... | @@ -308,9 +209,13 @@ pub const DeclGen = struct { |
| 308 | 209 | return self.inst_results.get(index).?; // Assertion means instruction does not dominate usage. |
| 309 | 210 | } |
| 310 | 211 | |
| 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(); | |
| 314 | 219 | } |
| 315 | 220 | |
| 316 | 221 | /// SPIR-V requires enabling specific integer sizes through capabilities, and so if they are not enabled, we need |
| ... | ... | @@ -392,32 +297,37 @@ pub const DeclGen = struct { |
| 392 | 297 | const int_info = ty.intInfo(target); |
| 393 | 298 | // TODO: Maybe it's useful to also return this value. |
| 394 | 299 | const maybe_backing_bits = self.backingIntBits(int_info.bits); |
| 395 | break :blk ArithmeticTypeInfo{ .bits = int_info.bits, .is_vector = false, .signedness = int_info.signedness, .class = if (maybe_backing_bits) |backing_bits| | |
| 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 | |
| 398 | 309 | else |
| 399 | ArithmeticTypeInfo.Class.strange_integer | |
| 400 | else | |
| 401 | .composite_integer }; | |
| 310 | .composite_integer, | |
| 311 | }; | |
| 402 | 312 | }, |
| 403 | 313 | // As of yet, there is no vector support in the self-hosted compiler. |
| 404 | .Vector => self.fail("TODO: SPIR-V backend: implement arithmeticTypeInfo for Vector", .{}), | |
| 314 | .Vector => self.todo("implement arithmeticTypeInfo for Vector", .{}), | |
| 405 | 315 | // TODO: For which types is this the case? |
| 406 | else => self.fail("TODO: SPIR-V backend: implement arithmeticTypeInfo for {}", .{ty}), | |
| 316 | else => self.todo("implement arithmeticTypeInfo for {}", .{ty}), | |
| 407 | 317 | }; |
| 408 | 318 | } |
| 409 | 319 | |
| 410 | 320 | /// Generate a constant representing `val`. |
| 411 | 321 | /// TODO: Deduplication? |
| 412 | fn genConstant(self: *DeclGen, ty: Type, val: Value) Error!ResultId { | |
| 322 | fn genConstant(self: *DeclGen, ty: Type, val: Value) Error!IdRef { | |
| 413 | 323 | 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); | |
| 417 | 327 | |
| 418 | 328 | 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(); | |
| 421 | 331 | } |
| 422 | 332 | |
| 423 | 333 | switch (ty.zigTypeTag()) { |
| ... | ... | @@ -425,101 +335,96 @@ pub const DeclGen = struct { |
| 425 | 335 | const int_info = ty.intInfo(target); |
| 426 | 336 | const backing_bits = self.backingIntBits(int_info.bits) orelse { |
| 427 | 337 | // Integers too big for any native type are represented as "composite integers": An array of largestSupportedIntBits. |
| 428 | return self.fail("TODO: SPIR-V backend: implement composite int constants for {}", .{ty}); | |
| 338 | return self.todo("implement composite int constants for {}", .{ty}); | |
| 429 | 339 | }; |
| 430 | 340 | |
| 431 | 341 | // We can just use toSignedInt/toUnsignedInt here as it returns u64 - a type large enough to hold any |
| 432 | 342 | // SPIR-V native type (up to i/u64 with Int64). If SPIR-V ever supports native ints of a larger size, this |
| 433 | 343 | // might need to be updated. |
| 434 | 344 | assert(self.largestSupportedIntBits() <= std.meta.bitCount(u64)); |
| 345 | ||
| 346 | // Note, value is required to be sign-extended, so we don't need to mask off the upper bits. | |
| 347 | // See https://www.khronos.org/registry/SPIR-V/specs/unified1/SPIRV.html#Literal | |
| 435 | 348 | var int_bits = if (ty.isSignedInt()) @bitCast(u64, val.toSignedInt()) else val.toUnsignedInt(); |
| 436 | 349 | |
| 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 | }); | |
| 457 | 361 | }, |
| 458 | 362 | .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 | } | |
| 461 | 369 | }, |
| 462 | 370 | .Float => { |
| 463 | 371 | // 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? | |
| 482 | 381 | else => unreachable, |
| 483 | } | |
| 382 | }; | |
| 383 | ||
| 384 | try section.emit(self.spv.gpa, .OpConstant, .{ | |
| 385 | .id_result_type = result_type_id, | |
| 386 | .id_result = result_id, | |
| 387 | .value = value, | |
| 388 | }); | |
| 484 | 389 | }, |
| 485 | 390 | .Void => unreachable, |
| 486 | else => return self.fail("TODO: SPIR-V backend: constant generation of type {}", .{ty}), | |
| 391 | else => return self.todo("constant generation of type {}", .{ty}), | |
| 487 | 392 | } |
| 488 | 393 | |
| 489 | return result_id; | |
| 394 | return result_id.toRef(); | |
| 490 | 395 | } |
| 491 | 396 | |
| 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 | } | |
| 497 | 401 | |
| 402 | /// Turn a Zig type into a SPIR-V Type, and return a reference to it. | |
| 403 | fn resolveType(self: *DeclGen, ty: Type) Error!SpvType.Ref { | |
| 498 | 404 | 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: { | |
| 506 | 413 | const int_info = ty.intInfo(target); |
| 507 | 414 | const backing_bits = self.backingIntBits(int_info.bits) orelse { |
| 508 | // Integers too big for any native type are represented as "composite integers": An array of largestSupportedIntBits. | |
| 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}); | |
| 510 | 418 | }; |
| 511 | 419 | |
| 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)); | |
| 521 | 426 | }, |
| 522 | .Float => { | |
| 427 | .Float => blk: { | |
| 523 | 428 | // We can (and want) not really emulate floating points with other floating point types like with the integer types, |
| 524 | 429 | // so if the float is not supported, just return an error. |
| 525 | 430 | const bits = ty.floatBits(target); |
| ... | ... | @@ -535,37 +440,34 @@ pub const DeclGen = struct { |
| 535 | 440 | return self.fail("Floating point width of {} bits is not supported for the current SPIR-V feature set", .{bits}); |
| 536 | 441 | } |
| 537 | 442 | |
| 538 | try writeInstruction(code, .OpTypeFloat, &[_]Word{ result_id, bits }); | |
| 443 | const payload = try self.spv.arena.create(SpvType.Payload.Float); | |
| 444 | payload.* = .{ | |
| 445 | .width = bits, | |
| 446 | }; | |
| 447 | break :blk try self.spv.resolveType(SpvType.initPayload(&payload.base)); | |
| 539 | 448 | }, |
| 540 | .Fn => { | |
| 449 | .Fn => blk: { | |
| 541 | 450 | // We only support zig-calling-convention functions, no varargs. |
| 542 | 451 | if (ty.fnCallingConvention() != .Unspecified) |
| 543 | 452 | return self.fail("Unsupported calling convention for SPIR-V", .{}); |
| 544 | 453 | 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", .{}); | |
| 554 | 455 | |
| 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 | } | |
| 556 | 460 | |
| 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()); | |
| 560 | 462 | |
| 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", .{}); | |
| 566 | 470 | }, |
| 567 | // When recursively generating a type, we cannot infer the pointer's storage class. See genPointerType. | |
| 568 | .Pointer => return self.fail("Cannot create pointer with unknown storage class", .{}), | |
| 569 | 471 | .Vector => { |
| 570 | 472 | // Although not 100% the same, Zig vectors map quite neatly to SPIR-V vectors (including many integer and float operations |
| 571 | 473 | // which work on them), so simply use those. |
| ... | ... | @@ -575,41 +477,42 @@ pub const DeclGen = struct { |
| 575 | 477 | // is adequate at all for this. |
| 576 | 478 | |
| 577 | 479 | // TODO: Vectors are not yet supported by the self-hosted compiler itself it seems. |
| 578 | return self.fail("TODO: SPIR-V backend: implement type Vector", .{}); | |
| 480 | return self.todo("Implement type Vector", .{}); | |
| 579 | 481 | }, |
| 482 | ||
| 580 | 483 | .Null, |
| 581 | 484 | .Undefined, |
| 582 | 485 | .EnumLiteral, |
| 583 | 486 | .ComptimeFloat, |
| 584 | 487 | .ComptimeInt, |
| 585 | 488 | .Type, |
| 586 | => unreachable, // Must be const or comptime. | |
| 489 | => unreachable, // Must be comptime. | |
| 587 | 490 | |
| 588 | 491 | .BoundFn => unreachable, // this type will be deleted from the language. |
| 589 | 492 | |
| 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 | }; | |
| 595 | 495 | } |
| 596 | 496 | |
| 597 | 497 | /// SPIR-V requires pointers to have a storage class (address space), and so we have a special function for that. |
| 598 | 498 | /// TODO: The result of this needs to be cached. |
| 599 | fn genPointerType(self: *DeclGen, ty: Type, storage_class: spec.StorageClass) !ResultId { | |
| 499 | fn genPointerType(self: *DeclGen, ty: Type, storage_class: spec.StorageClass) !IdResultType { | |
| 600 | 500 | assert(ty.zigTypeTag() == .Pointer); |
| 601 | 501 | |
| 602 | const code = &self.spv.binary.types_globals_constants; | |
| 603 | const result_id = self.spv.allocResultId(); | |
| 502 | const result_id = self.spv.allocId(); | |
| 604 | 503 | |
| 605 | 504 | // TODO: There are many constraints which are ignored for now: We may only create pointers to certain types, and to other types |
| 606 | 505 | // if more capabilities are enabled. For example, we may only create pointers to f16 if Float16Buffer is enabled. |
| 607 | 506 | // These also relates to the pointer's address space. |
| 608 | const child_id = try self.genType(ty.elemType()); | |
| 507 | const child_id = try self.resolveTypeId(ty.elemType()); | |
| 609 | 508 | |
| 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 | }); | |
| 611 | 514 | |
| 612 | return result_id; | |
| 515 | return result_id.toResultType(); | |
| 613 | 516 | } |
| 614 | 517 | |
| 615 | 518 | fn genDecl(self: *DeclGen) !void { |
| ... | ... | @@ -618,41 +521,47 @@ pub const DeclGen = struct { |
| 618 | 521 | |
| 619 | 522 | if (decl.val.castTag(.function)) |_| { |
| 620 | 523 | 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(), | |
| 627 | 530 | }); |
| 628 | 531 | |
| 629 | 532 | const params = decl.ty.fnParamLen(); |
| 630 | 533 | var i: usize = 0; |
| 631 | 534 | |
| 632 | try self.args.ensureUnusedCapacity(params); | |
| 535 | try self.args.ensureUnusedCapacity(self.spv.gpa, params); | |
| 633 | 536 | 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()); | |
| 638 | 544 | } |
| 639 | 545 | |
| 640 | 546 | // TODO: This could probably be done in a better way... |
| 641 | const root_block_id = self.spv.allocResultId(); | |
| 547 | const root_block_id = self.spv.allocId(); | |
| 642 | 548 | |
| 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(); | |
| 647 | 555 | |
| 648 | 556 | const main_body = self.air.getMainBody(); |
| 649 | 557 | try self.genBody(main_body); |
| 650 | 558 | |
| 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, {}); | |
| 654 | 562 | } else { |
| 655 | return self.fail("TODO: SPIR-V backend: generate decl type {}", .{decl.ty.zigTypeTag()}); | |
| 563 | // TODO | |
| 564 | // return self.todo("generate decl type {}", .{decl.ty.zigTypeTag()}); | |
| 656 | 565 | } |
| 657 | 566 | } |
| 658 | 567 | |
| ... | ... | @@ -666,9 +575,9 @@ pub const DeclGen = struct { |
| 666 | 575 | const air_tags = self.air.instructions.items(.tag); |
| 667 | 576 | const result_id = switch (air_tags[inst]) { |
| 668 | 577 | // 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), | |
| 672 | 581 | |
| 673 | 582 | .bit_and => try self.airBinOpSimple(inst, .OpBitwiseAnd), |
| 674 | 583 | .bit_or => try self.airBinOpSimple(inst, .OpBitwiseOr), |
| ... | ... | @@ -678,12 +587,12 @@ pub const DeclGen = struct { |
| 678 | 587 | |
| 679 | 588 | .not => try self.airNot(inst), |
| 680 | 589 | |
| 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), | |
| 687 | 596 | |
| 688 | 597 | .arg => self.airArg(), |
| 689 | 598 | .alloc => try self.airAlloc(inst), |
| ... | ... | @@ -701,27 +610,30 @@ pub const DeclGen = struct { |
| 701 | 610 | .unreach => return self.airUnreach(), |
| 702 | 611 | // zig fmt: on |
| 703 | 612 | |
| 704 | else => |tag| return self.fail("TODO: SPIR-V backend: implement AIR tag {s}", .{ | |
| 613 | else => |tag| return self.todo("implement AIR tag {s}", .{ | |
| 705 | 614 | @tagName(tag), |
| 706 | 615 | }), |
| 707 | 616 | }; |
| 708 | 617 | |
| 709 | try self.inst_results.putNoClobber(inst, result_id); | |
| 618 | try self.inst_results.putNoClobber(self.spv.gpa, inst, result_id); | |
| 710 | 619 | } |
| 711 | 620 | |
| 712 | fn airBinOpSimple(self: *DeclGen, inst: Air.Inst.Index, opcode: Opcode) !ResultId { | |
| 621 | fn airBinOpSimple(self: *DeclGen, inst: Air.Inst.Index, comptime opcode: Opcode) !IdRef { | |
| 713 | 622 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 714 | 623 | const lhs_id = try self.resolve(bin_op.lhs); |
| 715 | 624 | 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, | |
| 720 | 632 | }); |
| 721 | return result_id; | |
| 633 | return result_id.toRef(); | |
| 722 | 634 | } |
| 723 | 635 | |
| 724 | fn airArithOp(self: *DeclGen, inst: Air.Inst.Index, ops: [3]Opcode) !ResultId { | |
| 636 | fn airArithOp(self: *DeclGen, inst: Air.Inst.Index, comptime fop: Opcode, comptime sop: Opcode, comptime uop: Opcode) !IdRef { | |
| 725 | 637 | // LHS and RHS are guaranteed to have the same type, and AIR guarantees |
| 726 | 638 | // the result to be the same as the LHS and RHS, which matches SPIR-V. |
| 727 | 639 | const ty = self.air.typeOfIndex(inst); |
| ... | ... | @@ -729,8 +641,8 @@ pub const DeclGen = struct { |
| 729 | 641 | const lhs_id = try self.resolve(bin_op.lhs); |
| 730 | 642 | const rhs_id = try self.resolve(bin_op.rhs); |
| 731 | 643 | |
| 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); | |
| 734 | 646 | |
| 735 | 647 | assert(self.air.typeOf(bin_op.lhs).eql(ty)); |
| 736 | 648 | assert(self.air.typeOf(bin_op.rhs).eql(ty)); |
| ... | ... | @@ -741,10 +653,10 @@ pub const DeclGen = struct { |
| 741 | 653 | |
| 742 | 654 | const opcode_index: usize = switch (info.class) { |
| 743 | 655 | .composite_integer => { |
| 744 | return self.fail("TODO: SPIR-V backend: binary operations for composite integers", .{}); | |
| 656 | return self.todo("binary operations for composite integers", .{}); | |
| 745 | 657 | }, |
| 746 | 658 | .strange_integer => { |
| 747 | return self.fail("TODO: SPIR-V backend: binary operations for strange integers", .{}); | |
| 659 | return self.todo("binary operations for strange integers", .{}); | |
| 748 | 660 | }, |
| 749 | 661 | .integer => switch (info.signedness) { |
| 750 | 662 | .signed => @as(usize, 1), |
| ... | ... | @@ -753,21 +665,32 @@ pub const DeclGen = struct { |
| 753 | 665 | .float => 0, |
| 754 | 666 | else => unreachable, |
| 755 | 667 | }; |
| 756 | const opcode = ops[opcode_index]; | |
| 757 | try writeInstruction(&self.code, opcode, &[_]Word{ result_type_id, result_id, lhs_id, rhs_id }); | |
| 758 | 668 | |
| 669 | const operands = .{ | |
| 670 | .id_result_type = result_type_id, | |
| 671 | .id_result = result_id, | |
| 672 | .operand_1 = lhs_id, | |
| 673 | .operand_2 = rhs_id, | |
| 674 | }; | |
| 675 | ||
| 676 | switch (opcode_index) { | |
| 677 | 0 => try self.code.emit(self.spv.gpa, fop, operands), | |
| 678 | 1 => try self.code.emit(self.spv.gpa, sop, operands), | |
| 679 | 2 => try self.code.emit(self.spv.gpa, uop, operands), | |
| 680 | else => unreachable, | |
| 681 | } | |
| 759 | 682 | // TODO: Trap on overflow? Probably going to be annoying. |
| 760 | 683 | // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap. |
| 761 | 684 | |
| 762 | return result_id; | |
| 685 | return result_id.toRef(); | |
| 763 | 686 | } |
| 764 | 687 | |
| 765 | fn airCmp(self: *DeclGen, inst: Air.Inst.Index, ops: [3]Opcode) !ResultId { | |
| 688 | fn airCmp(self: *DeclGen, inst: Air.Inst.Index, comptime fop: Opcode, comptime sop: Opcode, comptime uop: Opcode) !IdRef { | |
| 766 | 689 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 767 | 690 | const lhs_id = try self.resolve(bin_op.lhs); |
| 768 | 691 | 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)); | |
| 771 | 694 | const op_ty = self.air.typeOf(bin_op.lhs); |
| 772 | 695 | assert(op_ty.eql(self.air.typeOf(bin_op.rhs))); |
| 773 | 696 | |
| ... | ... | @@ -777,10 +700,10 @@ pub const DeclGen = struct { |
| 777 | 700 | |
| 778 | 701 | const opcode_index: usize = switch (info.class) { |
| 779 | 702 | .composite_integer => { |
| 780 | return self.fail("TODO: SPIR-V backend: binary operations for composite integers", .{}); | |
| 703 | return self.todo("binary operations for composite integers", .{}); | |
| 781 | 704 | }, |
| 782 | 705 | .strange_integer => { |
| 783 | return self.fail("TODO: SPIR-V backend: comparison for strange integers", .{}); | |
| 706 | return self.todo("comparison for strange integers", .{}); | |
| 784 | 707 | }, |
| 785 | 708 | .float => 0, |
| 786 | 709 | .bool => 1, |
| ... | ... | @@ -789,53 +712,71 @@ pub const DeclGen = struct { |
| 789 | 712 | .unsigned => @as(usize, 2), |
| 790 | 713 | }, |
| 791 | 714 | }; |
| 792 | const opcode = ops[opcode_index]; | |
| 793 | 715 | |
| 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(); | |
| 796 | 731 | } |
| 797 | 732 | |
| 798 | fn airNot(self: *DeclGen, inst: Air.Inst.Index) !ResultId { | |
| 733 | fn airNot(self: *DeclGen, inst: Air.Inst.Index) !IdRef { | |
| 799 | 734 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 800 | 735 | 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(); | |
| 806 | 744 | } |
| 807 | 745 | |
| 808 | fn airAlloc(self: *DeclGen, inst: Air.Inst.Index) !ResultId { | |
| 746 | fn airAlloc(self: *DeclGen, inst: Air.Inst.Index) !IdRef { | |
| 809 | 747 | const ty = self.air.typeOfIndex(inst); |
| 810 | 748 | const storage_class = spec.StorageClass.Function; |
| 811 | 749 | const result_type_id = try self.genPointerType(ty, storage_class); |
| 812 | const result_id = self.spv.allocResultId(); | |
| 750 | const result_id = self.spv.allocId(); | |
| 813 | 751 | |
| 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 | |
| 815 | 753 | // variable declarations appear in the first block of the function. |
| 816 | try writeInstruction(&self.spv.binary.fn_decls, .OpVariable, &[_]Word{ result_type_id, result_id, @enumToInt(storage_class) }); | |
| 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(); | |
| 819 | 760 | } |
| 820 | 761 | |
| 821 | fn airArg(self: *DeclGen) ResultId { | |
| 762 | fn airArg(self: *DeclGen) IdRef { | |
| 822 | 763 | defer self.next_arg_index += 1; |
| 823 | 764 | return self.args.items[self.next_arg_index]; |
| 824 | 765 | } |
| 825 | 766 | |
| 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 | |
| 828 | 769 | // "return" a value from. This cannot be directly modelled in SPIR-V, so in a block instruction, we're going to split up |
| 829 | 770 | // the current block by first generating the code of the block, then a label, and then generate the rest of the current |
| 830 | 771 | // ir.Block in a different SPIR-V block. |
| 831 | 772 | |
| 832 | const label_id = self.spv.allocResultId(); | |
| 773 | const label_id = self.spv.allocId(); | |
| 833 | 774 | |
| 834 | 775 | // 4 chosen as arbitrary initial capacity. |
| 835 | 776 | var incoming_blocks = try std.ArrayListUnmanaged(IncomingBlock).initCapacity(self.spv.gpa, 4); |
| 836 | 777 | |
| 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(), | |
| 839 | 780 | .incoming_blocks = &incoming_blocks, |
| 840 | 781 | }); |
| 841 | 782 | defer { |
| ... | ... | @@ -849,7 +790,7 @@ pub const DeclGen = struct { |
| 849 | 790 | const body = self.air.extra[extra.end..][0..extra.data.body_len]; |
| 850 | 791 | |
| 851 | 792 | try self.genBody(body); |
| 852 | try self.beginSPIRVBlock(label_id); | |
| 793 | try self.beginSpvBlock(label_id); | |
| 853 | 794 | |
| 854 | 795 | // If this block didn't produce a value, simply return here. |
| 855 | 796 | if (!ty.hasRuntimeBits()) |
| ... | ... | @@ -857,21 +798,21 @@ pub const DeclGen = struct { |
| 857 | 798 | |
| 858 | 799 | // Combine the result from the blocks using the Phi instruction. |
| 859 | 800 | |
| 860 | const result_id = self.spv.allocResultId(); | |
| 801 | const result_id = self.spv.allocId(); | |
| 861 | 802 | |
| 862 | 803 | // 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 | |
| 864 | 805 | // an error for pointers. |
| 865 | const result_type_id = try self.genType(ty); | |
| 806 | const result_type_id = try self.resolveTypeId(ty); | |
| 866 | 807 | _ = result_type_id; |
| 867 | 808 | |
| 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... | |
| 869 | 810 | |
| 870 | 811 | for (incoming_blocks.items) |incoming| { |
| 871 | try self.code.appendSlice(&[_]Word{ incoming.break_value_id, incoming.src_label_id }); | |
| 812 | self.code.writeOperand(spec.PairIdRefIdRef, .{ incoming.break_value_id, incoming.src_label_id }); | |
| 872 | 813 | } |
| 873 | 814 | |
| 874 | return result_id; | |
| 815 | return result_id.toRef(); | |
| 875 | 816 | } |
| 876 | 817 | |
| 877 | 818 | fn airBr(self: *DeclGen, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -885,7 +826,7 @@ pub const DeclGen = struct { |
| 885 | 826 | try block.incoming_blocks.append(self.spv.gpa, .{ .src_label_id = self.current_block_label_id, .break_value_id = operand_id }); |
| 886 | 827 | } |
| 887 | 828 | |
| 888 | try writeInstruction(&self.code, .OpBranch, &[_]Word{block.label_id}); | |
| 829 | try self.code.emit(self.spv.gpa, .OpBranch, .{ .target_label = block.label_id }); | |
| 889 | 830 | } |
| 890 | 831 | |
| 891 | 832 | fn airCondBr(self: *DeclGen, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -896,63 +837,70 @@ pub const DeclGen = struct { |
| 896 | 837 | const condition_id = try self.resolve(pl_op.operand); |
| 897 | 838 | |
| 898 | 839 | // These will always generate a new SPIR-V block, since they are ir.Body and not ir.Block. |
| 899 | const then_label_id = self.spv.allocResultId(); | |
| 900 | const else_label_id = self.spv.allocResultId(); | |
| 840 | const then_label_id = self.spv.allocId(); | |
| 841 | const else_label_id = self.spv.allocId(); | |
| 901 | 842 | |
| 902 | 843 | // TODO: We can generate OpSelectionMerge here if we know the target block that both of these will resolve to, |
| 903 | 844 | // but i don't know if those will always resolve to the same block. |
| 904 | 845 | |
| 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(), | |
| 909 | 850 | }); |
| 910 | 851 | |
| 911 | try self.beginSPIRVBlock(then_label_id); | |
| 852 | try self.beginSpvBlock(then_label_id); | |
| 912 | 853 | try self.genBody(then_body); |
| 913 | try self.beginSPIRVBlock(else_label_id); | |
| 854 | try self.beginSpvBlock(else_label_id); | |
| 914 | 855 | try self.genBody(else_body); |
| 915 | 856 | } |
| 916 | 857 | |
| 917 | 858 | fn airDbgStmt(self: *DeclGen, inst: Air.Inst.Index) !void { |
| 918 | 859 | const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; |
| 919 | 860 | const src_fname_id = try self.spv.resolveSourceFileName(self.decl); |
| 920 | try writeInstruction(&self.code, .OpLine, &[_]Word{ src_fname_id, dbg_stmt.line, dbg_stmt.column }); | |
| 861 | try self.code.emit(self.spv.gpa, .OpLine, .{ | |
| 862 | .file = src_fname_id, | |
| 863 | .line = dbg_stmt.line, | |
| 864 | .column = dbg_stmt.column, | |
| 865 | }); | |
| 921 | 866 | } |
| 922 | 867 | |
| 923 | fn airLoad(self: *DeclGen, inst: Air.Inst.Index) !ResultId { | |
| 868 | fn airLoad(self: *DeclGen, inst: Air.Inst.Index) !IdRef { | |
| 924 | 869 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 925 | 870 | const operand_id = try self.resolve(ty_op.operand); |
| 926 | 871 | const ty = self.air.typeOfIndex(inst); |
| 927 | 872 | |
| 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(); | |
| 930 | 875 | |
| 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 | }; | |
| 935 | 879 | |
| 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 | }); | |
| 937 | 886 | |
| 938 | return result_id; | |
| 887 | return result_id.toRef(); | |
| 939 | 888 | } |
| 940 | 889 | |
| 941 | 890 | fn airLoop(self: *DeclGen, inst: Air.Inst.Index) !void { |
| 942 | 891 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 943 | 892 | const loop = self.air.extraData(Air.Block, ty_pl.payload); |
| 944 | 893 | const body = self.air.extra[loop.end..][0..loop.data.body_len]; |
| 945 | const loop_label_id = self.spv.allocResultId(); | |
| 894 | const loop_label_id = self.spv.allocId(); | |
| 946 | 895 | |
| 947 | 896 | // 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() }); | |
| 949 | 898 | |
| 950 | 899 | // TODO: Look into OpLoopMerge. |
| 951 | ||
| 952 | try self.beginSPIRVBlock(loop_label_id); | |
| 900 | try self.beginSpvBlock(loop_label_id); | |
| 953 | 901 | try self.genBody(body); |
| 954 | 902 | |
| 955 | try writeInstruction(&self.code, .OpBranch, &[_]Word{loop_label_id}); | |
| 903 | try self.code.emit(self.spv.gpa, .OpBranch, .{ .target_label = loop_label_id.toRef() }); | |
| 956 | 904 | } |
| 957 | 905 | |
| 958 | 906 | fn airRet(self: *DeclGen, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -960,9 +908,9 @@ pub const DeclGen = struct { |
| 960 | 908 | const operand_ty = self.air.typeOf(operand); |
| 961 | 909 | if (operand_ty.hasRuntimeBits()) { |
| 962 | 910 | const operand_id = try self.resolve(operand); |
| 963 | try writeInstruction(&self.code, .OpReturnValue, &[_]Word{operand_id}); | |
| 911 | try self.code.emit(self.spv.gpa, .OpReturnValue, .{ .value = operand_id }); | |
| 964 | 912 | } else { |
| 965 | try writeInstruction(&self.code, .OpReturn, &[_]Word{}); | |
| 913 | try self.code.emit(self.spv.gpa, .OpReturn, {}); | |
| 966 | 914 | } |
| 967 | 915 | } |
| 968 | 916 | |
| ... | ... | @@ -972,15 +920,18 @@ pub const DeclGen = struct { |
| 972 | 920 | const src_val_id = try self.resolve(bin_op.rhs); |
| 973 | 921 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 974 | 922 | |
| 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 | }; | |
| 979 | 926 | |
| 980 | try writeInstruction(&self.code, .OpStore, operands); | |
| 927 | try self.code.emit(self.spv.gpa, .OpStore, .{ | |
| 928 | .pointer = dst_ptr_id, | |
| 929 | .object = src_val_id, | |
| 930 | .memory_access = access, | |
| 931 | }); | |
| 981 | 932 | } |
| 982 | 933 | |
| 983 | 934 | fn airUnreach(self: *DeclGen) !void { |
| 984 | try writeInstruction(&self.code, .OpUnreachable, &[_]Word{}); | |
| 935 | try self.code.emit(self.spv.gpa, .OpUnreachable, {}); | |
| 985 | 936 | } |
| 986 | 937 | }; |
src/codegen/spirv/Module.zig created+428| ... | ... | @@ -0,0 +1,428 @@ |
| 1 | //! This structure represents a SPIR-V (sections) module being compiled, and keeps track of all relevant information. | |
| 2 | //! That includes the actual instructions, the current result-id bound, and data structures for querying result-id's | |
| 3 | //! of data which needs to be persistent over different calls to Decl code generation. | |
| 4 | //! | |
| 5 | //! A SPIR-V binary module supports both little- and big endian layout. The layout is detected by the magic word in the | |
| 6 | //! header. Therefore, we can ignore any byte order throughout the implementation, and just use the host byte order, | |
| 7 | //! and make this a problem for the consumer. | |
| 8 | const Module = @This(); | |
| 9 | ||
| 10 | const std = @import("std"); | |
| 11 | const Allocator = std.mem.Allocator; | |
| 12 | const assert = std.debug.assert; | |
| 13 | ||
| 14 | const ZigDecl = @import("../../Module.zig").Decl; | |
| 15 | ||
| 16 | const spec = @import("spec.zig"); | |
| 17 | const Word = spec.Word; | |
| 18 | const IdRef = spec.IdRef; | |
| 19 | const IdResult = spec.IdResult; | |
| 20 | const IdResultType = spec.IdResultType; | |
| 21 | ||
| 22 | const Section = @import("Section.zig"); | |
| 23 | const Type = @import("type.zig").Type; | |
| 24 | ||
| 25 | const TypeCache = std.ArrayHashMapUnmanaged(Type, IdResultType, Type.ShallowHashContext32, true); | |
| 26 | ||
| 27 | /// A general-purpose allocator which may be used to allocate resources for this module | |
| 28 | gpa: Allocator, | |
| 29 | ||
| 30 | /// An arena allocator used to store things that have the same lifetime as this module. | |
| 31 | arena: Allocator, | |
| 32 | ||
| 33 | /// Module layout, according to SPIR-V Spec section 2.4, "Logical Layout of a Module". | |
| 34 | sections: 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. | |
| 59 | next_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. | |
| 64 | source_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). | |
| 70 | type_cache: TypeCache = .{}, | |
| 71 | ||
| 72 | pub 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 | ||
| 80 | pub 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 | ||
| 95 | pub fn allocId(self: *Module) spec.IdResult { | |
| 96 | defer self.next_result_id += 1; | |
| 97 | return .{ .id = self.next_result_id }; | |
| 98 | } | |
| 99 | ||
| 100 | pub fn idBound(self: Module) Word { | |
| 101 | return self.next_result_id; | |
| 102 | } | |
| 103 | ||
| 104 | /// Emit this module as a spir-v binary. | |
| 105 | pub 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. | |
| 146 | pub 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. | |
| 173 | pub 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 | ||
| 181 | pub 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. | |
| 186 | pub 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. | |
| 191 | pub 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. | |
| 201 | pub 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 | ||
| 328 | fn 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. | |
| 3 | const Section = @This(); | |
| 4 | ||
| 5 | const std = @import("std"); | |
| 6 | const Allocator = std.mem.Allocator; | |
| 7 | const testing = std.testing; | |
| 8 | ||
| 9 | const spec = @import("spec.zig"); | |
| 10 | const Word = spec.Word; | |
| 11 | const DoubleWord = std.meta.Int(.unsigned, @bitSizeOf(Word) * 2); | |
| 12 | const Log2Word = std.math.Log2Int(Word); | |
| 13 | ||
| 14 | const Opcode = spec.Opcode; | |
| 15 | ||
| 16 | /// The instructions in this section. Memory is owned by the Module | |
| 17 | /// externally associated to this Section. | |
| 18 | instructions: std.ArrayListUnmanaged(Word) = .{}, | |
| 19 | ||
| 20 | pub fn deinit(section: *Section, allocator: Allocator) void { | |
| 21 | section.instructions.deinit(allocator); | |
| 22 | section.* = undefined; | |
| 23 | } | |
| 24 | ||
| 25 | /// Clear the instructions in this section | |
| 26 | pub fn reset(section: *Section) void { | |
| 27 | section.instructions.items.len = 0; | |
| 28 | } | |
| 29 | ||
| 30 | pub fn toWords(section: Section) []Word { | |
| 31 | return section.instructions.items; | |
| 32 | } | |
| 33 | ||
| 34 | /// Append the instructions from another section into this section. | |
| 35 | pub 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. | |
| 40 | pub 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 | ||
| 51 | pub 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. | |
| 64 | pub 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. | |
| 77 | pub 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 | ||
| 91 | pub fn writeWord(section: *Section, word: Word) void { | |
| 92 | section.instructions.appendAssumeCapacity(word); | |
| 93 | } | |
| 94 | ||
| 95 | pub fn writeWords(section: *Section, words: []const Word) void { | |
| 96 | section.instructions.appendSliceAssumeCapacity(words); | |
| 97 | } | |
| 98 | ||
| 99 | fn writeDoubleWord(section: *Section, dword: DoubleWord) void { | |
| 100 | section.writeWords(&.{ | |
| 101 | @truncate(Word, dword), | |
| 102 | @truncate(Word, dword >> @bitSizeOf(Word)), | |
| 103 | }); | |
| 104 | } | |
| 105 | ||
| 106 | fn 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 | ||
| 118 | pub 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 | ||
| 163 | fn 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 | ||
| 180 | fn 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 | ||
| 191 | fn 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 | ||
| 222 | fn 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 | ||
| 235 | fn instructionSize(comptime opcode: spec.Opcode, operands: opcode.Operands()) usize { | |
| 236 | return 1 + operandsSize(opcode.Operands(), operands); | |
| 237 | } | |
| 238 | ||
| 239 | fn 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 | ||
| 254 | fn 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 | ||
| 298 | fn 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 | ||
| 319 | fn 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 | ||
| 330 | test "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 | ||
| 339 | test "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 | ||
| 355 | test "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 | ||
| 380 | test "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 | ||
| 404 | test "SPIR-V Section emit() - extended union" { | |
| 405 | var section = Section{}; | |
| 406 | defer section.deinit(std.testing.allocator); | |
| 407 | ||
| 408 | try section.emit(std.testing.allocator, .OpExecutionMode, .{ | |
| 409 | .entry_point = .{ .id = 888 }, | |
| 410 | .mode = .{ | |
| 411 | .LocalSize = .{ .x_size = 4, .y_size = 8, .z_size = 16 }, | |
| 412 | }, | |
| 413 | }); | |
| 414 | ||
| 415 | try testing.expectEqualSlices(Word, &.{ | |
| 416 | (@as(Word, 6) << 16) | @enumToInt(Opcode.OpExecutionMode), | |
| 417 | 888, | |
| 418 | @enumToInt(spec.ExecutionMode.LocalSize), | |
| 419 | 4, | |
| 420 | 8, | |
| 421 | 16, | |
| 422 | }, section.instructions.items); | |
| 423 | } |
src/codegen/spirv/spec.zig+985-68| ... | ... | @@ -1,8 +1,46 @@ |
| 1 | 1 | //! This file is auto-generated by tools/gen_spirv_spec.zig. |
| 2 | 2 | |
| 3 | 3 | const Version = @import("std").builtin.Version; |
| 4 | ||
| 5 | pub const Word = u32; | |
| 6 | pub const IdResultType = struct { | |
| 7 | id: Word, | |
| 8 | pub fn toRef(self: IdResultType) IdRef { | |
| 9 | return .{ .id = self.id }; | |
| 10 | } | |
| 11 | }; | |
| 12 | pub 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 | }; | |
| 21 | pub const IdRef = struct { id: Word }; | |
| 22 | ||
| 23 | pub const IdMemorySemantics = IdRef; | |
| 24 | pub const IdScope = IdRef; | |
| 25 | ||
| 26 | pub const LiteralInteger = Word; | |
| 27 | pub const LiteralString = []const u8; | |
| 28 | pub const LiteralContextDependentNumber = union(enum) { | |
| 29 | int32: i32, | |
| 30 | uint32: u32, | |
| 31 | int64: i64, | |
| 32 | uint64: u64, | |
| 33 | float32: f32, | |
| 34 | float64: f64, | |
| 35 | }; | |
| 36 | pub const LiteralExtInstInteger = struct { inst: Word }; | |
| 37 | pub const LiteralSpecConstantOpInteger = struct { opcode: Opcode }; | |
| 38 | pub const PairLiteralIntegerIdRef = struct { value: LiteralInteger, label: IdRef }; | |
| 39 | pub const PairIdRefLiteralInteger = struct { target: IdRef, member: LiteralInteger }; | |
| 40 | pub const PairIdRefIdRef = [2]IdRef; | |
| 41 | ||
| 4 | 42 | pub const version = Version{ .major = 1, .minor = 5, .patch = 4 }; |
| 5 | pub const magic_number: u32 = 0x07230203; | |
| 43 | pub const magic_number: Word = 0x07230203; | |
| 6 | 44 | pub const Opcode = enum(u16) { |
| 7 | 45 | OpNop = 0, |
| 8 | 46 | OpUndef = 1, |
| ... | ... | @@ -381,11 +419,11 @@ pub const Opcode = enum(u16) { |
| 381 | 419 | OpImageSampleFootprintNV = 5283, |
| 382 | 420 | OpGroupNonUniformPartitionNV = 5296, |
| 383 | 421 | OpWritePackedPrimitiveIndices4x8NV = 5299, |
| 384 | OpReportIntersectionNV = 5334, | |
| 422 | OpReportIntersectionKHR = 5334, | |
| 385 | 423 | OpIgnoreIntersectionNV = 5335, |
| 386 | 424 | OpTerminateRayNV = 5336, |
| 387 | 425 | OpTraceNV = 5337, |
| 388 | OpTypeAccelerationStructureNV = 5341, | |
| 426 | OpTypeAccelerationStructureKHR = 5341, | |
| 389 | 427 | OpExecuteCallableNV = 5344, |
| 390 | 428 | OpTypeCooperativeMatrixNV = 5358, |
| 391 | 429 | OpCooperativeMatrixLoadNV = 5359, |
| ... | ... | @@ -580,10 +618,592 @@ pub const Opcode = enum(u16) { |
| 580 | 618 | OpTypeStructContinuedINTEL = 6090, |
| 581 | 619 | OpConstantCompositeContinuedINTEL = 6091, |
| 582 | 620 | OpSpecConstantCompositeContinuedINTEL = 6092, |
| 583 | _, | |
| 584 | 621 | |
| 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 | } | |
| 587 | 1207 | }; |
| 588 | 1208 | pub const ImageOperands = packed struct { |
| 589 | 1209 | Bias: bool align(@alignOf(u32)) = false, |
| ... | ... | @@ -618,6 +1238,46 @@ pub const ImageOperands = packed struct { |
| 618 | 1238 | _reserved_bit_29: bool = false, |
| 619 | 1239 | _reserved_bit_30: bool = false, |
| 620 | 1240 | _reserved_bit_31: bool = false, |
| 1241 | ||
| 1242 | pub const MakeTexelAvailableKHR: ImageOperands = .{ .MakeTexelAvailable = true }; | |
| 1243 | pub const MakeTexelVisibleKHR: ImageOperands = .{ .MakeTexelVisible = true }; | |
| 1244 | pub const NonPrivateTexelKHR: ImageOperands = .{ .NonPrivateTexel = true }; | |
| 1245 | pub const VolatileTexelKHR: ImageOperands = .{ .VolatileTexel = true }; | |
| 1246 | ||
| 1247 | pub const Extended = struct { | |
| 1248 | Bias: ?struct { id_ref: IdRef } = null, | |
| 1249 | Lod: ?struct { id_ref: IdRef } = null, | |
| 1250 | Grad: ?struct { id_ref_0: IdRef, id_ref_1: IdRef } = null, | |
| 1251 | ConstOffset: ?struct { id_ref: IdRef } = null, | |
| 1252 | Offset: ?struct { id_ref: IdRef } = null, | |
| 1253 | ConstOffsets: ?struct { id_ref: IdRef } = null, | |
| 1254 | Sample: ?struct { id_ref: IdRef } = null, | |
| 1255 | MinLod: ?struct { id_ref: IdRef } = null, | |
| 1256 | MakeTexelAvailable: ?struct { id_scope: IdScope } = null, | |
| 1257 | MakeTexelVisible: ?struct { id_scope: IdScope } = null, | |
| 1258 | NonPrivateTexel: bool = false, | |
| 1259 | VolatileTexel: bool = false, | |
| 1260 | SignExtend: bool = false, | |
| 1261 | ZeroExtend: bool = false, | |
| 1262 | _reserved_bit_14: bool = false, | |
| 1263 | _reserved_bit_15: bool = false, | |
| 1264 | _reserved_bit_16: bool = false, | |
| 1265 | _reserved_bit_17: bool = false, | |
| 1266 | _reserved_bit_18: bool = false, | |
| 1267 | _reserved_bit_19: bool = false, | |
| 1268 | _reserved_bit_20: bool = false, | |
| 1269 | _reserved_bit_21: bool = false, | |
| 1270 | _reserved_bit_22: bool = false, | |
| 1271 | _reserved_bit_23: bool = false, | |
| 1272 | _reserved_bit_24: bool = false, | |
| 1273 | _reserved_bit_25: bool = false, | |
| 1274 | _reserved_bit_26: bool = false, | |
| 1275 | _reserved_bit_27: bool = false, | |
| 1276 | _reserved_bit_28: bool = false, | |
| 1277 | _reserved_bit_29: bool = false, | |
| 1278 | _reserved_bit_30: bool = false, | |
| 1279 | _reserved_bit_31: bool = false, | |
| 1280 | }; | |
| 621 | 1281 | }; |
| 622 | 1282 | pub const FPFastMathMode = packed struct { |
| 623 | 1283 | NotNaN: bool align(@alignOf(u32)) = false, |
| ... | ... | @@ -720,6 +1380,41 @@ pub const LoopControl = packed struct { |
| 720 | 1380 | _reserved_bit_29: bool = false, |
| 721 | 1381 | _reserved_bit_30: bool = false, |
| 722 | 1382 | _reserved_bit_31: bool = false, |
| 1383 | ||
| 1384 | pub const Extended = struct { | |
| 1385 | Unroll: bool = false, | |
| 1386 | DontUnroll: bool = false, | |
| 1387 | DependencyInfinite: bool = false, | |
| 1388 | DependencyLength: ?struct { literal_integer: LiteralInteger } = null, | |
| 1389 | MinIterations: ?struct { literal_integer: LiteralInteger } = null, | |
| 1390 | MaxIterations: ?struct { literal_integer: LiteralInteger } = null, | |
| 1391 | IterationMultiple: ?struct { literal_integer: LiteralInteger } = null, | |
| 1392 | PeelCount: ?struct { literal_integer: LiteralInteger } = null, | |
| 1393 | PartialCount: ?struct { literal_integer: LiteralInteger } = null, | |
| 1394 | _reserved_bit_9: bool = false, | |
| 1395 | _reserved_bit_10: bool = false, | |
| 1396 | _reserved_bit_11: bool = false, | |
| 1397 | _reserved_bit_12: bool = false, | |
| 1398 | _reserved_bit_13: bool = false, | |
| 1399 | _reserved_bit_14: bool = false, | |
| 1400 | _reserved_bit_15: bool = false, | |
| 1401 | InitiationIntervalINTEL: ?struct { literal_integer: LiteralInteger } = null, | |
| 1402 | MaxConcurrencyINTEL: ?struct { literal_integer: LiteralInteger } = null, | |
| 1403 | DependencyArrayINTEL: ?struct { literal_integer: LiteralInteger } = null, | |
| 1404 | PipelineEnableINTEL: ?struct { literal_integer: LiteralInteger } = null, | |
| 1405 | LoopCoalesceINTEL: ?struct { literal_integer: LiteralInteger } = null, | |
| 1406 | MaxInterleavingINTEL: ?struct { literal_integer: LiteralInteger } = null, | |
| 1407 | SpeculatedIterationsINTEL: ?struct { literal_integer: LiteralInteger } = null, | |
| 1408 | NoFusionINTEL: ?struct { literal_integer: LiteralInteger } = null, | |
| 1409 | _reserved_bit_24: bool = false, | |
| 1410 | _reserved_bit_25: bool = false, | |
| 1411 | _reserved_bit_26: bool = false, | |
| 1412 | _reserved_bit_27: bool = false, | |
| 1413 | _reserved_bit_28: bool = false, | |
| 1414 | _reserved_bit_29: bool = false, | |
| 1415 | _reserved_bit_30: bool = false, | |
| 1416 | _reserved_bit_31: bool = false, | |
| 1417 | }; | |
| 723 | 1418 | }; |
| 724 | 1419 | pub const FunctionControl = packed struct { |
| 725 | 1420 | Inline: bool align(@alignOf(u32)) = false, |
| ... | ... | @@ -788,6 +1483,10 @@ pub const MemorySemantics = packed struct { |
| 788 | 1483 | _reserved_bit_29: bool = false, |
| 789 | 1484 | _reserved_bit_30: bool = false, |
| 790 | 1485 | _reserved_bit_31: bool = false, |
| 1486 | ||
| 1487 | pub const OutputMemoryKHR: MemorySemantics = .{ .OutputMemory = true }; | |
| 1488 | pub const MakeAvailableKHR: MemorySemantics = .{ .MakeAvailable = true }; | |
| 1489 | pub const MakeVisibleKHR: MemorySemantics = .{ .MakeVisible = true }; | |
| 791 | 1490 | }; |
| 792 | 1491 | pub const MemoryAccess = packed struct { |
| 793 | 1492 | Volatile: bool align(@alignOf(u32)) = false, |
| ... | ... | @@ -822,6 +1521,45 @@ pub const MemoryAccess = packed struct { |
| 822 | 1521 | _reserved_bit_29: bool = false, |
| 823 | 1522 | _reserved_bit_30: bool = false, |
| 824 | 1523 | _reserved_bit_31: bool = false, |
| 1524 | ||
| 1525 | pub const MakePointerAvailableKHR: MemoryAccess = .{ .MakePointerAvailable = true }; | |
| 1526 | pub const MakePointerVisibleKHR: MemoryAccess = .{ .MakePointerVisible = true }; | |
| 1527 | pub const NonPrivatePointerKHR: MemoryAccess = .{ .NonPrivatePointer = true }; | |
| 1528 | ||
| 1529 | pub const Extended = struct { | |
| 1530 | Volatile: bool = false, | |
| 1531 | Aligned: ?struct { literal_integer: LiteralInteger } = null, | |
| 1532 | Nontemporal: bool = false, | |
| 1533 | MakePointerAvailable: ?struct { id_scope: IdScope } = null, | |
| 1534 | MakePointerVisible: ?struct { id_scope: IdScope } = null, | |
| 1535 | NonPrivatePointer: bool = false, | |
| 1536 | _reserved_bit_6: bool = false, | |
| 1537 | _reserved_bit_7: bool = false, | |
| 1538 | _reserved_bit_8: bool = false, | |
| 1539 | _reserved_bit_9: bool = false, | |
| 1540 | _reserved_bit_10: bool = false, | |
| 1541 | _reserved_bit_11: bool = false, | |
| 1542 | _reserved_bit_12: bool = false, | |
| 1543 | _reserved_bit_13: bool = false, | |
| 1544 | _reserved_bit_14: bool = false, | |
| 1545 | _reserved_bit_15: bool = false, | |
| 1546 | _reserved_bit_16: bool = false, | |
| 1547 | _reserved_bit_17: bool = false, | |
| 1548 | _reserved_bit_18: bool = false, | |
| 1549 | _reserved_bit_19: bool = false, | |
| 1550 | _reserved_bit_20: bool = false, | |
| 1551 | _reserved_bit_21: bool = false, | |
| 1552 | _reserved_bit_22: bool = false, | |
| 1553 | _reserved_bit_23: bool = false, | |
| 1554 | _reserved_bit_24: bool = false, | |
| 1555 | _reserved_bit_25: bool = false, | |
| 1556 | _reserved_bit_26: bool = false, | |
| 1557 | _reserved_bit_27: bool = false, | |
| 1558 | _reserved_bit_28: bool = false, | |
| 1559 | _reserved_bit_29: bool = false, | |
| 1560 | _reserved_bit_30: bool = false, | |
| 1561 | _reserved_bit_31: bool = false, | |
| 1562 | }; | |
| 825 | 1563 | }; |
| 826 | 1564 | pub const KernelProfilingInfo = packed struct { |
| 827 | 1565 | CmdExecTime: bool align(@alignOf(u32)) = false, |
| ... | ... | @@ -932,7 +1670,6 @@ pub const SourceLanguage = enum(u32) { |
| 932 | 1670 | OpenCL_C = 3, |
| 933 | 1671 | OpenCL_CPP = 4, |
| 934 | 1672 | HLSL = 5, |
| 935 | _, | |
| 936 | 1673 | }; |
| 937 | 1674 | pub const ExecutionModel = enum(u32) { |
| 938 | 1675 | Vertex = 0, |
| ... | ... | @@ -944,33 +1681,35 @@ pub const ExecutionModel = enum(u32) { |
| 944 | 1681 | Kernel = 6, |
| 945 | 1682 | TaskNV = 5267, |
| 946 | 1683 | MeshNV = 5268, |
| 947 | RayGenerationNV = 5313, | |
| 948 | 1684 | RayGenerationKHR = 5313, |
| 949 | IntersectionNV = 5314, | |
| 950 | 1685 | IntersectionKHR = 5314, |
| 951 | AnyHitNV = 5315, | |
| 952 | 1686 | AnyHitKHR = 5315, |
| 953 | ClosestHitNV = 5316, | |
| 954 | 1687 | ClosestHitKHR = 5316, |
| 955 | MissNV = 5317, | |
| 956 | 1688 | MissKHR = 5317, |
| 957 | CallableNV = 5318, | |
| 958 | 1689 | CallableKHR = 5318, |
| 959 | _, | |
| 1690 | ||
| 1691 | pub const RayGenerationNV = ExecutionModel.RayGenerationKHR; | |
| 1692 | pub const IntersectionNV = ExecutionModel.IntersectionKHR; | |
| 1693 | pub const AnyHitNV = ExecutionModel.AnyHitKHR; | |
| 1694 | pub const ClosestHitNV = ExecutionModel.ClosestHitKHR; | |
| 1695 | pub const MissNV = ExecutionModel.MissKHR; | |
| 1696 | pub const CallableNV = ExecutionModel.CallableKHR; | |
| 960 | 1697 | }; |
| 961 | 1698 | pub const AddressingModel = enum(u32) { |
| 962 | 1699 | Logical = 0, |
| 963 | 1700 | Physical32 = 1, |
| 964 | 1701 | Physical64 = 2, |
| 965 | 1702 | PhysicalStorageBuffer64 = 5348, |
| 966 | _, | |
| 1703 | ||
| 1704 | pub const PhysicalStorageBuffer64EXT = AddressingModel.PhysicalStorageBuffer64; | |
| 967 | 1705 | }; |
| 968 | 1706 | pub const MemoryModel = enum(u32) { |
| 969 | 1707 | Simple = 0, |
| 970 | 1708 | GLSL450 = 1, |
| 971 | 1709 | OpenCL = 2, |
| 972 | 1710 | Vulkan = 3, |
| 973 | _, | |
| 1711 | ||
| 1712 | pub const VulkanKHR = MemoryModel.Vulkan; | |
| 974 | 1713 | }; |
| 975 | 1714 | pub const ExecutionMode = enum(u32) { |
| 976 | 1715 | Invocations = 0, |
| ... | ... | @@ -1039,7 +1778,75 @@ pub const ExecutionMode = enum(u32) { |
| 1039 | 1778 | NoGlobalOffsetINTEL = 5895, |
| 1040 | 1779 | NumSIMDWorkitemsINTEL = 5896, |
| 1041 | 1780 | SchedulerTargetFmaxMhzINTEL = 5903, |
| 1042 | _, | |
| 1781 | ||
| 1782 | pub const Extended = union(ExecutionMode) { | |
| 1783 | Invocations: struct { literal_integer: LiteralInteger }, | |
| 1784 | SpacingEqual, | |
| 1785 | SpacingFractionalEven, | |
| 1786 | SpacingFractionalOdd, | |
| 1787 | VertexOrderCw, | |
| 1788 | VertexOrderCcw, | |
| 1789 | PixelCenterInteger, | |
| 1790 | OriginUpperLeft, | |
| 1791 | OriginLowerLeft, | |
| 1792 | EarlyFragmentTests, | |
| 1793 | PointMode, | |
| 1794 | Xfb, | |
| 1795 | DepthReplacing, | |
| 1796 | DepthGreater, | |
| 1797 | DepthLess, | |
| 1798 | DepthUnchanged, | |
| 1799 | LocalSize: struct { x_size: LiteralInteger, y_size: LiteralInteger, z_size: LiteralInteger }, | |
| 1800 | LocalSizeHint: struct { x_size: LiteralInteger, y_size: LiteralInteger, z_size: LiteralInteger }, | |
| 1801 | InputPoints, | |
| 1802 | InputLines, | |
| 1803 | InputLinesAdjacency, | |
| 1804 | Triangles, | |
| 1805 | InputTrianglesAdjacency, | |
| 1806 | Quads, | |
| 1807 | Isolines, | |
| 1808 | OutputVertices: struct { vertex_count: LiteralInteger }, | |
| 1809 | OutputPoints, | |
| 1810 | OutputLineStrip, | |
| 1811 | OutputTriangleStrip, | |
| 1812 | VecTypeHint: struct { vector_type: LiteralInteger }, | |
| 1813 | ContractionOff, | |
| 1814 | Initializer, | |
| 1815 | Finalizer, | |
| 1816 | SubgroupSize: struct { subgroup_size: LiteralInteger }, | |
| 1817 | SubgroupsPerWorkgroup: struct { subgroups_per_workgroup: LiteralInteger }, | |
| 1818 | SubgroupsPerWorkgroupId: struct { subgroups_per_workgroup: IdRef }, | |
| 1819 | LocalSizeId: struct { x_size: IdRef, y_size: IdRef, z_size: IdRef }, | |
| 1820 | LocalSizeHintId: struct { local_size_hint: IdRef }, | |
| 1821 | PostDepthCoverage, | |
| 1822 | DenormPreserve: struct { target_width: LiteralInteger }, | |
| 1823 | DenormFlushToZero: struct { target_width: LiteralInteger }, | |
| 1824 | SignedZeroInfNanPreserve: struct { target_width: LiteralInteger }, | |
| 1825 | RoundingModeRTE: struct { target_width: LiteralInteger }, | |
| 1826 | RoundingModeRTZ: struct { target_width: LiteralInteger }, | |
| 1827 | StencilRefReplacingEXT, | |
| 1828 | OutputLinesNV, | |
| 1829 | OutputPrimitivesNV: struct { primitive_count: LiteralInteger }, | |
| 1830 | DerivativeGroupQuadsNV, | |
| 1831 | DerivativeGroupLinearNV, | |
| 1832 | OutputTrianglesNV, | |
| 1833 | PixelInterlockOrderedEXT, | |
| 1834 | PixelInterlockUnorderedEXT, | |
| 1835 | SampleInterlockOrderedEXT, | |
| 1836 | SampleInterlockUnorderedEXT, | |
| 1837 | ShadingRateInterlockOrderedEXT, | |
| 1838 | ShadingRateInterlockUnorderedEXT, | |
| 1839 | SharedLocalMemorySizeINTEL: struct { size: LiteralInteger }, | |
| 1840 | RoundingModeRTPINTEL: struct { target_width: LiteralInteger }, | |
| 1841 | RoundingModeRTNINTEL: struct { target_width: LiteralInteger }, | |
| 1842 | FloatingPointModeALTINTEL: struct { target_width: LiteralInteger }, | |
| 1843 | FloatingPointModeIEEEINTEL: struct { target_width: LiteralInteger }, | |
| 1844 | MaxWorkgroupSizeINTEL: struct { literal_integer_0: LiteralInteger, literal_integer_1: LiteralInteger, literal_integer_2: LiteralInteger }, | |
| 1845 | MaxWorkDimINTEL: struct { literal_integer: LiteralInteger }, | |
| 1846 | NoGlobalOffsetINTEL, | |
| 1847 | NumSIMDWorkitemsINTEL: struct { literal_integer: LiteralInteger }, | |
| 1848 | SchedulerTargetFmaxMhzINTEL: struct { literal_integer: LiteralInteger }, | |
| 1849 | }; | |
| 1043 | 1850 | }; |
| 1044 | 1851 | pub const StorageClass = enum(u32) { |
| 1045 | 1852 | UniformConstant = 0, |
| ... | ... | @@ -1065,7 +1872,14 @@ pub const StorageClass = enum(u32) { |
| 1065 | 1872 | CodeSectionINTEL = 5605, |
| 1066 | 1873 | DeviceOnlyINTEL = 5936, |
| 1067 | 1874 | HostOnlyINTEL = 5937, |
| 1068 | _, | |
| 1875 | ||
| 1876 | pub const CallableDataNV = StorageClass.CallableDataKHR; | |
| 1877 | pub const IncomingCallableDataNV = StorageClass.IncomingCallableDataKHR; | |
| 1878 | pub const RayPayloadNV = StorageClass.RayPayloadKHR; | |
| 1879 | pub const HitAttributeNV = StorageClass.HitAttributeKHR; | |
| 1880 | pub const IncomingRayPayloadNV = StorageClass.IncomingRayPayloadKHR; | |
| 1881 | pub const ShaderRecordBufferNV = StorageClass.ShaderRecordBufferKHR; | |
| 1882 | pub const PhysicalStorageBufferEXT = StorageClass.PhysicalStorageBuffer; | |
| 1069 | 1883 | }; |
| 1070 | 1884 | pub const Dim = enum(u32) { |
| 1071 | 1885 | @"1D" = 0, |
| ... | ... | @@ -1075,7 +1889,6 @@ pub const Dim = enum(u32) { |
| 1075 | 1889 | Rect = 4, |
| 1076 | 1890 | Buffer = 5, |
| 1077 | 1891 | SubpassData = 6, |
| 1078 | _, | |
| 1079 | 1892 | }; |
| 1080 | 1893 | pub const SamplerAddressingMode = enum(u32) { |
| 1081 | 1894 | None = 0, |
| ... | ... | @@ -1083,12 +1896,10 @@ pub const SamplerAddressingMode = enum(u32) { |
| 1083 | 1896 | Clamp = 2, |
| 1084 | 1897 | Repeat = 3, |
| 1085 | 1898 | RepeatMirrored = 4, |
| 1086 | _, | |
| 1087 | 1899 | }; |
| 1088 | 1900 | pub const SamplerFilterMode = enum(u32) { |
| 1089 | 1901 | Nearest = 0, |
| 1090 | 1902 | Linear = 1, |
| 1091 | _, | |
| 1092 | 1903 | }; |
| 1093 | 1904 | pub const ImageFormat = enum(u32) { |
| 1094 | 1905 | Unknown = 0, |
| ... | ... | @@ -1133,7 +1944,6 @@ pub const ImageFormat = enum(u32) { |
| 1133 | 1944 | R8ui = 39, |
| 1134 | 1945 | R64ui = 40, |
| 1135 | 1946 | R64i = 41, |
| 1136 | _, | |
| 1137 | 1947 | }; |
| 1138 | 1948 | pub const ImageChannelOrder = enum(u32) { |
| 1139 | 1949 | R = 0, |
| ... | ... | @@ -1156,7 +1966,6 @@ pub const ImageChannelOrder = enum(u32) { |
| 1156 | 1966 | sRGBA = 17, |
| 1157 | 1967 | sBGRA = 18, |
| 1158 | 1968 | ABGR = 19, |
| 1159 | _, | |
| 1160 | 1969 | }; |
| 1161 | 1970 | pub const ImageChannelDataType = enum(u32) { |
| 1162 | 1971 | SnormInt8 = 0, |
| ... | ... | @@ -1176,36 +1985,30 @@ pub const ImageChannelDataType = enum(u32) { |
| 1176 | 1985 | Float = 14, |
| 1177 | 1986 | UnormInt24 = 15, |
| 1178 | 1987 | UnormInt101010_2 = 16, |
| 1179 | _, | |
| 1180 | 1988 | }; |
| 1181 | 1989 | pub const FPRoundingMode = enum(u32) { |
| 1182 | 1990 | RTE = 0, |
| 1183 | 1991 | RTZ = 1, |
| 1184 | 1992 | RTP = 2, |
| 1185 | 1993 | RTN = 3, |
| 1186 | _, | |
| 1187 | 1994 | }; |
| 1188 | 1995 | pub const FPDenormMode = enum(u32) { |
| 1189 | 1996 | Preserve = 0, |
| 1190 | 1997 | FlushToZero = 1, |
| 1191 | _, | |
| 1192 | 1998 | }; |
| 1193 | 1999 | pub const FPOperationMode = enum(u32) { |
| 1194 | 2000 | IEEE = 0, |
| 1195 | 2001 | ALT = 1, |
| 1196 | _, | |
| 1197 | 2002 | }; |
| 1198 | 2003 | pub const LinkageType = enum(u32) { |
| 1199 | 2004 | Export = 0, |
| 1200 | 2005 | Import = 1, |
| 1201 | 2006 | LinkOnceODR = 2, |
| 1202 | _, | |
| 1203 | 2007 | }; |
| 1204 | 2008 | pub const AccessQualifier = enum(u32) { |
| 1205 | 2009 | ReadOnly = 0, |
| 1206 | 2010 | WriteOnly = 1, |
| 1207 | 2011 | ReadWrite = 2, |
| 1208 | _, | |
| 1209 | 2012 | }; |
| 1210 | 2013 | pub const FunctionParameterAttribute = enum(u32) { |
| 1211 | 2014 | Zext = 0, |
| ... | ... | @@ -1216,7 +2019,6 @@ pub const FunctionParameterAttribute = enum(u32) { |
| 1216 | 2019 | NoCapture = 5, |
| 1217 | 2020 | NoWrite = 6, |
| 1218 | 2021 | NoReadWrite = 7, |
| 1219 | _, | |
| 1220 | 2022 | }; |
| 1221 | 2023 | pub const Decoration = enum(u32) { |
| 1222 | 2024 | RelaxedPrecision = 0, |
| ... | ... | @@ -1278,11 +2080,8 @@ pub const Decoration = enum(u32) { |
| 1278 | 2080 | PerTaskNV = 5273, |
| 1279 | 2081 | PerVertexNV = 5285, |
| 1280 | 2082 | NonUniform = 5300, |
| 1281 | NonUniformEXT = 5300, | |
| 1282 | 2083 | RestrictPointer = 5355, |
| 1283 | RestrictPointerEXT = 5355, | |
| 1284 | 2084 | AliasedPointer = 5356, |
| 1285 | AliasedPointerEXT = 5356, | |
| 1286 | 2085 | SIMTCallINTEL = 5599, |
| 1287 | 2086 | ReferencedIndirectlyINTEL = 5602, |
| 1288 | 2087 | ClobberINTEL = 5607, |
| ... | ... | @@ -1293,9 +2092,7 @@ pub const Decoration = enum(u32) { |
| 1293 | 2092 | StackCallINTEL = 5627, |
| 1294 | 2093 | GlobalVariableOffsetINTEL = 5628, |
| 1295 | 2094 | CounterBuffer = 5634, |
| 1296 | HlslCounterBufferGOOGLE = 5634, | |
| 1297 | 2095 | UserSemantic = 5635, |
| 1298 | HlslSemanticGOOGLE = 5635, | |
| 1299 | 2096 | UserTypeGOOGLE = 5636, |
| 1300 | 2097 | FunctionRoundingModeINTEL = 5822, |
| 1301 | 2098 | FunctionDenormModeINTEL = 5823, |
| ... | ... | @@ -1322,7 +2119,113 @@ pub const Decoration = enum(u32) { |
| 1322 | 2119 | FunctionFloatingPointModeINTEL = 6080, |
| 1323 | 2120 | SingleElementVectorINTEL = 6085, |
| 1324 | 2121 | VectorComputeCallableFunctionINTEL = 6087, |
| 1325 | _, | |
| 2122 | ||
| 2123 | pub const NonUniformEXT = Decoration.NonUniform; | |
| 2124 | pub const RestrictPointerEXT = Decoration.RestrictPointer; | |
| 2125 | pub const AliasedPointerEXT = Decoration.AliasedPointer; | |
| 2126 | pub const HlslCounterBufferGOOGLE = Decoration.CounterBuffer; | |
| 2127 | pub const HlslSemanticGOOGLE = Decoration.UserSemantic; | |
| 2128 | ||
| 2129 | pub const Extended = union(Decoration) { | |
| 2130 | RelaxedPrecision, | |
| 2131 | SpecId: struct { specialization_constant_id: LiteralInteger }, | |
| 2132 | Block, | |
| 2133 | BufferBlock, | |
| 2134 | RowMajor, | |
| 2135 | ColMajor, | |
| 2136 | ArrayStride: struct { array_stride: LiteralInteger }, | |
| 2137 | MatrixStride: struct { matrix_stride: LiteralInteger }, | |
| 2138 | GLSLShared, | |
| 2139 | GLSLPacked, | |
| 2140 | CPacked, | |
| 2141 | BuiltIn: struct { built_in: BuiltIn }, | |
| 2142 | NoPerspective, | |
| 2143 | Flat, | |
| 2144 | Patch, | |
| 2145 | Centroid, | |
| 2146 | Sample, | |
| 2147 | Invariant, | |
| 2148 | Restrict, | |
| 2149 | Aliased, | |
| 2150 | Volatile, | |
| 2151 | Constant, | |
| 2152 | Coherent, | |
| 2153 | NonWritable, | |
| 2154 | NonReadable, | |
| 2155 | Uniform, | |
| 2156 | UniformId: struct { execution: IdScope }, | |
| 2157 | SaturatedConversion, | |
| 2158 | Stream: struct { stream_number: LiteralInteger }, | |
| 2159 | Location: struct { location: LiteralInteger }, | |
| 2160 | Component: struct { component: LiteralInteger }, | |
| 2161 | Index: struct { index: LiteralInteger }, | |
| 2162 | Binding: struct { binding_point: LiteralInteger }, | |
| 2163 | DescriptorSet: struct { descriptor_set: LiteralInteger }, | |
| 2164 | Offset: struct { byte_offset: LiteralInteger }, | |
| 2165 | XfbBuffer: struct { xfb_buffer_number: LiteralInteger }, | |
| 2166 | XfbStride: struct { xfb_stride: LiteralInteger }, | |
| 2167 | FuncParamAttr: struct { function_parameter_attribute: FunctionParameterAttribute }, | |
| 2168 | FPRoundingMode: struct { fprounding_mode: FPRoundingMode }, | |
| 2169 | FPFastMathMode: struct { fpfast_math_mode: FPFastMathMode }, | |
| 2170 | LinkageAttributes: struct { name: LiteralString, linkage_type: LinkageType }, | |
| 2171 | NoContraction, | |
| 2172 | InputAttachmentIndex: struct { attachment_index: LiteralInteger }, | |
| 2173 | Alignment: struct { alignment: LiteralInteger }, | |
| 2174 | MaxByteOffset: struct { max_byte_offset: LiteralInteger }, | |
| 2175 | AlignmentId: struct { alignment: IdRef }, | |
| 2176 | MaxByteOffsetId: struct { max_byte_offset: IdRef }, | |
| 2177 | NoSignedWrap, | |
| 2178 | NoUnsignedWrap, | |
| 2179 | ExplicitInterpAMD, | |
| 2180 | OverrideCoverageNV, | |
| 2181 | PassthroughNV, | |
| 2182 | ViewportRelativeNV, | |
| 2183 | SecondaryViewportRelativeNV: struct { offset: LiteralInteger }, | |
| 2184 | PerPrimitiveNV, | |
| 2185 | PerViewNV, | |
| 2186 | PerTaskNV, | |
| 2187 | PerVertexNV, | |
| 2188 | NonUniform, | |
| 2189 | RestrictPointer, | |
| 2190 | AliasedPointer, | |
| 2191 | SIMTCallINTEL: struct { n: LiteralInteger }, | |
| 2192 | ReferencedIndirectlyINTEL, | |
| 2193 | ClobberINTEL: struct { register: LiteralString }, | |
| 2194 | SideEffectsINTEL, | |
| 2195 | VectorComputeVariableINTEL, | |
| 2196 | FuncParamIOKindINTEL: struct { kind: LiteralInteger }, | |
| 2197 | VectorComputeFunctionINTEL, | |
| 2198 | StackCallINTEL, | |
| 2199 | GlobalVariableOffsetINTEL: struct { offset: LiteralInteger }, | |
| 2200 | CounterBuffer: struct { counter_buffer: IdRef }, | |
| 2201 | UserSemantic: struct { semantic: LiteralString }, | |
| 2202 | UserTypeGOOGLE: struct { user_type: LiteralString }, | |
| 2203 | FunctionRoundingModeINTEL: struct { target_width: LiteralInteger, fp_rounding_mode: FPRoundingMode }, | |
| 2204 | FunctionDenormModeINTEL: struct { target_width: LiteralInteger, fp_denorm_mode: FPDenormMode }, | |
| 2205 | RegisterINTEL, | |
| 2206 | MemoryINTEL: struct { memory_type: LiteralString }, | |
| 2207 | NumbanksINTEL: struct { banks: LiteralInteger }, | |
| 2208 | BankwidthINTEL: struct { bank_width: LiteralInteger }, | |
| 2209 | MaxPrivateCopiesINTEL: struct { maximum_copies: LiteralInteger }, | |
| 2210 | SinglepumpINTEL, | |
| 2211 | DoublepumpINTEL, | |
| 2212 | MaxReplicatesINTEL: struct { maximum_replicates: LiteralInteger }, | |
| 2213 | SimpleDualPortINTEL, | |
| 2214 | MergeINTEL: struct { merge_key: LiteralString, merge_type: LiteralString }, | |
| 2215 | BankBitsINTEL: struct { bank_bits: []const LiteralInteger = &.{} }, | |
| 2216 | ForcePow2DepthINTEL: struct { force_key: LiteralInteger }, | |
| 2217 | BurstCoalesceINTEL, | |
| 2218 | CacheSizeINTEL: struct { cache_size_in_bytes: LiteralInteger }, | |
| 2219 | DontStaticallyCoalesceINTEL, | |
| 2220 | PrefetchINTEL: struct { prefetcher_size_in_bytes: LiteralInteger }, | |
| 2221 | StallEnableINTEL, | |
| 2222 | FuseLoopsInFunctionINTEL, | |
| 2223 | BufferLocationINTEL: struct { buffer_location_id: LiteralInteger }, | |
| 2224 | IOPipeStorageINTEL: struct { io_pipe_id: LiteralInteger }, | |
| 2225 | FunctionFloatingPointModeINTEL: struct { target_width: LiteralInteger, fp_operation_mode: FPOperationMode }, | |
| 2226 | SingleElementVectorINTEL, | |
| 2227 | VectorComputeCallableFunctionINTEL, | |
| 2228 | }; | |
| 1326 | 2229 | }; |
| 1327 | 2230 | pub const BuiltIn = enum(u32) { |
| 1328 | 2231 | Position = 0, |
| ... | ... | @@ -1367,15 +2270,10 @@ pub const BuiltIn = enum(u32) { |
| 1367 | 2270 | VertexIndex = 42, |
| 1368 | 2271 | InstanceIndex = 43, |
| 1369 | 2272 | SubgroupEqMask = 4416, |
| 1370 | SubgroupEqMaskKHR = 4416, | |
| 1371 | 2273 | SubgroupGeMask = 4417, |
| 1372 | SubgroupGeMaskKHR = 4417, | |
| 1373 | 2274 | SubgroupGtMask = 4418, |
| 1374 | SubgroupGtMaskKHR = 4418, | |
| 1375 | 2275 | SubgroupLeMask = 4419, |
| 1376 | SubgroupLeMaskKHR = 4419, | |
| 1377 | 2276 | SubgroupLtMask = 4420, |
| 1378 | SubgroupLtMaskKHR = 4420, | |
| 1379 | 2277 | BaseVertex = 4424, |
| 1380 | 2278 | BaseInstance = 4425, |
| 1381 | 2279 | DrawIndex = 4426, |
| ... | ... | @@ -1408,42 +2306,47 @@ pub const BuiltIn = enum(u32) { |
| 1408 | 2306 | BaryCoordNV = 5286, |
| 1409 | 2307 | BaryCoordNoPerspNV = 5287, |
| 1410 | 2308 | FragSizeEXT = 5292, |
| 1411 | FragmentSizeNV = 5292, | |
| 1412 | 2309 | FragInvocationCountEXT = 5293, |
| 1413 | InvocationsPerPixelNV = 5293, | |
| 1414 | LaunchIdNV = 5319, | |
| 1415 | 2310 | LaunchIdKHR = 5319, |
| 1416 | LaunchSizeNV = 5320, | |
| 1417 | 2311 | LaunchSizeKHR = 5320, |
| 1418 | WorldRayOriginNV = 5321, | |
| 1419 | 2312 | WorldRayOriginKHR = 5321, |
| 1420 | WorldRayDirectionNV = 5322, | |
| 1421 | 2313 | WorldRayDirectionKHR = 5322, |
| 1422 | ObjectRayOriginNV = 5323, | |
| 1423 | 2314 | ObjectRayOriginKHR = 5323, |
| 1424 | ObjectRayDirectionNV = 5324, | |
| 1425 | 2315 | ObjectRayDirectionKHR = 5324, |
| 1426 | RayTminNV = 5325, | |
| 1427 | 2316 | RayTminKHR = 5325, |
| 1428 | RayTmaxNV = 5326, | |
| 1429 | 2317 | RayTmaxKHR = 5326, |
| 1430 | InstanceCustomIndexNV = 5327, | |
| 1431 | 2318 | InstanceCustomIndexKHR = 5327, |
| 1432 | ObjectToWorldNV = 5330, | |
| 1433 | 2319 | ObjectToWorldKHR = 5330, |
| 1434 | WorldToObjectNV = 5331, | |
| 1435 | 2320 | WorldToObjectKHR = 5331, |
| 1436 | 2321 | HitTNV = 5332, |
| 1437 | HitKindNV = 5333, | |
| 1438 | 2322 | HitKindKHR = 5333, |
| 1439 | IncomingRayFlagsNV = 5351, | |
| 1440 | 2323 | IncomingRayFlagsKHR = 5351, |
| 1441 | 2324 | RayGeometryIndexKHR = 5352, |
| 1442 | 2325 | WarpsPerSMNV = 5374, |
| 1443 | 2326 | SMCountNV = 5375, |
| 1444 | 2327 | WarpIDNV = 5376, |
| 1445 | 2328 | SMIDNV = 5377, |
| 1446 | _, | |
| 2329 | ||
| 2330 | pub const SubgroupEqMaskKHR = BuiltIn.SubgroupEqMask; | |
| 2331 | pub const SubgroupGeMaskKHR = BuiltIn.SubgroupGeMask; | |
| 2332 | pub const SubgroupGtMaskKHR = BuiltIn.SubgroupGtMask; | |
| 2333 | pub const SubgroupLeMaskKHR = BuiltIn.SubgroupLeMask; | |
| 2334 | pub const SubgroupLtMaskKHR = BuiltIn.SubgroupLtMask; | |
| 2335 | pub const FragmentSizeNV = BuiltIn.FragSizeEXT; | |
| 2336 | pub const InvocationsPerPixelNV = BuiltIn.FragInvocationCountEXT; | |
| 2337 | pub const LaunchIdNV = BuiltIn.LaunchIdKHR; | |
| 2338 | pub const LaunchSizeNV = BuiltIn.LaunchSizeKHR; | |
| 2339 | pub const WorldRayOriginNV = BuiltIn.WorldRayOriginKHR; | |
| 2340 | pub const WorldRayDirectionNV = BuiltIn.WorldRayDirectionKHR; | |
| 2341 | pub const ObjectRayOriginNV = BuiltIn.ObjectRayOriginKHR; | |
| 2342 | pub const ObjectRayDirectionNV = BuiltIn.ObjectRayDirectionKHR; | |
| 2343 | pub const RayTminNV = BuiltIn.RayTminKHR; | |
| 2344 | pub const RayTmaxNV = BuiltIn.RayTmaxKHR; | |
| 2345 | pub const InstanceCustomIndexNV = BuiltIn.InstanceCustomIndexKHR; | |
| 2346 | pub const ObjectToWorldNV = BuiltIn.ObjectToWorldKHR; | |
| 2347 | pub const WorldToObjectNV = BuiltIn.WorldToObjectKHR; | |
| 2348 | pub const HitKindNV = BuiltIn.HitKindKHR; | |
| 2349 | pub const IncomingRayFlagsNV = BuiltIn.IncomingRayFlagsKHR; | |
| 1447 | 2350 | }; |
| 1448 | 2351 | pub const Scope = enum(u32) { |
| 1449 | 2352 | CrossDevice = 0, |
| ... | ... | @@ -1452,9 +2355,9 @@ pub const Scope = enum(u32) { |
| 1452 | 2355 | Subgroup = 3, |
| 1453 | 2356 | Invocation = 4, |
| 1454 | 2357 | QueueFamily = 5, |
| 1455 | QueueFamilyKHR = 5, | |
| 1456 | 2358 | ShaderCallKHR = 6, |
| 1457 | _, | |
| 2359 | ||
| 2360 | pub const QueueFamilyKHR = Scope.QueueFamily; | |
| 1458 | 2361 | }; |
| 1459 | 2362 | pub const GroupOperation = enum(u32) { |
| 1460 | 2363 | Reduce = 0, |
| ... | ... | @@ -1464,13 +2367,11 @@ pub const GroupOperation = enum(u32) { |
| 1464 | 2367 | PartitionedReduceNV = 6, |
| 1465 | 2368 | PartitionedInclusiveScanNV = 7, |
| 1466 | 2369 | PartitionedExclusiveScanNV = 8, |
| 1467 | _, | |
| 1468 | 2370 | }; |
| 1469 | 2371 | pub const KernelEnqueueFlags = enum(u32) { |
| 1470 | 2372 | NoWait = 0, |
| 1471 | 2373 | WaitKernel = 1, |
| 1472 | 2374 | WaitWorkGroup = 2, |
| 1473 | _, | |
| 1474 | 2375 | }; |
| 1475 | 2376 | pub const Capability = enum(u32) { |
| 1476 | 2377 | Matrix = 0, |
| ... | ... | @@ -1550,7 +2451,7 @@ pub const Capability = enum(u32) { |
| 1550 | 2451 | WorkgroupMemoryExplicitLayout16BitAccessKHR = 4430, |
| 1551 | 2452 | SubgroupVoteKHR = 4431, |
| 1552 | 2453 | StorageBuffer16BitAccess = 4433, |
| 1553 | StorageUniform16 = 4434, | |
| 2454 | UniformAndStorageBuffer16BitAccess = 4434, | |
| 1554 | 2455 | StoragePushConstant16 = 4435, |
| 1555 | 2456 | StorageInputOutput16 = 4436, |
| 1556 | 2457 | DeviceGroup = 4437, |
| ... | ... | @@ -1580,7 +2481,7 @@ pub const Capability = enum(u32) { |
| 1580 | 2481 | ShaderClockKHR = 5055, |
| 1581 | 2482 | SampleMaskOverrideCoverageNV = 5249, |
| 1582 | 2483 | GeometryShaderPassthroughNV = 5251, |
| 1583 | ShaderViewportIndexLayerNV = 5254, | |
| 2484 | ShaderViewportIndexLayerEXT = 5254, | |
| 1584 | 2485 | ShaderViewportMaskNV = 5255, |
| 1585 | 2486 | ShaderStereoViewNV = 5259, |
| 1586 | 2487 | PerViewAttributesNV = 5260, |
| ... | ... | @@ -1589,7 +2490,7 @@ pub const Capability = enum(u32) { |
| 1589 | 2490 | ImageFootprintNV = 5282, |
| 1590 | 2491 | FragmentBarycentricNV = 5284, |
| 1591 | 2492 | ComputeDerivativeGroupQuadsNV = 5288, |
| 1592 | ShadingRateNV = 5291, | |
| 2493 | FragmentDensityEXT = 5291, | |
| 1593 | 2494 | GroupNonUniformPartitionedNV = 5297, |
| 1594 | 2495 | ShaderNonUniform = 5301, |
| 1595 | 2496 | RuntimeDescriptorArray = 5302, |
| ... | ... | @@ -1654,21 +2555,37 @@ pub const Capability = enum(u32) { |
| 1654 | 2555 | AtomicFloat32AddEXT = 6033, |
| 1655 | 2556 | AtomicFloat64AddEXT = 6034, |
| 1656 | 2557 | LongConstantCompositeINTEL = 6089, |
| 1657 | _, | |
| 2558 | ||
| 2559 | pub const StorageUniformBufferBlock16 = Capability.StorageBuffer16BitAccess; | |
| 2560 | pub const StorageUniform16 = Capability.UniformAndStorageBuffer16BitAccess; | |
| 2561 | pub const ShaderViewportIndexLayerNV = Capability.ShaderViewportIndexLayerEXT; | |
| 2562 | pub const ShadingRateNV = Capability.FragmentDensityEXT; | |
| 2563 | pub const ShaderNonUniformEXT = Capability.ShaderNonUniform; | |
| 2564 | pub const RuntimeDescriptorArrayEXT = Capability.RuntimeDescriptorArray; | |
| 2565 | pub const InputAttachmentArrayDynamicIndexingEXT = Capability.InputAttachmentArrayDynamicIndexing; | |
| 2566 | pub const UniformTexelBufferArrayDynamicIndexingEXT = Capability.UniformTexelBufferArrayDynamicIndexing; | |
| 2567 | pub const StorageTexelBufferArrayDynamicIndexingEXT = Capability.StorageTexelBufferArrayDynamicIndexing; | |
| 2568 | pub const UniformBufferArrayNonUniformIndexingEXT = Capability.UniformBufferArrayNonUniformIndexing; | |
| 2569 | pub const SampledImageArrayNonUniformIndexingEXT = Capability.SampledImageArrayNonUniformIndexing; | |
| 2570 | pub const StorageBufferArrayNonUniformIndexingEXT = Capability.StorageBufferArrayNonUniformIndexing; | |
| 2571 | pub const StorageImageArrayNonUniformIndexingEXT = Capability.StorageImageArrayNonUniformIndexing; | |
| 2572 | pub const InputAttachmentArrayNonUniformIndexingEXT = Capability.InputAttachmentArrayNonUniformIndexing; | |
| 2573 | pub const UniformTexelBufferArrayNonUniformIndexingEXT = Capability.UniformTexelBufferArrayNonUniformIndexing; | |
| 2574 | pub const StorageTexelBufferArrayNonUniformIndexingEXT = Capability.StorageTexelBufferArrayNonUniformIndexing; | |
| 2575 | pub const VulkanMemoryModelKHR = Capability.VulkanMemoryModel; | |
| 2576 | pub const VulkanMemoryModelDeviceScopeKHR = Capability.VulkanMemoryModelDeviceScope; | |
| 2577 | pub const PhysicalStorageBufferAddressesEXT = Capability.PhysicalStorageBufferAddresses; | |
| 1658 | 2578 | }; |
| 1659 | 2579 | pub const RayQueryIntersection = enum(u32) { |
| 1660 | 2580 | RayQueryCandidateIntersectionKHR = 0, |
| 1661 | 2581 | RayQueryCommittedIntersectionKHR = 1, |
| 1662 | _, | |
| 1663 | 2582 | }; |
| 1664 | 2583 | pub const RayQueryCommittedIntersectionType = enum(u32) { |
| 1665 | 2584 | RayQueryCommittedIntersectionNoneKHR = 0, |
| 1666 | 2585 | RayQueryCommittedIntersectionTriangleKHR = 1, |
| 1667 | 2586 | RayQueryCommittedIntersectionGeneratedKHR = 2, |
| 1668 | _, | |
| 1669 | 2587 | }; |
| 1670 | 2588 | pub const RayQueryCandidateIntersectionType = enum(u32) { |
| 1671 | 2589 | RayQueryCandidateIntersectionTriangleKHR = 0, |
| 1672 | 2590 | RayQueryCandidateIntersectionAABBKHR = 1, |
| 1673 | _, | |
| 1674 | 2591 | }; |
src/codegen/spirv/type.zig created+433| ... | ... | @@ -0,0 +1,433 @@ |
| 1 | //! This module models a SPIR-V Type. These are distinct from Zig types, with some types | |
| 2 | //! which are not representable by Zig directly. | |
| 3 | ||
| 4 | const std = @import("std"); | |
| 5 | const assert = std.debug.assert; | |
| 6 | ||
| 7 | const spec = @import("spec.zig"); | |
| 8 | ||
| 9 | pub const Type = extern union { | |
| 10 | tag_if_small_enough: Tag, | |
| 11 | ptr_otherwise: *Payload, | |
| 12 | ||
| 13 | /// A reference to another SPIR-V type. | |
| 14 | pub const Ref = usize; | |
| 15 | ||
| 16 | pub fn initTag(comptime small_tag: Tag) Type { | |
| 17 | comptime assert(@enumToInt(small_tag) < Tag.no_payload_count); | |
| 18 | return .{ .tag_if_small_enough = small_tag }; | |
| 19 | } | |
| 20 | ||
| 21 | pub fn initPayload(pl: *Payload) Type { | |
| 22 | assert(@enumToInt(pl.tag) >= Tag.no_payload_count); | |
| 23 | return .{ .ptr_otherwise = pl }; | |
| 24 | } | |
| 25 | ||
| 26 | pub fn tag(self: Type) Tag { | |
| 27 | if (@enumToInt(self.tag_if_small_enough) < Tag.no_payload_count) { | |
| 28 | return self.tag_if_small_enough; | |
| 29 | } else { | |
| 30 | return self.ptr_otherwise.tag; | |
| 31 | } | |
| 32 | } | |
| 33 | ||
| 34 | pub fn castTag(self: Type, comptime t: Tag) ?*t.Type() { | |
| 35 | if (@enumToInt(self.tag_if_small_enough) < Tag.no_payload_count) | |
| 36 | return null; | |
| 37 | ||
| 38 | if (self.ptr_otherwise.tag == t) | |
| 39 | return self.payload(t); | |
| 40 | ||
| 41 | return null; | |
| 42 | } | |
| 43 | ||
| 44 | /// Access the payload of a type directly. | |
| 45 | pub fn payload(self: Type, comptime t: Tag) *t.Type() { | |
| 46 | assert(self.tag() == t); | |
| 47 | return @fieldParentPtr(t.Type(), "base", self.ptr_otherwise); | |
| 48 | } | |
| 49 | ||
| 50 | /// Perform a shallow equality test, comparing two types while assuming that any child types | |
| 51 | /// are equal only if their references are equal. | |
| 52 | pub fn eqlShallow(a: Type, b: Type) bool { | |
| 53 | if (a.tag_if_small_enough == b.tag_if_small_enough) | |
| 54 | return true; | |
| 55 | ||
| 56 | const tag_a = a.tag(); | |
| 57 | const tag_b = b.tag(); | |
| 58 | if (tag_a != tag_b) | |
| 59 | return false; | |
| 60 | ||
| 61 | inline for (@typeInfo(Tag).Enum.fields) |field| { | |
| 62 | const t = @field(Tag, field.name); | |
| 63 | if (t == tag_a) { | |
| 64 | return eqlPayloads(t, a, b); | |
| 65 | } | |
| 66 | } | |
| 67 | ||
| 68 | unreachable; | |
| 69 | } | |
| 70 | ||
| 71 | /// Compare the payload of two compatible tags, given that we already know the tag of both types. | |
| 72 | fn eqlPayloads(comptime t: Tag, a: Type, b: Type) bool { | |
| 73 | switch (t) { | |
| 74 | .void, | |
| 75 | .bool, | |
| 76 | .sampler, | |
| 77 | .event, | |
| 78 | .device_event, | |
| 79 | .reserve_id, | |
| 80 | .queue, | |
| 81 | .pipe_storage, | |
| 82 | .named_barrier, | |
| 83 | => return true, | |
| 84 | .int, | |
| 85 | .float, | |
| 86 | .vector, | |
| 87 | .matrix, | |
| 88 | .sampled_image, | |
| 89 | .array, | |
| 90 | .runtime_array, | |
| 91 | .@"opaque", | |
| 92 | .pointer, | |
| 93 | .pipe, | |
| 94 | .image, | |
| 95 | => return std.meta.eql(a.payload(t).*, b.payload(t).*), | |
| 96 | .@"struct" => { | |
| 97 | const struct_a = a.payload(.@"struct"); | |
| 98 | const struct_b = b.payload(.@"struct"); | |
| 99 | if (struct_a.members.len != struct_b.members.len) | |
| 100 | return false; | |
| 101 | for (struct_a.members) |mem_a, i| { | |
| 102 | if (!std.meta.eql(mem_a, struct_b.members[i])) | |
| 103 | return false; | |
| 104 | } | |
| 105 | return true; | |
| 106 | }, | |
| 107 | .@"function" => { | |
| 108 | const fn_a = a.payload(.function); | |
| 109 | const fn_b = b.payload(.function); | |
| 110 | if (fn_a.return_type != fn_b.return_type) | |
| 111 | return false; | |
| 112 | return std.mem.eql(Ref, fn_a.parameters, fn_b.parameters); | |
| 113 | }, | |
| 114 | } | |
| 115 | } | |
| 116 | ||
| 117 | /// Perform a shallow hash, which hashes the reference value of child types instead of recursing. | |
| 118 | pub fn hashShallow(self: Type) u64 { | |
| 119 | var hasher = std.hash.Wyhash.init(0); | |
| 120 | const t = self.tag(); | |
| 121 | std.hash.autoHash(&hasher, t); | |
| 122 | ||
| 123 | inline for (@typeInfo(Tag).Enum.fields) |field| { | |
| 124 | if (@field(Tag, field.name) == t) { | |
| 125 | switch (@field(Tag, field.name)) { | |
| 126 | .void, | |
| 127 | .bool, | |
| 128 | .sampler, | |
| 129 | .event, | |
| 130 | .device_event, | |
| 131 | .reserve_id, | |
| 132 | .queue, | |
| 133 | .pipe_storage, | |
| 134 | .named_barrier, | |
| 135 | => {}, | |
| 136 | else => self.hashPayload(@field(Tag, field.name), &hasher), | |
| 137 | } | |
| 138 | } | |
| 139 | } | |
| 140 | ||
| 141 | return hasher.final(); | |
| 142 | } | |
| 143 | ||
| 144 | /// Perform a shallow hash, given that we know the tag of the field ahead of time. | |
| 145 | fn hashPayload(self: Type, comptime t: Tag, hasher: *std.hash.Wyhash) void { | |
| 146 | const fields = @typeInfo(t.Type()).Struct.fields; | |
| 147 | const pl = self.payload(t); | |
| 148 | comptime assert(std.mem.eql(u8, fields[0].name, "base")); | |
| 149 | inline for (fields[1..]) |field| { // Skip the 'base' field. | |
| 150 | std.hash.autoHashStrat(hasher, @field(pl, field.name), .DeepRecursive); | |
| 151 | } | |
| 152 | } | |
| 153 | ||
| 154 | /// Hash context that hashes and compares types in a shallow fashion, useful for type caches. | |
| 155 | pub const ShallowHashContext32 = struct { | |
| 156 | pub fn hash(self: @This(), t: Type) u32 { | |
| 157 | _ = self; | |
| 158 | return @truncate(u32, t.hashShallow()); | |
| 159 | } | |
| 160 | pub fn eql(self: @This(), a: Type, b: Type) bool { | |
| 161 | _ = self; | |
| 162 | return a.eqlShallow(b); | |
| 163 | } | |
| 164 | }; | |
| 165 | ||
| 166 | /// Return the reference to any child type. Asserts the type is one of: | |
| 167 | /// - Vectors | |
| 168 | /// - Matrices | |
| 169 | /// - Images | |
| 170 | /// - SampledImages, | |
| 171 | /// - Arrays | |
| 172 | /// - RuntimeArrays | |
| 173 | /// - Pointers | |
| 174 | pub fn childType(self: Type) Ref { | |
| 175 | return switch (self.tag()) { | |
| 176 | .vector => self.payload(.vector).component_type, | |
| 177 | .matrix => self.payload(.matrix).column_type, | |
| 178 | .image => self.payload(.image).sampled_type, | |
| 179 | .sampled_image => self.payload(.sampled_image).image_type, | |
| 180 | .array => self.payload(.array).element_type, | |
| 181 | .runtime_array => self.payload(.runtime_array).element_type, | |
| 182 | .pointer => self.payload(.pointer).child_type, | |
| 183 | else => unreachable, | |
| 184 | }; | |
| 185 | } | |
| 186 | ||
| 187 | pub const Tag = enum(usize) { | |
| 188 | void, | |
| 189 | bool, | |
| 190 | sampler, | |
| 191 | event, | |
| 192 | device_event, | |
| 193 | reserve_id, | |
| 194 | queue, | |
| 195 | pipe_storage, | |
| 196 | named_barrier, | |
| 197 | ||
| 198 | // After this, the tag requires a payload. | |
| 199 | int, | |
| 200 | float, | |
| 201 | vector, | |
| 202 | matrix, | |
| 203 | image, | |
| 204 | sampled_image, | |
| 205 | array, | |
| 206 | runtime_array, | |
| 207 | @"struct", | |
| 208 | @"opaque", | |
| 209 | pointer, | |
| 210 | function, | |
| 211 | pipe, | |
| 212 | ||
| 213 | pub const last_no_payload_tag = Tag.named_barrier; | |
| 214 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; | |
| 215 | ||
| 216 | pub fn Type(comptime t: Tag) type { | |
| 217 | return switch (t) { | |
| 218 | .void, .bool, .sampler, .event, .device_event, .reserve_id, .queue, .pipe_storage, .named_barrier => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"), | |
| 219 | .int => Payload.Int, | |
| 220 | .float => Payload.Float, | |
| 221 | .vector => Payload.Vector, | |
| 222 | .matrix => Payload.Matrix, | |
| 223 | .image => Payload.Image, | |
| 224 | .sampled_image => Payload.SampledImage, | |
| 225 | .array => Payload.Array, | |
| 226 | .runtime_array => Payload.RuntimeArray, | |
| 227 | .@"struct" => Payload.Struct, | |
| 228 | .@"opaque" => Payload.Opaque, | |
| 229 | .pointer => Payload.Pointer, | |
| 230 | .function => Payload.Function, | |
| 231 | .pipe => Payload.Pipe, | |
| 232 | }; | |
| 233 | } | |
| 234 | }; | |
| 235 | ||
| 236 | pub const Payload = struct { | |
| 237 | tag: Tag, | |
| 238 | ||
| 239 | pub const Int = struct { | |
| 240 | base: Payload = .{ .tag = .int }, | |
| 241 | width: u32, | |
| 242 | signedness: std.builtin.Signedness, | |
| 243 | }; | |
| 244 | ||
| 245 | pub const Float = struct { | |
| 246 | base: Payload = .{ .tag = .float }, | |
| 247 | width: u32, | |
| 248 | }; | |
| 249 | ||
| 250 | pub const Vector = struct { | |
| 251 | base: Payload = .{ .tag = .vector }, | |
| 252 | component_type: Ref, | |
| 253 | component_count: u32, | |
| 254 | }; | |
| 255 | ||
| 256 | pub const Matrix = struct { | |
| 257 | base: Payload = .{ .tag = .matrix }, | |
| 258 | column_type: Ref, | |
| 259 | column_count: u32, | |
| 260 | }; | |
| 261 | ||
| 262 | pub const Image = struct { | |
| 263 | base: Payload = .{ .tag = .image }, | |
| 264 | sampled_type: Ref, | |
| 265 | dim: spec.Dim, | |
| 266 | depth: enum(u2) { | |
| 267 | no = 0, | |
| 268 | yes = 1, | |
| 269 | maybe = 2, | |
| 270 | }, | |
| 271 | arrayed: bool, | |
| 272 | multisampled: bool, | |
| 273 | sampled: enum(u2) { | |
| 274 | known_at_runtime = 0, | |
| 275 | with_sampler = 1, | |
| 276 | without_sampler = 2, | |
| 277 | }, | |
| 278 | format: spec.ImageFormat, | |
| 279 | access_qualifier: ?spec.AccessQualifier, | |
| 280 | }; | |
| 281 | ||
| 282 | pub const SampledImage = struct { | |
| 283 | base: Payload = .{ .tag = .sampled_image }, | |
| 284 | image_type: Ref, | |
| 285 | }; | |
| 286 | ||
| 287 | pub const Array = struct { | |
| 288 | base: Payload = .{ .tag = .array }, | |
| 289 | element_type: Ref, | |
| 290 | /// Note: Must be emitted as constant, not as literal! | |
| 291 | length: u32, | |
| 292 | /// Type has the 'ArrayStride' decoration. | |
| 293 | /// If zero, no stride is present. | |
| 294 | array_stride: u32, | |
| 295 | }; | |
| 296 | ||
| 297 | pub const RuntimeArray = struct { | |
| 298 | base: Payload = .{ .tag = .runtime_array }, | |
| 299 | element_type: Ref, | |
| 300 | /// Type has the 'ArrayStride' decoration. | |
| 301 | /// If zero, no stride is present. | |
| 302 | array_stride: u32, | |
| 303 | }; | |
| 304 | ||
| 305 | pub const Struct = struct { | |
| 306 | base: Payload = .{ .tag = .@"struct" }, | |
| 307 | members: []Member, | |
| 308 | decorations: StructDecorations, | |
| 309 | ||
| 310 | /// Extra information for decorations, packed for efficiency. Fields are stored sequentially by | |
| 311 | /// order of the `members` slice and `MemberDecorations` struct. | |
| 312 | member_decoration_extra: []u32, | |
| 313 | ||
| 314 | pub const Member = struct { | |
| 315 | ty: Ref, | |
| 316 | offset: u32, | |
| 317 | decorations: MemberDecorations, | |
| 318 | }; | |
| 319 | ||
| 320 | pub const StructDecorations = packed struct { | |
| 321 | /// Type has the 'Block' decoration. | |
| 322 | block: bool, | |
| 323 | /// Type has the 'BufferBlock' decoration. | |
| 324 | buffer_block: bool, | |
| 325 | /// Type has the 'GLSLShared' decoration. | |
| 326 | glsl_shared: bool, | |
| 327 | /// Type has the 'GLSLPacked' decoration. | |
| 328 | glsl_packed: bool, | |
| 329 | /// Type has the 'CPacked' decoration. | |
| 330 | c_packed: bool, | |
| 331 | }; | |
| 332 | ||
| 333 | pub const MemberDecorations = packed struct { | |
| 334 | /// Matrix layout for (arrays of) matrices. If this field is not .none, | |
| 335 | /// then there is also an extra field containing the matrix stride corresponding | |
| 336 | /// to the 'MatrixStride' decoration. | |
| 337 | matrix_layout: enum(u2) { | |
| 338 | /// Member has the 'RowMajor' decoration. The member type | |
| 339 | /// must be a matrix or an array of matrices. | |
| 340 | row_major, | |
| 341 | /// Member has the 'ColMajor' decoration. The member type | |
| 342 | /// must be a matrix or an array of matrices. | |
| 343 | col_major, | |
| 344 | /// Member is not a matrix or array of matrices. | |
| 345 | none, | |
| 346 | }, | |
| 347 | ||
| 348 | // Regular decorations, these do not imply extra fields. | |
| 349 | ||
| 350 | /// Member has the 'NoPerspective' decoration. | |
| 351 | no_perspective: bool, | |
| 352 | /// Member has the 'Flat' decoration. | |
| 353 | flat: bool, | |
| 354 | /// Member has the 'Patch' decoration. | |
| 355 | patch: bool, | |
| 356 | /// Member has the 'Centroid' decoration. | |
| 357 | centroid: bool, | |
| 358 | /// Member has the 'Sample' decoration. | |
| 359 | sample: bool, | |
| 360 | /// Member has the 'Invariant' decoration. | |
| 361 | /// Note: requires parent struct to have 'Block'. | |
| 362 | invariant: bool, | |
| 363 | /// Member has the 'Volatile' decoration. | |
| 364 | @"volatile": bool, | |
| 365 | /// Member has the 'Coherent' decoration. | |
| 366 | coherent: bool, | |
| 367 | /// Member has the 'NonWritable' decoration. | |
| 368 | non_writable: bool, | |
| 369 | /// Member has the 'NonReadable' decoration. | |
| 370 | non_readable: bool, | |
| 371 | ||
| 372 | // The following decorations all imply extra field(s). | |
| 373 | ||
| 374 | /// Member has the 'BuiltIn' decoration. | |
| 375 | /// This decoration has an extra field of type `spec.BuiltIn`. | |
| 376 | /// Note: If any member of a struct has the BuiltIn decoration, all members must have one. | |
| 377 | /// Note: Each builtin may only be reachable once for a particular entry point. | |
| 378 | /// Note: The member type may be constrained by a particular built-in, defined in the client API specification. | |
| 379 | builtin: bool, | |
| 380 | /// Member has the 'Stream' decoration. | |
| 381 | /// This member has an extra field of type `u32`. | |
| 382 | stream: bool, | |
| 383 | /// Member has the 'Location' decoration. | |
| 384 | /// This member has an extra field of type `u32`. | |
| 385 | location: bool, | |
| 386 | /// Member has the 'Component' decoration. | |
| 387 | /// This member has an extra field of type `u32`. | |
| 388 | component: bool, | |
| 389 | /// Member has the 'XfbBuffer' decoration. | |
| 390 | /// This member has an extra field of type `u32`. | |
| 391 | xfb_buffer: bool, | |
| 392 | /// Member has the 'XfbStride' decoration. | |
| 393 | /// This member has an extra field of type `u32`. | |
| 394 | xfb_stride: bool, | |
| 395 | /// Member has the 'UserSemantic' decoration. | |
| 396 | /// This member has an extra field of type `[]u8`, which is encoded | |
| 397 | /// by an `u32` containing the number of chars exactly, and then the string padded to | |
| 398 | /// a multiple of 4 bytes with zeroes. | |
| 399 | user_semantic: bool, | |
| 400 | }; | |
| 401 | }; | |
| 402 | ||
| 403 | pub const Opaque = struct { | |
| 404 | base: Payload = .{ .tag = .@"opaque" }, | |
| 405 | name: []u8, | |
| 406 | }; | |
| 407 | ||
| 408 | pub const Pointer = struct { | |
| 409 | base: Payload = .{ .tag = .pointer }, | |
| 410 | storage_class: spec.StorageClass, | |
| 411 | child_type: Ref, | |
| 412 | /// Type has the 'ArrayStride' decoration. | |
| 413 | /// This is valid for pointers to elements of an array. | |
| 414 | /// If zero, no stride is present. | |
| 415 | array_stride: u32, | |
| 416 | /// Type has the 'Alignment' decoration. | |
| 417 | alignment: ?u32, | |
| 418 | /// Type has the 'MaxByteOffset' decoration. | |
| 419 | max_byte_offset: ?u32, | |
| 420 | }; | |
| 421 | ||
| 422 | pub const Function = struct { | |
| 423 | base: Payload = .{ .tag = .function }, | |
| 424 | return_type: Ref, | |
| 425 | parameters: []Ref, | |
| 426 | }; | |
| 427 | ||
| 428 | pub const Pipe = struct { | |
| 429 | base: Payload = .{ .tag = .pipe }, | |
| 430 | qualifier: spec.AccessQualifier, | |
| 431 | }; | |
| 432 | }; | |
| 433 | }; |
src/link/SpirV.zig+103-69| ... | ... | @@ -24,6 +24,7 @@ const SpirV = @This(); |
| 24 | 24 | |
| 25 | 25 | const std = @import("std"); |
| 26 | 26 | const Allocator = std.mem.Allocator; |
| 27 | const ArenaAllocator = std.heap.ArenaAllocator; | |
| 27 | 28 | const assert = std.debug.assert; |
| 28 | 29 | const log = std.log.scoped(.link); |
| 29 | 30 | |
| ... | ... | @@ -31,19 +32,21 @@ const Module = @import("../Module.zig"); |
| 31 | 32 | const Compilation = @import("../Compilation.zig"); |
| 32 | 33 | const link = @import("../link.zig"); |
| 33 | 34 | const codegen = @import("../codegen/spirv.zig"); |
| 34 | const Word = codegen.Word; | |
| 35 | const ResultId = codegen.ResultId; | |
| 36 | 35 | const trace = @import("../tracy.zig").trace; |
| 37 | 36 | const build_options = @import("build_options"); |
| 38 | const spec = @import("../codegen/spirv/spec.zig"); | |
| 39 | 37 | const Air = @import("../Air.zig"); |
| 40 | 38 | const Liveness = @import("../Liveness.zig"); |
| 39 | const Value = @import("../value.zig").Value; | |
| 40 | ||
| 41 | const SpvModule = @import("../codegen/spirv/Module.zig"); | |
| 42 | const spec = @import("../codegen/spirv/spec.zig"); | |
| 43 | const IdResult = spec.IdResult; | |
| 41 | 44 | |
| 42 | 45 | // TODO: Should this struct be used at all rather than just a hashmap of aux data for every decl? |
| 43 | 46 | pub const FnData = struct { |
| 44 | 47 | // We're going to fill these in flushModule, and we're going to fill them unconditionally, |
| 45 | 48 | // so just set it to undefined. |
| 46 | id: ResultId = undefined, | |
| 49 | id: IdResult = undefined, | |
| 47 | 50 | }; |
| 48 | 51 | |
| 49 | 52 | base: link.File, |
| ... | ... | @@ -55,7 +58,15 @@ decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, DeclGenContext) = .{}, |
| 55 | 58 | |
| 56 | 59 | const DeclGenContext = struct { |
| 57 | 60 | air: Air, |
| 61 | air_value_arena: ArenaAllocator.State, | |
| 58 | 62 | liveness: Liveness, |
| 63 | ||
| 64 | fn deinit(self: *DeclGenContext, gpa: Allocator) void { | |
| 65 | self.air.deinit(gpa); | |
| 66 | self.liveness.deinit(gpa); | |
| 67 | self.air_value_arena.promote(gpa).deinit(); | |
| 68 | self.* = undefined; | |
| 69 | } | |
| 59 | 70 | }; |
| 60 | 71 | |
| 61 | 72 | pub fn createEmpty(gpa: Allocator, options: link.Options) !*SpirV { |
| ... | ... | @@ -113,12 +124,27 @@ pub fn updateFunc(self: *SpirV, module: *Module, func: *Module.Fn, air: Air, liv |
| 113 | 124 | @panic("Attempted to compile for architecture that was disabled by build configuration"); |
| 114 | 125 | } |
| 115 | 126 | _ = module; |
| 127 | ||
| 116 | 128 | // Keep track of all decls so we can iterate over them on flush(). |
| 117 | _ = try self.decl_table.getOrPut(self.base.allocator, func.owner_decl); | |
| 129 | const result = try self.decl_table.getOrPut(self.base.allocator, func.owner_decl); | |
| 130 | if (result.found_existing) { | |
| 131 | result.value_ptr.deinit(self.base.allocator); | |
| 132 | } | |
| 133 | ||
| 134 | var arena = ArenaAllocator.init(self.base.allocator); | |
| 135 | errdefer arena.deinit(); | |
| 136 | ||
| 137 | var new_air = try cloneAir(air, self.base.allocator, arena.allocator()); | |
| 138 | errdefer new_air.deinit(self.base.allocator); | |
| 118 | 139 | |
| 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 | }; | |
| 122 | 148 | } |
| 123 | 149 | |
| 124 | 150 | pub fn updateDecl(self: *SpirV, module: *Module, decl: *Module.Decl) !void { |
| ... | ... | @@ -143,7 +169,11 @@ pub fn updateDeclExports( |
| 143 | 169 | } |
| 144 | 170 | |
| 145 | 171 | pub fn freeDecl(self: *SpirV, decl: *Module.Decl) void { |
| 146 | assert(self.decl_table.swapRemove(decl)); | |
| 172 | const index = self.decl_table.getIndex(decl).?; | |
| 173 | if (decl.val.tag() == .function) { | |
| 174 | self.decl_table.values()[index].deinit(self.base.allocator); | |
| 175 | } | |
| 176 | self.decl_table.swapRemoveAt(index); | |
| 147 | 177 | } |
| 148 | 178 | |
| 149 | 179 | pub fn flush(self: *SpirV, comp: *Compilation) !void { |
| ... | ... | @@ -165,7 +195,10 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { |
| 165 | 195 | const module = self.base.options.module.?; |
| 166 | 196 | const target = comp.getTarget(); |
| 167 | 197 | |
| 168 | var spv = codegen.SPIRVModule.init(self.base.allocator, module); | |
| 198 | var arena = std.heap.ArenaAllocator.init(self.base.allocator); | |
| 199 | defer arena.deinit(); | |
| 200 | ||
| 201 | var spv = SpvModule.init(self.base.allocator, arena.allocator()); | |
| 169 | 202 | defer spv.deinit(); |
| 170 | 203 | |
| 171 | 204 | // Allocate an ID for every declaration before generating code, |
| ... | ... | @@ -173,73 +206,38 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { |
| 173 | 206 | // TODO: We're allocating an ID unconditionally now, are there |
| 174 | 207 | // declarations which don't generate a result? |
| 175 | 208 | // 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(); | |
| 181 | 212 | } |
| 182 | 213 | } |
| 183 | 214 | |
| 184 | 215 | // 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(); | |
| 206 | 218 | |
| 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; | |
| 214 | 223 | |
| 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; | |
| 224 | 226 | |
| 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 | } | |
| 229 | 232 | } |
| 230 | 233 | |
| 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); | |
| 235 | 236 | |
| 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.?); | |
| 240 | 238 | } |
| 241 | 239 | |
| 242 | fn writeCapabilities(binary: *std.ArrayList(Word), target: std.Target) !void { | |
| 240 | fn writeCapabilities(spv: *SpvModule, target: std.Target) !void { | |
| 243 | 241 | // TODO: Integrate with a hypothetical feature system |
| 244 | 242 | const cap: spec.Capability = switch (target.os.tag) { |
| 245 | 243 | .opencl => .Kernel, |
| ... | ... | @@ -248,10 +246,12 @@ fn writeCapabilities(binary: *std.ArrayList(Word), target: std.Target) !void { |
| 248 | 246 | else => unreachable, // TODO |
| 249 | 247 | }; |
| 250 | 248 | |
| 251 | try codegen.writeInstruction(binary, .OpCapability, &[_]Word{@enumToInt(cap)}); | |
| 249 | try spv.sections.capabilities.emit(spv.gpa, .OpCapability, .{ | |
| 250 | .capability = cap, | |
| 251 | }); | |
| 252 | 252 | } |
| 253 | 253 | |
| 254 | fn writeMemoryModel(binary: *std.ArrayList(Word), target: std.Target) !void { | |
| 254 | fn writeMemoryModel(spv: *SpvModule, target: std.Target) !void { | |
| 255 | 255 | const addressing_model = switch (target.os.tag) { |
| 256 | 256 | .opencl => switch (target.cpu.arch) { |
| 257 | 257 | .spirv32 => spec.AddressingModel.Physical32, |
| ... | ... | @@ -269,7 +269,41 @@ fn writeMemoryModel(binary: *std.ArrayList(Word), target: std.Target) !void { |
| 269 | 269 | else => unreachable, |
| 270 | 270 | }; |
| 271 | 271 | |
| 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, | |
| 274 | 276 | }); |
| 275 | 277 | } |
| 278 | ||
| 279 | fn 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 | ||
| 293 | fn cloneAir(air: Air, gpa: Allocator, value_arena: Allocator) !Air { | |
| 294 | const values = try gpa.alloc(Value, air.values.len); | |
| 295 | errdefer gpa.free(values); | |
| 296 | ||
| 297 | for (values) |*value, i| { | |
| 298 | value.* = try air.values[i].copy(value_arena); | |
| 299 | } | |
| 300 | ||
| 301 | var instructions = try air.instructions.toMultiArrayList().clone(gpa); | |
| 302 | errdefer instructions.deinit(gpa); | |
| 303 | ||
| 304 | return Air{ | |
| 305 | .instructions = instructions.slice(), | |
| 306 | .extra = try gpa.dupe(u32, air.extra), | |
| 307 | .values = values, | |
| 308 | }; | |
| 309 | } |
tools/gen_spirv_spec.zig+427-55| ... | ... | @@ -1,5 +1,8 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const g = @import("spirv/grammar.zig"); |
| 3 | const Allocator = std.mem.Allocator; | |
| 4 | ||
| 5 | const ExtendedStructSet = std.StringHashMap(void); | |
| 3 | 6 | |
| 4 | 7 | pub fn main() !void { |
| 5 | 8 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); |
| ... | ... | @@ -20,101 +23,308 @@ pub fn main() !void { |
| 20 | 23 | var tokens = std.json.TokenStream.init(spec); |
| 21 | 24 | var registry = try std.json.parse(g.Registry, &tokens, .{ .allocator = allocator }); |
| 22 | 25 | |
| 26 | const core_reg = switch (registry) { | |
| 27 | .core => |core_reg| core_reg, | |
| 28 | .extension => return error.TODOSpirVExtensionSpec, | |
| 29 | }; | |
| 30 | ||
| 23 | 31 | var bw = std.io.bufferedWriter(std.io.getStdOut().writer()); |
| 24 | try render(bw.writer(), registry); | |
| 32 | try render(bw.writer(), allocator, core_reg); | |
| 25 | 33 | try bw.flush(); |
| 26 | 34 | } |
| 27 | 35 | |
| 28 | fn 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. | |
| 38 | fn 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. | |
| 64 | fn 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 | ||
| 76 | fn render(writer: anytype, allocator: Allocator, registry: g.CoreRegistry) !void { | |
| 29 | 77 | try writer.writeAll( |
| 30 | 78 | \\//! This file is auto-generated by tools/gen_spirv_spec.zig. |
| 31 | 79 | \\ |
| 32 | 80 | \\const Version = @import("std").builtin.Version; |
| 33 | 81 | \\ |
| 82 | \\pub const Word = u32; | |
| 83 | \\pub const IdResultType = struct{ | |
| 84 | \\ id: Word, | |
| 85 | \\ pub fn toRef(self: IdResultType) IdRef { | |
| 86 | \\ return .{.id = self.id}; | |
| 87 | \\ } | |
| 88 | \\}; | |
| 89 | \\pub const IdResult = struct{ | |
| 90 | \\ id: Word, | |
| 91 | \\ pub fn toRef(self: IdResult) IdRef { | |
| 92 | \\ return .{.id = self.id}; | |
| 93 | \\ } | |
| 94 | \\ pub fn toResultType(self: IdResult) IdResultType { | |
| 95 | \\ return .{.id = self.id}; | |
| 96 | \\ } | |
| 97 | \\}; | |
| 98 | \\pub const IdRef = struct{ id: Word }; | |
| 99 | \\ | |
| 100 | \\pub const IdMemorySemantics = IdRef; | |
| 101 | \\pub const IdScope = IdRef; | |
| 102 | \\ | |
| 103 | \\pub const LiteralInteger = Word; | |
| 104 | \\pub const LiteralString = []const u8; | |
| 105 | \\pub const LiteralContextDependentNumber = union(enum) { | |
| 106 | \\ int32: i32, | |
| 107 | \\ uint32: u32, | |
| 108 | \\ int64: i64, | |
| 109 | \\ uint64: u64, | |
| 110 | \\ float32: f32, | |
| 111 | \\ float64: f64, | |
| 112 | \\}; | |
| 113 | \\pub const LiteralExtInstInteger = struct{ inst: Word }; | |
| 114 | \\pub const LiteralSpecConstantOpInteger = struct { opcode: Opcode }; | |
| 115 | \\pub const PairLiteralIntegerIdRef = struct { value: LiteralInteger, label: IdRef }; | |
| 116 | \\pub const PairIdRefLiteralInteger = struct { target: IdRef, member: LiteralInteger }; | |
| 117 | \\pub const PairIdRefIdRef = [2]IdRef; | |
| 118 | \\ | |
| 119 | \\ | |
| 34 | 120 | ); |
| 35 | 121 | |
| 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); | |
| 59 | 132 | } |
| 60 | 133 | |
| 61 | fn 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 }); | |
| 134 | fn 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 | } | |
| 65 | 165 | } |
| 66 | try writer.writeAll(" _,\n};\n"); | |
| 166 | ||
| 167 | const instructions_indices = inst_map.values(); | |
| 168 | ||
| 169 | try writer.writeAll("pub const Opcode = enum(u16) {\n"); | |
| 170 | for (instructions_indices) |i| { | |
| 171 | const inst = instructions[i]; | |
| 172 | try writer.print("{} = {},\n", .{ std.zig.fmtId(inst.opname), inst.opcode }); | |
| 173 | } | |
| 174 | ||
| 175 | try writer.writeByte('\n'); | |
| 176 | ||
| 177 | for (aliases.items) |alias| { | |
| 178 | try writer.print("pub const {} = Opcode.{};\n", .{ | |
| 179 | std.zig.fmtId(instructions[alias.inst].opname), | |
| 180 | std.zig.fmtId(instructions[alias.alias].opname), | |
| 181 | }); | |
| 182 | } | |
| 183 | ||
| 184 | try writer.writeAll( | |
| 185 | \\ | |
| 186 | \\pub fn Operands(comptime self: Opcode) type { | |
| 187 | \\return switch (self) { | |
| 188 | \\ | |
| 189 | ); | |
| 190 | ||
| 191 | for (instructions_indices) |i| { | |
| 192 | const inst = instructions[i]; | |
| 193 | try renderOperand(writer, .instruction, inst.opname, inst.operands, extended_structs); | |
| 194 | } | |
| 195 | try writer.writeAll("};\n}\n};\n"); | |
| 196 | _ = extended_structs; | |
| 67 | 197 | } |
| 68 | 198 | |
| 69 | fn renderOperandKinds(writer: anytype, kinds: []const g.OperandKind) !void { | |
| 199 | fn renderOperandKinds( | |
| 200 | writer: anytype, | |
| 201 | allocator: Allocator, | |
| 202 | kinds: []const g.OperandKind, | |
| 203 | extended_structs: ExtendedStructSet, | |
| 204 | ) !void { | |
| 70 | 205 | for (kinds) |kind| { |
| 71 | 206 | 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), | |
| 74 | 209 | else => {}, |
| 75 | 210 | } |
| 76 | 211 | } |
| 77 | 212 | } |
| 78 | 213 | |
| 79 | fn renderValueEnum(writer: anytype, enumeration: g.OperandKind) !void { | |
| 80 | try writer.print("pub const {s} = extern enum(u32) {{\n", .{enumeration.kind}); | |
| 81 | ||
| 214 | fn renderValueEnum( | |
| 215 | writer: anytype, | |
| 216 | allocator: Allocator, | |
| 217 | enumeration: g.OperandKind, | |
| 218 | extended_structs: ExtendedStructSet, | |
| 219 | ) !void { | |
| 82 | 220 | const enumerants = enumeration.enumerants orelse return error.InvalidRegistry; |
| 83 | for (enumerants) |enumerant| { | |
| 221 | ||
| 222 | var enum_map = std.AutoArrayHashMap(u32, usize).init(allocator); | |
| 223 | try enum_map.ensureTotalCapacity(enumerants.len); | |
| 224 | ||
| 225 | var aliases = std.ArrayList(struct { enumerant: usize, alias: usize }).init(allocator); | |
| 226 | try aliases.ensureTotalCapacity(enumerants.len); | |
| 227 | ||
| 228 | for (enumerants) |enumerant, i| { | |
| 229 | const result = enum_map.getOrPutAssumeCapacity(enumerant.value.int); | |
| 230 | if (!result.found_existing) { | |
| 231 | result.value_ptr.* = i; | |
| 232 | continue; | |
| 233 | } | |
| 234 | ||
| 235 | const existing = enumerants[result.value_ptr.*]; | |
| 236 | ||
| 237 | const tag_index = std.mem.indexOfDiff(u8, enumerant.enumerant, existing.enumerant).?; | |
| 238 | const enum_priority = tagPriorityScore(enumerant.enumerant[tag_index..]); | |
| 239 | const existing_priority = tagPriorityScore(existing.enumerant[tag_index..]); | |
| 240 | ||
| 241 | if (enum_priority < existing_priority) { | |
| 242 | aliases.appendAssumeCapacity(.{ .enumerant = result.value_ptr.*, .alias = i }); | |
| 243 | result.value_ptr.* = i; | |
| 244 | } else { | |
| 245 | aliases.appendAssumeCapacity(.{ .enumerant = i, .alias = result.value_ptr.* }); | |
| 246 | } | |
| 247 | } | |
| 248 | ||
| 249 | const enum_indices = enum_map.values(); | |
| 250 | ||
| 251 | try writer.print("pub const {s} = enum(u32) {{\n", .{std.zig.fmtId(enumeration.kind)}); | |
| 252 | ||
| 253 | for (enum_indices) |i| { | |
| 254 | const enumerant = enumerants[i]; | |
| 84 | 255 | if (enumerant.value != .int) return error.InvalidRegistry; |
| 85 | 256 | |
| 86 | try writer.print(" {} = {},\n", .{ std.zig.fmtId(enumerant.enumerant), enumerant.value.int }); | |
| 257 | try writer.print("{} = {},\n", .{ std.zig.fmtId(enumerant.enumerant), enumerant.value.int }); | |
| 258 | } | |
| 259 | ||
| 260 | try writer.writeByte('\n'); | |
| 261 | ||
| 262 | for (aliases.items) |alias| { | |
| 263 | try writer.print("pub const {} = {}.{};\n", .{ | |
| 264 | std.zig.fmtId(enumerants[alias.enumerant].enumerant), | |
| 265 | std.zig.fmtId(enumeration.kind), | |
| 266 | std.zig.fmtId(enumerants[alias.alias].enumerant), | |
| 267 | }); | |
| 268 | } | |
| 269 | ||
| 270 | if (!extended_structs.contains(enumeration.kind)) { | |
| 271 | try writer.writeAll("};\n"); | |
| 272 | return; | |
| 273 | } | |
| 274 | ||
| 275 | try writer.print("\npub const Extended = union({}) {{\n", .{std.zig.fmtId(enumeration.kind)}); | |
| 276 | ||
| 277 | for (enum_indices) |i| { | |
| 278 | const enumerant = enumerants[i]; | |
| 279 | try renderOperand(writer, .@"union", enumerant.enumerant, enumerant.parameters, extended_structs); | |
| 87 | 280 | } |
| 88 | 281 | |
| 89 | try writer.writeAll(" _,\n};\n"); | |
| 282 | try writer.writeAll("};\n};\n"); | |
| 90 | 283 | } |
| 91 | 284 | |
| 92 | fn renderBitEnum(writer: anytype, enumeration: g.OperandKind) !void { | |
| 93 | try writer.print("pub const {s} = packed struct {{\n", .{enumeration.kind}); | |
| 285 | fn 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)}); | |
| 94 | 292 | |
| 95 | var flags_by_bitpos = [_]?[]const u8{null} ** 32; | |
| 293 | var flags_by_bitpos = [_]?usize{null} ** 32; | |
| 96 | 294 | const enumerants = enumeration.enumerants orelse return error.InvalidRegistry; |
| 97 | for (enumerants) |enumerant| { | |
| 295 | ||
| 296 | var aliases = std.ArrayList(struct { flag: usize, alias: u5 }).init(allocator); | |
| 297 | try aliases.ensureTotalCapacity(enumerants.len); | |
| 298 | ||
| 299 | for (enumerants) |enumerant, i| { | |
| 98 | 300 | if (enumerant.value != .bitflag) return error.InvalidRegistry; |
| 99 | 301 | 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 | |
| 102 | 304 | } |
| 103 | 305 | |
| 306 | std.debug.assert(@popCount(u32, value) == 1); | |
| 307 | ||
| 104 | 308 | var bitpos = std.math.log2_int(u32, value); |
| 105 | 309 | 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 | } | |
| 109 | 320 | } else { |
| 110 | flags_by_bitpos[bitpos] = enumerant.enumerant; | |
| 321 | flags_by_bitpos[bitpos] = i; | |
| 111 | 322 | } |
| 112 | 323 | } |
| 113 | 324 | |
| 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)}); | |
| 118 | 328 | } else { |
| 119 | 329 | try writer.print("_reserved_bit_{}", .{bitpos}); |
| 120 | 330 | } |
| ... | ... | @@ -126,7 +336,169 @@ fn renderBitEnum(writer: anytype, enumeration: g.OperandKind) !void { |
| 126 | 336 | try writer.writeAll("= false,\n"); |
| 127 | 337 | } |
| 128 | 338 | |
| 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 | ||
| 369 | fn 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 | ||
| 443 | fn renderFieldName(writer: anytype, operands: []const g.Operand, field_index: usize) !void { | |
| 444 | const operand = operands[field_index]; | |
| 445 | ||
| 446 | // Should be enough for all names - adjust as needed. | |
| 447 | var name_buffer = std.BoundedArray(u8, 64){ | |
| 448 | .buffer = undefined, | |
| 449 | }; | |
| 450 | ||
| 451 | derive_from_kind: { | |
| 452 | // Operand names are often in the json encoded as "'Name'" (with two sets of quotes). | |
| 453 | // Additionally, some operands have ~ in them at the end (D~ref~). | |
| 454 | const name = std.mem.trim(u8, operand.name, "'~"); | |
| 455 | if (name.len == 0) { | |
| 456 | break :derive_from_kind; | |
| 457 | } | |
| 458 | ||
| 459 | // Some names have weird characters in them (like newlines) - skip any such ones. | |
| 460 | // Use the same loop to transform to snake-case. | |
| 461 | for (name) |c| { | |
| 462 | switch (c) { | |
| 463 | 'a'...'z', '0'...'9' => try name_buffer.append(c), | |
| 464 | 'A'...'Z' => try name_buffer.append(std.ascii.toLower(c)), | |
| 465 | ' ', '~' => try name_buffer.append('_'), | |
| 466 | else => break :derive_from_kind, | |
| 467 | } | |
| 468 | } | |
| 469 | ||
| 470 | // Assume there are no duplicate 'name' fields. | |
| 471 | try writer.print("{}", .{std.zig.fmtId(name_buffer.slice())}); | |
| 472 | return; | |
| 473 | } | |
| 474 | ||
| 475 | // Translate to snake case. | |
| 476 | name_buffer.len = 0; | |
| 477 | for (operand.kind) |c, i| { | |
| 478 | switch (c) { | |
| 479 | 'a'...'z', '0'...'9' => try name_buffer.append(c), | |
| 480 | 'A'...'Z' => if (i > 0 and std.ascii.isLower(operand.kind[i - 1])) { | |
| 481 | try name_buffer.appendSlice(&[_]u8{ '_', std.ascii.toLower(c) }); | |
| 482 | } else { | |
| 483 | try name_buffer.append(std.ascii.toLower(c)); | |
| 484 | }, | |
| 485 | else => unreachable, // Assume that the name is valid C-syntax (and contains no underscores). | |
| 486 | } | |
| 487 | } | |
| 488 | ||
| 489 | try writer.print("{}", .{std.zig.fmtId(name_buffer.slice())}); | |
| 490 | ||
| 491 | // For fields derived from type name, there could be any amount. | |
| 492 | // Simply check against all other fields, and if another similar one exists, add a number. | |
| 493 | const need_extra_index = for (operands) |other_operand, i| { | |
| 494 | if (i != field_index and std.mem.eql(u8, operand.kind, other_operand.kind)) { | |
| 495 | break true; | |
| 496 | } | |
| 497 | } else false; | |
| 498 | ||
| 499 | if (need_extra_index) { | |
| 500 | try writer.print("_{}", .{field_index}); | |
| 501 | } | |
| 130 | 502 | } |
| 131 | 503 | |
| 132 | 504 | fn parseHexInt(text: []const u8) !u31 { |
| ... | ... | @@ -142,7 +514,7 @@ fn usageAndExit(file: std.fs.File, arg0: []const u8, code: u8) noreturn { |
| 142 | 514 | \\ |
| 143 | 515 | \\Generates Zig bindings for a SPIR-V specification .json (either core or |
| 144 | 516 | \\extinst versions). The result, printed to stdout, should be used to update |
| 145 | \\files in src/codegen/spirv. | |
| 517 | \\files in src/codegen/spirv. Don't forget to format the output. | |
| 146 | 518 | \\ |
| 147 | 519 | \\The relevant specifications can be obtained from the SPIR-V registry: |
| 148 | 520 | \\https://github.com/KhronosGroup/SPIRV-Headers/blob/master/include/spirv/unified1/ |