| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const g = @import("spirv/grammar.zig"); | 2 | const g = @import("spirv/grammar.zig"); |
| | 3 | const Allocator = std.mem.Allocator; |
| | 4 | |
| | 5 | const ExtendedStructSet = std.StringHashMap(void); |
| 3 | | 6 | |
| 4 | pub fn main() !void { | 7 | pub fn main() !void { |
| 5 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 8 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); |
| ... | @@ -20,101 +23,308 @@ pub fn main() !void { | ... | @@ -20,101 +23,308 @@ pub fn main() !void { |
| 20 | var tokens = std.json.TokenStream.init(spec); | 23 | var tokens = std.json.TokenStream.init(spec); |
| 21 | var registry = try std.json.parse(g.Registry, &tokens, .{ .allocator = allocator }); | 24 | var registry = try std.json.parse(g.Registry, &tokens, .{ .allocator = allocator }); |
| 22 | | 25 | |
| | 26 | const core_reg = switch (registry) { |
| | 27 | .core => |core_reg| core_reg, |
| | 28 | .extension => return error.TODOSpirVExtensionSpec, |
| | 29 | }; |
| | 30 | |
| 23 | var bw = std.io.bufferedWriter(std.io.getStdOut().writer()); | 31 | var bw = std.io.bufferedWriter(std.io.getStdOut().writer()); |
| 24 | try render(bw.writer(), registry); | 32 | try render(bw.writer(), allocator, core_reg); |
| 25 | try bw.flush(); | 33 | try bw.flush(); |
| 26 | } | 34 | } |
| 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 | try writer.writeAll( | 77 | try writer.writeAll( |
| 30 | \\//! This file is auto-generated by tools/gen_spirv_spec.zig. | 78 | \\//! This file is auto-generated by tools/gen_spirv_spec.zig. |
| 31 | \\ | 79 | \\ |
| 32 | \\const Version = @import("std").builtin.Version; | 80 | \\const Version = @import("std").builtin.Version; |
| 33 | \\ | 81 | \\ |
| | 82 | \\pub const Word = u32; |
| | 83 | \\pub const IdResultType = struct{ |
| | 84 | \\ id: Word, |
| | 85 | \\ pub fn toRef(self: IdResultType) IdRef { |
| | 86 | \\ return .{.id = self.id}; |
| | 87 | \\ } |
| | 88 | \\}; |
| | 89 | \\pub const IdResult = struct{ |
| | 90 | \\ id: Word, |
| | 91 | \\ pub fn toRef(self: IdResult) IdRef { |
| | 92 | \\ return .{.id = self.id}; |
| | 93 | \\ } |
| | 94 | \\ pub fn toResultType(self: IdResult) IdResultType { |
| | 95 | \\ return .{.id = self.id}; |
| | 96 | \\ } |
| | 97 | \\}; |
| | 98 | \\pub const IdRef = struct{ id: Word }; |
| | 99 | \\ |
| | 100 | \\pub const IdMemorySemantics = IdRef; |
| | 101 | \\pub const IdScope = IdRef; |
| | 102 | \\ |
| | 103 | \\pub const LiteralInteger = Word; |
| | 104 | \\pub const LiteralString = []const u8; |
| | 105 | \\pub const LiteralContextDependentNumber = union(enum) { |
| | 106 | \\ int32: i32, |
| | 107 | \\ uint32: u32, |
| | 108 | \\ int64: i64, |
| | 109 | \\ uint64: u64, |
| | 110 | \\ float32: f32, |
| | 111 | \\ float64: f64, |
| | 112 | \\}; |
| | 113 | \\pub const LiteralExtInstInteger = struct{ inst: Word }; |
| | 114 | \\pub const LiteralSpecConstantOpInteger = struct { opcode: Opcode }; |
| | 115 | \\pub const PairLiteralIntegerIdRef = struct { value: LiteralInteger, label: IdRef }; |
| | 116 | \\pub const PairIdRefLiteralInteger = struct { target: IdRef, member: LiteralInteger }; |
| | 117 | \\pub const PairIdRefIdRef = [2]IdRef; |
| | 118 | \\ |
| | 119 | \\ |
| 34 | ); | 120 | ); |
| 35 | | 121 | |
| 36 | switch (registry) { | 122 | try writer.print( |
| 37 | .core => |core_reg| { | 123 | \\pub const version = Version{{ .major = {}, .minor = {}, .patch = {} }}; |
| 38 | try writer.print( | 124 | \\pub const magic_number: Word = {s}; |
| 39 | \\pub const version = Version{{ .major = {}, .minor = {}, .patch = {} }}; | 125 | \\ |
| 40 | \\pub const magic_number: u32 = {s}; | 126 | , |
| 41 | \\ | 127 | .{ registry.major_version, registry.minor_version, registry.revision, registry.magic_number }, |
| 42 | , | 128 | ); |
| 43 | .{ core_reg.major_version, core_reg.minor_version, core_reg.revision, core_reg.magic_number }, | 129 | const extended_structs = try extendedStructs(allocator, registry.operand_kinds); |
| 44 | ); | 130 | try renderOpcodes(writer, allocator, registry.instructions, extended_structs); |
| 45 | try renderOpcodes(writer, core_reg.instructions); | 131 | try renderOperandKinds(writer, allocator, registry.operand_kinds, extended_structs); |
| 46 | try renderOperandKinds(writer, core_reg.operand_kinds); | | |
| 47 | }, | | |
| 48 | .extension => |ext_reg| { | | |
| 49 | try writer.print( | | |
| 50 | \\pub const version = Version{{ .major = {}, .minor = 0, .patch = {} }}; | | |
| 51 | \\ | | |
| 52 | , | | |
| 53 | .{ ext_reg.version, ext_reg.revision }, | | |
| 54 | ); | | |
| 55 | try renderOpcodes(writer, ext_reg.instructions); | | |
| 56 | try renderOperandKinds(writer, ext_reg.operand_kinds); | | |
| 57 | }, | | |
| 58 | } | | |
| 59 | } | 132 | } |
| 60 | | 133 | |
| 61 | fn renderOpcodes(writer: anytype, instructions: []const g.Instruction) !void { | 134 | fn renderOpcodes( |
| 62 | try writer.writeAll("pub const Opcode = extern enum(u16) {\n"); | 135 | writer: anytype, |
| 63 | for (instructions) |instr| { | 136 | allocator: Allocator, |
| 64 | try writer.print(" {} = {},\n", .{ std.zig.fmtId(instr.opname), instr.opcode }); | 137 | instructions: []const g.Instruction, |
| | 138 | extended_structs: ExtendedStructSet, |
| | 139 | ) !void { |
| | 140 | var inst_map = std.AutoArrayHashMap(u32, usize).init(allocator); |
| | 141 | try inst_map.ensureTotalCapacity(instructions.len); |
| | 142 | |
| | 143 | var aliases = std.ArrayList(struct { inst: usize, alias: usize }).init(allocator); |
| | 144 | try aliases.ensureTotalCapacity(instructions.len); |
| | 145 | |
| | 146 | for (instructions) |inst, i| { |
| | 147 | const result = inst_map.getOrPutAssumeCapacity(inst.opcode); |
| | 148 | if (!result.found_existing) { |
| | 149 | result.value_ptr.* = i; |
| | 150 | continue; |
| | 151 | } |
| | 152 | |
| | 153 | const existing = instructions[result.value_ptr.*]; |
| | 154 | |
| | 155 | const tag_index = std.mem.indexOfDiff(u8, inst.opname, existing.opname).?; |
| | 156 | const inst_priority = tagPriorityScore(inst.opname[tag_index..]); |
| | 157 | const existing_priority = tagPriorityScore(existing.opname[tag_index..]); |
| | 158 | |
| | 159 | if (inst_priority < existing_priority) { |
| | 160 | aliases.appendAssumeCapacity(.{ .inst = result.value_ptr.*, .alias = i }); |
| | 161 | result.value_ptr.* = i; |
| | 162 | } else { |
| | 163 | aliases.appendAssumeCapacity(.{ .inst = i, .alias = result.value_ptr.* }); |
| | 164 | } |
| 65 | } | 165 | } |
| 66 | try writer.writeAll(" _,\n};\n"); | 166 | |
| | 167 | const instructions_indices = inst_map.values(); |
| | 168 | |
| | 169 | try writer.writeAll("pub const Opcode = enum(u16) {\n"); |
| | 170 | for (instructions_indices) |i| { |
| | 171 | const inst = instructions[i]; |
| | 172 | try writer.print("{} = {},\n", .{ std.zig.fmtId(inst.opname), inst.opcode }); |
| | 173 | } |
| | 174 | |
| | 175 | try writer.writeByte('\n'); |
| | 176 | |
| | 177 | for (aliases.items) |alias| { |
| | 178 | try writer.print("pub const {} = Opcode.{};\n", .{ |
| | 179 | std.zig.fmtId(instructions[alias.inst].opname), |
| | 180 | std.zig.fmtId(instructions[alias.alias].opname), |
| | 181 | }); |
| | 182 | } |
| | 183 | |
| | 184 | try writer.writeAll( |
| | 185 | \\ |
| | 186 | \\pub fn Operands(comptime self: Opcode) type { |
| | 187 | \\return switch (self) { |
| | 188 | \\ |
| | 189 | ); |
| | 190 | |
| | 191 | for (instructions_indices) |i| { |
| | 192 | const inst = instructions[i]; |
| | 193 | try renderOperand(writer, .instruction, inst.opname, inst.operands, extended_structs); |
| | 194 | } |
| | 195 | try writer.writeAll("};\n}\n};\n"); |
| | 196 | _ = extended_structs; |
| 67 | } | 197 | } |
| 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 | for (kinds) |kind| { | 205 | for (kinds) |kind| { |
| 71 | switch (kind.category) { | 206 | switch (kind.category) { |
| 72 | .ValueEnum => try renderValueEnum(writer, kind), | 207 | .ValueEnum => try renderValueEnum(writer, allocator, kind, extended_structs), |
| 73 | .BitEnum => try renderBitEnum(writer, kind), | 208 | .BitEnum => try renderBitEnum(writer, allocator, kind, extended_structs), |
| 74 | else => {}, | 209 | else => {}, |
| 75 | } | 210 | } |
| 76 | } | 211 | } |
| 77 | } | 212 | } |
| 78 | | 213 | |
| 79 | fn renderValueEnum(writer: anytype, enumeration: g.OperandKind) !void { | 214 | fn renderValueEnum( |
| 80 | try writer.print("pub const {s} = extern enum(u32) {{\n", .{enumeration.kind}); | 215 | writer: anytype, |
| 81 | | 216 | allocator: Allocator, |
| | 217 | enumeration: g.OperandKind, |
| | 218 | extended_structs: ExtendedStructSet, |
| | 219 | ) !void { |
| 82 | const enumerants = enumeration.enumerants orelse return error.InvalidRegistry; | 220 | const enumerants = enumeration.enumerants orelse return error.InvalidRegistry; |
| 83 | for (enumerants) |enumerant| { | 221 | |
| | 222 | var enum_map = std.AutoArrayHashMap(u32, usize).init(allocator); |
| | 223 | try enum_map.ensureTotalCapacity(enumerants.len); |
| | 224 | |
| | 225 | var aliases = std.ArrayList(struct { enumerant: usize, alias: usize }).init(allocator); |
| | 226 | try aliases.ensureTotalCapacity(enumerants.len); |
| | 227 | |
| | 228 | for (enumerants) |enumerant, i| { |
| | 229 | const result = enum_map.getOrPutAssumeCapacity(enumerant.value.int); |
| | 230 | if (!result.found_existing) { |
| | 231 | result.value_ptr.* = i; |
| | 232 | continue; |
| | 233 | } |
| | 234 | |
| | 235 | const existing = enumerants[result.value_ptr.*]; |
| | 236 | |
| | 237 | const tag_index = std.mem.indexOfDiff(u8, enumerant.enumerant, existing.enumerant).?; |
| | 238 | const enum_priority = tagPriorityScore(enumerant.enumerant[tag_index..]); |
| | 239 | const existing_priority = tagPriorityScore(existing.enumerant[tag_index..]); |
| | 240 | |
| | 241 | if (enum_priority < existing_priority) { |
| | 242 | aliases.appendAssumeCapacity(.{ .enumerant = result.value_ptr.*, .alias = i }); |
| | 243 | result.value_ptr.* = i; |
| | 244 | } else { |
| | 245 | aliases.appendAssumeCapacity(.{ .enumerant = i, .alias = result.value_ptr.* }); |
| | 246 | } |
| | 247 | } |
| | 248 | |
| | 249 | const enum_indices = enum_map.values(); |
| | 250 | |
| | 251 | try writer.print("pub const {s} = enum(u32) {{\n", .{std.zig.fmtId(enumeration.kind)}); |
| | 252 | |
| | 253 | for (enum_indices) |i| { |
| | 254 | const enumerant = enumerants[i]; |
| 84 | if (enumerant.value != .int) return error.InvalidRegistry; | 255 | if (enumerant.value != .int) return error.InvalidRegistry; |
| 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 { | 285 | fn renderBitEnum( |
| 93 | try writer.print("pub const {s} = packed struct {{\n", .{enumeration.kind}); | 286 | writer: anytype, |
| | 287 | allocator: Allocator, |
| | 288 | enumeration: g.OperandKind, |
| | 289 | extended_structs: ExtendedStructSet, |
| | 290 | ) !void { |
| | 291 | try writer.print("pub const {s} = packed struct {{\n", .{std.zig.fmtId(enumeration.kind)}); |
| 94 | | 292 | |
| 95 | var flags_by_bitpos = [_]?[]const u8{null} ** 32; | 293 | var flags_by_bitpos = [_]?usize{null} ** 32; |
| 96 | const enumerants = enumeration.enumerants orelse return error.InvalidRegistry; | 294 | const enumerants = enumeration.enumerants orelse return error.InvalidRegistry; |
| 97 | for (enumerants) |enumerant| { | 295 | |
| | 296 | var aliases = std.ArrayList(struct { flag: usize, alias: u5 }).init(allocator); |
| | 297 | try aliases.ensureTotalCapacity(enumerants.len); |
| | 298 | |
| | 299 | for (enumerants) |enumerant, i| { |
| 98 | if (enumerant.value != .bitflag) return error.InvalidRegistry; | 300 | if (enumerant.value != .bitflag) return error.InvalidRegistry; |
| 99 | const value = try parseHexInt(enumerant.value.bitflag); | 301 | const value = try parseHexInt(enumerant.value.bitflag); |
| 100 | if (@popCount(u32, value) != 1) { | 302 | if (@popCount(u32, value) == 0) { |
| 101 | continue; // Skip combinations and 'none' items | 303 | continue; // Skip 'none' items |
| 102 | } | 304 | } |
| 103 | | 305 | |
| | 306 | std.debug.assert(@popCount(u32, value) == 1); |
| | 307 | |
| 104 | var bitpos = std.math.log2_int(u32, value); | 308 | var bitpos = std.math.log2_int(u32, value); |
| 105 | if (flags_by_bitpos[bitpos]) |*existing| { | 309 | if (flags_by_bitpos[bitpos]) |*existing| { |
| 106 | // Keep the shortest | 310 | const tag_index = std.mem.indexOfDiff(u8, enumerant.enumerant, enumerants[existing.*].enumerant).?; |
| 107 | if (enumerant.enumerant.len < existing.len) | 311 | const enum_priority = tagPriorityScore(enumerant.enumerant[tag_index..]); |
| 108 | existing.* = enumerant.enumerant; | 312 | const existing_priority = tagPriorityScore(enumerants[existing.*].enumerant[tag_index..]); |
| | 313 | |
| | 314 | if (enum_priority < existing_priority) { |
| | 315 | aliases.appendAssumeCapacity(.{ .flag = existing.*, .alias = bitpos }); |
| | 316 | existing.* = i; |
| | 317 | } else { |
| | 318 | aliases.appendAssumeCapacity(.{ .flag = i, .alias = bitpos }); |
| | 319 | } |
| 109 | } else { | 320 | } else { |
| 110 | flags_by_bitpos[bitpos] = enumerant.enumerant; | 321 | flags_by_bitpos[bitpos] = i; |
| 111 | } | 322 | } |
| 112 | } | 323 | } |
| 113 | | 324 | |
| 114 | for (flags_by_bitpos) |maybe_flag_name, bitpos| { | 325 | for (flags_by_bitpos) |maybe_flag_index, bitpos| { |
| 115 | try writer.writeAll(" "); | 326 | if (maybe_flag_index) |flag_index| { |
| 116 | if (maybe_flag_name) |flag_name| { | 327 | try writer.print("{}", .{std.zig.fmtId(enumerants[flag_index].enumerant)}); |
| 117 | try writer.writeAll(flag_name); | | |
| 118 | } else { | 328 | } else { |
| 119 | try writer.print("_reserved_bit_{}", .{bitpos}); | 329 | try writer.print("_reserved_bit_{}", .{bitpos}); |
| 120 | } | 330 | } |
| ... | @@ -126,7 +336,169 @@ fn renderBitEnum(writer: anytype, enumeration: g.OperandKind) !void { | ... | @@ -126,7 +336,169 @@ fn renderBitEnum(writer: anytype, enumeration: g.OperandKind) !void { |
| 126 | try writer.writeAll("= false,\n"); | 336 | try writer.writeAll("= false,\n"); |
| 127 | } | 337 | } |
| 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 | fn parseHexInt(text: []const u8) !u31 { | 504 | fn parseHexInt(text: []const u8) !u31 { |
| ... | @@ -142,7 +514,7 @@ fn usageAndExit(file: std.fs.File, arg0: []const u8, code: u8) noreturn { | ... | @@ -142,7 +514,7 @@ fn usageAndExit(file: std.fs.File, arg0: []const u8, code: u8) noreturn { |
| 142 | \\ | 514 | \\ |
| 143 | \\Generates Zig bindings for a SPIR-V specification .json (either core or | 515 | \\Generates Zig bindings for a SPIR-V specification .json (either core or |
| 144 | \\extinst versions). The result, printed to stdout, should be used to update | 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 | \\The relevant specifications can be obtained from the SPIR-V registry: | 519 | \\The relevant specifications can be obtained from the SPIR-V registry: |
| 148 | \\https://github.com/KhronosGroup/SPIRV-Headers/blob/master/include/spirv/unified1/ | 520 | \\https://github.com/KhronosGroup/SPIRV-Headers/blob/master/include/spirv/unified1/ |