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 db7c59e..ee9794c 100644
Binary files a/example/shader.spv and b/example/shader.spv differ
diff --git a/example/shader.spvasm b/example/shader.spvasm
index 7913d32..2e81960 100644
--- a/example/shader.spvasm
+++ b/example/shader.spvasm
@@ -1,7 +1,7 @@
; SPIR-V
; Version: 1.0
; Generator: SirLynix Nazara ShaderLang Compiler; 4226
-; Bound: 20
+; Bound: 23
; Schema: 0
OpCapability Shader
OpMemoryModel Logical GLSL450
@@ -24,17 +24,20 @@
%_ptr_Function_FragOut = OpTypePointer Function %FragOut
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
+ %float_4 = OpConstant %float 4
+ %float_3 = OpConstant %float 3
+ %float_2 = OpConstant %float 2
%float_1 = OpConstant %float 1
%_ptr_Function_v4float = OpTypePointer Function %v4float
%color = OpVariable %_ptr_Output_v4float Output
%main = OpFunction %void None %2
- %13 = OpLabel
- %14 = OpVariable %_ptr_Function_FragOut Function
- %15 = OpCompositeConstruct %v4float %float_1 %float_1 %float_1 %float_1
- %16 = OpAccessChain %_ptr_Function_v4float %14 %int_0
- OpStore %16 %15
- %18 = OpLoad %FragOut %14
- %19 = OpCompositeExtract %v4float %18 0
- OpStore %color %19
+ %16 = OpLabel
+ %17 = OpVariable %_ptr_Function_FragOut Function
+ %18 = OpCompositeConstruct %v4float %float_4 %float_3 %float_2 %float_1
+ %19 = OpAccessChain %_ptr_Function_v4float %17 %int_0
+ OpStore %19 %18
+ %21 = OpLoad %FragOut %17
+ %22 = OpCompositeExtract %v4float %21 0
+ OpStore %color %22
OpReturn
OpFunctionEnd
diff --git a/src/Module.zig b/src/Module.zig
index cdb4034..a36ef22 100644
--- a/src/Module.zig
+++ b/src/Module.zig
@@ -72,9 +72,9 @@ geometry_output_count: SpvWord,
geometry_input: SpvWord,
geometry_output: SpvWord,
-input_locations: std.AutoHashMap(SpvWord, []Value),
-output_locations: std.AutoHashMap(SpvWord, []Value),
-bindings: std.AutoHashMap(SpvBinding, []Value),
+input_locations: std.ArrayList(SpvWord),
+output_locations: std.ArrayList(SpvWord),
+bindings: std.AutoHashMap(SpvBinding, Value),
push_constants: []Value,
pub fn init(allocator: std.mem.Allocator, source: []const SpvWord) ModuleError!Self {
@@ -87,9 +87,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, []Value).init(allocator),
- .output_locations = std.AutoHashMap(SpvWord, []Value).init(allocator),
- .bindings = std.AutoHashMap(SpvBinding, []Value).init(allocator),
+ .input_locations = std.ArrayList(SpvWord).empty,
+ .output_locations = std.ArrayList(SpvWord).empty,
+ .bindings = std.AutoHashMap(SpvBinding, Value).init(allocator),
});
errdefer self.deinit(allocator);
@@ -120,7 +120,7 @@ pub fn init(allocator: std.mem.Allocator, source: []const SpvWord) ModuleError!S
_ = self.it.skip(); // Skip schema
try self.pass(allocator); // Setup pass
- try self.populateMaps();
+ try self.populateMaps(allocator);
if (std.process.hasEnvVarConstant("SPIRV_INTERPRETER_DEBUG_LOGS")) {
var capability_set_names: std.ArrayList([]const u8) = .empty;
@@ -196,13 +196,12 @@ fn pass(self: *Self, allocator: std.mem.Allocator) ModuleError!void {
}
}
-fn populateMaps(self: *Self) ModuleError!void {
+fn populateMaps(self: *Self, allocator: std.mem.Allocator) ModuleError!void {
for (self.results, 0..) |result, id| {
if (result.variant == null or std.meta.activeTag(result.variant.?) != .Variable) continue;
- const variable = result.variant.?.Variable;
- switch (variable.storage_class) {
+ switch (result.variant.?.Variable.storage_class) {
.Output => 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.*;
}
}