spirv: snake-case the spec

This commit is contained in:
Ali Cheraghi 2025-07-12 16:05:45 +03:30 committed by Alex Rønne Petersen
parent 2f3cd175d3
commit f43f89a705
13 changed files with 13948 additions and 12088 deletions

File diff suppressed because it is too large Load diff

View file

@ -7,8 +7,7 @@ const assert = std.debug.assert;
const spec = @import("spec.zig"); const spec = @import("spec.zig");
const Opcode = spec.Opcode; const Opcode = spec.Opcode;
const Word = spec.Word; const Word = spec.Word;
const IdRef = spec.IdRef; const Id = spec.Id;
const IdResult = spec.IdResult;
const StorageClass = spec.StorageClass; const StorageClass = spec.StorageClass;
const SpvModule = @import("Module.zig"); const SpvModule = @import("Module.zig");
@ -127,10 +126,10 @@ const AsmValue = union(enum) {
unresolved_forward_reference, unresolved_forward_reference,
/// This result-value is a normal result produced by a different instruction. /// This result-value is a normal result produced by a different instruction.
value: IdRef, value: Id,
/// This result-value represents a type registered into the module's type system. /// This result-value represents a type registered into the module's type system.
ty: IdRef, ty: Id,
/// This is a pre-supplied constant integer value. /// This is a pre-supplied constant integer value.
constant: u32, constant: u32,
@ -141,7 +140,7 @@ const AsmValue = union(enum) {
/// Retrieve the result-id of this AsmValue. Asserts that this AsmValue /// Retrieve the result-id of this AsmValue. Asserts that this AsmValue
/// is of a variant that allows the result to be obtained (not an unresolved /// is of a variant that allows the result to be obtained (not an unresolved
/// forward declaration, not in the process of being declared, etc). /// forward declaration, not in the process of being declared, etc).
pub fn resultId(self: AsmValue) IdRef { pub fn resultId(self: AsmValue) Id {
return switch (self) { return switch (self) {
.just_declared, .just_declared,
.unresolved_forward_reference, .unresolved_forward_reference,
@ -314,7 +313,7 @@ fn processInstruction(self: *Assembler) !void {
return; return;
}, },
else => switch (self.inst.opcode.class()) { else => switch (self.inst.opcode.class()) {
.TypeDeclaration => try self.processTypeInstruction(), .type_declaration => try self.processTypeInstruction(),
else => (try self.processGenericInstruction()) orelse return, else => (try self.processGenericInstruction()) orelse return,
}, },
}; };
@ -392,7 +391,7 @@ fn processTypeInstruction(self: *Assembler) !AsmValue {
break :blk result_id; break :blk result_id;
}, },
.OpTypeStruct => blk: { .OpTypeStruct => blk: {
const ids = try self.gpa.alloc(IdRef, operands[1..].len); const ids = try self.gpa.alloc(Id, operands[1..].len);
defer self.gpa.free(ids); defer self.gpa.free(ids);
for (operands[1..], ids) |op, *id| id.* = try self.resolveRefId(op.ref_id); for (operands[1..], ids) |op, *id| id.* = try self.resolveRefId(op.ref_id);
const result_id = self.spv.allocId(); const result_id = self.spv.allocId();
@ -429,7 +428,7 @@ fn processTypeInstruction(self: *Assembler) !AsmValue {
const param_operands = operands[2..]; const param_operands = operands[2..];
const return_type = try self.resolveRefId(operands[1].ref_id); const return_type = try self.resolveRefId(operands[1].ref_id);
const param_types = try self.spv.gpa.alloc(IdRef, param_operands.len); const param_types = try self.spv.gpa.alloc(Id, param_operands.len);
defer self.spv.gpa.free(param_types); defer self.spv.gpa.free(param_types);
for (param_types, param_operands) |*param, operand| { for (param_types, param_operands) |*param, operand| {
param.* = try self.resolveRefId(operand.ref_id); param.* = try self.resolveRefId(operand.ref_id);
@ -457,17 +456,17 @@ fn processGenericInstruction(self: *Assembler) !?AsmValue {
const operands = self.inst.operands.items; const operands = self.inst.operands.items;
var maybe_spv_decl_index: ?SpvModule.Decl.Index = null; var maybe_spv_decl_index: ?SpvModule.Decl.Index = null;
const section = switch (self.inst.opcode.class()) { const section = switch (self.inst.opcode.class()) {
.ConstantCreation => &self.spv.sections.types_globals_constants, .constant_creation => &self.spv.sections.types_globals_constants,
.Annotation => &self.spv.sections.annotations, .annotation => &self.spv.sections.annotations,
.TypeDeclaration => unreachable, // Handled elsewhere. .type_declaration => unreachable, // Handled elsewhere.
else => switch (self.inst.opcode) { else => switch (self.inst.opcode) {
.OpEntryPoint => unreachable, .OpEntryPoint => unreachable,
.OpExecutionMode, .OpExecutionModeId => &self.spv.sections.execution_modes, .OpExecutionMode, .OpExecutionModeId => &self.spv.sections.execution_modes,
.OpVariable => section: { .OpVariable => section: {
const storage_class: spec.StorageClass = @enumFromInt(operands[2].value); const storage_class: spec.StorageClass = @enumFromInt(operands[2].value);
if (storage_class == .Function) break :section &self.func.prologue; if (storage_class == .function) break :section &self.func.prologue;
maybe_spv_decl_index = try self.spv.allocDecl(.global); maybe_spv_decl_index = try self.spv.allocDecl(.global);
if (self.spv.version.minor < 4 and storage_class != .Input and storage_class != .Output) { if (self.spv.version.minor < 4 and storage_class != .input and storage_class != .output) {
// Before version 1.4, the interfaces storage classes are limited to the Input and Output // Before version 1.4, the interfaces storage classes are limited to the Input and Output
break :section &self.spv.sections.types_globals_constants; break :section &self.spv.sections.types_globals_constants;
} }
@ -481,7 +480,7 @@ fn processGenericInstruction(self: *Assembler) !?AsmValue {
}, },
}; };
var maybe_result_id: ?IdResult = null; var maybe_result_id: ?Id = null;
const first_word = section.instructions.items.len; const first_word = section.instructions.items.len;
// At this point we're not quite sure how many operands this instruction is going to have, // At this point we're not quite sure how many operands this instruction is going to have,
// so insert 0 and patch up the actual opcode word later. // so insert 0 and patch up the actual opcode word later.
@ -504,12 +503,12 @@ fn processGenericInstruction(self: *Assembler) !?AsmValue {
else else
self.spv.allocId(); self.spv.allocId();
try section.ensureUnusedCapacity(self.spv.gpa, 1); try section.ensureUnusedCapacity(self.spv.gpa, 1);
section.writeOperand(IdResult, maybe_result_id.?); section.writeOperand(Id, maybe_result_id.?);
}, },
.ref_id => |index| { .ref_id => |index| {
const result = try self.resolveRef(index); const result = try self.resolveRef(index);
try section.ensureUnusedCapacity(self.spv.gpa, 1); try section.ensureUnusedCapacity(self.spv.gpa, 1);
section.writeOperand(spec.IdRef, result.resultId()); section.writeOperand(spec.Id, result.resultId());
}, },
.string => |offset| { .string => |offset| {
const text = std.mem.sliceTo(self.inst.string_bytes.items[offset..], 0); const text = std.mem.sliceTo(self.inst.string_bytes.items[offset..], 0);
@ -558,7 +557,7 @@ fn resolveRef(self: *Assembler, ref: AsmValue.Ref) !AsmValue {
} }
} }
fn resolveRefId(self: *Assembler, ref: AsmValue.Ref) !IdRef { fn resolveRefId(self: *Assembler, ref: AsmValue.Ref) !Id {
const value = try self.resolveRef(ref); const value = try self.resolveRef(ref);
return value.resultId(); return value.resultId();
} }
@ -600,7 +599,7 @@ fn parseInstruction(self: *Assembler) !void {
const expected_operands = inst.operands; const expected_operands = inst.operands;
// This is a loop because the result-id is not always the first operand. // This is a loop because the result-id is not always the first operand.
const requires_lhs_result = for (expected_operands) |op| { const requires_lhs_result = for (expected_operands) |op| {
if (op.kind == .IdResult) break true; if (op.kind == .id_result) break true;
} else false; } else false;
if (requires_lhs_result and maybe_lhs_result == null) { if (requires_lhs_result and maybe_lhs_result == null) {
@ -614,7 +613,7 @@ fn parseInstruction(self: *Assembler) !void {
} }
for (expected_operands) |operand| { for (expected_operands) |operand| {
if (operand.kind == .IdResult) { if (operand.kind == .id_result) {
try self.inst.operands.append(self.gpa, .{ .result_id = maybe_lhs_result.? }); try self.inst.operands.append(self.gpa, .{ .result_id = maybe_lhs_result.? });
continue; continue;
} }
@ -646,11 +645,11 @@ fn parseOperand(self: *Assembler, kind: spec.OperandKind) Error!void {
.value_enum => try self.parseValueEnum(kind), .value_enum => try self.parseValueEnum(kind),
.id => try self.parseRefId(), .id => try self.parseRefId(),
else => switch (kind) { else => switch (kind) {
.LiteralInteger => try self.parseLiteralInteger(), .literal_integer => try self.parseLiteralInteger(),
.LiteralString => try self.parseString(), .literal_string => try self.parseString(),
.LiteralContextDependentNumber => try self.parseContextDependentNumber(), .literal_context_dependent_number => try self.parseContextDependentNumber(),
.LiteralExtInstInteger => try self.parseLiteralExtInstInteger(), .literal_ext_inst_integer => try self.parseLiteralExtInstInteger(),
.PairIdRefIdRef => try self.parsePhiSource(), .pair_id_ref_id_ref => try self.parsePhiSource(),
else => return self.todo("parse operand of type {s}", .{@tagName(kind)}), else => return self.todo("parse operand of type {s}", .{@tagName(kind)}),
}, },
} }

View file

@ -15,9 +15,7 @@ const Wyhash = std.hash.Wyhash;
const spec = @import("spec.zig"); const spec = @import("spec.zig");
const Word = spec.Word; const Word = spec.Word;
const IdRef = spec.IdRef; const Id = spec.Id;
const IdResult = spec.IdResult;
const IdResultType = spec.IdResultType;
const Section = @import("Section.zig"); const Section = @import("Section.zig");
@ -82,7 +80,7 @@ pub const Decl = struct {
/// - For `func`, this is the result-id of the associated OpFunction instruction. /// - For `func`, this is the result-id of the associated OpFunction instruction.
/// - For `global`, this is the result-id of the associated OpVariable instruction. /// - For `global`, this is the result-id of the associated OpVariable instruction.
/// - For `invocation_global`, this is the result-id of the associated InvocationGlobal instruction. /// - For `invocation_global`, this is the result-id of the associated InvocationGlobal instruction.
result_id: IdRef, result_id: Id,
/// The offset of the first dependency of this decl in the `decl_deps` array. /// The offset of the first dependency of this decl in the `decl_deps` array.
begin_dep: u32, begin_dep: u32,
/// The past-end offset of the dependencies of this decl in the `decl_deps` array. /// The past-end offset of the dependencies of this decl in the `decl_deps` array.
@ -150,7 +148,7 @@ sections: struct {
next_result_id: Word, next_result_id: Word,
/// Cache for results of OpString instructions. /// Cache for results of OpString instructions.
strings: std.StringArrayHashMapUnmanaged(IdRef) = .empty, strings: std.StringArrayHashMapUnmanaged(Id) = .empty,
/// Some types shouldn't be emitted more than one time, but cannot be caught by /// Some types shouldn't be emitted more than one time, but cannot be caught by
/// the `intern_map` during codegen. Sometimes, IDs are compared to check if /// the `intern_map` during codegen. Sometimes, IDs are compared to check if
@ -161,20 +159,20 @@ strings: std.StringArrayHashMapUnmanaged(IdRef) = .empty,
/// Additionally, this is used for other values which can be cached, for example, /// Additionally, this is used for other values which can be cached, for example,
/// built-in variables. /// built-in variables.
cache: struct { cache: struct {
bool_type: ?IdRef = null, bool_type: ?Id = null,
void_type: ?IdRef = null, void_type: ?Id = null,
int_types: std.AutoHashMapUnmanaged(std.builtin.Type.Int, IdRef) = .empty, int_types: std.AutoHashMapUnmanaged(std.builtin.Type.Int, Id) = .empty,
float_types: std.AutoHashMapUnmanaged(std.builtin.Type.Float, IdRef) = .empty, float_types: std.AutoHashMapUnmanaged(std.builtin.Type.Float, Id) = .empty,
vector_types: std.AutoHashMapUnmanaged(struct { IdRef, u32 }, IdRef) = .empty, vector_types: std.AutoHashMapUnmanaged(struct { Id, u32 }, Id) = .empty,
array_types: std.AutoHashMapUnmanaged(struct { IdRef, IdRef }, IdRef) = .empty, array_types: std.AutoHashMapUnmanaged(struct { Id, Id }, Id) = .empty,
capabilities: std.AutoHashMapUnmanaged(spec.Capability, void) = .empty, capabilities: std.AutoHashMapUnmanaged(spec.Capability, void) = .empty,
extensions: std.StringHashMapUnmanaged(void) = .empty, extensions: std.StringHashMapUnmanaged(void) = .empty,
extended_instruction_set: std.AutoHashMapUnmanaged(spec.InstructionSet, IdRef) = .empty, extended_instruction_set: std.AutoHashMapUnmanaged(spec.InstructionSet, Id) = .empty,
decorations: std.AutoHashMapUnmanaged(struct { IdRef, spec.Decoration }, void) = .empty, decorations: std.AutoHashMapUnmanaged(struct { Id, spec.Decoration }, void) = .empty,
builtins: std.AutoHashMapUnmanaged(struct { IdRef, spec.BuiltIn }, Decl.Index) = .empty, builtins: std.AutoHashMapUnmanaged(struct { Id, spec.BuiltIn }, Decl.Index) = .empty,
bool_const: [2]?IdRef = .{ null, null }, bool_const: [2]?Id = .{ null, null },
} = .{}, } = .{},
/// Set of Decls, referred to by Decl.Index. /// Set of Decls, referred to by Decl.Index.
@ -185,7 +183,7 @@ decls: std.ArrayListUnmanaged(Decl) = .empty,
decl_deps: std.ArrayListUnmanaged(Decl.Index) = .empty, decl_deps: std.ArrayListUnmanaged(Decl.Index) = .empty,
/// The list of entry points that should be exported from this module. /// The list of entry points that should be exported from this module.
entry_points: std.AutoArrayHashMapUnmanaged(IdRef, EntryPoint) = .empty, entry_points: std.AutoArrayHashMapUnmanaged(Id, EntryPoint) = .empty,
pub fn init(gpa: Allocator, target: *const std.Target) Module { pub fn init(gpa: Allocator, target: *const std.Target) Module {
const version_minor: u8 = blk: { const version_minor: u8 = blk: {
@ -245,7 +243,7 @@ pub const IdRange = struct {
base: u32, base: u32,
len: u32, len: u32,
pub fn at(range: IdRange, i: usize) IdResult { pub fn at(range: IdRange, i: usize) Id {
assert(i < range.len); assert(i < range.len);
return @enumFromInt(range.base + i); return @enumFromInt(range.base + i);
} }
@ -259,7 +257,7 @@ pub fn allocIds(self: *Module, n: u32) IdRange {
}; };
} }
pub fn allocId(self: *Module) IdResult { pub fn allocId(self: *Module) Id {
return self.allocIds(1).at(0); return self.allocIds(1).at(0);
} }
@ -275,7 +273,7 @@ fn addEntryPointDeps(
self: *Module, self: *Module,
decl_index: Decl.Index, decl_index: Decl.Index,
seen: *std.DynamicBitSetUnmanaged, seen: *std.DynamicBitSetUnmanaged,
interface: *std.ArrayList(IdRef), interface: *std.ArrayList(Id),
) !void { ) !void {
const decl = self.declPtr(decl_index); const decl = self.declPtr(decl_index);
const deps = self.decl_deps.items[decl.begin_dep..decl.end_dep]; const deps = self.decl_deps.items[decl.begin_dep..decl.end_dep];
@ -299,7 +297,7 @@ fn entryPoints(self: *Module) !Section {
var entry_points = Section{}; var entry_points = Section{};
errdefer entry_points.deinit(self.gpa); errdefer entry_points.deinit(self.gpa);
var interface = std.ArrayList(IdRef).init(self.gpa); var interface = std.ArrayList(Id).init(self.gpa);
defer interface.deinit(); defer interface.deinit();
var seen = try std.DynamicBitSetUnmanaged.initEmpty(self.gpa, self.decls.items.len); var seen = try std.DynamicBitSetUnmanaged.initEmpty(self.gpa, self.decls.items.len);
@ -317,12 +315,12 @@ fn entryPoints(self: *Module) !Section {
.interface = interface.items, .interface = interface.items,
}); });
if (entry_point.exec_mode == null and entry_point.exec_model == .Fragment) { if (entry_point.exec_mode == null and entry_point.exec_model == .fragment) {
switch (self.target.os.tag) { switch (self.target.os.tag) {
.vulkan, .opengl => |tag| { .vulkan, .opengl => |tag| {
try self.sections.execution_modes.emit(self.gpa, .OpExecutionMode, .{ try self.sections.execution_modes.emit(self.gpa, .OpExecutionMode, .{
.entry_point = entry_point_id, .entry_point = entry_point_id,
.mode = if (tag == .vulkan) .OriginUpperLeft else .OriginLowerLeft, .mode = if (tag == .vulkan) .origin_upper_left else .origin_lower_left,
}); });
}, },
.opencl => {}, .opencl => {},
@ -338,59 +336,59 @@ pub fn finalize(self: *Module, a: Allocator) ![]Word {
// Emit capabilities and extensions // Emit capabilities and extensions
switch (self.target.os.tag) { switch (self.target.os.tag) {
.opengl => { .opengl => {
try self.addCapability(.Shader); try self.addCapability(.shader);
try self.addCapability(.Matrix); try self.addCapability(.matrix);
}, },
.vulkan => { .vulkan => {
try self.addCapability(.Shader); try self.addCapability(.shader);
try self.addCapability(.Matrix); try self.addCapability(.matrix);
if (self.target.cpu.arch == .spirv64) { if (self.target.cpu.arch == .spirv64) {
try self.addExtension("SPV_KHR_physical_storage_buffer"); try self.addExtension("SPV_KHR_physical_storage_buffer");
try self.addCapability(.PhysicalStorageBufferAddresses); try self.addCapability(.physical_storage_buffer_addresses);
} }
}, },
.opencl, .amdhsa => { .opencl, .amdhsa => {
try self.addCapability(.Kernel); try self.addCapability(.kernel);
try self.addCapability(.Addresses); try self.addCapability(.addresses);
}, },
else => unreachable, else => unreachable,
} }
if (self.target.cpu.arch == .spirv64) try self.addCapability(.Int64); if (self.target.cpu.arch == .spirv64) try self.addCapability(.int64);
if (self.target.cpu.has(.spirv, .int64)) try self.addCapability(.Int64); if (self.target.cpu.has(.spirv, .int64)) try self.addCapability(.int64);
if (self.target.cpu.has(.spirv, .float16)) try self.addCapability(.Float16); if (self.target.cpu.has(.spirv, .float16)) try self.addCapability(.float16);
if (self.target.cpu.has(.spirv, .float64)) try self.addCapability(.Float64); if (self.target.cpu.has(.spirv, .float64)) try self.addCapability(.float64);
if (self.target.cpu.has(.spirv, .generic_pointer)) try self.addCapability(.GenericPointer); if (self.target.cpu.has(.spirv, .generic_pointer)) try self.addCapability(.generic_pointer);
if (self.target.cpu.has(.spirv, .vector16)) try self.addCapability(.Vector16); if (self.target.cpu.has(.spirv, .vector16)) try self.addCapability(.vector16);
if (self.target.cpu.has(.spirv, .storage_push_constant16)) { if (self.target.cpu.has(.spirv, .storage_push_constant16)) {
try self.addExtension("SPV_KHR_16bit_storage"); try self.addExtension("SPV_KHR_16bit_storage");
try self.addCapability(.StoragePushConstant16); try self.addCapability(.storage_push_constant16);
} }
if (self.target.cpu.has(.spirv, .arbitrary_precision_integers)) { if (self.target.cpu.has(.spirv, .arbitrary_precision_integers)) {
try self.addExtension("SPV_INTEL_arbitrary_precision_integers"); try self.addExtension("SPV_INTEL_arbitrary_precision_integers");
try self.addCapability(.ArbitraryPrecisionIntegersINTEL); try self.addCapability(.arbitrary_precision_integers_intel);
} }
if (self.target.cpu.has(.spirv, .variable_pointers)) { if (self.target.cpu.has(.spirv, .variable_pointers)) {
try self.addExtension("SPV_KHR_variable_pointers"); try self.addExtension("SPV_KHR_variable_pointers");
try self.addCapability(.VariablePointersStorageBuffer); try self.addCapability(.variable_pointers_storage_buffer);
try self.addCapability(.VariablePointers); try self.addCapability(.variable_pointers);
} }
// These are well supported // These are well supported
try self.addCapability(.Int8); try self.addCapability(.int8);
try self.addCapability(.Int16); try self.addCapability(.int16);
// Emit memory model // Emit memory model
const addressing_model: spec.AddressingModel = switch (self.target.os.tag) { const addressing_model: spec.AddressingModel = switch (self.target.os.tag) {
.opengl => .Logical, .opengl => .logical,
.vulkan => if (self.target.cpu.arch == .spirv32) .Logical else .PhysicalStorageBuffer64, .vulkan => if (self.target.cpu.arch == .spirv32) .logical else .physical_storage_buffer64,
.opencl => if (self.target.cpu.arch == .spirv32) .Physical32 else .Physical64, .opencl => if (self.target.cpu.arch == .spirv32) .physical32 else .physical64,
.amdhsa => .Physical64, .amdhsa => .physical64,
else => unreachable, else => unreachable,
}; };
try self.sections.memory_model.emit(self.gpa, .OpMemoryModel, .{ try self.sections.memory_model.emit(self.gpa, .OpMemoryModel, .{
.addressing_model = addressing_model, .addressing_model = addressing_model,
.memory_model = switch (self.target.os.tag) { .memory_model = switch (self.target.os.tag) {
.opencl => .OpenCL, .opencl => .open_cl,
.vulkan, .opengl => .GLSL450, .vulkan, .opengl => .glsl450,
else => unreachable, else => unreachable,
}, },
}); });
@ -411,7 +409,7 @@ pub fn finalize(self: *Module, a: Allocator) ![]Word {
var source = Section{}; var source = Section{};
defer source.deinit(self.gpa); defer source.deinit(self.gpa);
try self.sections.debug_strings.emit(self.gpa, .OpSource, .{ try self.sections.debug_strings.emit(self.gpa, .OpSource, .{
.source_language = .Zig, .source_language = .zig,
.version = 0, .version = 0,
// We cannot emit these because the Khronos translator does not parse this instruction // We cannot emit these because the Khronos translator does not parse this instruction
// correctly. // correctly.
@ -473,7 +471,7 @@ pub fn addExtension(self: *Module, ext: []const u8) !void {
} }
/// Imports or returns the existing id of an extended instruction set /// Imports or returns the existing id of an extended instruction set
pub fn importInstructionSet(self: *Module, set: spec.InstructionSet) !IdRef { pub fn importInstructionSet(self: *Module, set: spec.InstructionSet) !Id {
assert(set != .core); assert(set != .core);
const gop = try self.cache.extended_instruction_set.getOrPut(self.gpa, set); const gop = try self.cache.extended_instruction_set.getOrPut(self.gpa, set);
@ -490,7 +488,7 @@ pub fn importInstructionSet(self: *Module, set: spec.InstructionSet) !IdRef {
} }
/// Fetch the result-id of an instruction corresponding to a string. /// Fetch the result-id of an instruction corresponding to a string.
pub fn resolveString(self: *Module, string: []const u8) !IdRef { pub fn resolveString(self: *Module, string: []const u8) !Id {
if (self.strings.get(string)) |id| { if (self.strings.get(string)) |id| {
return id; return id;
} }
@ -506,7 +504,7 @@ pub fn resolveString(self: *Module, string: []const u8) !IdRef {
return id; return id;
} }
pub fn structType(self: *Module, result_id: IdResult, types: []const IdRef, maybe_names: ?[]const []const u8) !void { pub fn structType(self: *Module, result_id: Id, types: []const Id, maybe_names: ?[]const []const u8) !void {
try self.sections.types_globals_constants.emit(self.gpa, .OpTypeStruct, .{ try self.sections.types_globals_constants.emit(self.gpa, .OpTypeStruct, .{
.id_result = result_id, .id_result = result_id,
.id_ref = types, .id_ref = types,
@ -520,7 +518,7 @@ pub fn structType(self: *Module, result_id: IdResult, types: []const IdRef, mayb
} }
} }
pub fn boolType(self: *Module) !IdRef { pub fn boolType(self: *Module) !Id {
if (self.cache.bool_type) |id| return id; if (self.cache.bool_type) |id| return id;
const result_id = self.allocId(); const result_id = self.allocId();
@ -531,7 +529,7 @@ pub fn boolType(self: *Module) !IdRef {
return result_id; return result_id;
} }
pub fn voidType(self: *Module) !IdRef { pub fn voidType(self: *Module) !Id {
if (self.cache.void_type) |id| return id; if (self.cache.void_type) |id| return id;
const result_id = self.allocId(); const result_id = self.allocId();
@ -543,7 +541,7 @@ pub fn voidType(self: *Module) !IdRef {
return result_id; return result_id;
} }
pub fn intType(self: *Module, signedness: std.builtin.Signedness, bits: u16) !IdRef { pub fn intType(self: *Module, signedness: std.builtin.Signedness, bits: u16) !Id {
assert(bits > 0); assert(bits > 0);
const entry = try self.cache.int_types.getOrPut(self.gpa, .{ .signedness = signedness, .bits = bits }); const entry = try self.cache.int_types.getOrPut(self.gpa, .{ .signedness = signedness, .bits = bits });
if (!entry.found_existing) { if (!entry.found_existing) {
@ -566,7 +564,7 @@ pub fn intType(self: *Module, signedness: std.builtin.Signedness, bits: u16) !Id
return entry.value_ptr.*; return entry.value_ptr.*;
} }
pub fn floatType(self: *Module, bits: u16) !IdRef { pub fn floatType(self: *Module, bits: u16) !Id {
assert(bits > 0); assert(bits > 0);
const entry = try self.cache.float_types.getOrPut(self.gpa, .{ .bits = bits }); const entry = try self.cache.float_types.getOrPut(self.gpa, .{ .bits = bits });
if (!entry.found_existing) { if (!entry.found_existing) {
@ -581,7 +579,7 @@ pub fn floatType(self: *Module, bits: u16) !IdRef {
return entry.value_ptr.*; return entry.value_ptr.*;
} }
pub fn vectorType(self: *Module, len: u32, child_ty_id: IdRef) !IdRef { pub fn vectorType(self: *Module, len: u32, child_ty_id: Id) !Id {
const entry = try self.cache.vector_types.getOrPut(self.gpa, .{ child_ty_id, len }); const entry = try self.cache.vector_types.getOrPut(self.gpa, .{ child_ty_id, len });
if (!entry.found_existing) { if (!entry.found_existing) {
const result_id = self.allocId(); const result_id = self.allocId();
@ -595,7 +593,7 @@ pub fn vectorType(self: *Module, len: u32, child_ty_id: IdRef) !IdRef {
return entry.value_ptr.*; return entry.value_ptr.*;
} }
pub fn arrayType(self: *Module, len_id: IdRef, child_ty_id: IdRef) !IdRef { pub fn arrayType(self: *Module, len_id: Id, child_ty_id: Id) !Id {
const entry = try self.cache.array_types.getOrPut(self.gpa, .{ child_ty_id, len_id }); const entry = try self.cache.array_types.getOrPut(self.gpa, .{ child_ty_id, len_id });
if (!entry.found_existing) { if (!entry.found_existing) {
const result_id = self.allocId(); const result_id = self.allocId();
@ -609,7 +607,7 @@ pub fn arrayType(self: *Module, len_id: IdRef, child_ty_id: IdRef) !IdRef {
return entry.value_ptr.*; return entry.value_ptr.*;
} }
pub fn functionType(self: *Module, return_ty_id: IdRef, param_type_ids: []const IdRef) !IdRef { pub fn functionType(self: *Module, return_ty_id: Id, param_type_ids: []const Id) !Id {
const result_id = self.allocId(); const result_id = self.allocId();
try self.sections.types_globals_constants.emit(self.gpa, .OpTypeFunction, .{ try self.sections.types_globals_constants.emit(self.gpa, .OpTypeFunction, .{
.id_result = result_id, .id_result = result_id,
@ -619,7 +617,7 @@ pub fn functionType(self: *Module, return_ty_id: IdRef, param_type_ids: []const
return result_id; return result_id;
} }
pub fn constant(self: *Module, result_ty_id: IdRef, value: spec.LiteralContextDependentNumber) !IdRef { pub fn constant(self: *Module, result_ty_id: Id, value: spec.LiteralContextDependentNumber) !Id {
const result_id = self.allocId(); const result_id = self.allocId();
const section = &self.sections.types_globals_constants; const section = &self.sections.types_globals_constants;
try section.emit(self.gpa, .OpConstant, .{ try section.emit(self.gpa, .OpConstant, .{
@ -630,7 +628,7 @@ pub fn constant(self: *Module, result_ty_id: IdRef, value: spec.LiteralContextDe
return result_id; return result_id;
} }
pub fn constBool(self: *Module, value: bool) !IdRef { pub fn constBool(self: *Module, value: bool) !Id {
if (self.cache.bool_const[@intFromBool(value)]) |b| return b; if (self.cache.bool_const[@intFromBool(value)]) |b| return b;
const result_ty_id = try self.boolType(); const result_ty_id = try self.boolType();
@ -653,7 +651,7 @@ pub fn constBool(self: *Module, value: bool) !IdRef {
/// Return a pointer to a builtin variable. `result_ty_id` must be a **pointer** /// Return a pointer to a builtin variable. `result_ty_id` must be a **pointer**
/// with storage class `.Input`. /// with storage class `.Input`.
pub fn builtin(self: *Module, result_ty_id: IdRef, spirv_builtin: spec.BuiltIn) !Decl.Index { pub fn builtin(self: *Module, result_ty_id: Id, spirv_builtin: spec.BuiltIn) !Decl.Index {
const entry = try self.cache.builtins.getOrPut(self.gpa, .{ result_ty_id, spirv_builtin }); const entry = try self.cache.builtins.getOrPut(self.gpa, .{ result_ty_id, spirv_builtin });
if (!entry.found_existing) { if (!entry.found_existing) {
const decl_index = try self.allocDecl(.global); const decl_index = try self.allocDecl(.global);
@ -662,15 +660,15 @@ pub fn builtin(self: *Module, result_ty_id: IdRef, spirv_builtin: spec.BuiltIn)
try self.sections.types_globals_constants.emit(self.gpa, .OpVariable, .{ try self.sections.types_globals_constants.emit(self.gpa, .OpVariable, .{
.id_result_type = result_ty_id, .id_result_type = result_ty_id,
.id_result = result_id, .id_result = result_id,
.storage_class = .Input, .storage_class = .input,
}); });
try self.decorate(result_id, .{ .BuiltIn = .{ .built_in = spirv_builtin } }); try self.decorate(result_id, .{ .built_in = .{ .built_in = spirv_builtin } });
try self.declareDeclDeps(decl_index, &.{}); try self.declareDeclDeps(decl_index, &.{});
} }
return entry.value_ptr.*; return entry.value_ptr.*;
} }
pub fn constUndef(self: *Module, ty_id: IdRef) !IdRef { pub fn constUndef(self: *Module, ty_id: Id) !Id {
const result_id = self.allocId(); const result_id = self.allocId();
try self.sections.types_globals_constants.emit(self.gpa, .OpUndef, .{ try self.sections.types_globals_constants.emit(self.gpa, .OpUndef, .{
.id_result_type = ty_id, .id_result_type = ty_id,
@ -679,7 +677,7 @@ pub fn constUndef(self: *Module, ty_id: IdRef) !IdRef {
return result_id; return result_id;
} }
pub fn constNull(self: *Module, ty_id: IdRef) !IdRef { pub fn constNull(self: *Module, ty_id: Id) !Id {
const result_id = self.allocId(); const result_id = self.allocId();
try self.sections.types_globals_constants.emit(self.gpa, .OpConstantNull, .{ try self.sections.types_globals_constants.emit(self.gpa, .OpConstantNull, .{
.id_result_type = ty_id, .id_result_type = ty_id,
@ -691,7 +689,7 @@ pub fn constNull(self: *Module, ty_id: IdRef) !IdRef {
/// Decorate a result-id. /// Decorate a result-id.
pub fn decorate( pub fn decorate(
self: *Module, self: *Module,
target: IdRef, target: Id,
decoration: spec.Decoration.Extended, decoration: spec.Decoration.Extended,
) !void { ) !void {
const entry = try self.cache.decorations.getOrPut(self.gpa, .{ target, decoration }); const entry = try self.cache.decorations.getOrPut(self.gpa, .{ target, decoration });
@ -707,7 +705,7 @@ pub fn decorate(
/// We really don't have to and shouldn't need to cache this. /// We really don't have to and shouldn't need to cache this.
pub fn decorateMember( pub fn decorateMember(
self: *Module, self: *Module,
structure_type: IdRef, structure_type: Id,
member: u32, member: u32,
decoration: spec.Decoration.Extended, decoration: spec.Decoration.Extended,
) !void { ) !void {
@ -762,20 +760,20 @@ pub fn declareEntryPoint(
if (!gop.found_existing) gop.value_ptr.exec_mode = exec_mode; if (!gop.found_existing) gop.value_ptr.exec_mode = exec_mode;
} }
pub fn debugName(self: *Module, target: IdResult, name: []const u8) !void { pub fn debugName(self: *Module, target: Id, name: []const u8) !void {
try self.sections.debug_names.emit(self.gpa, .OpName, .{ try self.sections.debug_names.emit(self.gpa, .OpName, .{
.target = target, .target = target,
.name = name, .name = name,
}); });
} }
pub fn debugNameFmt(self: *Module, target: IdResult, comptime fmt: []const u8, args: anytype) !void { pub fn debugNameFmt(self: *Module, target: Id, comptime fmt: []const u8, args: anytype) !void {
const name = try std.fmt.allocPrint(self.gpa, fmt, args); const name = try std.fmt.allocPrint(self.gpa, fmt, args);
defer self.gpa.free(name); defer self.gpa.free(name);
try self.debugName(target, name); try self.debugName(target, name);
} }
pub fn memberDebugName(self: *Module, target: IdResult, member: u32, name: []const u8) !void { pub fn memberDebugName(self: *Module, target: Id, member: u32, name: []const u8) !void {
try self.sections.debug_names.emit(self.gpa, .OpMemberName, .{ try self.sections.debug_names.emit(self.gpa, .OpMemberName, .{
.type = target, .type = target,
.member = member, .member = member,

View file

@ -79,7 +79,7 @@ pub fn emit(
pub fn emitBranch( pub fn emitBranch(
section: *Section, section: *Section,
allocator: Allocator, allocator: Allocator,
target_label: spec.IdRef, target_label: spec.Id,
) !void { ) !void {
try section.emit(allocator, .OpBranch, .{ try section.emit(allocator, .OpBranch, .{
.target_label = target_label, .target_label = target_label,
@ -94,8 +94,8 @@ pub fn emitSpecConstantOp(
) !void { ) !void {
const word_count = operandsSize(opcode.Operands(), operands); const word_count = operandsSize(opcode.Operands(), operands);
try section.emitRaw(allocator, .OpSpecConstantOp, 1 + word_count); try section.emitRaw(allocator, .OpSpecConstantOp, 1 + word_count);
section.writeOperand(spec.IdRef, operands.id_result_type); section.writeOperand(spec.Id, operands.id_result_type);
section.writeOperand(spec.IdRef, operands.id_result); section.writeOperand(spec.Id, operands.id_result);
section.writeOperand(Opcode, opcode); section.writeOperand(Opcode, opcode);
const fields = @typeInfo(opcode.Operands()).@"struct".fields; const fields = @typeInfo(opcode.Operands()).@"struct".fields;
@ -134,7 +134,7 @@ fn writeOperands(section: *Section, comptime Operands: type, operands: Operands)
pub fn writeOperand(section: *Section, comptime Operand: type, operand: Operand) void { pub fn writeOperand(section: *Section, comptime Operand: type, operand: Operand) void {
switch (Operand) { switch (Operand) {
spec.IdResult => section.writeWord(@intFromEnum(operand)), spec.Id => section.writeWord(@intFromEnum(operand)),
spec.LiteralInteger => section.writeWord(operand), spec.LiteralInteger => section.writeWord(operand),
@ -266,7 +266,7 @@ fn operandsSize(comptime Operands: type, operands: Operands) usize {
fn operandSize(comptime Operand: type, operand: Operand) usize { fn operandSize(comptime Operand: type, operand: Operand) usize {
return switch (Operand) { return switch (Operand) {
spec.IdResult, spec.Id,
spec.LiteralInteger, spec.LiteralInteger,
spec.LiteralExtInstInteger, spec.LiteralExtInstInteger,
=> 1, => 1,

View file

@ -5,9 +5,7 @@
{ {
"opname": "InvocationGlobal", "opname": "InvocationGlobal",
"opcode": 0, "opcode": 0,
"operands": [ "operands": [{ "kind": "IdRef", "name": "initializer function" }]
{ "kind": "IdRef", "name": "initializer function" }
]
} }
] ]
} }

File diff suppressed because it is too large Load diff

View file

@ -42,7 +42,7 @@ const Value = @import("../Value.zig");
const SpvModule = @import("../codegen/spirv/Module.zig"); const SpvModule = @import("../codegen/spirv/Module.zig");
const Section = @import("../codegen/spirv/Section.zig"); const Section = @import("../codegen/spirv/Section.zig");
const spec = @import("../codegen/spirv/spec.zig"); const spec = @import("../codegen/spirv/spec.zig");
const IdResult = spec.IdResult; const Id = spec.Id;
const Word = spec.Word; const Word = spec.Word;
const BinaryModule = @import("SpirV/BinaryModule.zig"); const BinaryModule = @import("SpirV/BinaryModule.zig");
@ -144,15 +144,15 @@ pub fn updateExports(
const cc = Type.fromInterned(nav_ty).fnCallingConvention(zcu); const cc = Type.fromInterned(nav_ty).fnCallingConvention(zcu);
const exec_model: spec.ExecutionModel = switch (target.os.tag) { const exec_model: spec.ExecutionModel = switch (target.os.tag) {
.vulkan, .opengl => switch (cc) { .vulkan, .opengl => switch (cc) {
.spirv_vertex => .Vertex, .spirv_vertex => .vertex,
.spirv_fragment => .Fragment, .spirv_fragment => .fragment,
.spirv_kernel => .GLCompute, .spirv_kernel => .gl_compute,
// TODO: We should integrate with the Linkage capability and export this function // TODO: We should integrate with the Linkage capability and export this function
.spirv_device => return, .spirv_device => return,
else => unreachable, else => unreachable,
}, },
.opencl => switch (cc) { .opencl => switch (cc) {
.spirv_kernel => .Kernel, .spirv_kernel => .kernel,
// TODO: We should integrate with the Linkage capability and export this function // TODO: We should integrate with the Linkage capability and export this function
.spirv_device => return, .spirv_device => return,
else => unreachable, else => unreachable,

View file

@ -7,7 +7,7 @@ const spec = @import("../../codegen/spirv/spec.zig");
const Opcode = spec.Opcode; const Opcode = spec.Opcode;
const Word = spec.Word; const Word = spec.Word;
const InstructionSet = spec.InstructionSet; const InstructionSet = spec.InstructionSet;
const ResultId = spec.IdResult; const ResultId = spec.Id;
const BinaryModule = @This(); const BinaryModule = @This();
@ -254,8 +254,8 @@ pub const Parser = struct {
// with ALL operations that return an int or float. // with ALL operations that return an int or float.
const spec_operands = inst_spec.operands; const spec_operands = inst_spec.operands;
if (spec_operands.len >= 2 and if (spec_operands.len >= 2 and
spec_operands[0].kind == .IdResultType and spec_operands[0].kind == .id_result_type and
spec_operands[1].kind == .IdResult) spec_operands[1].kind == .id_result)
{ {
if (operands.len < 2) return error.InvalidOperands; if (operands.len < 2) return error.InvalidOperands;
if (binary.arith_type_width.get(@enumFromInt(operands[0]))) |width| { if (binary.arith_type_width.get(@enumFromInt(operands[0]))) |width| {
@ -288,8 +288,8 @@ pub const Parser = struct {
var offset: usize = 0; var offset: usize = 0;
switch (inst.opcode) { switch (inst.opcode) {
.OpSpecConstantOp => { .OpSpecConstantOp => {
assert(operands[0].kind == .IdResultType); assert(operands[0].kind == .id_result_type);
assert(operands[1].kind == .IdResult); assert(operands[1].kind == .id_result);
offset = try self.parseOperandsResultIds(binary, inst, operands[0..2], offset, offsets); offset = try self.parseOperandsResultIds(binary, inst, operands[0..2], offset, offsets);
if (offset >= inst.operands.len) return error.InvalidPhysicalFormat; if (offset >= inst.operands.len) return error.InvalidPhysicalFormat;
@ -297,13 +297,13 @@ pub const Parser = struct {
const spec_index = self.opcode_table.get(mapSetAndOpcode(.core, spec_opcode)) orelse const spec_index = self.opcode_table.get(mapSetAndOpcode(.core, spec_opcode)) orelse
return error.InvalidPhysicalFormat; return error.InvalidPhysicalFormat;
const spec_operands = InstructionSet.core.instructions()[spec_index].operands; const spec_operands = InstructionSet.core.instructions()[spec_index].operands;
assert(spec_operands[0].kind == .IdResultType); assert(spec_operands[0].kind == .id_result_type);
assert(spec_operands[1].kind == .IdResult); assert(spec_operands[1].kind == .id_result);
offset = try self.parseOperandsResultIds(binary, inst, spec_operands[2..], offset + 1, offsets); offset = try self.parseOperandsResultIds(binary, inst, spec_operands[2..], offset + 1, offsets);
}, },
.OpExtInst => { .OpExtInst => {
assert(operands[0].kind == .IdResultType); assert(operands[0].kind == .id_result_type);
assert(operands[1].kind == .IdResult); assert(operands[1].kind == .id_result);
offset = try self.parseOperandsResultIds(binary, inst, operands[0..2], offset, offsets); offset = try self.parseOperandsResultIds(binary, inst, operands[0..2], offset, offsets);
if (offset + 1 >= inst.operands.len) return error.InvalidPhysicalFormat; if (offset + 1 >= inst.operands.len) return error.InvalidPhysicalFormat;
@ -405,8 +405,8 @@ pub const Parser = struct {
offset += 1; offset += 1;
}, },
else => switch (kind) { else => switch (kind) {
.LiteralInteger, .LiteralFloat => offset += 1, .literal_integer, .literal_float => offset += 1,
.LiteralString => while (true) { .literal_string => while (true) {
if (offset >= inst.operands.len) return error.InvalidPhysicalFormat; if (offset >= inst.operands.len) return error.InvalidPhysicalFormat;
const word = inst.operands[offset]; const word = inst.operands[offset];
offset += 1; offset += 1;
@ -419,7 +419,7 @@ pub const Parser = struct {
break; break;
} }
}, },
.LiteralContextDependentNumber => { .literal_context_dependent_number => {
assert(inst.opcode == .OpConstant or inst.opcode == .OpSpecConstantOp); assert(inst.opcode == .OpConstant or inst.opcode == .OpSpecConstantOp);
const bit_width = binary.arith_type_width.get(@enumFromInt(inst.operands[0])) orelse { const bit_width = binary.arith_type_width.get(@enumFromInt(inst.operands[0])) orelse {
log.err("invalid LiteralContextDependentNumber type {}", .{inst.operands[0]}); log.err("invalid LiteralContextDependentNumber type {}", .{inst.operands[0]});
@ -431,9 +431,9 @@ pub const Parser = struct {
else => unreachable, else => unreachable,
}; };
}, },
.LiteralExtInstInteger => unreachable, .literal_ext_inst_integer => unreachable,
.LiteralSpecConstantOpInteger => unreachable, .literal_spec_constant_op_integer => unreachable,
.PairLiteralIntegerIdRef => { // Switch case .pair_literal_integer_id_ref => { // Switch case
assert(inst.opcode == .OpSwitch); assert(inst.opcode == .OpSwitch);
const bit_width = binary.arith_type_width.get(@enumFromInt(inst.operands[0])) orelse { const bit_width = binary.arith_type_width.get(@enumFromInt(inst.operands[0])) orelse {
log.err("invalid OpSwitch type {}", .{inst.operands[0]}); log.err("invalid OpSwitch type {}", .{inst.operands[0]});
@ -447,11 +447,11 @@ pub const Parser = struct {
try offsets.append(@intCast(offset)); try offsets.append(@intCast(offset));
offset += 1; offset += 1;
}, },
.PairIdRefLiteralInteger => { .pair_id_ref_literal_integer => {
try offsets.append(@intCast(offset)); try offsets.append(@intCast(offset));
offset += 2; offset += 2;
}, },
.PairIdRefIdRef => { .pair_id_ref_id_ref => {
try offsets.append(@intCast(offset)); try offsets.append(@intCast(offset));
try offsets.append(@intCast(offset + 1)); try offsets.append(@intCast(offset + 1));
offset += 2; offset += 2;

View file

@ -7,7 +7,7 @@ const BinaryModule = @import("BinaryModule.zig");
const Section = @import("../../codegen/spirv/Section.zig"); const Section = @import("../../codegen/spirv/Section.zig");
const spec = @import("../../codegen/spirv/spec.zig"); const spec = @import("../../codegen/spirv/spec.zig");
const Opcode = spec.Opcode; const Opcode = spec.Opcode;
const ResultId = spec.IdResult; const ResultId = spec.Id;
const Word = spec.Word; const Word = spec.Word;
fn canDeduplicate(opcode: Opcode) bool { fn canDeduplicate(opcode: Opcode) bool {
@ -20,9 +20,9 @@ fn canDeduplicate(opcode: Opcode) bool {
// Debug decoration-style instructions // Debug decoration-style instructions
.OpName, .OpMemberName => true, .OpName, .OpMemberName => true,
else => switch (opcode.class()) { else => switch (opcode.class()) {
.TypeDeclaration, .type_declaration,
.ConstantCreation, .constant_creation,
.Annotation, .annotation,
=> true, => true,
else => false, else => false,
}, },
@ -86,8 +86,8 @@ const ModuleInfo = struct {
if (!canDeduplicate(inst.opcode)) continue; if (!canDeduplicate(inst.opcode)) continue;
const result_id_index: u16 = switch (inst.opcode.class()) { const result_id_index: u16 = switch (inst.opcode.class()) {
.TypeDeclaration, .Annotation, .Debug => 0, .type_declaration, .annotation, .debug => 0,
.ConstantCreation => 1, .constant_creation => 1,
else => unreachable, else => unreachable,
}; };
@ -101,13 +101,13 @@ const ModuleInfo = struct {
}; };
switch (inst.opcode.class()) { switch (inst.opcode.class()) {
.Annotation, .Debug => { .annotation, .debug => {
try decorations.append(arena, .{ try decorations.append(arena, .{
.target_id = result_id, .target_id = result_id,
.entity = entity, .entity = entity,
}); });
}, },
.TypeDeclaration, .ConstantCreation => { .type_declaration, .constant_creation => {
const entry = try entities.getOrPut(result_id); const entry = try entities.getOrPut(result_id);
if (entry.found_existing) { if (entry.found_existing) {
log.err("type or constant {f} has duplicate definition", .{result_id}); log.err("type or constant {f} has duplicate definition", .{result_id});
@ -469,7 +469,7 @@ pub fn run(parser: *BinaryModule.Parser, binary: *BinaryModule, progress: std.Pr
const inst_spec = parser.getInstSpec(inst.opcode).?; const inst_spec = parser.getInstSpec(inst.opcode).?;
const maybe_result_id_offset: ?u16 = for (0..2) |i| { const maybe_result_id_offset: ?u16 = for (0..2) |i| {
if (inst_spec.operands.len > i and inst_spec.operands[i].kind == .IdResult) { if (inst_spec.operands.len > i and inst_spec.operands[i].kind == .id_result) {
break @intCast(i); break @intCast(i);
} }
} else null; } else null;
@ -488,7 +488,7 @@ pub fn run(parser: *BinaryModule.Parser, binary: *BinaryModule, progress: std.Pr
} }
switch (inst.opcode.class()) { switch (inst.opcode.class()) {
.Annotation, .Debug => { .annotation, .debug => {
// For decoration-style instructions, only emit them // For decoration-style instructions, only emit them
// if the target is not removed. // if the target is not removed.
const target: ResultId = @enumFromInt(inst.operands[0]); const target: ResultId = @enumFromInt(inst.operands[0]);
@ -515,7 +515,7 @@ pub fn run(parser: *BinaryModule.Parser, binary: *BinaryModule, progress: std.Pr
// Debug and Annotation instructions don't need the forward pointer, and it // Debug and Annotation instructions don't need the forward pointer, and it
// messes up the logical layout of the module. // messes up the logical layout of the module.
switch (inst.opcode.class()) { switch (inst.opcode.class()) {
.TypeDeclaration, .ConstantCreation, .Memory => {}, .type_declaration, .constant_creation, .memory => {},
else => continue, else => continue,
} }

View file

@ -6,7 +6,7 @@ const log = std.log.scoped(.spirv_link);
const BinaryModule = @import("BinaryModule.zig"); const BinaryModule = @import("BinaryModule.zig");
const Section = @import("../../codegen/spirv/Section.zig"); const Section = @import("../../codegen/spirv/Section.zig");
const spec = @import("../../codegen/spirv/spec.zig"); const spec = @import("../../codegen/spirv/spec.zig");
const ResultId = spec.IdResult; const ResultId = spec.Id;
const Word = spec.Word; const Word = spec.Word;
/// This structure contains all the stuff that we need to parse from the module in /// This structure contains all the stuff that we need to parse from the module in
@ -626,7 +626,7 @@ const ModuleBuilder = struct {
try self.section.emit(self.arena, .OpVariable, .{ try self.section.emit(self.arena, .OpVariable, .{
.id_result_type = global_info.ty, .id_result_type = global_info.ty,
.id_result = id, .id_result = id,
.storage_class = .Function, .storage_class = .function,
.initializer = null, .initializer = null,
}); });
} }

View file

@ -15,14 +15,14 @@ const BinaryModule = @import("BinaryModule.zig");
const Section = @import("../../codegen/spirv/Section.zig"); const Section = @import("../../codegen/spirv/Section.zig");
const spec = @import("../../codegen/spirv/spec.zig"); const spec = @import("../../codegen/spirv/spec.zig");
const Opcode = spec.Opcode; const Opcode = spec.Opcode;
const ResultId = spec.IdResult; const ResultId = spec.Id;
const Word = spec.Word; const Word = spec.Word;
/// Return whether a particular opcode's instruction can be pruned. /// Return whether a particular opcode's instruction can be pruned.
/// These are idempotent instructions at globals scope and instructions /// These are idempotent instructions at globals scope and instructions
/// within functions that do not have any side effects. /// within functions that do not have any side effects.
/// The opcodes that return true here do not necessarily need to /// The opcodes that return true here do not necessarily need to
/// have an .IdResult. If they don't, then they are regarded /// have an .Id. If they don't, then they are regarded
/// as 'decoration'-style instructions that don't keep their /// as 'decoration'-style instructions that don't keep their
/// operands alive, but will be emitted if they are. /// operands alive, but will be emitted if they are.
fn canPrune(op: Opcode) bool { fn canPrune(op: Opcode) bool {
@ -34,12 +34,12 @@ fn canPrune(op: Opcode) bool {
// instruction has any non-trivial side effects (like OpLoad // instruction has any non-trivial side effects (like OpLoad
// with the Volatile memory semantics). // with the Volatile memory semantics).
return switch (op.class()) { return switch (op.class()) {
.TypeDeclaration, .type_declaration,
.Conversion, .conversion,
.Arithmetic, .arithmetic,
.RelationalAndLogical, .relational_and_logical,
.Bit, .bit,
.Annotation, .annotation,
=> true, => true,
else => switch (op) { else => switch (op) {
.OpFunction, .OpFunction,
@ -111,7 +111,7 @@ const ModuleInfo = struct {
// Result-id can only be the first or second operand // Result-id can only be the first or second operand
const maybe_result_id: ?ResultId = for (0..2) |i| { const maybe_result_id: ?ResultId = for (0..2) |i| {
if (inst_spec.operands.len > i and inst_spec.operands[i].kind == .IdResult) { if (inst_spec.operands.len > i and inst_spec.operands[i].kind == .id_result) {
break @enumFromInt(inst.operands[i]); break @enumFromInt(inst.operands[i]);
} }
} else null; } else null;
@ -305,7 +305,7 @@ pub fn run(parser: *BinaryModule.Parser, binary: *BinaryModule, progress: std.Pr
// Result-id can only be the first or second operand // Result-id can only be the first or second operand
const result_id: ResultId = for (0..2) |i| { const result_id: ResultId = for (0..2) |i| {
if (inst_spec.operands.len > i and inst_spec.operands[i].kind == .IdResult) { if (inst_spec.operands.len > i and inst_spec.operands[i].kind == .id_result) {
break @enumFromInt(inst.operands[i]); break @enumFromInt(inst.operands[i]);
} }
} else { } else {

View file

@ -59,26 +59,28 @@ const set_names = std.StaticStringMap([]const u8).initComptime(.{
.{ "nonsemantic.debugprintf", "NonSemantic.DebugPrintf" }, .{ "nonsemantic.debugprintf", "NonSemantic.DebugPrintf" },
.{ "spv-amd-shader-explicit-vertex-parameter", "SPV_AMD_shader_explicit_vertex_parameter" }, .{ "spv-amd-shader-explicit-vertex-parameter", "SPV_AMD_shader_explicit_vertex_parameter" },
.{ "nonsemantic.debugbreak", "NonSemantic.DebugBreak" }, .{ "nonsemantic.debugbreak", "NonSemantic.DebugBreak" },
.{ "tosa.001000.1", "SPV_EXT_INST_TYPE_TOSA_001000_1" },
.{ "zig", "zig" }, .{ "zig", "zig" },
}); });
pub fn main() !void { var arena = std.heap.ArenaAllocator.init(std.heap.smp_allocator);
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); const allocator = arena.allocator();
defer arena.deinit();
const a = arena.allocator();
const args = try std.process.argsAlloc(a); pub fn main() !void {
defer arena.deinit();
const args = try std.process.argsAlloc(allocator);
if (args.len != 3) { if (args.len != 3) {
usageAndExit(args[0], 1); usageAndExit(args[0], 1);
} }
const json_path = try std.fs.path.join(a, &.{ args[1], "include/spirv/unified1/" }); const json_path = try std.fs.path.join(allocator, &.{ args[1], "include/spirv/unified1/" });
const dir = try std.fs.cwd().openDir(json_path, .{ .iterate = true }); const dir = try std.fs.cwd().openDir(json_path, .{ .iterate = true });
const core_spec = try readRegistry(CoreRegistry, a, dir, "spirv.core.grammar.json"); const core_spec = try readRegistry(CoreRegistry, dir, "spirv.core.grammar.json");
std.sort.block(Instruction, core_spec.instructions, CmpInst{}, CmpInst.lt); std.sort.block(Instruction, core_spec.instructions, CmpInst{}, CmpInst.lt);
var exts = std.ArrayList(Extension).init(a); var exts = std.ArrayList(Extension).init(allocator);
var it = dir.iterate(); var it = dir.iterate();
while (try it.next()) |entry| { while (try it.next()) |entry| {
@ -86,18 +88,43 @@ pub fn main() !void {
continue; continue;
} }
try readExtRegistry(&exts, a, dir, entry.name); try readExtRegistry(&exts, dir, entry.name);
} }
try readExtRegistry(&exts, a, std.fs.cwd(), args[2]); try readExtRegistry(&exts, std.fs.cwd(), args[2]);
var buffer: [4000]u8 = undefined; const output_buf = try allocator.alloc(u8, 1024 * 1024);
var w = std.fs.File.stdout().writerStreaming(&buffer); var fbs = std.io.fixedBufferStream(output_buf);
try render(&w, a, core_spec, exts.items); var adapter = fbs.writer().adaptToNewApi();
try w.flush(); const w = &adapter.new_interface;
try render(w, core_spec, exts.items);
var output: [:0]u8 = @ptrCast(fbs.getWritten());
output[output.len] = 0;
var tree = try std.zig.Ast.parse(allocator, output, .zig);
var color: std.zig.Color = .on;
if (tree.errors.len != 0) {
try std.zig.printAstErrorsToStderr(allocator, tree, "", color);
return;
} }
fn readExtRegistry(exts: *std.ArrayList(Extension), a: Allocator, dir: std.fs.Dir, sub_path: []const u8) !void { var zir = try std.zig.AstGen.generate(allocator, tree);
if (zir.hasCompileErrors()) {
var wip_errors: std.zig.ErrorBundle.Wip = undefined;
try wip_errors.init(allocator);
defer wip_errors.deinit();
try wip_errors.addZirErrorMessages(zir, tree, output, "");
var error_bundle = try wip_errors.toOwnedBundle("");
defer error_bundle.deinit(allocator);
error_bundle.renderToStdErr(color.renderOptions());
}
const formatted_output = try tree.render(allocator);
_ = try std.fs.File.stdout().write(formatted_output);
}
fn readExtRegistry(exts: *std.ArrayList(Extension), dir: std.fs.Dir, sub_path: []const u8) !void {
const filename = std.fs.path.basename(sub_path); const filename = std.fs.path.basename(sub_path);
if (!std.mem.startsWith(u8, filename, "extinst.")) { if (!std.mem.startsWith(u8, filename, "extinst.")) {
return; return;
@ -105,22 +132,22 @@ fn readExtRegistry(exts: *std.ArrayList(Extension), a: Allocator, dir: std.fs.Di
std.debug.assert(std.mem.endsWith(u8, filename, ".grammar.json")); std.debug.assert(std.mem.endsWith(u8, filename, ".grammar.json"));
const name = filename["extinst.".len .. filename.len - ".grammar.json".len]; const name = filename["extinst.".len .. filename.len - ".grammar.json".len];
const spec = try readRegistry(ExtensionRegistry, a, dir, sub_path); const spec = try readRegistry(ExtensionRegistry, dir, sub_path);
std.sort.block(Instruction, spec.instructions, CmpInst{}, CmpInst.lt); std.sort.block(Instruction, spec.instructions, CmpInst{}, CmpInst.lt);
try exts.append(.{ .name = set_names.get(name).?, .spec = spec }); try exts.append(.{ .name = set_names.get(name).?, .spec = spec });
} }
fn readRegistry(comptime RegistryType: type, a: Allocator, dir: std.fs.Dir, path: []const u8) !RegistryType { fn readRegistry(comptime RegistryType: type, dir: std.fs.Dir, path: []const u8) !RegistryType {
const spec = try dir.readFileAlloc(a, path, std.math.maxInt(usize)); const spec = try dir.readFileAlloc(allocator, path, std.math.maxInt(usize));
// Required for json parsing. // Required for json parsing.
@setEvalBranchQuota(10000); @setEvalBranchQuota(10000);
var scanner = std.json.Scanner.initCompleteInput(a, spec); var scanner = std.json.Scanner.initCompleteInput(allocator, spec);
var diagnostics = std.json.Diagnostics{}; var diagnostics = std.json.Diagnostics{};
scanner.enableDiagnostics(&diagnostics); scanner.enableDiagnostics(&diagnostics);
const parsed = std.json.parseFromTokenSource(RegistryType, a, &scanner, .{}) catch |err| { const parsed = std.json.parseFromTokenSource(RegistryType, allocator, &scanner, .{}) catch |err| {
std.debug.print("{s}:{}:{}:\n", .{ path, diagnostics.getLine(), diagnostics.getColumn() }); std.debug.print("{s}:{}:{}:\n", .{ path, diagnostics.getLine(), diagnostics.getColumn() });
return err; return err;
}; };
@ -129,11 +156,8 @@ fn readRegistry(comptime RegistryType: type, a: Allocator, dir: std.fs.Dir, path
/// Returns a set with types that require an extra struct for the `Instruction` interface /// Returns a set with types that require an extra struct for the `Instruction` interface
/// to the spir-v spec, or whether the original type can be used. /// to the spir-v spec, or whether the original type can be used.
fn extendedStructs( fn extendedStructs(kinds: []const OperandKind) !ExtendedStructSet {
a: Allocator, var map = ExtendedStructSet.init(allocator);
kinds: []const OperandKind,
) !ExtendedStructSet {
var map = ExtendedStructSet.init(a);
try map.ensureTotalCapacity(@as(u32, @intCast(kinds.len))); try map.ensureTotalCapacity(@as(u32, @intCast(kinds.len)));
for (kinds) |kind| { for (kinds) |kind| {
@ -167,7 +191,7 @@ fn tagPriorityScore(tag: []const u8) usize {
} }
} }
fn render(writer: *std.io.Writer, a: Allocator, registry: CoreRegistry, extensions: []const Extension) !void { fn render(writer: *std.io.Writer, registry: CoreRegistry, extensions: []const Extension) !void {
try writer.writeAll( try writer.writeAll(
\\//! This file is auto-generated by tools/gen_spirv_spec.zig. \\//! This file is auto-generated by tools/gen_spirv_spec.zig.
\\ \\
@ -185,22 +209,17 @@ fn render(writer: *std.io.Writer, a: Allocator, registry: CoreRegistry, extensio
\\}; \\};
\\ \\
\\pub const Word = u32; \\pub const Word = u32;
\\pub const IdResult = enum(Word) { \\pub const Id = enum(Word) {
\\ none, \\ none,
\\ _, \\ _,
\\ \\
\\ pub fn format(self: IdResult, writer: *std.io.Writer) std.io.Writer.Error!void { \\ pub fn format(self: Id, writer: *std.io.Writer) std.io.Writer.Error!void {
\\ switch (self) { \\ switch (self) {
\\ .none => try writer.writeAll("(none)"), \\ .none => try writer.writeAll("(none)"),
\\ else => try writer.print("%{d}", .{@intFromEnum(self)}), \\ else => try writer.print("%{d}", .{@intFromEnum(self)}),
\\ } \\ }
\\ } \\ }
\\}; \\};
\\pub const IdResultType = IdResult;
\\pub const IdRef = IdResult;
\\
\\pub const IdMemorySemantics = IdRef;
\\pub const IdScope = IdRef;
\\ \\
\\pub const LiteralInteger = Word; \\pub const LiteralInteger = Word;
\\pub const LiteralFloat = Word; \\pub const LiteralFloat = Word;
@ -215,9 +234,9 @@ fn render(writer: *std.io.Writer, a: Allocator, registry: CoreRegistry, extensio
\\}; \\};
\\pub const LiteralExtInstInteger = struct{ inst: Word }; \\pub const LiteralExtInstInteger = struct{ inst: Word };
\\pub const LiteralSpecConstantOpInteger = struct { opcode: Opcode }; \\pub const LiteralSpecConstantOpInteger = struct { opcode: Opcode };
\\pub const PairLiteralIntegerIdRef = struct { value: LiteralInteger, label: IdRef }; \\pub const PairLiteralIntegerIdRef = struct { value: LiteralInteger, label: Id };
\\pub const PairIdRefLiteralInteger = struct { target: IdRef, member: LiteralInteger }; \\pub const PairIdRefLiteralInteger = struct { target: Id, member: LiteralInteger };
\\pub const PairIdRefIdRef = [2]IdRef; \\pub const PairIdRefIdRef = [2]Id;
\\ \\
\\pub const Quantifier = enum { \\pub const Quantifier = enum {
\\ required, \\ required,
@ -255,7 +274,7 @@ fn render(writer: *std.io.Writer, a: Allocator, registry: CoreRegistry, extensio
); );
try writer.print( try writer.print(
\\pub const version = Version{{ .major = {}, .minor = {}, .patch = {} }}; \\pub const version: Version = .{{ .major = {}, .minor = {}, .patch = {} }};
\\pub const magic_number: Word = {s}; \\pub const magic_number: Word = {s};
\\ \\
\\ \\
@ -266,7 +285,7 @@ fn render(writer: *std.io.Writer, a: Allocator, registry: CoreRegistry, extensio
// Merge the operand kinds from all extensions together. // Merge the operand kinds from all extensions together.
// var all_operand_kinds = std.ArrayList(OperandKind).init(a); // var all_operand_kinds = std.ArrayList(OperandKind).init(a);
// try all_operand_kinds.appendSlice(registry.operand_kinds); // try all_operand_kinds.appendSlice(registry.operand_kinds);
var all_operand_kinds = OperandKindMap.init(a); var all_operand_kinds = OperandKindMap.init(allocator);
for (registry.operand_kinds) |kind| { for (registry.operand_kinds) |kind| {
try all_operand_kinds.putNoClobber(.{ "core", kind.kind }, kind); try all_operand_kinds.putNoClobber(.{ "core", kind.kind }, kind);
} }
@ -279,35 +298,33 @@ fn render(writer: *std.io.Writer, a: Allocator, registry: CoreRegistry, extensio
try all_operand_kinds.ensureUnusedCapacity(ext.spec.operand_kinds.len); try all_operand_kinds.ensureUnusedCapacity(ext.spec.operand_kinds.len);
for (ext.spec.operand_kinds) |kind| { for (ext.spec.operand_kinds) |kind| {
var new_kind = kind; var new_kind = kind;
new_kind.kind = try std.mem.join(a, ".", &.{ ext.name, kind.kind }); new_kind.kind = try std.mem.join(allocator, ".", &.{ ext.name, kind.kind });
try all_operand_kinds.putNoClobber(.{ ext.name, kind.kind }, new_kind); try all_operand_kinds.putNoClobber(.{ ext.name, kind.kind }, new_kind);
} }
} }
const extended_structs = try extendedStructs(a, all_operand_kinds.values()); const extended_structs = try extendedStructs(all_operand_kinds.values());
// Note: extensions don't seem to have class. // Note: extensions don't seem to have class.
try renderClass(writer, a, registry.instructions); try renderClass(writer, registry.instructions);
try renderOperandKind(writer, all_operand_kinds.values()); try renderOperandKind(writer, all_operand_kinds.values());
try renderOpcodes(writer, a, registry.instructions, extended_structs); try renderOpcodes(writer, registry.instructions, extended_structs);
try renderOperandKinds(writer, a, all_operand_kinds.values(), extended_structs); try renderOperandKinds(writer, all_operand_kinds.values(), extended_structs);
try renderInstructionSet(writer, a, registry, extensions, all_operand_kinds); try renderInstructionSet(writer, registry, extensions, all_operand_kinds);
} }
fn renderInstructionSet( fn renderInstructionSet(
writer: anytype, writer: anytype,
a: Allocator,
core: CoreRegistry, core: CoreRegistry,
extensions: []const Extension, extensions: []const Extension,
all_operand_kinds: OperandKindMap, all_operand_kinds: OperandKindMap,
) !void { ) !void {
_ = a;
try writer.writeAll( try writer.writeAll(
\\pub const InstructionSet = enum { \\pub const InstructionSet = enum {
\\ core, \\ core,
); );
for (extensions) |ext| { for (extensions) |ext| {
try writer.print("{p},\n", .{std.zig.fmtId(ext.name)}); try writer.print("{f},\n", .{formatId(ext.name)});
} }
try writer.writeAll( try writer.writeAll(
@ -340,14 +357,14 @@ fn renderInstructionsCase(
// but there aren't so many total aliases and that would add more overhead in total. We will // but there aren't so many total aliases and that would add more overhead in total. We will
// just filter those out when needed. // just filter those out when needed.
try writer.print(".{p_} => &[_]Instruction{{\n", .{std.zig.fmtId(set_name)}); try writer.print(".{f} => &.{{\n", .{formatId(set_name)});
for (instructions) |inst| { for (instructions) |inst| {
try writer.print( try writer.print(
\\.{{ \\.{{
\\ .name = "{s}", \\ .name = "{s}",
\\ .opcode = {}, \\ .opcode = {},
\\ .operands = &[_]Operand{{ \\ .operands = &.{{
\\ \\
, .{ inst.opname, inst.opcode }); , .{ inst.opname, inst.opcode });
@ -362,7 +379,7 @@ fn renderInstructionsCase(
const kind = all_operand_kinds.get(.{ set_name, operand.kind }) orelse const kind = all_operand_kinds.get(.{ set_name, operand.kind }) orelse
all_operand_kinds.get(.{ "core", operand.kind }).?; all_operand_kinds.get(.{ "core", operand.kind }).?;
try writer.print(".{{.kind = .{p_}, .quantifier = .{s}}},\n", .{ std.zig.fmtId(kind.kind), quantifier }); try writer.print(".{{.kind = .{f}, .quantifier = .{s}}},\n", .{ formatId(kind.kind), quantifier });
} }
try writer.writeAll( try writer.writeAll(
@ -378,54 +395,69 @@ fn renderInstructionsCase(
); );
} }
fn renderClass(writer: anytype, a: Allocator, instructions: []const Instruction) !void { fn renderClass(writer: anytype, instructions: []const Instruction) !void {
var class_map = std.StringArrayHashMap(void).init(a); var class_map = std.StringArrayHashMap(void).init(allocator);
for (instructions) |inst| { for (instructions) |inst| {
if (std.mem.eql(u8, inst.class.?, "@exclude")) { if (std.mem.eql(u8, inst.class.?, "@exclude")) continue;
continue;
}
try class_map.put(inst.class.?, {}); try class_map.put(inst.class.?, {});
} }
try writer.writeAll("pub const Class = enum {\n"); try writer.writeAll("pub const Class = enum {\n");
for (class_map.keys()) |class| { for (class_map.keys()) |class| {
try renderInstructionClass(writer, class); try writer.print("{f},\n", .{formatId(class)});
try writer.writeAll(",\n");
} }
try writer.writeAll("};\n\n"); try writer.writeAll("};\n\n");
} }
fn renderInstructionClass(writer: anytype, class: []const u8) !void { const Formatter = struct {
// Just assume that these wont clobber zig builtin types. data: []const u8,
var prev_was_sep = true;
for (class) |c| { fn format(f: Formatter, writer: *std.io.Writer) std.io.Writer.Error!void {
var id_buf: [128]u8 = undefined;
var fbs = std.io.fixedBufferStream(&id_buf);
const fw = fbs.writer();
for (f.data, 0..) |c, i| {
switch (c) { switch (c) {
'-', '_' => prev_was_sep = true, '-', '_', '.', '~', ' ' => fw.writeByte('_') catch return error.WriteFailed,
else => if (prev_was_sep) { 'a'...'z', '0'...'9' => fw.writeByte(c) catch return error.WriteFailed,
try writer.writeByte(std.ascii.toUpper(c)); 'A'...'Z' => {
prev_was_sep = false; if ((i > 0 and std.ascii.isLower(f.data[i - 1])) or
(i > 0 and std.ascii.isUpper(f.data[i - 1]) and
i + 1 < f.data.len and std.ascii.isLower(f.data[i + 1])))
{
_ = fw.write(&.{ '_', std.ascii.toLower(c) }) catch return error.WriteFailed;
} else { } else {
try writer.writeByte(std.ascii.toLower(c)); fw.writeByte(std.ascii.toLower(c)) catch return error.WriteFailed;
}
}, },
else => unreachable,
} }
} }
// make sure that this won't clobber with zig keywords
try writer.print("{f}", .{std.zig.fmtId(fbs.getWritten())});
}
};
fn formatId(identifier: []const u8) std.fmt.Alt(Formatter, Formatter.format) {
return .{ .data = .{ .data = identifier } };
} }
fn renderOperandKind(writer: anytype, operands: []const OperandKind) !void { fn renderOperandKind(writer: anytype, operands: []const OperandKind) !void {
try writer.writeAll( try writer.writeAll(
\\pub const OperandKind = enum { \\pub const OperandKind = enum {
\\ Opcode, \\ opcode,
\\ \\
); );
for (operands) |operand| { for (operands) |operand| {
try writer.print("{p},\n", .{std.zig.fmtId(operand.kind)}); try writer.print("{f},\n", .{formatId(operand.kind)});
} }
try writer.writeAll( try writer.writeAll(
\\ \\
\\pub fn category(self: OperandKind) OperandCategory { \\pub fn category(self: OperandKind) OperandCategory {
\\ return switch (self) { \\ return switch (self) {
\\ .Opcode => .literal, \\ .opcode => .literal,
\\ \\
); );
for (operands) |operand| { for (operands) |operand| {
@ -436,26 +468,26 @@ fn renderOperandKind(writer: anytype, operands: []const OperandKind) !void {
.Literal => "literal", .Literal => "literal",
.Composite => "composite", .Composite => "composite",
}; };
try writer.print(".{p_} => .{s},\n", .{ std.zig.fmtId(operand.kind), cat }); try writer.print(".{f} => .{s},\n", .{ formatId(operand.kind), cat });
} }
try writer.writeAll( try writer.writeAll(
\\ }; \\ };
\\} \\}
\\pub fn enumerants(self: OperandKind) []const Enumerant { \\pub fn enumerants(self: OperandKind) []const Enumerant {
\\ return switch (self) { \\ return switch (self) {
\\ .Opcode => unreachable, \\ .opcode => unreachable,
\\ \\
); );
for (operands) |operand| { for (operands) |operand| {
switch (operand.category) { switch (operand.category) {
.BitEnum, .ValueEnum => {}, .BitEnum, .ValueEnum => {},
else => { else => {
try writer.print(".{p_} => unreachable,\n", .{std.zig.fmtId(operand.kind)}); try writer.print(".{f} => unreachable,\n", .{formatId(operand.kind)});
continue; continue;
}, },
} }
try writer.print(".{p_} => &[_]Enumerant{{", .{std.zig.fmtId(operand.kind)}); try writer.print(".{f} => &.{{", .{formatId(operand.kind)});
for (operand.enumerants.?) |enumerant| { for (operand.enumerants.?) |enumerant| {
if (enumerant.value == .bitflag and std.mem.eql(u8, enumerant.enumerant, "None")) { if (enumerant.value == .bitflag and std.mem.eql(u8, enumerant.enumerant, "None")) {
continue; continue;
@ -474,32 +506,30 @@ fn renderEnumerant(writer: anytype, enumerant: Enumerant) !void {
.bitflag => |flag| try writer.writeAll(flag), .bitflag => |flag| try writer.writeAll(flag),
.int => |int| try writer.print("{}", .{int}), .int => |int| try writer.print("{}", .{int}),
} }
try writer.writeAll(", .parameters = &[_]OperandKind{"); try writer.writeAll(", .parameters = &.{");
for (enumerant.parameters, 0..) |param, i| { for (enumerant.parameters, 0..) |param, i| {
if (i != 0) if (i != 0)
try writer.writeAll(", "); try writer.writeAll(", ");
// Note, param.quantifier will always be one. // Note, param.quantifier will always be one.
try writer.print(".{p_}", .{std.zig.fmtId(param.kind)}); try writer.print(".{f}", .{formatId(param.kind)});
} }
try writer.writeAll("}}"); try writer.writeAll("}}");
} }
fn renderOpcodes( fn renderOpcodes(
writer: anytype, writer: anytype,
a: Allocator,
instructions: []const Instruction, instructions: []const Instruction,
extended_structs: ExtendedStructSet, extended_structs: ExtendedStructSet,
) !void { ) !void {
var inst_map = std.AutoArrayHashMap(u32, usize).init(a); var inst_map = std.AutoArrayHashMap(u32, usize).init(allocator);
try inst_map.ensureTotalCapacity(instructions.len); try inst_map.ensureTotalCapacity(instructions.len);
var aliases = std.ArrayList(struct { inst: usize, alias: usize }).init(a); var aliases = std.ArrayList(struct { inst: usize, alias: usize }).init(allocator);
try aliases.ensureTotalCapacity(instructions.len); try aliases.ensureTotalCapacity(instructions.len);
for (instructions, 0..) |inst, i| { for (instructions, 0..) |inst, i| {
if (std.mem.eql(u8, inst.class.?, "@exclude")) { if (std.mem.eql(u8, inst.class.?, "@exclude")) continue;
continue;
}
const result = inst_map.getOrPutAssumeCapacity(inst.opcode); const result = inst_map.getOrPutAssumeCapacity(inst.opcode);
if (!result.found_existing) { if (!result.found_existing) {
result.value_ptr.* = i; result.value_ptr.* = i;
@ -525,7 +555,7 @@ fn renderOpcodes(
try writer.writeAll("pub const Opcode = enum(u16) {\n"); try writer.writeAll("pub const Opcode = enum(u16) {\n");
for (instructions_indices) |i| { for (instructions_indices) |i| {
const inst = instructions[i]; const inst = instructions[i];
try writer.print("{p} = {},\n", .{ std.zig.fmtId(inst.opname), inst.opcode }); try writer.print("{f} = {},\n", .{ std.zig.fmtId(inst.opname), inst.opcode });
} }
try writer.writeAll( try writer.writeAll(
@ -533,9 +563,9 @@ fn renderOpcodes(
); );
for (aliases.items) |alias| { for (aliases.items) |alias| {
try writer.print("pub const {} = Opcode.{p_};\n", .{ try writer.print("pub const {f} = Opcode.{f};\n", .{
std.zig.fmtId(instructions[alias.inst].opname), formatId(instructions[alias.inst].opname),
std.zig.fmtId(instructions[alias.alias].opname), formatId(instructions[alias.alias].opname),
}); });
} }
@ -548,7 +578,7 @@ fn renderOpcodes(
for (instructions_indices) |i| { for (instructions_indices) |i| {
const inst = instructions[i]; const inst = instructions[i];
try renderOperand(writer, .instruction, inst.opname, inst.operands, extended_structs); try renderOperand(writer, .instruction, inst.opname, inst.operands, extended_structs, false);
} }
try writer.writeAll( try writer.writeAll(
@ -561,9 +591,7 @@ fn renderOpcodes(
for (instructions_indices) |i| { for (instructions_indices) |i| {
const inst = instructions[i]; const inst = instructions[i];
try writer.print(".{p_} => .", .{std.zig.fmtId(inst.opname)}); try writer.print(".{f} => .{f},\n", .{ std.zig.fmtId(inst.opname), formatId(inst.class.?) });
try renderInstructionClass(writer, inst.class.?);
try writer.writeAll(",\n");
} }
try writer.writeAll( try writer.writeAll(
@ -576,14 +604,13 @@ fn renderOpcodes(
fn renderOperandKinds( fn renderOperandKinds(
writer: anytype, writer: anytype,
a: Allocator,
kinds: []const OperandKind, kinds: []const OperandKind,
extended_structs: ExtendedStructSet, extended_structs: ExtendedStructSet,
) !void { ) !void {
for (kinds) |kind| { for (kinds) |kind| {
switch (kind.category) { switch (kind.category) {
.ValueEnum => try renderValueEnum(writer, a, kind, extended_structs), .ValueEnum => try renderValueEnum(writer, kind, extended_structs),
.BitEnum => try renderBitEnum(writer, a, kind, extended_structs), .BitEnum => try renderBitEnum(writer, kind, extended_structs),
else => {}, else => {},
} }
} }
@ -591,20 +618,18 @@ fn renderOperandKinds(
fn renderValueEnum( fn renderValueEnum(
writer: anytype, writer: anytype,
a: Allocator,
enumeration: OperandKind, enumeration: OperandKind,
extended_structs: ExtendedStructSet, extended_structs: ExtendedStructSet,
) !void { ) !void {
const enumerants = enumeration.enumerants orelse return error.InvalidRegistry; const enumerants = enumeration.enumerants orelse return error.InvalidRegistry;
var enum_map = std.AutoArrayHashMap(u32, usize).init(a); var enum_map = std.AutoArrayHashMap(u32, usize).init(allocator);
try enum_map.ensureTotalCapacity(enumerants.len); try enum_map.ensureTotalCapacity(enumerants.len);
var aliases = std.ArrayList(struct { enumerant: usize, alias: usize }).init(a); var aliases = std.ArrayList(struct { enumerant: usize, alias: usize }).init(allocator);
try aliases.ensureTotalCapacity(enumerants.len); try aliases.ensureTotalCapacity(enumerants.len);
for (enumerants, 0..) |enumerant, i| { for (enumerants, 0..) |enumerant, i| {
try writer.context.flush();
const value: u31 = switch (enumerant.value) { const value: u31 = switch (enumerant.value) {
.int => |value| value, .int => |value| value,
// Some extensions declare ints as string // Some extensions declare ints as string
@ -632,25 +657,25 @@ fn renderValueEnum(
const enum_indices = enum_map.values(); const enum_indices = enum_map.values();
try writer.print("pub const {} = enum(u32) {{\n", .{std.zig.fmtId(enumeration.kind)}); try writer.print("pub const {f} = enum(u32) {{\n", .{std.zig.fmtId(enumeration.kind)});
for (enum_indices) |i| { for (enum_indices) |i| {
const enumerant = enumerants[i]; const enumerant = enumerants[i];
// if (enumerant.value != .int) return error.InvalidRegistry; // if (enumerant.value != .int) return error.InvalidRegistry;
switch (enumerant.value) { switch (enumerant.value) {
.int => |value| try writer.print("{p} = {},\n", .{ std.zig.fmtId(enumerant.enumerant), value }), .int => |value| try writer.print("{f} = {},\n", .{ formatId(enumerant.enumerant), value }),
.bitflag => |value| try writer.print("{p} = {s},\n", .{ std.zig.fmtId(enumerant.enumerant), value }), .bitflag => |value| try writer.print("{f} = {s},\n", .{ formatId(enumerant.enumerant), value }),
} }
} }
try writer.writeByte('\n'); try writer.writeByte('\n');
for (aliases.items) |alias| { for (aliases.items) |alias| {
try writer.print("pub const {} = {}.{p_};\n", .{ try writer.print("pub const {f} = {f}.{f};\n", .{
std.zig.fmtId(enumerants[alias.enumerant].enumerant), formatId(enumerants[alias.enumerant].enumerant),
std.zig.fmtId(enumeration.kind), std.zig.fmtId(enumeration.kind),
std.zig.fmtId(enumerants[alias.alias].enumerant), formatId(enumerants[alias.alias].enumerant),
}); });
} }
@ -659,11 +684,11 @@ fn renderValueEnum(
return; return;
} }
try writer.print("\npub const Extended = union({}) {{\n", .{std.zig.fmtId(enumeration.kind)}); try writer.print("\npub const Extended = union({f}) {{\n", .{std.zig.fmtId(enumeration.kind)});
for (enum_indices) |i| { for (enum_indices) |i| {
const enumerant = enumerants[i]; const enumerant = enumerants[i];
try renderOperand(writer, .@"union", enumerant.enumerant, enumerant.parameters, extended_structs); try renderOperand(writer, .@"union", enumerant.enumerant, enumerant.parameters, extended_structs, true);
} }
try writer.writeAll("};\n};\n"); try writer.writeAll("};\n};\n");
@ -671,16 +696,15 @@ fn renderValueEnum(
fn renderBitEnum( fn renderBitEnum(
writer: anytype, writer: anytype,
a: Allocator,
enumeration: OperandKind, enumeration: OperandKind,
extended_structs: ExtendedStructSet, extended_structs: ExtendedStructSet,
) !void { ) !void {
try writer.print("pub const {} = packed struct {{\n", .{std.zig.fmtId(enumeration.kind)}); try writer.print("pub const {f} = packed struct {{\n", .{std.zig.fmtId(enumeration.kind)});
var flags_by_bitpos = [_]?usize{null} ** 32; var flags_by_bitpos = [_]?usize{null} ** 32;
const enumerants = enumeration.enumerants orelse return error.InvalidRegistry; const enumerants = enumeration.enumerants orelse return error.InvalidRegistry;
var aliases = std.ArrayList(struct { flag: usize, alias: u5 }).init(a); var aliases = std.ArrayList(struct { flag: usize, alias: u5 }).init(allocator);
try aliases.ensureTotalCapacity(enumerants.len); try aliases.ensureTotalCapacity(enumerants.len);
for (enumerants, 0..) |enumerant, i| { for (enumerants, 0..) |enumerant, i| {
@ -715,7 +739,7 @@ fn renderBitEnum(
for (flags_by_bitpos, 0..) |maybe_flag_index, bitpos| { for (flags_by_bitpos, 0..) |maybe_flag_index, bitpos| {
if (maybe_flag_index) |flag_index| { if (maybe_flag_index) |flag_index| {
try writer.print("{p_}", .{std.zig.fmtId(enumerants[flag_index].enumerant)}); try writer.print("{f}", .{formatId(enumerants[flag_index].enumerant)});
} else { } else {
try writer.print("_reserved_bit_{}", .{bitpos}); try writer.print("_reserved_bit_{}", .{bitpos});
} }
@ -726,10 +750,10 @@ fn renderBitEnum(
try writer.writeByte('\n'); try writer.writeByte('\n');
for (aliases.items) |alias| { for (aliases.items) |alias| {
try writer.print("pub const {}: {} = .{{.{p_} = true}};\n", .{ try writer.print("pub const {f}: {f} = .{{.{f} = true}};\n", .{
std.zig.fmtId(enumerants[alias.flag].enumerant), formatId(enumerants[alias.flag].enumerant),
std.zig.fmtId(enumeration.kind), std.zig.fmtId(enumeration.kind),
std.zig.fmtId(enumerants[flags_by_bitpos[alias.alias].?].enumerant), formatId(enumerants[flags_by_bitpos[alias.alias].?].enumerant),
}); });
} }
@ -747,7 +771,7 @@ fn renderBitEnum(
}; };
const enumerant = enumerants[flag_index]; const enumerant = enumerants[flag_index];
try renderOperand(writer, .mask, enumerant.enumerant, enumerant.parameters, extended_structs); try renderOperand(writer, .mask, enumerant.enumerant, enumerant.parameters, extended_structs, true);
} }
try writer.writeAll("};\n};\n"); try writer.writeAll("};\n};\n");
@ -763,11 +787,18 @@ fn renderOperand(
field_name: []const u8, field_name: []const u8,
parameters: []const Operand, parameters: []const Operand,
extended_structs: ExtendedStructSet, extended_structs: ExtendedStructSet,
snake_case: bool,
) !void { ) !void {
if (kind == .instruction) { if (kind == .instruction) {
try writer.writeByte('.'); try writer.writeByte('.');
} }
try writer.print("{}", .{std.zig.fmtId(field_name)});
if (snake_case) {
try writer.print("{f}", .{formatId(field_name)});
} else {
try writer.print("{f}", .{std.zig.fmtId(field_name)});
}
if (parameters.len == 0) { if (parameters.len == 0) {
switch (kind) { switch (kind) {
.@"union" => try writer.writeAll(",\n"), .@"union" => try writer.writeAll(",\n"),
@ -804,7 +835,11 @@ fn renderOperand(
} }
} }
try writer.print("{}", .{std.zig.fmtId(param.kind)}); if (std.mem.startsWith(u8, param.kind, "Id")) {
_ = try writer.write("Id");
} else {
try writer.print("{f}", .{std.zig.fmtId(param.kind)});
}
if (extended_structs.contains(param.kind)) { if (extended_structs.contains(param.kind)) {
try writer.writeAll(".Extended"); try writer.writeAll(".Extended");
@ -830,49 +865,24 @@ fn renderOperand(
fn renderFieldName(writer: anytype, operands: []const Operand, field_index: usize) !void { fn renderFieldName(writer: anytype, operands: []const Operand, field_index: usize) !void {
const operand = operands[field_index]; const operand = operands[field_index];
// Should be enough for all names - adjust as needed.
var name_backing_buffer: [64]u8 = undefined;
var name_buffer = std.ArrayListUnmanaged(u8).initBuffer(&name_backing_buffer);
derive_from_kind: { derive_from_kind: {
// Operand names are often in the json encoded as "'Name'" (with two sets of quotes). // Operand names are often in the json encoded as "'Name'" (with two sets of quotes).
// Additionally, some operands have ~ in them at the end (D~ref~). // Additionally, some operands have ~ in them at the end (D~ref~).
const name = std.mem.trim(u8, operand.name, "'~"); const name = std.mem.trim(u8, operand.name, "'~");
if (name.len == 0) { if (name.len == 0) break :derive_from_kind;
break :derive_from_kind;
}
// Some names have weird characters in them (like newlines) - skip any such ones.
// Use the same loop to transform to snake-case.
for (name) |c| { for (name) |c| {
switch (c) { switch (c) {
'a'...'z', '0'...'9' => name_buffer.appendAssumeCapacity(c), 'a'...'z', '0'...'9', 'A'...'Z', ' ', '~' => continue,
'A'...'Z' => name_buffer.appendAssumeCapacity(std.ascii.toLower(c)),
' ', '~' => name_buffer.appendAssumeCapacity('_'),
else => break :derive_from_kind, else => break :derive_from_kind,
} }
} }
// Assume there are no duplicate 'name' fields. try writer.print("{f}", .{formatId(name)});
try writer.print("{p_}", .{std.zig.fmtId(name_buffer.items)});
return; return;
} }
// Translate to snake case. try writer.print("{f}", .{formatId(operand.kind)});
name_buffer.items.len = 0;
for (operand.kind, 0..) |c, i| {
switch (c) {
'a'...'z', '0'...'9' => name_buffer.appendAssumeCapacity(c),
'A'...'Z' => if (i > 0 and std.ascii.isLower(operand.kind[i - 1])) {
name_buffer.appendSliceAssumeCapacity(&[_]u8{ '_', std.ascii.toLower(c) });
} else {
name_buffer.appendAssumeCapacity(std.ascii.toLower(c));
},
else => unreachable, // Assume that the name is valid C-syntax (and contains no underscores).
}
}
try writer.print("{p_}", .{std.zig.fmtId(name_buffer.items)});
// For fields derived from type name, there could be any amount. // For fields derived from type name, there could be any amount.
// Simply check against all other fields, and if another similar one exists, add a number. // Simply check against all other fields, and if another similar one exists, add a number.

View file

@ -37,9 +37,11 @@ pub const InstructionPrintingClass = struct {
pub const Instruction = struct { pub const Instruction = struct {
opname: []const u8, opname: []const u8,
class: ?[]const u8 = null, // Note: Only available in the core registry. class: ?[]const u8 = null, // Note: Only available in the core registry.
aliases: [][]const u8 = &[_][]const u8{},
opcode: u32, opcode: u32,
operands: []Operand = &[_]Operand{}, operands: []Operand = &[_]Operand{},
capabilities: [][]const u8 = &[_][]const u8{}, capabilities: [][]const u8 = &[_][]const u8{},
provisional: bool = false,
// DebugModuleINTEL has this... // DebugModuleINTEL has this...
capability: ?[]const u8 = null, capability: ?[]const u8 = null,
extensions: [][]const u8 = &[_][]const u8{}, extensions: [][]const u8 = &[_][]const u8{},
@ -81,6 +83,7 @@ pub const OperandKind = struct {
pub const Enumerant = struct { pub const Enumerant = struct {
enumerant: []const u8, enumerant: []const u8,
aliases: [][]const u8 = &[_][]const u8{},
value: union(enum) { value: union(enum) {
bitflag: []const u8, // Hexadecimal representation of the value bitflag: []const u8, // Hexadecimal representation of the value
int: u31, int: u31,
@ -100,6 +103,7 @@ pub const Enumerant = struct {
pub const jsonStringify = @compileError("not supported"); pub const jsonStringify = @compileError("not supported");
}, },
capabilities: [][]const u8 = &[_][]const u8{}, capabilities: [][]const u8 = &[_][]const u8{},
provisional: bool = false,
/// Valid for .ValueEnum and .BitEnum /// Valid for .ValueEnum and .BitEnum
extensions: [][]const u8 = &[_][]const u8{}, extensions: [][]const u8 = &[_][]const u8{},
/// `quantifier` will always be `null`. /// `quantifier` will always be `null`.