From 39a8eb63bce8aab7da631455dfdab03f6e65078d Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Sun, 11 Jan 2026 23:40:50 +0100 Subject: [PATCH] working POC --- .gdb_history | 30 ++++++ README.md | 32 ++++++- build.zig | 2 +- build.zig.zon | 6 +- example/main.zig | 2 +- example/shader.nzsl | 2 +- example/shader.spv | Bin 544 -> 592 bytes example/shader.spvasm | 21 +++-- src/Module.zig | 25 +++-- src/Result.zig | 215 ++++++++++++++++++++++-------------------- src/Runtime.zig | 13 ++- src/opcodes.zig | 151 ++++++++++++++++------------- 12 files changed, 301 insertions(+), 198 deletions(-) create mode 100644 .gdb_history diff --git a/.gdb_history b/.gdb_history new file mode 100644 index 0000000..d21deb4 --- /dev/null +++ b/.gdb_history @@ -0,0 +1,30 @@ +run +q +run +bt +q +run +bt +q +q +b Runtime.deinit +run +n +q +b Runtime.deinit +run +b +n +n +n +n +n +n +q +b Rutnime.deinit +b Runtime.deinit +run +n +w +q +q diff --git a/README.md b/README.md index 9daeafb..bc2b688 100644 --- a/README.md +++ b/README.md @@ -1 +1,31 @@ -test +# SPIR-V Interpreter + +A small footprint SPIR-V interpreter with zero dependencies to execute SPIR-V shaders on the CPU. It is designed to be used with multiple runtimes concurrently. + +```zig +const std = @import("std"); +const spv = @import("spv"); + +const shader_source = @embedFile("shader.spv"); + +pub fn main() !void { + { + var gpa: std.heap.DebugAllocator(.{}) = .init; + defer _ = gpa.deinit(); + + const allocator = gpa.allocator(); + + var module = try spv.Module.init(allocator, @ptrCast(@alignCast(shader_source))); + defer module.deinit(allocator); + + var rt = try spv.Runtime.init(allocator, &module); + defer rt.deinit(allocator); + + try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main")); + var output: [4]f32 = undefined; + try rt.readOutput(f32, output[0..output.len], try rt.getResultByName("color")); + std.log.info("Output: Vec4[{d}, {d}, {d}, {d}]", .{ output[0], output[1], output[2], output[3] }); + } + std.log.info("Successfully executed", .{}); +} +``` diff --git a/build.zig b/build.zig index b9ed751..ae03483 100644 --- a/build.zig +++ b/build.zig @@ -17,7 +17,7 @@ pub fn build(b: *std.Build) void { .name = "spirv_interpreter", .root_module = mod, .linkage = .dynamic, - //.use_llvm = true, + .use_llvm = true, }); const lib_install = b.addInstallArtifact(lib, .{}); diff --git a/build.zig.zon b/build.zig.zon index e134d3c..a4a23e5 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -2,9 +2,9 @@ .name = .SPIRV_Interpreter, .version = "0.0.1", .dependencies = .{ - .pretty = .{ - .url = "https://github.com/timfayz/pretty/archive/refs/heads/main.tar.gz", - .hash = "pretty-0.10.6-Tm65r6lPAQCBxgwzehYPeqsCXQDT9kt2ktJuO-2tRfE6", + .pretty = .{ // For debugging purposes + .url = "git+https://github.com/Kbz-8/pretty#117674465efd4d07d5ae9d9d8ca59c2c323a65ba", + .hash = "pretty-0.10.6-Tm65r99UAQDEJMgZysD10qE8dinBHr064fPM6YkxVPfB", }, }, .minimum_zig_version = "0.15.2", diff --git a/example/main.zig b/example/main.zig index a969671..92056f2 100644 --- a/example/main.zig +++ b/example/main.zig @@ -19,7 +19,7 @@ pub fn main() !void { try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main")); var output: [4]f32 = undefined; try rt.readOutput(f32, output[0..output.len], try rt.getResultByName("color")); - std.log.info("Result: Vec4[{d}, {d}, {d}, {d}]", .{ output[0], output[1], output[2], output[3] }); + std.log.info("Output: Vec4[{d}, {d}, {d}, {d}]", .{ output[0], output[1], output[2], output[3] }); } std.log.info("Successfully executed", .{}); } diff --git a/example/shader.nzsl b/example/shader.nzsl index a9494e9..94ac674 100644 --- a/example/shader.nzsl +++ b/example/shader.nzsl @@ -10,6 +10,6 @@ struct FragOut fn main() -> FragOut { let output: FragOut; - output.color = vec4[f32](1.0, 1.0, 1.0, 1.0); + output.color = vec4[f32](4.0, 3.0, 2.0, 1.0); return output; } diff --git a/example/shader.spv b/example/shader.spv index db7c59e3283a69e393568976cf771022cc8154e4..ee9794c05a18b8db9ef7d1c45dbd32339b94b5fc 100644 GIT binary patch delta 249 zcmZ3$a)E`HnMs+Qfq{{sNkE-Jd?K$hBmYE0VIbie4vVet`nMs+Qfq{{sNkE-JWFoIJBhN%bVIbie4 for (result.decorations.items) |decoration| switch (decoration.rtype) { - .Location => self.output_locations.put(@intCast(id), variable.values) catch return ModuleError.OutOfMemory, + .Location => self.output_locations.append(allocator, @intCast(id)) catch return ModuleError.OutOfMemory, else => {}, }, else => {}, @@ -212,8 +211,8 @@ fn populateMaps(self: *Self) ModuleError!void { pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { allocator.free(self.code); - self.input_locations.deinit(); - self.output_locations.deinit(); + self.input_locations.deinit(allocator); + self.output_locations.deinit(allocator); self.bindings.deinit(); for (self.entry_points.items) |entry| { allocator.free(entry.name); diff --git a/src/Result.zig b/src/Result.zig index 7e38bb4..cd36c09 100644 --- a/src/Result.zig +++ b/src/Result.zig @@ -55,7 +55,7 @@ const Decoration = struct { }; pub const Value = union(Type) { - Void: struct {}, + Void: noreturn, Bool: bool, Int: extern union { sint8: i8, @@ -77,69 +77,90 @@ pub const Value = union(Type) { Array: struct {}, RuntimeArray: struct {}, Structure: []Value, - Function: struct {}, + Function: noreturn, Image: struct {}, Sampler: struct {}, SampledImage: struct {}, - Pointer: struct {}, + Pointer: noreturn, - fn initMembers(self: *Value, allocator: std.mem.Allocator, results: []const Self, target: SpvWord) RuntimeError!void { + pub inline fn getCompositeDataOrNull(self: *const Value) ?[]Value { + return switch (self.*) { + .Vector => |v| v, + .Matrix => |m| m, + .Array => |_| unreachable, + .Structure => |s| s, + else => null, + }; + } + + fn init(allocator: std.mem.Allocator, results: []const Self, target: SpvWord) RuntimeError!Value { const resolved = results[target].resolveType(results); const member_count = resolved.getMemberCounts(); - switch (resolved.variant.?) { + return switch (resolved.variant.?) { .Type => |t| switch (t) { - .Bool, .Int, .Float => std.debug.assert(member_count == 1), - .Structure => |s| { - self.* = .{ - .Structure = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory, - }; - for (self.Structure, s.members) |*value, member| { - value.* = switch (member) { - inline else => |tag| @unionInit(Value, @tagName(tag), undefined), - }; - } - }, - .Matrix => |m| { - self.* = .{ - .Matrix = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory, - }; - for (self.Matrix) |*value| { - value.* = switch (m.column_type) { - inline else => |tag| @unionInit(Value, @tagName(tag), undefined), - }; - } - }, - .Array => |a| { - _ = a; - }, - .Vector => |v| { - self.* = .{ - .Vector = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory, - }; + .Bool => .{ .Bool = false }, + .Int => .{ .Int = .{ .uint64 = 0 } }, + .Float => .{ .Float = .{ .float64 = 0.0 } }, + .Vector => |v| blk: { + var self: Value = .{ .Vector = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory }; + errdefer self.deinit(allocator); + for (self.Vector) |*value| { - value.* = switch (v.components_type) { - inline else => |tag| @unionInit(Value, @tagName(tag), undefined), - }; + value.* = try Value.init(allocator, results, v.components_type_word); } + break :blk self; }, - else => {}, + .Matrix => |m| blk: { + var self: Value = .{ .Matrix = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory }; + errdefer self.deinit(allocator); + + for (self.Matrix) |*value| { + value.* = try Value.init(allocator, results, m.column_type_word); + } + break :blk self; + }, + .Array => |_| { + unreachable; + }, + .Structure => |s| blk: { + var self: Value = .{ .Structure = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory }; + errdefer self.deinit(allocator); + + for (self.Structure, s.members_type_word) |*value, member_type_word| { + value.* = try Value.init(allocator, results, member_type_word); + } + break :blk self; + }, + else => unreachable, }, - else => {}, - } + else => unreachable, + }; } /// Performs a deep copy - fn dupe(self: *const Value, allocator: std.mem.Allocator) RuntimeError!Value { + pub fn dupe(self: *const Value, allocator: std.mem.Allocator) RuntimeError!Value { return switch (self.*) { .Vector => |v| .{ - .Vector = allocator.dupe(Value, v) catch return RuntimeError.OutOfMemory, + .Vector = blk: { + const values = allocator.dupe(Value, v) catch return RuntimeError.OutOfMemory; + for (values, v) |*new_value, value| new_value.* = try value.dupe(allocator); + break :blk values; + }, }, .Matrix => |m| .{ - .Matrix = allocator.dupe(Value, m) catch return RuntimeError.OutOfMemory, + .Matrix = blk: { + const values = allocator.dupe(Value, m) catch return RuntimeError.OutOfMemory; + for (values, m) |*new_value, value| new_value.* = try value.dupe(allocator); + break :blk values; + }, }, .Structure => |s| .{ - .Structure = allocator.dupe(Value, s) catch return RuntimeError.OutOfMemory, + .Structure = blk: { + const values = allocator.dupe(Value, s) catch return RuntimeError.OutOfMemory; + for (values, s) |*new_value, value| new_value.* = try value.dupe(allocator); + break :blk values; + }, }, else => self.*, }; @@ -147,9 +168,18 @@ pub const Value = union(Type) { fn deinit(self: *Value, allocator: std.mem.Allocator) void { switch (self.*) { - .Structure => |values| allocator.free(values), - .Matrix => |values| allocator.free(values), - .Vector => |values| allocator.free(values), + .Vector => |values| { + for (values) |*value| value.deinit(allocator); + allocator.free(values); + }, + .Matrix => |values| { + for (values) |*value| value.deinit(allocator); + allocator.free(values); + }, + .Structure => |values| { + for (values) |*value| value.deinit(allocator); + allocator.free(values); + }, else => {}, } } @@ -208,9 +238,9 @@ variant: ?union(Variant) { }, Variable: struct { storage_class: spv.SpvStorageClass, - values: []Value, + value: Value, }, - Constant: []Value, + Constant: Value, Function: struct { source_location: usize, return_type: SpvWord, @@ -219,7 +249,7 @@ variant: ?union(Variant) { }, AccessChain: struct { target: SpvWord, - values: []Value, + value: Value, }, FunctionParameter: struct {}, Label: struct { @@ -254,14 +284,9 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { }, else => {}, }, - .Constant => |values| { - for (values) |*value| value.deinit(allocator); - allocator.free(values); - }, - .Variable => |v| { - for (v.values) |*value| value.deinit(allocator); - allocator.free(v.values); - }, + .Constant => |*v| v.deinit(allocator), + .Variable => |*v| v.value.deinit(allocator), + //.AccessChain => |*a| a.value.deinit(allocator), else => {}, } } @@ -310,24 +335,10 @@ pub fn dupe(self: *const Self, allocator: std.mem.Allocator) RuntimeError!Self { .Variable => |v| break :blk .{ .Variable = .{ .storage_class = v.storage_class, - .values = blk2: { - const values = allocator.dupe(Value, v.values) catch return RuntimeError.OutOfMemory; - for (values, v.values) |*new_value, value| { - new_value.* = try value.dupe(allocator); - } - break :blk2 values; - }, - }, - }, - .Constant => |c| break :blk .{ - .Constant = blk2: { - const values = allocator.dupe(Value, c) catch return RuntimeError.OutOfMemory; - for (values, c) |*new_value, value| { - new_value.* = try value.dupe(allocator); - } - break :blk2 values; + .value = try v.value.dupe(allocator), }, }, + .Constant => |c| break :blk .{ .Constant = try c.dupe(allocator) }, .Function => |f| break :blk .{ .Function = .{ .source_location = f.source_location, @@ -336,9 +347,8 @@ pub fn dupe(self: *const Self, allocator: std.mem.Allocator) RuntimeError!Self { .params = allocator.dupe(SpvWord, f.params) catch return RuntimeError.OutOfMemory, }, }, - else => {}, + else => break :blk variant, } - break :blk variant; } break :blk null; }, @@ -376,37 +386,42 @@ pub fn getMemberCounts(self: *const Self) usize { return 0; } -pub fn initValues(allocator: std.mem.Allocator, values: []Value, results: []const Self, resolved: *const Self) RuntimeError!void { - switch (resolved.variant.?) { +pub fn initValue(allocator: std.mem.Allocator, member_count: usize, results: []const Self, resolved: *const Self) RuntimeError!Value { + return switch (resolved.variant.?) { .Type => |t| switch (t) { - .Bool => values[0] = .{ .Bool = undefined }, - .Int => values[0] = .{ .Int = undefined }, - .Float => values[0] = .{ .Float = undefined }, - .Vector => |v| { - for (values) |*value| { - value.* = switch (v.components_type) { - inline else => |tag| @unionInit(Value, @tagName(tag), undefined), - }; + .Bool => .{ .Bool = false }, + .Int => .{ .Int = .{ .uint64 = 0 } }, + .Float => .{ .Float = .{ .float64 = 0.0 } }, + .Vector => |v| blk: { + const value: Value = .{ .Vector = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory }; + errdefer allocator.free(value.Vector); + for (value.Vector) |*val| { + val.* = try Value.init(allocator, results, v.components_type_word); } + break :blk value; }, - .Matrix => |m| { - for (values) |*value| { - try value.initMembers(allocator, results, m.column_type_word); + .Matrix => |m| blk: { + const value: Value = .{ .Matrix = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory }; + errdefer allocator.free(value.Matrix); + for (value.Matrix) |*v| { + v.* = try Value.init(allocator, results, m.column_type_word); } + break :blk value; }, - .Array => |a| { // TODO - _ = a; - }, - .Structure => |s| { - for (values, s.members_type_word) |*value, member_type_word| { - try value.initMembers(allocator, results, member_type_word); + .Array => |_| RuntimeError.ToDo, + .Structure => |s| blk: { + const value: Value = .{ .Structure = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory }; + errdefer allocator.free(value.Structure); + for (value.Structure, s.members_type_word) |*v, member_type_word| { + v.* = try Value.init(allocator, results, member_type_word); } + break :blk value; }, - .Image => {}, // TODO - .Sampler => {}, // No op - .SampledImage => {}, // TODO - else => return RuntimeError.InvalidSpirV, + .Image => RuntimeError.ToDo, + .Sampler => RuntimeError.ToDo, + .SampledImage => RuntimeError.ToDo, + else => RuntimeError.InvalidSpirV, }, - else => return RuntimeError.InvalidSpirV, - } + else => RuntimeError.InvalidSpirV, + }; } diff --git a/src/Runtime.zig b/src/Runtime.zig index 1375690..96c23f5 100644 --- a/src/Runtime.zig +++ b/src/Runtime.zig @@ -123,11 +123,18 @@ pub fn callEntryPoint(self: *Self, allocator: std.mem.Allocator, entry_point_ind _ = it_tmp.skipN(word_count); self.it = it_tmp; } + + //@import("pretty").print(allocator, self.results, .{ + // .tab_size = 4, + // .max_depth = 0, + // .struct_max_len = 0, + // .array_max_len = 0, + //}) catch return RuntimeError.OutOfMemory; } pub fn readOutput(self: *const Self, comptime T: type, output: []T, result: SpvWord) error{NotFound}!void { - if (self.mod.output_locations.get(result)) |out| { - self.readValue(T, output, &out[0]); + if (std.mem.indexOf(SpvWord, self.mod.output_locations.items, &.{result})) |_| { + self.readValue(T, output, &self.results[result].variant.?.Variable.value); } else { return error.NotFound; } @@ -143,6 +150,8 @@ fn readValue(self: *const Self, comptime T: type, output: []T, value: *const Res .Bool => |b| { if (T == bool) { output[0] = b; + } else { + unreachable; } }, .Int => |i| { diff --git a/src/opcodes.zig b/src/opcodes.zig index adbff05..1924569 100644 --- a/src/opcodes.zig +++ b/src/opcodes.zig @@ -19,8 +19,7 @@ pub const SetupDispatcher = block: { @setEvalBranchQuota(65535); break :block std.EnumMap(spv.SpvOp, OpCodeFunc).init(.{ .Capability = opCapability, - .CompositeConstruct = opCompositeConstruct, - .CompositeExtract = opCompositeExtract, + .CompositeConstruct = opCompositeConstructSetup, .Constant = opConstant, .Decorate = opDecorate, .EntryPoint = opEntryPoint, @@ -52,6 +51,8 @@ pub const RuntimeDispatcher = block: { @setEvalBranchQuota(65535); break :block std.EnumMap(spv.SpvOp, OpCodeFunc).init(.{ .AccessChain = opAccessChain, + .CompositeConstruct = opCompositeConstruct, + .CompositeExtract = opCompositeExtract, .Load = opLoad, .Return = opReturn, .Store = opStore, @@ -195,9 +196,7 @@ fn opMemoryModel(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!vo fn opName(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { const id = try rt.it.next(); - if (id >= rt.mod.results.len) return RuntimeError.InvalidSpirV; var result = &rt.mod.results[id]; - result.* = Result.init(); result.name = try readStringN(allocator, &rt.it, word_count - 1); } @@ -375,7 +374,7 @@ fn opTypeFunction(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtim fn opConstant(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { const target = try setupConstant(allocator, rt); // No check on null and sizes, absolute trust in this shit - switch (target.variant.?.Constant[0]) { + switch (target.variant.?.Constant) { .Int => |*i| { if (word_count - 2 != 1) { i.uint64 = @as(u64, try rt.it.next()) | (@as(u64, try rt.it.next()) >> 32); @@ -410,11 +409,9 @@ fn opVariable(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) R target.variant = .{ .Variable = .{ .storage_class = storage_class, - .values = allocator.alloc(Result.Value, member_count) catch return RuntimeError.OutOfMemory, + .value = try Result.initValue(allocator, member_count, rt.mod.results, resolved), }, }; - errdefer allocator.free(target.variant.?.Variable.values); - try Result.initValues(allocator, target.variant.?.Variable.values, rt.mod.results, resolved); _ = initializer; } @@ -462,12 +459,36 @@ fn opLabel(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { }; } -fn opCompositeConstruct(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { +fn opCompositeConstructSetup(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { _ = try setupConstant(allocator, rt); } -fn opCompositeExtract(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { - _ = try setupConstant(allocator, rt); +fn opCompositeConstruct(_: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { + _ = rt.it.skip(); + const id = try rt.it.next(); + + const index_count = word_count - 2; + const target = (rt.results[id].variant orelse return RuntimeError.InvalidSpirV).Constant.getCompositeDataOrNull() orelse return RuntimeError.InvalidSpirV; + for (target[0..index_count]) |*elem| { + const value = (rt.results[try rt.it.next()].variant orelse return RuntimeError.InvalidSpirV).Constant; + elem.* = value; + } +} + +fn opCompositeExtract(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { + _ = rt.it.skip(); + const id = try rt.it.next(); + const composite_id = try rt.it.next(); + + const index_count = word_count - 3; + var composite = (rt.results[composite_id].variant orelse return RuntimeError.InvalidSpirV).Constant; + for (0..index_count) |_| { + const member_id = try rt.it.next(); + composite = (composite.getCompositeDataOrNull() orelse return RuntimeError.InvalidSpirV)[member_id]; + } + rt.results[id].variant = .{ + .Constant = try composite.dupe(allocator), + }; } fn opFunctionEnd(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { @@ -479,71 +500,71 @@ fn opAccessChain(_: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) Runtim const id = try rt.it.next(); const base_id = try rt.it.next(); - const target = &rt.results[id]; - - target.variant = .{ - .AccessChain = .{ - .target = var_type, - .values = undefined, - }, - }; - const base = &rt.results[base_id]; - const values = blk: { + var value_ptr = blk: { if (base.variant) |variant| { switch (variant) { - .Variable => |v| break :blk v.values, + .Variable => |v| break :blk &v.value, + .Constant => |v| break :blk &v, else => {}, } } return RuntimeError.InvalidSpirV; }; - var value_ptr = &values[0]; - const index_count = word_count - 4; + const index_count = word_count - 3; for (0..index_count) |_| { const member = &rt.results[try rt.it.next()]; const member_value = switch (member.variant orelse return RuntimeError.InvalidSpirV) { - .Constant => |c| &c[0], + .Constant => |c| &c, + .Variable => |v| &v.value, else => return RuntimeError.InvalidSpirV, }; switch (member_value.*) { .Int => |i| { - if (i.uint32 > values.len) return RuntimeError.InvalidSpirV; - value_ptr = switch (value_ptr.*) { - .Vector => |v| &v[i.uint32], - .Matrix => |m| &m[i.uint32], + switch (value_ptr.*) { + .Vector => |v| { + if (i.uint32 > v.len) return RuntimeError.InvalidSpirV; + value_ptr = &v[i.uint32]; + }, + .Matrix => |m| { + if (i.uint32 > m.len) return RuntimeError.InvalidSpirV; + value_ptr = &m[i.uint32]; + }, .Array => |_| return RuntimeError.ToDo, - .Structure => |s| &s[i.uint32], + .Structure => |s| { + if (i.uint32 > s.len) return RuntimeError.InvalidSpirV; + value_ptr = &s[i.uint32]; + }, else => return RuntimeError.InvalidSpirV, - }; + } }, else => return RuntimeError.InvalidSpirV, } } - target.variant.?.AccessChain.values = switch (value_ptr.*) { - .Vector => |v| v, - .Matrix => |m| m, - .Array => |_| return RuntimeError.ToDo, - .Structure => |s| s, - else => @as([*]Result.Value, @ptrCast(value_ptr))[0..1], + + rt.results[id].variant = .{ + .AccessChain = .{ + .target = var_type, + .value = value_ptr.*, + }, }; } fn opStore(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { const ptr_id = try rt.it.next(); const val_id = try rt.it.next(); - copyValues( + copyValue( switch (rt.results[ptr_id].variant orelse return RuntimeError.InvalidSpirV) { - .Variable => |v| v.values, - .Constant => |c| c, - .AccessChain => |a| a.values, + .Variable => |*v| &v.value, + .Constant => |*c| c, + .AccessChain => |*a| &a.value, else => return RuntimeError.InvalidSpirV, }, switch (rt.results[val_id].variant orelse return RuntimeError.InvalidSpirV) { - .Variable => |v| v.values, - .Constant => |c| c, - .AccessChain => |a| a.values, + .Variable => |v| &v.value, + .Constant => |c| &c, + .AccessChain => |a| &a.value, else => return RuntimeError.InvalidSpirV, }, ); @@ -557,17 +578,17 @@ fn opLoad(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { _ = rt.it.skip(); const id = try rt.it.next(); const ptr_id = try rt.it.next(); - copyValues( + copyValue( switch (rt.results[id].variant orelse return RuntimeError.InvalidSpirV) { - .Variable => |v| v.values, - .Constant => |c| c, - .AccessChain => |a| a.values, + .Variable => |*v| &v.value, + .Constant => |*c| c, + .AccessChain => |*a| &a.value, else => return RuntimeError.InvalidSpirV, }, switch (rt.results[ptr_id].variant orelse return RuntimeError.InvalidSpirV) { - .Variable => |v| v.values, - .Constant => |c| c, - .AccessChain => |a| a.values, + .Variable => |v| &v.value, + .Constant => |c| &c, + .AccessChain => |a| &a.value, else => return RuntimeError.InvalidSpirV, }, ); @@ -584,7 +605,7 @@ fn opReturn(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { } } -inline fn setupConstant(allocator: std.mem.Allocator, rt: *Runtime) RuntimeError!*Result { +fn setupConstant(allocator: std.mem.Allocator, rt: *Runtime) RuntimeError!*Result { const res_type = try rt.it.next(); const id = try rt.it.next(); const target = &rt.mod.results[id]; @@ -594,9 +615,7 @@ inline fn setupConstant(allocator: std.mem.Allocator, rt: *Runtime) RuntimeError if (member_count == 0) { return RuntimeError.InvalidSpirV; } - target.variant = .{ .Constant = allocator.alloc(Result.Value, member_count) catch return RuntimeError.OutOfMemory }; - errdefer allocator.free(target.variant.?.Constant); - try Result.initValues(allocator, target.variant.?.Constant, rt.mod.results, resolved); + target.variant = .{ .Constant = try Result.initValue(allocator, member_count, rt.mod.results, resolved) }; return target; } @@ -629,17 +648,15 @@ fn readStringN(allocator: std.mem.Allocator, it: *WordIterator, n: usize) Runtim } fn copyValue(dst: *Result.Value, src: *const Result.Value) void { - switch (src.*) { - .Vector => |v| copyValues(dst.Vector, v), - .Matrix => |m| copyValues(dst.Matrix, m), - .Array => |_| unreachable, - .Structure => |s| copyValues(dst.Structure, s), - else => dst.* = src.*, - } -} - -inline fn copyValues(dst: []Result.Value, src: []const Result.Value) void { - for (dst, src) |*d, *s| { - copyValue(d, s); + if (src.getCompositeDataOrNull()) |src_slice| { + if (dst.getCompositeDataOrNull()) |dst_slice| { + for (0..@min(dst_slice.len, src_slice.len)) |i| { + copyValue(&dst_slice[i], &src_slice[i]); + } + } else { + unreachable; + } + } else { + dst.* = src.*; } }