diff --git a/build.zig b/build.zig index e53b783..b9ed751 100644 --- a/build.zig +++ b/build.zig @@ -10,6 +10,9 @@ pub fn build(b: *std.Build) void { .optimize = optimize, }); + const pretty = b.dependency("pretty", .{ .target = target, .optimize = optimize }); + mod.addImport("pretty", pretty.module("pretty")); + const lib = b.addLibrary(.{ .name = "spirv_interpreter", .root_module = mod, diff --git a/build.zig.zon b/build.zig.zon index b10ac98..e134d3c 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,7 +1,12 @@ .{ .name = .SPIRV_Interpreter, .version = "0.0.1", - .dependencies = .{}, + .dependencies = .{ + .pretty = .{ + .url = "https://github.com/timfayz/pretty/archive/refs/heads/main.tar.gz", + .hash = "pretty-0.10.6-Tm65r6lPAQCBxgwzehYPeqsCXQDT9kt2ktJuO-2tRfE6", + }, + }, .minimum_zig_version = "0.15.2", .paths = .{ "build.zig", diff --git a/src/Module.zig b/src/Module.zig index 2520fca..df7824c 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -4,18 +4,21 @@ const lib = @import("lib.zig"); const spv = @import("spv.zig"); const op = @import("opcodes.zig"); +const pretty = @import("pretty"); + const SpvVoid = spv.SpvVoid; const SpvByte = spv.SpvByte; const SpvWord = spv.SpvWord; const SpvBool = spv.SpvBool; -const SpvMember = spv.SpvMember; const SpvBinding = spv.SpvBinding; const Result = @import("Result.zig"); const Runtime = @import("Runtime.zig"); const WordIterator = @import("WordIterator.zig"); +const SpvValue = Result.SpvValue; + const Self = @This(); const SpvEntryPoint = struct { @@ -71,10 +74,10 @@ geometry_output_count: SpvWord, geometry_input: SpvWord, geometry_output: SpvWord, -input_locations: std.AutoHashMap(SpvWord, *SpvMember), -output_locations: std.AutoHashMap(SpvWord, *SpvMember), -bindings: std.AutoHashMap(SpvBinding, *SpvMember), -push_constants: []SpvMember, +input_locations: std.AutoHashMap(SpvWord, *SpvValue), +output_locations: std.AutoHashMap(SpvWord, *SpvValue), +bindings: std.AutoHashMap(SpvBinding, *SpvValue), +push_constants: []SpvValue, pub fn init(allocator: std.mem.Allocator, source: []const SpvWord) ModuleError!Self { var self: Self = std.mem.zeroInit(Self, .{ @@ -87,9 +90,9 @@ pub fn init(allocator: std.mem.Allocator, source: []const SpvWord) ModuleError!S .local_size_x = 1, .local_size_y = 1, .local_size_z = 1, - .input_locations = std.AutoHashMap(SpvWord, *SpvMember).init(allocator), - .output_locations = std.AutoHashMap(SpvWord, *SpvMember).init(allocator), - .bindings = std.AutoHashMap(SpvBinding, *SpvMember).init(allocator), + .input_locations = std.AutoHashMap(SpvWord, *SpvValue).init(allocator), + .output_locations = std.AutoHashMap(SpvWord, *SpvValue).init(allocator), + .bindings = std.AutoHashMap(SpvBinding, *SpvValue).init(allocator), }); errdefer self.deinit(allocator); @@ -162,6 +165,8 @@ pub fn init(allocator: std.mem.Allocator, source: []const SpvWord) ModuleError!S capabilities, entry_points, }); + + pretty.print(allocator, self.results, .{ .tab_size = 4, .max_depth = 0 }) catch return ModuleError.OutOfMemory; } return self; diff --git a/src/Result.zig b/src/Result.zig index b795fa6..259c177 100644 --- a/src/Result.zig +++ b/src/Result.zig @@ -6,11 +6,9 @@ const SpvByte = spv.SpvByte; const SpvWord = spv.SpvWord; const SpvBool = spv.SpvBool; -const Type = enum { - None, +pub const Variant = enum { String, Extension, - FunctionType, Type, Variable, Constant, @@ -20,7 +18,7 @@ const Type = enum { Label, }; -const ValueType = enum { +pub const Type = enum { Void, Bool, Int, @@ -30,6 +28,7 @@ const ValueType = enum { Array, RuntimeArray, Structure, + Function, Image, Sampler, SampledImage, @@ -53,6 +52,36 @@ const Decoration = struct { index: SpvWord, }; +pub const SpvValue = union(Type) { + Void: noreturn, + Bool: bool, + Int: union { + sint8: i8, + sint16: i16, + sint32: i32, + sint64: i64, + uint8: u8, + uint16: u16, + uint32: u32, + uint64: u64, + }, + Float: union { + float16: f16, + float32: f32, + float64: f64, + }, + Vector: noreturn, + Matrix: noreturn, + Array: struct {}, + RuntimeArray: struct {}, + Structure: struct {}, + Function: noreturn, + Image: struct {}, + Sampler: struct {}, + SampledImage: struct {}, + Pointer: noreturn, +}; + const Self = @This(); name: ?[]const u8, @@ -60,55 +89,55 @@ name: ?[]const u8, parent: ?*const Self, member_names: std.ArrayList([]const u8), -members: std.ArrayList(spv.SpvMember), decorations: std.ArrayList(Decoration), -res_type: Type, -type_data: union(Type) { - None: struct {}, +variant: ?union(Variant) { 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, - }, + Type: union(Type) { + Void: struct {}, + Bool: struct {}, + Int: struct { + bit_length: SpvWord, + is_signed: bool, + }, + Float: struct { + bit_length: SpvWord, + }, + Vector: struct { + components_type: Type, + member_count: SpvWord, + }, + Matrix: struct { + column_type: Type, + member_count: SpvWord, + }, + Array: struct {}, + RuntimeArray: struct {}, + Structure: struct { + /// Allocated array + members: []const SpvWord, + }, + Function: struct { + return_type: SpvWord, + /// Allocated array + params: []const SpvWord, + }, + Image: struct {}, + Sampler: struct {}, + SampledImage: struct {}, + Pointer: struct { + storage_class: spv.SpvStorageClass, + target: SpvWord, }, - member_count: SpvWord = 0, - id: SpvWord = 0, }, - Variable: struct {}, - Constant: struct {}, - Function: struct { - /// Allocated array - params: []SpvWord, + Variable: struct { + storage_class: spv.SpvStorageClass, + value: SpvValue, }, + Constant: SpvValue, + Function: struct {}, AccessChain: struct {}, FunctionParameter: struct {}, Label: struct {}, @@ -119,10 +148,8 @@ pub fn init() Self { .name = null, .parent = null, .member_names = .empty, - .members = .empty, .decorations = .empty, - .res_type = .None, - .type_data = undefined, + .variant = null, }; } @@ -133,12 +160,31 @@ 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 => {}, - //} + if (self.variant) |variant| { + switch (variant) { + .Type => |t| { + switch (t) { + .Function => |data| allocator.free(data.params), + .Structure => |data| allocator.free(data.members), + else => {}, + } + }, + else => {}, + } + } self.member_names.deinit(allocator); - self.members.deinit(allocator); self.decorations.deinit(allocator); } + +pub fn resolveType(self: *const Self, results: []const Self) *const Self { + return if (self.variant) |variant| + switch (variant) { + .Type => |t| switch (t) { + .Pointer => |ptr| &results[ptr.target], + else => self, + }, + else => self, + } + else + self; +} diff --git a/src/Runtime.zig b/src/Runtime.zig index 89a5914..f7a19f5 100644 --- a/src/Runtime.zig +++ b/src/Runtime.zig @@ -15,6 +15,7 @@ const Self = @This(); pub const RuntimeError = error{ InvalidSpirV, + UnsupportedSpirV, OutOfMemory, Unreachable, Killed, diff --git a/src/opcodes.zig b/src/opcodes.zig index 886bc9b..2fd0412 100644 --- a/src/opcodes.zig +++ b/src/opcodes.zig @@ -14,9 +14,6 @@ const SpvWord = spv.SpvWord; const SpvBool = spv.SpvBool; // DUMB INDEV OPCODES TODO : -// OpTypeFunction X -// OpTypeStruct X -// OpConstant X // OpVariable X // OpFunction X // OpLabel X @@ -34,6 +31,7 @@ pub const SetupDispatcher = block: { @setEvalBranchQuota(65535); break :block std.EnumMap(spv.SpvOp, OpCodeFunc).init(.{ .Capability = opCapability, + .Constant = opConstant, .Decorate = opDecorate, .EntryPoint = opEntryPoint, .ExecutionMode = opExecutionMode, @@ -43,13 +41,16 @@ pub const SetupDispatcher = block: { .Name = opName, .Source = opSource, .SourceExtension = opSourceExtension, - .TypeVoid = opTypeVoid, .TypeBool = opTypeBool, - .TypeInt = opTypeInt, .TypeFloat = opTypeFloat, - .TypeVector = opTypeVector, + .TypeFunction = opTypeFunction, + .TypeInt = opTypeInt, .TypeMatrix = opTypeMatrix, .TypePointer = opTypePointer, + .TypeStruct = opTypeStruct, + .TypeVector = opTypeVector, + .TypeVoid = opTypeVoid, + .Variable = opVariable, }); }; @@ -203,114 +204,180 @@ fn opSourceExtension(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Run 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 = .{ + rt.mod.results.items[id].variant = .{ .Type = .{ - .value_type = .Void, - .data = .{ - .Void = .{}, - }, + .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 = .{ + rt.mod.results.items[id].variant = .{ .Type = .{ - .value_type = .Bool, - .data = .{ - .Bool = .{}, - }, - .member_count = 1, + .Bool = .{}, }, }; } 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 = .{ + rt.mod.results.items[id].variant = .{ .Type = .{ - .value_type = .Int, - .data = .{ - .Int = .{ - .bit_length = try rt.it.next(), - .is_signed = if (try rt.it.next() != 0) true else false, - }, + .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 = .{ + rt.mod.results.items[id].variant = .{ .Type = .{ - .value_type = .Float, - .data = .{ - .Float = .{ - .bit_length = try rt.it.next(), - }, + .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 = .{ + rt.mod.results.items[id].variant = .{ .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, - }, + .Vector = .{ + .components_type = switch (rt.mod.results.items[try rt.it.next()].variant.?) { + .Type => |t| @as(Result.Type, t), + else => return RuntimeError.InvalidSpirV, }, + .member_count = try rt.it.next(), }, - .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 = .{ + rt.mod.results.items[id].variant = .{ .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, - }, + .Matrix = .{ + .column_type = switch (rt.mod.results.items[try rt.it.next()].variant.?) { + .Type => |t| @as(Result.Type, t), + else => return RuntimeError.InvalidSpirV, }, + .member_count = try rt.it.next(), }, - .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 = .{ + rt.mod.results.items[id].variant = .{ .Type = .{ - .value_type = .Pointer, - .data = .{ - .Pointer = .{ - .storage_class = try rt.it.nextAs(spv.SpvStorageClass), + .Pointer = .{ + .storage_class = try rt.it.nextAs(spv.SpvStorageClass), + .target = try rt.it.next(), + }, + }, + }; +} + +fn opTypeStruct(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { + const id = try rt.it.next(); + rt.mod.results.items[id].variant = .{ + .Type = .{ + .Structure = .{ + .members = blk: { + const members = allocator.alloc(SpvWord, word_count - 1) catch return RuntimeError.OutOfMemory; + errdefer allocator.free(members); + for (members) |*member| { + member.* = try rt.it.next(); + } + break :blk members; }, }, - .id = try rt.it.next(), + }, + }; +} + +fn opTypeFunction(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { + const id = try rt.it.next(); + rt.mod.results.items[id].variant = .{ + .Type = .{ + .Function = .{ + .return_type = try rt.it.next(), + .params = blk: { + const params = allocator.alloc(SpvWord, word_count - 2) catch return RuntimeError.OutOfMemory; + errdefer allocator.free(params); + for (params) |*param| { + param.* = try rt.it.next(); + } + break :blk params; + }, + }, + }, + }; +} + +fn opConstant(_: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { + const var_type = try rt.it.next(); + const id = try rt.it.next(); + rt.mod.results.items[id].variant = .{ + .Constant = value: { + const resolved = rt.mod.results.items[var_type].resolveType(rt.mod.results.items); + if (resolved.variant) |variant| { + switch (variant) { + .Type => |t| switch (t) { + .Int => { + break :value if (word_count - 2 != 1) .{ + .Int = .{ + .uint64 = try rt.it.next() | (@as(u64, try rt.it.next()) >> 32), + }, + } else .{ + .Int = .{ + .uint32 = try rt.it.next(), + }, + }; + }, + .Float => { + break :value if (word_count - 2 != 1) .{ + .Float = .{ + .float64 = @bitCast(@as(u64, try rt.it.next()) | (@as(u64, try rt.it.next()) >> 32)), + }, + } else .{ + .Float = .{ + .float32 = @bitCast(try rt.it.next()), + }, + }; + }, + else => {}, // Will fallback on error, + }, + else => {}, // Will fallback on error + } + } + return RuntimeError.InvalidSpirV; + }, + }; +} + +fn opVariable(_: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { + const var_type = try rt.it.next(); + const id = try rt.it.next(); + const storage_class = try rt.it.nextAs(spv.SpvStorageClass); + const initializer: ?SpvWord = if (word_count >= 4) try rt.it.next() else null; + _ = initializer; + + rt.mod.results.items[id].variant = .{ + .Variable = .{ + .storage_class = storage_class, + .value = value: { + const resolved = rt.mod.results.items[var_type].resolveType(rt.mod.results.items); + _ = resolved; + break :value undefined; + }, }, }; } diff --git a/src/spv.zig b/src/spv.zig index 245068e..05ae91b 100644 --- a/src/spv.zig +++ b/src/spv.zig @@ -68,20 +68,6 @@ pub fn vendorName(id: u32) []const u8 { }; } -pub const SpvMember = struct { - type: SpvWord, - value: union { - boolean: bool, - int32: i32, - uint32: u32, - uint64: u64, - float32: f32, - float64: f64, - // TODO: sampler, image, sampledimage - }, - members: []SpvMember, -}; - pub const SpvBinding = struct { set: SpvWord, binding: SpvWord,