From c228d86e91348bfb73a07383ca32d182dbde9155 Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Thu, 1 Jan 2026 04:18:11 +0100 Subject: [PATCH] adding types opcodes --- src/Module.zig | 10 +- src/Result.zig | 99 +++++++++++++++---- src/WordIterator.zig | 18 +++- src/opcodes.zig | 231 +++++++++++++++++++++++++++++++++++++------ 4 files changed, 301 insertions(+), 57 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index ef0d307..2520fca 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -95,7 +95,7 @@ pub fn init(allocator: std.mem.Allocator, source: []const SpvWord) ModuleError!S self.it = WordIterator.init(self.code); - const magic = self.it.next() orelse return ModuleError.InvalidSpirV; + const magic = self.it.next() catch return ModuleError.InvalidSpirV; if (magic != spv.SpvMagicNumber) { return ModuleError.InvalidMagic; } @@ -103,15 +103,15 @@ pub fn init(allocator: std.mem.Allocator, source: []const SpvWord) ModuleError!S return ModuleError.UnsupportedEndianness; } - const version = self.it.next() orelse return ModuleError.InvalidSpirV; + const version = self.it.next() catch return ModuleError.InvalidSpirV; self.version_major = @intCast((version & 0x00FF0000) >> 16); self.version_minor = @intCast((version & 0x0000FF00) >> 8); - const generator = self.it.next() orelse return ModuleError.InvalidSpirV; + const generator = self.it.next() catch return ModuleError.InvalidSpirV; self.generator_id = @intCast((generator & 0xFFFF0000) >> 16); self.generator_version = @intCast(generator & 0x0000FFFF); - self.bound = self.it.next() orelse return ModuleError.InvalidSpirV; + self.bound = self.it.next() catch return ModuleError.InvalidSpirV; self.results.resize(allocator, self.bound) catch return ModuleError.OutOfMemory; for (self.results.items) |*result| { result.* = Result.init(); @@ -181,7 +181,7 @@ fn checkEndiannessFromSpvMagic(magic: SpvWord) bool { fn pass(self: *Self, allocator: std.mem.Allocator, opcodes: std.EnumSet(spv.SpvOp)) ModuleError!void { var rt = Runtime.init(self); defer rt.deinit(); - while (rt.it.next()) |opcode_data| { + while (rt.it.nextOrNull()) |opcode_data| { const word_count = ((opcode_data & (~spv.SpvOpCodeMask)) >> spv.SpvWordCountShift) - 1; const opcode = (opcode_data & spv.SpvOpCodeMask); diff --git a/src/Result.zig b/src/Result.zig index 01808a0..b795fa6 100644 --- a/src/Result.zig +++ b/src/Result.zig @@ -6,20 +6,36 @@ const SpvByte = spv.SpvByte; const SpvWord = spv.SpvWord; const SpvBool = spv.SpvBool; -const RType = enum { +const Type = enum { None, String, Extension, - Function_type, + FunctionType, Type, Variable, Constant, Function, - Access_chain, - Function_parameter, + AccessChain, + FunctionParameter, Label, }; +const ValueType = enum { + Void, + Bool, + Int, + Float, + Vector, + Matrix, + Array, + RuntimeArray, + Structure, + Image, + Sampler, + SampledImage, + Pointer, +}; + const ImageInfo = struct { dim: spv.SpvDim, depth: SpvByte, @@ -33,16 +49,14 @@ const ImageInfo = struct { const Decoration = struct { rtype: spv.SpvDecoration, literal_1: SpvWord, - literal_2: SpvWord, + literal_2: ?SpvWord, index: SpvWord, }; const Self = @This(); name: ?[]const u8, -ptr: SpvWord, -storage_class: spv.SpvStorageClass, parent: ?*const Self, member_names: std.ArrayList([]const u8), @@ -50,20 +64,66 @@ members: std.ArrayList(spv.SpvMember), decorations: std.ArrayList(Decoration), -/// Only for functions -return_type: SpvWord, - -rtype: RType, +res_type: Type, +type_data: union(Type) { + None: struct {}, + String: []const u8, + Extension: struct {}, + FunctionType: struct { + return_type: SpvWord, + }, + Type: struct { + value_type: ValueType, + data: union(ValueType) { + Void: struct {}, + Bool: struct {}, + Int: struct { + bit_length: SpvWord, + is_signed: bool, + }, + Float: struct { + bit_length: SpvWord, + }, + Vector: struct { + components_type: ValueType, + }, + Matrix: struct { + column_type: ValueType, + }, + Array: struct {}, + RuntimeArray: struct {}, + Structure: struct {}, + Image: struct {}, + Sampler: struct {}, + SampledImage: struct {}, + Pointer: struct { + storage_class: spv.SpvStorageClass, + }, + }, + member_count: SpvWord = 0, + id: SpvWord = 0, + }, + Variable: struct {}, + Constant: struct {}, + Function: struct { + /// Allocated array + params: []SpvWord, + }, + AccessChain: struct {}, + FunctionParameter: struct {}, + Label: struct {}, +}, pub fn init() Self { - return std.mem.zeroInit(Self, .{ + return .{ .name = null, .parent = null, - .member_names = std.ArrayList([]const u8).empty, - .members = std.ArrayList(spv.SpvMember).empty, - .decorations = std.ArrayList(Decoration).empty, - .rtype = RType.None, - }); + .member_names = .empty, + .members = .empty, + .decorations = .empty, + .res_type = .None, + .type_data = undefined, + }; } pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { @@ -73,6 +133,11 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { for (self.member_names.items) |name| { allocator.free(name); } + // FIXME + //switch (self.type_data) { + // .Function => |data| allocator.free(data.params), + // else => {}, + //} self.member_names.deinit(allocator); self.members.deinit(allocator); self.decorations.deinit(allocator); diff --git a/src/WordIterator.zig b/src/WordIterator.zig index 8075958..158083f 100644 --- a/src/WordIterator.zig +++ b/src/WordIterator.zig @@ -1,6 +1,8 @@ const std = @import("std"); const spv = @import("spv.zig"); +const RuntimeError = @import("Runtime.zig").RuntimeError; + const SpvWord = spv.SpvWord; const Self = @This(); @@ -15,16 +17,22 @@ pub fn init(buffer: []const SpvWord) Self { }; } -pub fn next(self: *Self) ?SpvWord { +pub fn nextOrNull(self: *Self) ?SpvWord { const word = self.peek() orelse return null; self.index += 1; return word; } -pub fn nextAs(self: *Self, comptime E: type) ?E { - const word = self.peek() orelse return null; - self.index += 1; - return std.enums.fromInt(E, word); +pub inline fn nextAsOrNull(self: *Self, comptime E: type) ?E { + return if (self.nextOrNull()) |word| std.enums.fromInt(E, word) else null; +} + +pub inline fn next(self: *Self) RuntimeError!SpvWord { + return self.nextOrNull() orelse return RuntimeError.InvalidSpirV; +} + +pub fn nextAs(self: *Self, comptime E: type) RuntimeError!E { + return self.nextAsOrNull(E) orelse return RuntimeError.InvalidSpirV; } pub fn peek(self: *const Self) ?SpvWord { diff --git a/src/opcodes.zig b/src/opcodes.zig index a2739ce..886bc9b 100644 --- a/src/opcodes.zig +++ b/src/opcodes.zig @@ -14,15 +14,8 @@ const SpvWord = spv.SpvWord; const SpvBool = spv.SpvBool; // DUMB INDEV OPCODES TODO : -// OpDecorate X -// OpMemberDecorate X -// OpTypeVoid X // OpTypeFunction X -// OpTypeFloat X -// OpTypeVector X -// OpTypePointer X // OpTypeStruct X -// OpTypeInt X // OpConstant X // OpVariable X // OpFunction X @@ -41,26 +34,90 @@ pub const SetupDispatcher = block: { @setEvalBranchQuota(65535); break :block std.EnumMap(spv.SpvOp, OpCodeFunc).init(.{ .Capability = opCapability, + .Decorate = opDecorate, .EntryPoint = opEntryPoint, .ExecutionMode = opExecutionMode, - .MemoryModel = opMemoryModel, + .MemberDecorate = opDecorateMember, .MemberName = opMemberName, + .MemoryModel = opMemoryModel, .Name = opName, .Source = opSource, .SourceExtension = opSourceExtension, + .TypeVoid = opTypeVoid, + .TypeBool = opTypeBool, + .TypeInt = opTypeInt, + .TypeFloat = opTypeFloat, + .TypeVector = opTypeVector, + .TypeMatrix = opTypeMatrix, + .TypePointer = opTypePointer, }); }; fn opCapability(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { - if (rt.it.nextAs(spv.SpvCapability)) |capability| { - rt.mod.capabilities.insert(capability); + rt.mod.capabilities.insert(try rt.it.nextAs(spv.SpvCapability)); +} + +fn addDecoration(allocator: std.mem.Allocator, rt: *Runtime, target: SpvWord, decoration_type: spv.SpvDecoration, member: ?SpvWord) RuntimeError!void { + var decoration = rt.mod.results.items[target].decorations.addOne(allocator) catch return RuntimeError.OutOfMemory; + decoration.rtype = decoration_type; + decoration.index = if (member) |memb| memb else 0; + + switch (decoration_type) { + .SpecId, + .ArrayStride, + .MatrixStride, + .BuiltIn, + .UniformId, + .Stream, + .Location, + .Component, + .Index, + .Binding, + .DescriptorSet, + .Offset, + .XfbBuffer, + .XfbStride, + .FuncParamAttr, + .FPRoundingMode, + .FPFastMathMode, + .InputAttachmentIndex, + .Alignment, + .MaxByteOffset, + .AlignmentId, + .MaxByteOffsetId, + .SecondaryViewportRelativeNV, + .CounterBuffer, + .UserSemantic, + .UserTypeGOOGLE, + => { + decoration.literal_1 = try rt.it.next(); + decoration.literal_2 = null; + }, + .LinkageAttributes => { + decoration.literal_1 = try rt.it.next(); + decoration.literal_2 = try rt.it.next(); + }, + else => {}, } } +fn opDecorate(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { + const target = try rt.it.next(); + const decoration_type = try rt.it.nextAs(spv.SpvDecoration); + try addDecoration(allocator, rt, target, decoration_type, null); +} + +fn opDecorateMember(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { + const target = try rt.it.next(); + const member = try rt.it.next(); + const decoration_type = try rt.it.nextAs(spv.SpvDecoration); + try addDecoration(allocator, rt, target, decoration_type, member); +} + fn opEntryPoint(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { const entry = rt.mod.entry_points.addOne(allocator) catch return RuntimeError.OutOfMemory; - entry.exec_model = rt.it.nextAs(spv.SpvExecutionModel) orelse return RuntimeError.InvalidSpirV; - entry.id = rt.it.next() orelse return RuntimeError.InvalidSpirV; + entry.exec_model = try rt.it.nextAs(spv.SpvExecutionModel); + entry.id = try rt.it.next(); entry.name = try readString(allocator, &rt.it); var interface_count = word_count - @divExact(entry.name.len, 4) - 2; @@ -68,7 +125,7 @@ fn opEntryPoint(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) if (interface_count != 0) { var interface_index: u32 = 0; while (interface_count != 0) { - entry.globals[interface_index] = rt.it.next() orelse return RuntimeError.InvalidSpirV; + entry.globals[interface_index] = try rt.it.next(); interface_index += 1; interface_count -= 1; } @@ -77,16 +134,16 @@ fn opEntryPoint(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) fn opExecutionMode(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { _ = rt.it.skip(); - const mode = rt.it.nextAs(spv.SpvExecutionMode) orelse return RuntimeError.InvalidSpirV; + const mode = try rt.it.nextAs(spv.SpvExecutionMode); switch (mode) { .LocalSize => { - rt.mod.local_size_x = rt.it.next() orelse return RuntimeError.InvalidSpirV; - rt.mod.local_size_y = rt.it.next() orelse return RuntimeError.InvalidSpirV; - rt.mod.local_size_z = rt.it.next() orelse return RuntimeError.InvalidSpirV; + rt.mod.local_size_x = try rt.it.next(); + rt.mod.local_size_y = try rt.it.next(); + rt.mod.local_size_z = try rt.it.next(); }, - .Invocations => rt.mod.geometry_invocations = rt.it.next() orelse return RuntimeError.InvalidSpirV, - .OutputVertices => rt.mod.geometry_output_count = rt.it.next() orelse return RuntimeError.InvalidSpirV, + .Invocations => rt.mod.geometry_invocations = try rt.it.next(), + .OutputVertices => rt.mod.geometry_output_count = try rt.it.next(), .InputPoints, .InputLines, .Triangles, .InputLinesAdjacency, .InputTrianglesAdjacency => rt.mod.geometry_input = @intFromEnum(mode), .OutputPoints, .OutputLineStrip, .OutputTriangleStrip => rt.mod.geometry_output = @intFromEnum(mode), else => {}, @@ -94,8 +151,8 @@ fn opExecutionMode(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError! } fn opMemberName(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { - const id = rt.it.next() orelse return RuntimeError.InvalidSpirV; - const memb = rt.it.next() orelse return RuntimeError.InvalidSpirV; + const id = try rt.it.next(); + const memb = try rt.it.next(); var result = &rt.mod.results.items[id]; @@ -108,12 +165,12 @@ fn opMemberName(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) } fn opMemoryModel(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { - rt.mod.addressing = rt.it.nextAs(spv.SpvAddressingModel) orelse return RuntimeError.InvalidSpirV; - rt.mod.memory_model = rt.it.nextAs(spv.SpvMemoryModel) orelse return RuntimeError.InvalidSpirV; + rt.mod.addressing = try rt.it.nextAs(spv.SpvAddressingModel); + rt.mod.memory_model = try rt.it.nextAs(spv.SpvMemoryModel); } fn opName(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { - const id = rt.it.next() orelse return RuntimeError.InvalidSpirV; + const id = try rt.it.next(); if (id >= rt.mod.results.items.len) return RuntimeError.InvalidSpirV; var result = &rt.mod.results.items[id]; result.* = Result.init(); @@ -122,17 +179,17 @@ fn opName(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) Runti fn opSource(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { var file = rt.mod.files.addOne(allocator) catch return RuntimeError.OutOfMemory; - file.lang = rt.it.nextAs(spv.SpvSourceLanguage) orelse return RuntimeError.InvalidSpirV; - file.lang_version = rt.it.next() orelse return RuntimeError.InvalidSpirV; + file.lang = try rt.it.nextAs(spv.SpvSourceLanguage); + file.lang_version = try rt.it.next(); if (word_count > 2) { - const id = rt.it.next() orelse return RuntimeError.InvalidSpirV; + const id = try rt.it.next(); if (id >= rt.mod.results.items.len) return RuntimeError.InvalidSpirV; if (rt.mod.results.items[id].name) |name| { file.file_name = name; } } if (word_count > 3) { - const id = rt.it.next() orelse return RuntimeError.InvalidSpirV; + const id = try rt.it.next(); if (id >= rt.mod.results.items.len) return RuntimeError.InvalidSpirV; if (rt.mod.results.items[id].name) |name| { file.source = name; @@ -144,9 +201,123 @@ fn opSourceExtension(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Run rt.mod.extensions.append(allocator, try readStringN(allocator, &rt.it, word_count)) catch return RuntimeError.OutOfMemory; } +fn opTypeVoid(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { + const id = try rt.it.next(); + rt.mod.results.items[id].res_type = .Type; + rt.mod.results.items[id].type_data = .{ + .Type = .{ + .value_type = .Void, + .data = .{ + .Void = .{}, + }, + }, + }; +} + +fn opTypeBool(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { + const id = try rt.it.next(); + rt.mod.results.items[id].res_type = .Type; + rt.mod.results.items[id].type_data = .{ + .Type = .{ + .value_type = .Bool, + .data = .{ + .Bool = .{}, + }, + .member_count = 1, + }, + }; +} + +fn opTypeInt(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { + const id = try rt.it.next(); + rt.mod.results.items[id].res_type = .Type; + rt.mod.results.items[id].type_data = .{ + .Type = .{ + .value_type = .Int, + .data = .{ + .Int = .{ + .bit_length = try rt.it.next(), + .is_signed = if (try rt.it.next() != 0) true else false, + }, + }, + .member_count = 1, + }, + }; +} + +fn opTypeFloat(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { + const id = try rt.it.next(); + rt.mod.results.items[id].res_type = .Type; + rt.mod.results.items[id].type_data = .{ + .Type = .{ + .value_type = .Float, + .data = .{ + .Float = .{ + .bit_length = try rt.it.next(), + }, + }, + .member_count = 1, + }, + }; +} + +fn opTypeVector(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { + const id = try rt.it.next(); + rt.mod.results.items[id].res_type = .Type; + rt.mod.results.items[id].type_data = .{ + .Type = .{ + .value_type = .Vector, + .data = .{ + .Vector = .{ + .components_type = switch (rt.mod.results.items[try rt.it.next()].type_data) { + .Type => |data| data.value_type, + else => return RuntimeError.InvalidSpirV, + }, + }, + }, + .member_count = try rt.it.next(), + }, + }; +} + +fn opTypeMatrix(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { + const id = try rt.it.next(); + rt.mod.results.items[id].res_type = .Type; + rt.mod.results.items[id].type_data = .{ + .Type = .{ + .value_type = .Matrix, + .data = .{ + .Matrix = .{ + .column_type = switch (rt.mod.results.items[try rt.it.next()].type_data) { + .Type => |data| data.value_type, + else => return RuntimeError.InvalidSpirV, + }, + }, + }, + .member_count = try rt.it.next(), + }, + }; +} + +fn opTypePointer(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { + const id = try rt.it.next(); + rt.mod.results.items[id].res_type = .Type; + rt.mod.results.items[id].type_data = .{ + .Type = .{ + .value_type = .Pointer, + .data = .{ + .Pointer = .{ + .storage_class = try rt.it.nextAs(spv.SpvStorageClass), + }, + }, + .id = try rt.it.next(), + }, + }; +} + fn readString(allocator: std.mem.Allocator, it: *WordIterator) RuntimeError![]const u8 { var str: std.ArrayList(u8) = .empty; - while (it.next()) |word| { + while (it.nextOrNull()) |word| { (str.addOne(allocator) catch return RuntimeError.OutOfMemory).* = @truncate(word & 0x000000FF); (str.addOne(allocator) catch return RuntimeError.OutOfMemory).* = @truncate((word & 0x0000FF00) >> 8); (str.addOne(allocator) catch return RuntimeError.OutOfMemory).* = @truncate((word & 0x00FF0000) >> 16); @@ -161,7 +332,7 @@ fn readString(allocator: std.mem.Allocator, it: *WordIterator) RuntimeError![]co fn readStringN(allocator: std.mem.Allocator, it: *WordIterator, n: usize) RuntimeError![]const u8 { var str = std.ArrayList(u8).initCapacity(allocator, n * 4) catch return RuntimeError.OutOfMemory; for (0..n) |_| { - if (it.next()) |word| { + if (it.nextOrNull()) |word| { str.addOneAssumeCapacity().* = @truncate(word & 0x000000FF); str.addOneAssumeCapacity().* = @truncate((word & 0x0000FF00) >> 8); str.addOneAssumeCapacity().* = @truncate((word & 0x00FF0000) >> 16);