| ... | @@ -1,45 +1,110 @@ | ... | @@ -1,45 +1,110 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const g = @import("spirv/grammar.zig"); | | |
| 3 | const Allocator = std.mem.Allocator; | 2 | const Allocator = std.mem.Allocator; |
| | 3 | const g = @import("spirv/grammar.zig"); |
| | 4 | const CoreRegistry = g.CoreRegistry; |
| | 5 | const ExtensionRegistry = g.ExtensionRegistry; |
| | 6 | const Instruction = g.Instruction; |
| | 7 | const OperandKind = g.OperandKind; |
| | 8 | const Enumerant = g.Enumerant; |
| | 9 | const Operand = g.Operand; |
| 4 | | 10 | |
| 5 | const ExtendedStructSet = std.StringHashMap(void); | 11 | const ExtendedStructSet = std.StringHashMap(void); |
| 6 | | 12 | |
| | 13 | const Extension = struct { |
| | 14 | name: []const u8, |
| | 15 | spec: ExtensionRegistry, |
| | 16 | }; |
| | 17 | |
| | 18 | const CmpInst = struct { |
| | 19 | fn lt(_: CmpInst, a: Instruction, b: Instruction) bool { |
| | 20 | return a.opcode < b.opcode; |
| | 21 | } |
| | 22 | }; |
| | 23 | |
| | 24 | const StringPair = struct { []const u8, []const u8 }; |
| | 25 | |
| | 26 | const StringPairContext = struct { |
| | 27 | pub fn hash(_: @This(), a: StringPair) u32 { |
| | 28 | var hasher = std.hash.Wyhash.init(0); |
| | 29 | const x, const y = a; |
| | 30 | hasher.update(x); |
| | 31 | hasher.update(y); |
| | 32 | return @truncate(hasher.final()); |
| | 33 | } |
| | 34 | |
| | 35 | pub fn eql(_: @This(), a: StringPair, b: StringPair, b_index: usize) bool { |
| | 36 | _ = b_index; |
| | 37 | const a_x, const a_y = a; |
| | 38 | const b_x, const b_y = b; |
| | 39 | return std.mem.eql(u8, a_x, b_x) and std.mem.eql(u8, a_y, b_y); |
| | 40 | } |
| | 41 | }; |
| | 42 | |
| | 43 | const OperandKindMap = std.ArrayHashMap(StringPair, OperandKind, StringPairContext, true); |
| | 44 | |
| 7 | pub fn main() !void { | 45 | pub fn main() !void { |
| 8 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 46 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); |
| 9 | defer arena.deinit(); | 47 | defer arena.deinit(); |
| 10 | const allocator = arena.allocator(); | 48 | const a = arena.allocator(); |
| 11 | | 49 | |
| 12 | const args = try std.process.argsAlloc(allocator); | 50 | const args = try std.process.argsAlloc(a); |
| 13 | if (args.len != 2) { | 51 | if (args.len != 2) { |
| 14 | usageAndExit(std.io.getStdErr(), args[0], 1); | 52 | usageAndExit(args[0], 1); |
| | 53 | } |
| | 54 | |
| | 55 | const json_path = try std.fs.path.join(a, &.{ args[1], "include/spirv/unified1/" }); |
| | 56 | const dir = try std.fs.cwd().openDir(json_path, .{ .iterate = true }); |
| | 57 | |
| | 58 | // const spec_path = try std.fs.path.join(a, &.{spirv_headers_dir_path, "spirv.core.grammar.json"}); |
| | 59 | // const core_spec = try std.fs.cwd().readFileAlloc(a, spec_path, std.math.maxInt(usize)); |
| | 60 | |
| | 61 | const core_spec = try readRegistry(CoreRegistry, a, dir, "spirv.core.grammar.json"); |
| | 62 | std.sort.block(Instruction, core_spec.instructions, CmpInst{}, CmpInst.lt); |
| | 63 | |
| | 64 | var exts = std.ArrayList(Extension).init(a); |
| | 65 | |
| | 66 | var it = dir.iterate(); |
| | 67 | while (try it.next()) |entry| { |
| | 68 | if (entry.kind != .file or !std.mem.startsWith(u8, entry.name, "extinst.")) { |
| | 69 | continue; |
| | 70 | } |
| | 71 | |
| | 72 | std.debug.assert(std.mem.endsWith(u8, entry.name, ".grammar.json")); |
| | 73 | const name = entry.name["extinst.".len .. entry.name.len - ".grammar.json".len]; |
| | 74 | const spec = try readRegistry(ExtensionRegistry, a, dir, entry.name); |
| | 75 | |
| | 76 | std.sort.block(Instruction, spec.instructions, CmpInst{}, CmpInst.lt); |
| | 77 | |
| | 78 | try exts.append(.{ .name = try a.dupe(u8, name), .spec = spec }); |
| 15 | } | 79 | } |
| 16 | | 80 | |
| 17 | const spec_path = args[1]; | 81 | var bw = std.io.bufferedWriter(std.io.getStdOut().writer()); |
| 18 | const spec = try std.fs.cwd().readFileAlloc(allocator, spec_path, std.math.maxInt(usize)); | 82 | try render(bw.writer(), a, core_spec, exts.items); |
| | 83 | try bw.flush(); |
| | 84 | } |
| 19 | | 85 | |
| | 86 | fn readRegistry(comptime RegistryType: type, a: Allocator, dir: std.fs.Dir, path: []const u8) !RegistryType { |
| | 87 | const spec = try dir.readFileAlloc(a, path, std.math.maxInt(usize)); |
| 20 | // Required for json parsing. | 88 | // Required for json parsing. |
| 21 | @setEvalBranchQuota(10000); | 89 | @setEvalBranchQuota(10000); |
| 22 | | 90 | |
| 23 | var scanner = std.json.Scanner.initCompleteInput(allocator, spec); | 91 | var scanner = std.json.Scanner.initCompleteInput(a, spec); |
| 24 | var diagnostics = std.json.Diagnostics{}; | 92 | var diagnostics = std.json.Diagnostics{}; |
| 25 | scanner.enableDiagnostics(&diagnostics); | 93 | scanner.enableDiagnostics(&diagnostics); |
| 26 | const parsed = std.json.parseFromTokenSource(g.CoreRegistry, allocator, &scanner, .{}) catch |err| { | 94 | const parsed = std.json.parseFromTokenSource(RegistryType, a, &scanner, .{}) catch |err| { |
| 27 | std.debug.print("line,col: {},{}\n", .{ diagnostics.getLine(), diagnostics.getColumn() }); | 95 | std.debug.print("{s}:{}:{}:\n", .{ path, diagnostics.getLine(), diagnostics.getColumn() }); |
| 28 | return err; | 96 | return err; |
| 29 | }; | 97 | }; |
| 30 | | 98 | return parsed.value; |
| 31 | var bw = std.io.bufferedWriter(std.io.getStdOut().writer()); | | |
| 32 | try render(bw.writer(), allocator, parsed.value); | | |
| 33 | try bw.flush(); | | |
| 34 | } | 99 | } |
| 35 | | 100 | |
| 36 | /// Returns a set with types that require an extra struct for the `Instruction` interface | 101 | /// 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. | 102 | /// to the spir-v spec, or whether the original type can be used. |
| 38 | fn extendedStructs( | 103 | fn extendedStructs( |
| 39 | arena: Allocator, | 104 | a: Allocator, |
| 40 | kinds: []const g.OperandKind, | 105 | kinds: []const OperandKind, |
| 41 | ) !ExtendedStructSet { | 106 | ) !ExtendedStructSet { |
| 42 | var map = ExtendedStructSet.init(arena); | 107 | var map = ExtendedStructSet.init(a); |
| 43 | try map.ensureTotalCapacity(@as(u32, @intCast(kinds.len))); | 108 | try map.ensureTotalCapacity(@as(u32, @intCast(kinds.len))); |
| 44 | | 109 | |
| 45 | for (kinds) |kind| { | 110 | for (kinds) |kind| { |
| ... | @@ -73,7 +138,7 @@ fn tagPriorityScore(tag: []const u8) usize { | ... | @@ -73,7 +138,7 @@ fn tagPriorityScore(tag: []const u8) usize { |
| 73 | } | 138 | } |
| 74 | } | 139 | } |
| 75 | | 140 | |
| 76 | fn render(writer: anytype, allocator: Allocator, registry: g.CoreRegistry) !void { | 141 | fn render(writer: anytype, a: Allocator, registry: CoreRegistry, extensions: []const Extension) !void { |
| 77 | try writer.writeAll( | 142 | try writer.writeAll( |
| 78 | \\//! This file is auto-generated by tools/gen_spirv_spec.zig. | 143 | \\//! This file is auto-generated by tools/gen_spirv_spec.zig. |
| 79 | \\ | 144 | \\ |
| ... | @@ -99,6 +164,7 @@ fn render(writer: anytype, allocator: Allocator, registry: g.CoreRegistry) !void | ... | @@ -99,6 +164,7 @@ fn render(writer: anytype, allocator: Allocator, registry: g.CoreRegistry) !void |
| 99 | \\pub const IdScope = IdRef; | 164 | \\pub const IdScope = IdRef; |
| 100 | \\ | 165 | \\ |
| 101 | \\pub const LiteralInteger = Word; | 166 | \\pub const LiteralInteger = Word; |
| | 167 | \\pub const LiteralFloat = Word; |
| 102 | \\pub const LiteralString = []const u8; | 168 | \\pub const LiteralString = []const u8; |
| 103 | \\pub const LiteralContextDependentNumber = union(enum) { | 169 | \\pub const LiteralContextDependentNumber = union(enum) { |
| 104 | \\ int32: i32, | 170 | \\ int32: i32, |
| ... | @@ -139,6 +205,12 @@ fn render(writer: anytype, allocator: Allocator, registry: g.CoreRegistry) !void | ... | @@ -139,6 +205,12 @@ fn render(writer: anytype, allocator: Allocator, registry: g.CoreRegistry) !void |
| 139 | \\ parameters: []const OperandKind, | 205 | \\ parameters: []const OperandKind, |
| 140 | \\}; | 206 | \\}; |
| 141 | \\ | 207 | \\ |
| | 208 | \\pub const Instruction = struct { |
| | 209 | \\ name: []const u8, |
| | 210 | \\ opcode: Word, |
| | 211 | \\ operands: []const Operand, |
| | 212 | \\}; |
| | 213 | \\ |
| 142 | \\ | 214 | \\ |
| 143 | ); | 215 | ); |
| 144 | | 216 | |
| ... | @@ -151,15 +223,123 @@ fn render(writer: anytype, allocator: Allocator, registry: g.CoreRegistry) !void | ... | @@ -151,15 +223,123 @@ fn render(writer: anytype, allocator: Allocator, registry: g.CoreRegistry) !void |
| 151 | .{ registry.major_version, registry.minor_version, registry.revision, registry.magic_number }, | 223 | .{ registry.major_version, registry.minor_version, registry.revision, registry.magic_number }, |
| 152 | ); | 224 | ); |
| 153 | | 225 | |
| 154 | const extended_structs = try extendedStructs(allocator, registry.operand_kinds); | 226 | // Merge the operand kinds from all extensions together. |
| 155 | try renderClass(writer, allocator, registry.instructions); | 227 | // var all_operand_kinds = std.ArrayList(OperandKind).init(a); |
| 156 | try renderOperandKind(writer, registry.operand_kinds); | 228 | // try all_operand_kinds.appendSlice(registry.operand_kinds); |
| 157 | try renderOpcodes(writer, allocator, registry.instructions, extended_structs); | 229 | var all_operand_kinds = OperandKindMap.init(a); |
| 158 | try renderOperandKinds(writer, allocator, registry.operand_kinds, extended_structs); | 230 | for (registry.operand_kinds) |kind| { |
| | 231 | try all_operand_kinds.putNoClobber(.{ "core", kind.kind }, kind); |
| | 232 | } |
| | 233 | for (extensions) |ext| { |
| | 234 | // Note: extensions may define the same operand kind, with different |
| | 235 | // parameters. Instead of trying to merge them, just discriminate them |
| | 236 | // using the name of the extension. This is similar to what |
| | 237 | // the official headers do. |
| | 238 | |
| | 239 | try all_operand_kinds.ensureUnusedCapacity(ext.spec.operand_kinds.len); |
| | 240 | for (ext.spec.operand_kinds) |kind| { |
| | 241 | var new_kind = kind; |
| | 242 | new_kind.kind = try std.mem.join(a, ".", &.{ ext.name, kind.kind }); |
| | 243 | try all_operand_kinds.putNoClobber(.{ ext.name, kind.kind }, new_kind); |
| | 244 | } |
| | 245 | } |
| | 246 | |
| | 247 | const extended_structs = try extendedStructs(a, all_operand_kinds.values()); |
| | 248 | // Note: extensions don't seem to have class. |
| | 249 | try renderClass(writer, a, registry.instructions); |
| | 250 | try renderOperandKind(writer, all_operand_kinds.values()); |
| | 251 | try renderOpcodes(writer, a, registry.instructions, extended_structs); |
| | 252 | try renderOperandKinds(writer, a, all_operand_kinds.values(), extended_structs); |
| | 253 | try renderInstructionSet(writer, a, registry, extensions, all_operand_kinds); |
| | 254 | } |
| | 255 | |
| | 256 | fn renderInstructionSet( |
| | 257 | writer: anytype, |
| | 258 | a: Allocator, |
| | 259 | core: CoreRegistry, |
| | 260 | extensions: []const Extension, |
| | 261 | all_operand_kinds: OperandKindMap, |
| | 262 | ) !void { |
| | 263 | _ = a; |
| | 264 | try writer.writeAll( |
| | 265 | \\pub const InstructionSet = enum { |
| | 266 | \\ core, |
| | 267 | ); |
| | 268 | |
| | 269 | for (extensions) |ext| { |
| | 270 | try writer.print("{},\n", .{std.zig.fmtId(ext.name)}); |
| | 271 | } |
| | 272 | |
| | 273 | try writer.writeAll( |
| | 274 | \\ |
| | 275 | \\ pub fn instructions(self: InstructionSet) []const Instruction { |
| | 276 | \\ return switch (self) { |
| | 277 | \\ |
| | 278 | ); |
| | 279 | |
| | 280 | try renderInstructionsCase(writer, "core", core.instructions, all_operand_kinds); |
| | 281 | for (extensions) |ext| { |
| | 282 | try renderInstructionsCase(writer, ext.name, ext.spec.instructions, all_operand_kinds); |
| | 283 | } |
| | 284 | |
| | 285 | try writer.writeAll( |
| | 286 | \\ }; |
| | 287 | \\ } |
| | 288 | \\}; |
| | 289 | \\ |
| | 290 | ); |
| | 291 | } |
| | 292 | |
| | 293 | fn renderInstructionsCase( |
| | 294 | writer: anytype, |
| | 295 | set_name: []const u8, |
| | 296 | instructions: []const Instruction, |
| | 297 | all_operand_kinds: OperandKindMap, |
| | 298 | ) !void { |
| | 299 | // Note: theoretically we could dedup from tags and give every instruction a list of aliases, |
| | 300 | // but there aren't so many total aliases and that would add more overhead in total. We will |
| | 301 | // just filter those out when needed. |
| | 302 | |
| | 303 | try writer.print(".{} => &[_]Instruction{{\n", .{std.zig.fmtId(set_name)}); |
| | 304 | |
| | 305 | for (instructions) |inst| { |
| | 306 | try writer.print( |
| | 307 | \\.{{ |
| | 308 | \\ .name = "{s}", |
| | 309 | \\ .opcode = {}, |
| | 310 | \\ .operands = &[_]Operand{{ |
| | 311 | \\ |
| | 312 | , .{ inst.opname, inst.opcode }); |
| | 313 | |
| | 314 | for (inst.operands) |operand| { |
| | 315 | const quantifier = if (operand.quantifier) |q| |
| | 316 | switch (q) { |
| | 317 | .@"?" => "optional", |
| | 318 | .@"*" => "variadic", |
| | 319 | } |
| | 320 | else |
| | 321 | "required"; |
| | 322 | |
| | 323 | const kind = all_operand_kinds.get(.{ set_name, operand.kind }) orelse |
| | 324 | all_operand_kinds.get(.{ "core", operand.kind }).?; |
| | 325 | try writer.print(".{{.kind = .{}, .quantifier = .{s}}},\n", .{ std.zig.fmtId(kind.kind), quantifier }); |
| | 326 | } |
| | 327 | |
| | 328 | try writer.writeAll( |
| | 329 | \\ }, |
| | 330 | \\}, |
| | 331 | \\ |
| | 332 | ); |
| | 333 | } |
| | 334 | |
| | 335 | try writer.writeAll( |
| | 336 | \\}, |
| | 337 | \\ |
| | 338 | ); |
| 159 | } | 339 | } |
| 160 | | 340 | |
| 161 | fn renderClass(writer: anytype, allocator: Allocator, instructions: []const g.Instruction) !void { | 341 | fn renderClass(writer: anytype, a: Allocator, instructions: []const Instruction) !void { |
| 162 | var class_map = std.StringArrayHashMap(void).init(allocator); | 342 | var class_map = std.StringArrayHashMap(void).init(a); |
| 163 | | 343 | |
| 164 | for (instructions) |inst| { | 344 | for (instructions) |inst| { |
| 165 | if (std.mem.eql(u8, inst.class.?, "@exclude")) { | 345 | if (std.mem.eql(u8, inst.class.?, "@exclude")) { |
| ... | @@ -173,7 +353,7 @@ fn renderClass(writer: anytype, allocator: Allocator, instructions: []const g.In | ... | @@ -173,7 +353,7 @@ fn renderClass(writer: anytype, allocator: Allocator, instructions: []const g.In |
| 173 | try renderInstructionClass(writer, class); | 353 | try renderInstructionClass(writer, class); |
| 174 | try writer.writeAll(",\n"); | 354 | try writer.writeAll(",\n"); |
| 175 | } | 355 | } |
| 176 | try writer.writeAll("};\n"); | 356 | try writer.writeAll("};\n\n"); |
| 177 | } | 357 | } |
| 178 | | 358 | |
| 179 | fn renderInstructionClass(writer: anytype, class: []const u8) !void { | 359 | fn renderInstructionClass(writer: anytype, class: []const u8) !void { |
| ... | @@ -192,7 +372,7 @@ fn renderInstructionClass(writer: anytype, class: []const u8) !void { | ... | @@ -192,7 +372,7 @@ fn renderInstructionClass(writer: anytype, class: []const u8) !void { |
| 192 | } | 372 | } |
| 193 | } | 373 | } |
| 194 | | 374 | |
| 195 | fn renderOperandKind(writer: anytype, operands: []const g.OperandKind) !void { | 375 | fn renderOperandKind(writer: anytype, operands: []const OperandKind) !void { |
| 196 | try writer.writeAll("pub const OperandKind = enum {\n"); | 376 | try writer.writeAll("pub const OperandKind = enum {\n"); |
| 197 | for (operands) |operand| { | 377 | for (operands) |operand| { |
| 198 | try writer.print("{},\n", .{std.zig.fmtId(operand.kind)}); | 378 | try writer.print("{},\n", .{std.zig.fmtId(operand.kind)}); |
| ... | @@ -242,7 +422,7 @@ fn renderOperandKind(writer: anytype, operands: []const g.OperandKind) !void { | ... | @@ -242,7 +422,7 @@ fn renderOperandKind(writer: anytype, operands: []const g.OperandKind) !void { |
| 242 | try writer.writeAll("};\n}\n};\n"); | 422 | try writer.writeAll("};\n}\n};\n"); |
| 243 | } | 423 | } |
| 244 | | 424 | |
| 245 | fn renderEnumerant(writer: anytype, enumerant: g.Enumerant) !void { | 425 | fn renderEnumerant(writer: anytype, enumerant: Enumerant) !void { |
| 246 | try writer.print(".{{.name = \"{s}\", .value = ", .{enumerant.enumerant}); | 426 | try writer.print(".{{.name = \"{s}\", .value = ", .{enumerant.enumerant}); |
| 247 | switch (enumerant.value) { | 427 | switch (enumerant.value) { |
| 248 | .bitflag => |flag| try writer.writeAll(flag), | 428 | .bitflag => |flag| try writer.writeAll(flag), |
| ... | @@ -260,14 +440,14 @@ fn renderEnumerant(writer: anytype, enumerant: g.Enumerant) !void { | ... | @@ -260,14 +440,14 @@ fn renderEnumerant(writer: anytype, enumerant: g.Enumerant) !void { |
| 260 | | 440 | |
| 261 | fn renderOpcodes( | 441 | fn renderOpcodes( |
| 262 | writer: anytype, | 442 | writer: anytype, |
| 263 | allocator: Allocator, | 443 | a: Allocator, |
| 264 | instructions: []const g.Instruction, | 444 | instructions: []const Instruction, |
| 265 | extended_structs: ExtendedStructSet, | 445 | extended_structs: ExtendedStructSet, |
| 266 | ) !void { | 446 | ) !void { |
| 267 | var inst_map = std.AutoArrayHashMap(u32, usize).init(allocator); | 447 | var inst_map = std.AutoArrayHashMap(u32, usize).init(a); |
| 268 | try inst_map.ensureTotalCapacity(instructions.len); | 448 | try inst_map.ensureTotalCapacity(instructions.len); |
| 269 | | 449 | |
| 270 | var aliases = std.ArrayList(struct { inst: usize, alias: usize }).init(allocator); | 450 | var aliases = std.ArrayList(struct { inst: usize, alias: usize }).init(a); |
| 271 | try aliases.ensureTotalCapacity(instructions.len); | 451 | try aliases.ensureTotalCapacity(instructions.len); |
| 272 | | 452 | |
| 273 | for (instructions, 0..) |inst, i| { | 453 | for (instructions, 0..) |inst, i| { |
| ... | @@ -323,31 +503,6 @@ fn renderOpcodes( | ... | @@ -323,31 +503,6 @@ fn renderOpcodes( |
| 323 | try renderOperand(writer, .instruction, inst.opname, inst.operands, extended_structs); | 503 | try renderOperand(writer, .instruction, inst.opname, inst.operands, extended_structs); |
| 324 | } | 504 | } |
| 325 | | 505 | |
| 326 | try writer.writeAll( | | |
| 327 | \\}; | | |
| 328 | \\} | | |
| 329 | \\pub fn operands(self: Opcode) []const Operand { | | |
| 330 | \\return switch (self) { | | |
| 331 | \\ | | |
| 332 | ); | | |
| 333 | | | |
| 334 | for (instructions_indices) |i| { | | |
| 335 | const inst = instructions[i]; | | |
| 336 | try writer.print(".{} => &[_]Operand{{", .{std.zig.fmtId(inst.opname)}); | | |
| 337 | for (inst.operands) |operand| { | | |
| 338 | const quantifier = if (operand.quantifier) |q| | | |
| 339 | switch (q) { | | |
| 340 | .@"?" => "optional", | | |
| 341 | .@"*" => "variadic", | | |
| 342 | } | | |
| 343 | else | | |
| 344 | "required"; | | |
| 345 | | | |
| 346 | try writer.print(".{{.kind = .{s}, .quantifier = .{s}}},", .{ operand.kind, quantifier }); | | |
| 347 | } | | |
| 348 | try writer.writeAll("},\n"); | | |
| 349 | } | | |
| 350 | | | |
| 351 | try writer.writeAll( | 506 | try writer.writeAll( |
| 352 | \\}; | 507 | \\}; |
| 353 | \\} | 508 | \\} |
| ... | @@ -368,14 +523,14 @@ fn renderOpcodes( | ... | @@ -368,14 +523,14 @@ fn renderOpcodes( |
| 368 | | 523 | |
| 369 | fn renderOperandKinds( | 524 | fn renderOperandKinds( |
| 370 | writer: anytype, | 525 | writer: anytype, |
| 371 | allocator: Allocator, | 526 | a: Allocator, |
| 372 | kinds: []const g.OperandKind, | 527 | kinds: []const OperandKind, |
| 373 | extended_structs: ExtendedStructSet, | 528 | extended_structs: ExtendedStructSet, |
| 374 | ) !void { | 529 | ) !void { |
| 375 | for (kinds) |kind| { | 530 | for (kinds) |kind| { |
| 376 | switch (kind.category) { | 531 | switch (kind.category) { |
| 377 | .ValueEnum => try renderValueEnum(writer, allocator, kind, extended_structs), | 532 | .ValueEnum => try renderValueEnum(writer, a, kind, extended_structs), |
| 378 | .BitEnum => try renderBitEnum(writer, allocator, kind, extended_structs), | 533 | .BitEnum => try renderBitEnum(writer, a, kind, extended_structs), |
| 379 | else => {}, | 534 | else => {}, |
| 380 | } | 535 | } |
| 381 | } | 536 | } |
| ... | @@ -383,20 +538,26 @@ fn renderOperandKinds( | ... | @@ -383,20 +538,26 @@ fn renderOperandKinds( |
| 383 | | 538 | |
| 384 | fn renderValueEnum( | 539 | fn renderValueEnum( |
| 385 | writer: anytype, | 540 | writer: anytype, |
| 386 | allocator: Allocator, | 541 | a: Allocator, |
| 387 | enumeration: g.OperandKind, | 542 | enumeration: OperandKind, |
| 388 | extended_structs: ExtendedStructSet, | 543 | extended_structs: ExtendedStructSet, |
| 389 | ) !void { | 544 | ) !void { |
| 390 | const enumerants = enumeration.enumerants orelse return error.InvalidRegistry; | 545 | const enumerants = enumeration.enumerants orelse return error.InvalidRegistry; |
| 391 | | 546 | |
| 392 | var enum_map = std.AutoArrayHashMap(u32, usize).init(allocator); | 547 | var enum_map = std.AutoArrayHashMap(u32, usize).init(a); |
| 393 | try enum_map.ensureTotalCapacity(enumerants.len); | 548 | try enum_map.ensureTotalCapacity(enumerants.len); |
| 394 | | 549 | |
| 395 | var aliases = std.ArrayList(struct { enumerant: usize, alias: usize }).init(allocator); | 550 | var aliases = std.ArrayList(struct { enumerant: usize, alias: usize }).init(a); |
| 396 | try aliases.ensureTotalCapacity(enumerants.len); | 551 | try aliases.ensureTotalCapacity(enumerants.len); |
| 397 | | 552 | |
| 398 | for (enumerants, 0..) |enumerant, i| { | 553 | for (enumerants, 0..) |enumerant, i| { |
| 399 | const result = enum_map.getOrPutAssumeCapacity(enumerant.value.int); | 554 | try writer.context.flush(); |
| | 555 | const value: u31 = switch (enumerant.value) { |
| | 556 | .int => |value| value, |
| | 557 | // Some extensions declare ints as string |
| | 558 | .bitflag => |value| try std.fmt.parseInt(u31, value, 10), |
| | 559 | }; |
| | 560 | const result = enum_map.getOrPutAssumeCapacity(value); |
| 400 | if (!result.found_existing) { | 561 | if (!result.found_existing) { |
| 401 | result.value_ptr.* = i; | 562 | result.value_ptr.* = i; |
| 402 | continue; | 563 | continue; |
| ... | @@ -422,9 +583,12 @@ fn renderValueEnum( | ... | @@ -422,9 +583,12 @@ fn renderValueEnum( |
| 422 | | 583 | |
| 423 | for (enum_indices) |i| { | 584 | for (enum_indices) |i| { |
| 424 | const enumerant = enumerants[i]; | 585 | const enumerant = enumerants[i]; |
| 425 | if (enumerant.value != .int) return error.InvalidRegistry; | 586 | // if (enumerant.value != .int) return error.InvalidRegistry; |
| 426 | | 587 | |
| 427 | try writer.print("{} = {},\n", .{ std.zig.fmtId(enumerant.enumerant), enumerant.value.int }); | 588 | switch (enumerant.value) { |
| | 589 | .int => |value| try writer.print("{} = {},\n", .{ std.zig.fmtId(enumerant.enumerant), value }), |
| | 590 | .bitflag => |value| try writer.print("{} = {s},\n", .{ std.zig.fmtId(enumerant.enumerant), value }), |
| | 591 | } |
| 428 | } | 592 | } |
| 429 | | 593 | |
| 430 | try writer.writeByte('\n'); | 594 | try writer.writeByte('\n'); |
| ... | @@ -454,8 +618,8 @@ fn renderValueEnum( | ... | @@ -454,8 +618,8 @@ fn renderValueEnum( |
| 454 | | 618 | |
| 455 | fn renderBitEnum( | 619 | fn renderBitEnum( |
| 456 | writer: anytype, | 620 | writer: anytype, |
| 457 | allocator: Allocator, | 621 | a: Allocator, |
| 458 | enumeration: g.OperandKind, | 622 | enumeration: OperandKind, |
| 459 | extended_structs: ExtendedStructSet, | 623 | extended_structs: ExtendedStructSet, |
| 460 | ) !void { | 624 | ) !void { |
| 461 | try writer.print("pub const {s} = packed struct {{\n", .{std.zig.fmtId(enumeration.kind)}); | 625 | try writer.print("pub const {s} = packed struct {{\n", .{std.zig.fmtId(enumeration.kind)}); |
| ... | @@ -463,7 +627,7 @@ fn renderBitEnum( | ... | @@ -463,7 +627,7 @@ fn renderBitEnum( |
| 463 | var flags_by_bitpos = [_]?usize{null} ** 32; | 627 | var flags_by_bitpos = [_]?usize{null} ** 32; |
| 464 | const enumerants = enumeration.enumerants orelse return error.InvalidRegistry; | 628 | const enumerants = enumeration.enumerants orelse return error.InvalidRegistry; |
| 465 | | 629 | |
| 466 | var aliases = std.ArrayList(struct { flag: usize, alias: u5 }).init(allocator); | 630 | var aliases = std.ArrayList(struct { flag: usize, alias: u5 }).init(a); |
| 467 | try aliases.ensureTotalCapacity(enumerants.len); | 631 | try aliases.ensureTotalCapacity(enumerants.len); |
| 468 | | 632 | |
| 469 | for (enumerants, 0..) |enumerant, i| { | 633 | for (enumerants, 0..) |enumerant, i| { |
| ... | @@ -471,6 +635,10 @@ fn renderBitEnum( | ... | @@ -471,6 +635,10 @@ fn renderBitEnum( |
| 471 | const value = try parseHexInt(enumerant.value.bitflag); | 635 | const value = try parseHexInt(enumerant.value.bitflag); |
| 472 | if (value == 0) { | 636 | if (value == 0) { |
| 473 | continue; // Skip 'none' items | 637 | continue; // Skip 'none' items |
| | 638 | } else if (std.mem.eql(u8, enumerant.enumerant, "FlagIsPublic")) { |
| | 639 | // This flag is special and poorly defined in the json files. |
| | 640 | // Just skip it for now |
| | 641 | continue; |
| 474 | } | 642 | } |
| 475 | | 643 | |
| 476 | std.debug.assert(@popCount(value) == 1); | 644 | std.debug.assert(@popCount(value) == 1); |
| ... | @@ -540,7 +708,7 @@ fn renderOperand( | ... | @@ -540,7 +708,7 @@ fn renderOperand( |
| 540 | mask, | 708 | mask, |
| 541 | }, | 709 | }, |
| 542 | field_name: []const u8, | 710 | field_name: []const u8, |
| 543 | parameters: []const g.Operand, | 711 | parameters: []const Operand, |
| 544 | extended_structs: ExtendedStructSet, | 712 | extended_structs: ExtendedStructSet, |
| 545 | ) !void { | 713 | ) !void { |
| 546 | if (kind == .instruction) { | 714 | if (kind == .instruction) { |
| ... | @@ -606,7 +774,7 @@ fn renderOperand( | ... | @@ -606,7 +774,7 @@ fn renderOperand( |
| 606 | try writer.writeAll(",\n"); | 774 | try writer.writeAll(",\n"); |
| 607 | } | 775 | } |
| 608 | | 776 | |
| 609 | fn renderFieldName(writer: anytype, operands: []const g.Operand, field_index: usize) !void { | 777 | fn renderFieldName(writer: anytype, operands: []const Operand, field_index: usize) !void { |
| 610 | const operand = operands[field_index]; | 778 | const operand = operands[field_index]; |
| 611 | | 779 | |
| 612 | // Should be enough for all names - adjust as needed. | 780 | // Should be enough for all names - adjust as needed. |
| ... | @@ -673,16 +841,16 @@ fn parseHexInt(text: []const u8) !u31 { | ... | @@ -673,16 +841,16 @@ fn parseHexInt(text: []const u8) !u31 { |
| 673 | return try std.fmt.parseInt(u31, text[prefix.len..], 16); | 841 | return try std.fmt.parseInt(u31, text[prefix.len..], 16); |
| 674 | } | 842 | } |
| 675 | | 843 | |
| 676 | fn usageAndExit(file: std.fs.File, arg0: []const u8, code: u8) noreturn { | 844 | fn usageAndExit(arg0: []const u8, code: u8) noreturn { |
| 677 | file.writer().print( | 845 | std.io.getStdErr().writer().print( |
| 678 | \\Usage: {s} <spirv json spec> | 846 | \\Usage: {s} <SPIRV-Headers repository path> |
| 679 | \\ | 847 | \\ |
| 680 | \\Generates Zig bindings for a SPIR-V specification .json (either core or | 848 | \\Generates Zig bindings for SPIR-V specifications found in the SPIRV-Headers |
| 681 | \\extinst versions). The result, printed to stdout, should be used to update | 849 | \\repository. The result, printed to stdout, should be used to update |
| 682 | \\files in src/codegen/spirv. Don't forget to format the output. | 850 | \\files in src/codegen/spirv. Don't forget to format the output. |
| 683 | \\ | 851 | \\ |
| 684 | \\The relevant specifications can be obtained from the SPIR-V registry: | 852 | \\<SPIRV-Headers repository path> should point to a clone of |
| 685 | \\https://github.com/KhronosGroup/SPIRV-Headers/blob/master/include/spirv/unified1/ | 853 | \\https://github.com/KhronosGroup/SPIRV-Headers/ |
| 686 | \\ | 854 | \\ |
| 687 | , .{arg0}) catch std.process.exit(1); | 855 | , .{arg0}) catch std.process.exit(1); |
| 688 | std.process.exit(code); | 856 | std.process.exit(code); |