working POC
This commit is contained in:
30
.gdb_history
git.filemode.normal_file
30
.gdb_history
git.filemode.normal_file
@@ -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
|
||||||
32
README.md
32
README.md
@@ -1 +1,31 @@
|
|||||||
test
|
# SPIR-V Interpreter <a href="https://git.kbz8.me/kbz_8/SPIRV-Interpreter/actions?workflows=build.yml"><img src="https://git.kbz8.me/kbz_8/SPIRV-Interpreter/actions/workflows/build.yml/badge.svg"></a> <a href="https://git.kbz8.me/kbz_8/SPIRV-Interpreter/actions?workflows=test.yml"><img src="https://git.kbz8.me/kbz_8/SPIRV-Interpreter/actions/workflows/test.yml/badge.svg"></a>
|
||||||
|
|
||||||
|
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", .{});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ pub fn build(b: *std.Build) void {
|
|||||||
.name = "spirv_interpreter",
|
.name = "spirv_interpreter",
|
||||||
.root_module = mod,
|
.root_module = mod,
|
||||||
.linkage = .dynamic,
|
.linkage = .dynamic,
|
||||||
//.use_llvm = true,
|
.use_llvm = true,
|
||||||
});
|
});
|
||||||
const lib_install = b.addInstallArtifact(lib, .{});
|
const lib_install = b.addInstallArtifact(lib, .{});
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
.name = .SPIRV_Interpreter,
|
.name = .SPIRV_Interpreter,
|
||||||
.version = "0.0.1",
|
.version = "0.0.1",
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
.pretty = .{
|
.pretty = .{ // For debugging purposes
|
||||||
.url = "https://github.com/timfayz/pretty/archive/refs/heads/main.tar.gz",
|
.url = "git+https://github.com/Kbz-8/pretty#117674465efd4d07d5ae9d9d8ca59c2c323a65ba",
|
||||||
.hash = "pretty-0.10.6-Tm65r6lPAQCBxgwzehYPeqsCXQDT9kt2ktJuO-2tRfE6",
|
.hash = "pretty-0.10.6-Tm65r99UAQDEJMgZysD10qE8dinBHr064fPM6YkxVPfB",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.minimum_zig_version = "0.15.2",
|
.minimum_zig_version = "0.15.2",
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ pub fn main() !void {
|
|||||||
try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main"));
|
try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main"));
|
||||||
var output: [4]f32 = undefined;
|
var output: [4]f32 = undefined;
|
||||||
try rt.readOutput(f32, output[0..output.len], try rt.getResultByName("color"));
|
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", .{});
|
std.log.info("Successfully executed", .{});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,6 @@ struct FragOut
|
|||||||
fn main() -> FragOut
|
fn main() -> FragOut
|
||||||
{
|
{
|
||||||
let output: 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;
|
return output;
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -1,7 +1,7 @@
|
|||||||
; SPIR-V
|
; SPIR-V
|
||||||
; Version: 1.0
|
; Version: 1.0
|
||||||
; Generator: SirLynix Nazara ShaderLang Compiler; 4226
|
; Generator: SirLynix Nazara ShaderLang Compiler; 4226
|
||||||
; Bound: 20
|
; Bound: 23
|
||||||
; Schema: 0
|
; Schema: 0
|
||||||
OpCapability Shader
|
OpCapability Shader
|
||||||
OpMemoryModel Logical GLSL450
|
OpMemoryModel Logical GLSL450
|
||||||
@@ -24,17 +24,20 @@
|
|||||||
%_ptr_Function_FragOut = OpTypePointer Function %FragOut
|
%_ptr_Function_FragOut = OpTypePointer Function %FragOut
|
||||||
%int = OpTypeInt 32 1
|
%int = OpTypeInt 32 1
|
||||||
%int_0 = OpConstant %int 0
|
%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
|
%float_1 = OpConstant %float 1
|
||||||
%_ptr_Function_v4float = OpTypePointer Function %v4float
|
%_ptr_Function_v4float = OpTypePointer Function %v4float
|
||||||
%color = OpVariable %_ptr_Output_v4float Output
|
%color = OpVariable %_ptr_Output_v4float Output
|
||||||
%main = OpFunction %void None %2
|
%main = OpFunction %void None %2
|
||||||
%13 = OpLabel
|
%16 = OpLabel
|
||||||
%14 = OpVariable %_ptr_Function_FragOut Function
|
%17 = OpVariable %_ptr_Function_FragOut Function
|
||||||
%15 = OpCompositeConstruct %v4float %float_1 %float_1 %float_1 %float_1
|
%18 = OpCompositeConstruct %v4float %float_4 %float_3 %float_2 %float_1
|
||||||
%16 = OpAccessChain %_ptr_Function_v4float %14 %int_0
|
%19 = OpAccessChain %_ptr_Function_v4float %17 %int_0
|
||||||
OpStore %16 %15
|
OpStore %19 %18
|
||||||
%18 = OpLoad %FragOut %14
|
%21 = OpLoad %FragOut %17
|
||||||
%19 = OpCompositeExtract %v4float %18 0
|
%22 = OpCompositeExtract %v4float %21 0
|
||||||
OpStore %color %19
|
OpStore %color %22
|
||||||
OpReturn
|
OpReturn
|
||||||
OpFunctionEnd
|
OpFunctionEnd
|
||||||
|
|||||||
@@ -72,9 +72,9 @@ geometry_output_count: SpvWord,
|
|||||||
geometry_input: SpvWord,
|
geometry_input: SpvWord,
|
||||||
geometry_output: SpvWord,
|
geometry_output: SpvWord,
|
||||||
|
|
||||||
input_locations: std.AutoHashMap(SpvWord, []Value),
|
input_locations: std.ArrayList(SpvWord),
|
||||||
output_locations: std.AutoHashMap(SpvWord, []Value),
|
output_locations: std.ArrayList(SpvWord),
|
||||||
bindings: std.AutoHashMap(SpvBinding, []Value),
|
bindings: std.AutoHashMap(SpvBinding, Value),
|
||||||
push_constants: []Value,
|
push_constants: []Value,
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator, source: []const SpvWord) ModuleError!Self {
|
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_x = 1,
|
||||||
.local_size_y = 1,
|
.local_size_y = 1,
|
||||||
.local_size_z = 1,
|
.local_size_z = 1,
|
||||||
.input_locations = std.AutoHashMap(SpvWord, []Value).init(allocator),
|
.input_locations = std.ArrayList(SpvWord).empty,
|
||||||
.output_locations = std.AutoHashMap(SpvWord, []Value).init(allocator),
|
.output_locations = std.ArrayList(SpvWord).empty,
|
||||||
.bindings = std.AutoHashMap(SpvBinding, []Value).init(allocator),
|
.bindings = std.AutoHashMap(SpvBinding, Value).init(allocator),
|
||||||
});
|
});
|
||||||
errdefer self.deinit(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
|
_ = self.it.skip(); // Skip schema
|
||||||
|
|
||||||
try self.pass(allocator); // Setup pass
|
try self.pass(allocator); // Setup pass
|
||||||
try self.populateMaps();
|
try self.populateMaps(allocator);
|
||||||
|
|
||||||
if (std.process.hasEnvVarConstant("SPIRV_INTERPRETER_DEBUG_LOGS")) {
|
if (std.process.hasEnvVarConstant("SPIRV_INTERPRETER_DEBUG_LOGS")) {
|
||||||
var capability_set_names: std.ArrayList([]const u8) = .empty;
|
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| {
|
for (self.results, 0..) |result, id| {
|
||||||
if (result.variant == null or std.meta.activeTag(result.variant.?) != .Variable) continue;
|
if (result.variant == null or std.meta.activeTag(result.variant.?) != .Variable) continue;
|
||||||
const variable = result.variant.?.Variable;
|
switch (result.variant.?.Variable.storage_class) {
|
||||||
switch (variable.storage_class) {
|
|
||||||
.Output => for (result.decorations.items) |decoration| switch (decoration.rtype) {
|
.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 => {},
|
||||||
},
|
},
|
||||||
else => {},
|
else => {},
|
||||||
@@ -212,8 +211,8 @@ fn populateMaps(self: *Self) ModuleError!void {
|
|||||||
|
|
||||||
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
||||||
allocator.free(self.code);
|
allocator.free(self.code);
|
||||||
self.input_locations.deinit();
|
self.input_locations.deinit(allocator);
|
||||||
self.output_locations.deinit();
|
self.output_locations.deinit(allocator);
|
||||||
self.bindings.deinit();
|
self.bindings.deinit();
|
||||||
for (self.entry_points.items) |entry| {
|
for (self.entry_points.items) |entry| {
|
||||||
allocator.free(entry.name);
|
allocator.free(entry.name);
|
||||||
|
|||||||
215
src/Result.zig
215
src/Result.zig
@@ -55,7 +55,7 @@ const Decoration = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const Value = union(Type) {
|
pub const Value = union(Type) {
|
||||||
Void: struct {},
|
Void: noreturn,
|
||||||
Bool: bool,
|
Bool: bool,
|
||||||
Int: extern union {
|
Int: extern union {
|
||||||
sint8: i8,
|
sint8: i8,
|
||||||
@@ -77,69 +77,90 @@ pub const Value = union(Type) {
|
|||||||
Array: struct {},
|
Array: struct {},
|
||||||
RuntimeArray: struct {},
|
RuntimeArray: struct {},
|
||||||
Structure: []Value,
|
Structure: []Value,
|
||||||
Function: struct {},
|
Function: noreturn,
|
||||||
Image: struct {},
|
Image: struct {},
|
||||||
Sampler: struct {},
|
Sampler: struct {},
|
||||||
SampledImage: 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 resolved = results[target].resolveType(results);
|
||||||
const member_count = resolved.getMemberCounts();
|
const member_count = resolved.getMemberCounts();
|
||||||
|
|
||||||
switch (resolved.variant.?) {
|
return switch (resolved.variant.?) {
|
||||||
.Type => |t| switch (t) {
|
.Type => |t| switch (t) {
|
||||||
.Bool, .Int, .Float => std.debug.assert(member_count == 1),
|
.Bool => .{ .Bool = false },
|
||||||
.Structure => |s| {
|
.Int => .{ .Int = .{ .uint64 = 0 } },
|
||||||
self.* = .{
|
.Float => .{ .Float = .{ .float64 = 0.0 } },
|
||||||
.Structure = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory,
|
.Vector => |v| blk: {
|
||||||
};
|
var self: Value = .{ .Vector = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory };
|
||||||
for (self.Structure, s.members) |*value, member| {
|
errdefer self.deinit(allocator);
|
||||||
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,
|
|
||||||
};
|
|
||||||
for (self.Vector) |*value| {
|
for (self.Vector) |*value| {
|
||||||
value.* = switch (v.components_type) {
|
value.* = try Value.init(allocator, results, v.components_type_word);
|
||||||
inline else => |tag| @unionInit(Value, @tagName(tag), undefined),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
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
|
/// 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.*) {
|
return switch (self.*) {
|
||||||
.Vector => |v| .{
|
.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 => |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 => |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.*,
|
else => self.*,
|
||||||
};
|
};
|
||||||
@@ -147,9 +168,18 @@ pub const Value = union(Type) {
|
|||||||
|
|
||||||
fn deinit(self: *Value, allocator: std.mem.Allocator) void {
|
fn deinit(self: *Value, allocator: std.mem.Allocator) void {
|
||||||
switch (self.*) {
|
switch (self.*) {
|
||||||
.Structure => |values| allocator.free(values),
|
.Vector => |values| {
|
||||||
.Matrix => |values| allocator.free(values),
|
for (values) |*value| value.deinit(allocator);
|
||||||
.Vector => |values| allocator.free(values),
|
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 => {},
|
else => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,9 +238,9 @@ variant: ?union(Variant) {
|
|||||||
},
|
},
|
||||||
Variable: struct {
|
Variable: struct {
|
||||||
storage_class: spv.SpvStorageClass,
|
storage_class: spv.SpvStorageClass,
|
||||||
values: []Value,
|
value: Value,
|
||||||
},
|
},
|
||||||
Constant: []Value,
|
Constant: Value,
|
||||||
Function: struct {
|
Function: struct {
|
||||||
source_location: usize,
|
source_location: usize,
|
||||||
return_type: SpvWord,
|
return_type: SpvWord,
|
||||||
@@ -219,7 +249,7 @@ variant: ?union(Variant) {
|
|||||||
},
|
},
|
||||||
AccessChain: struct {
|
AccessChain: struct {
|
||||||
target: SpvWord,
|
target: SpvWord,
|
||||||
values: []Value,
|
value: Value,
|
||||||
},
|
},
|
||||||
FunctionParameter: struct {},
|
FunctionParameter: struct {},
|
||||||
Label: struct {
|
Label: struct {
|
||||||
@@ -254,14 +284,9 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
|||||||
},
|
},
|
||||||
else => {},
|
else => {},
|
||||||
},
|
},
|
||||||
.Constant => |values| {
|
.Constant => |*v| v.deinit(allocator),
|
||||||
for (values) |*value| value.deinit(allocator);
|
.Variable => |*v| v.value.deinit(allocator),
|
||||||
allocator.free(values);
|
//.AccessChain => |*a| a.value.deinit(allocator),
|
||||||
},
|
|
||||||
.Variable => |v| {
|
|
||||||
for (v.values) |*value| value.deinit(allocator);
|
|
||||||
allocator.free(v.values);
|
|
||||||
},
|
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -310,24 +335,10 @@ pub fn dupe(self: *const Self, allocator: std.mem.Allocator) RuntimeError!Self {
|
|||||||
.Variable => |v| break :blk .{
|
.Variable => |v| break :blk .{
|
||||||
.Variable = .{
|
.Variable = .{
|
||||||
.storage_class = v.storage_class,
|
.storage_class = v.storage_class,
|
||||||
.values = blk2: {
|
.value = try v.value.dupe(allocator),
|
||||||
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;
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
.Constant => |c| break :blk .{ .Constant = try c.dupe(allocator) },
|
||||||
.Function => |f| break :blk .{
|
.Function => |f| break :blk .{
|
||||||
.Function = .{
|
.Function = .{
|
||||||
.source_location = f.source_location,
|
.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,
|
.params = allocator.dupe(SpvWord, f.params) catch return RuntimeError.OutOfMemory,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
else => {},
|
else => break :blk variant,
|
||||||
}
|
}
|
||||||
break :blk variant;
|
|
||||||
}
|
}
|
||||||
break :blk null;
|
break :blk null;
|
||||||
},
|
},
|
||||||
@@ -376,37 +386,42 @@ pub fn getMemberCounts(self: *const Self) usize {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initValues(allocator: std.mem.Allocator, values: []Value, results: []const Self, resolved: *const Self) RuntimeError!void {
|
pub fn initValue(allocator: std.mem.Allocator, member_count: usize, results: []const Self, resolved: *const Self) RuntimeError!Value {
|
||||||
switch (resolved.variant.?) {
|
return switch (resolved.variant.?) {
|
||||||
.Type => |t| switch (t) {
|
.Type => |t| switch (t) {
|
||||||
.Bool => values[0] = .{ .Bool = undefined },
|
.Bool => .{ .Bool = false },
|
||||||
.Int => values[0] = .{ .Int = undefined },
|
.Int => .{ .Int = .{ .uint64 = 0 } },
|
||||||
.Float => values[0] = .{ .Float = undefined },
|
.Float => .{ .Float = .{ .float64 = 0.0 } },
|
||||||
.Vector => |v| {
|
.Vector => |v| blk: {
|
||||||
for (values) |*value| {
|
const value: Value = .{ .Vector = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory };
|
||||||
value.* = switch (v.components_type) {
|
errdefer allocator.free(value.Vector);
|
||||||
inline else => |tag| @unionInit(Value, @tagName(tag), undefined),
|
for (value.Vector) |*val| {
|
||||||
};
|
val.* = try Value.init(allocator, results, v.components_type_word);
|
||||||
}
|
}
|
||||||
|
break :blk value;
|
||||||
},
|
},
|
||||||
.Matrix => |m| {
|
.Matrix => |m| blk: {
|
||||||
for (values) |*value| {
|
const value: Value = .{ .Matrix = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory };
|
||||||
try value.initMembers(allocator, results, m.column_type_word);
|
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
|
.Array => |_| RuntimeError.ToDo,
|
||||||
_ = a;
|
.Structure => |s| blk: {
|
||||||
},
|
const value: Value = .{ .Structure = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory };
|
||||||
.Structure => |s| {
|
errdefer allocator.free(value.Structure);
|
||||||
for (values, s.members_type_word) |*value, member_type_word| {
|
for (value.Structure, s.members_type_word) |*v, member_type_word| {
|
||||||
try value.initMembers(allocator, results, member_type_word);
|
v.* = try Value.init(allocator, results, member_type_word);
|
||||||
}
|
}
|
||||||
|
break :blk value;
|
||||||
},
|
},
|
||||||
.Image => {}, // TODO
|
.Image => RuntimeError.ToDo,
|
||||||
.Sampler => {}, // No op
|
.Sampler => RuntimeError.ToDo,
|
||||||
.SampledImage => {}, // TODO
|
.SampledImage => RuntimeError.ToDo,
|
||||||
else => return RuntimeError.InvalidSpirV,
|
else => RuntimeError.InvalidSpirV,
|
||||||
},
|
},
|
||||||
else => return RuntimeError.InvalidSpirV,
|
else => RuntimeError.InvalidSpirV,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,11 +123,18 @@ pub fn callEntryPoint(self: *Self, allocator: std.mem.Allocator, entry_point_ind
|
|||||||
_ = it_tmp.skipN(word_count);
|
_ = it_tmp.skipN(word_count);
|
||||||
self.it = it_tmp;
|
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 {
|
pub fn readOutput(self: *const Self, comptime T: type, output: []T, result: SpvWord) error{NotFound}!void {
|
||||||
if (self.mod.output_locations.get(result)) |out| {
|
if (std.mem.indexOf(SpvWord, self.mod.output_locations.items, &.{result})) |_| {
|
||||||
self.readValue(T, output, &out[0]);
|
self.readValue(T, output, &self.results[result].variant.?.Variable.value);
|
||||||
} else {
|
} else {
|
||||||
return error.NotFound;
|
return error.NotFound;
|
||||||
}
|
}
|
||||||
@@ -143,6 +150,8 @@ fn readValue(self: *const Self, comptime T: type, output: []T, value: *const Res
|
|||||||
.Bool => |b| {
|
.Bool => |b| {
|
||||||
if (T == bool) {
|
if (T == bool) {
|
||||||
output[0] = b;
|
output[0] = b;
|
||||||
|
} else {
|
||||||
|
unreachable;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.Int => |i| {
|
.Int => |i| {
|
||||||
|
|||||||
151
src/opcodes.zig
151
src/opcodes.zig
@@ -19,8 +19,7 @@ pub const SetupDispatcher = block: {
|
|||||||
@setEvalBranchQuota(65535);
|
@setEvalBranchQuota(65535);
|
||||||
break :block std.EnumMap(spv.SpvOp, OpCodeFunc).init(.{
|
break :block std.EnumMap(spv.SpvOp, OpCodeFunc).init(.{
|
||||||
.Capability = opCapability,
|
.Capability = opCapability,
|
||||||
.CompositeConstruct = opCompositeConstruct,
|
.CompositeConstruct = opCompositeConstructSetup,
|
||||||
.CompositeExtract = opCompositeExtract,
|
|
||||||
.Constant = opConstant,
|
.Constant = opConstant,
|
||||||
.Decorate = opDecorate,
|
.Decorate = opDecorate,
|
||||||
.EntryPoint = opEntryPoint,
|
.EntryPoint = opEntryPoint,
|
||||||
@@ -52,6 +51,8 @@ pub const RuntimeDispatcher = block: {
|
|||||||
@setEvalBranchQuota(65535);
|
@setEvalBranchQuota(65535);
|
||||||
break :block std.EnumMap(spv.SpvOp, OpCodeFunc).init(.{
|
break :block std.EnumMap(spv.SpvOp, OpCodeFunc).init(.{
|
||||||
.AccessChain = opAccessChain,
|
.AccessChain = opAccessChain,
|
||||||
|
.CompositeConstruct = opCompositeConstruct,
|
||||||
|
.CompositeExtract = opCompositeExtract,
|
||||||
.Load = opLoad,
|
.Load = opLoad,
|
||||||
.Return = opReturn,
|
.Return = opReturn,
|
||||||
.Store = opStore,
|
.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 {
|
fn opName(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void {
|
||||||
const id = try rt.it.next();
|
const id = try rt.it.next();
|
||||||
if (id >= rt.mod.results.len) return RuntimeError.InvalidSpirV;
|
|
||||||
var result = &rt.mod.results[id];
|
var result = &rt.mod.results[id];
|
||||||
result.* = Result.init();
|
|
||||||
result.name = try readStringN(allocator, &rt.it, word_count - 1);
|
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 {
|
fn opConstant(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void {
|
||||||
const target = try setupConstant(allocator, rt);
|
const target = try setupConstant(allocator, rt);
|
||||||
// No check on null and sizes, absolute trust in this shit
|
// No check on null and sizes, absolute trust in this shit
|
||||||
switch (target.variant.?.Constant[0]) {
|
switch (target.variant.?.Constant) {
|
||||||
.Int => |*i| {
|
.Int => |*i| {
|
||||||
if (word_count - 2 != 1) {
|
if (word_count - 2 != 1) {
|
||||||
i.uint64 = @as(u64, try rt.it.next()) | (@as(u64, try rt.it.next()) >> 32);
|
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 = .{
|
target.variant = .{
|
||||||
.Variable = .{
|
.Variable = .{
|
||||||
.storage_class = storage_class,
|
.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;
|
_ = 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);
|
_ = try setupConstant(allocator, rt);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn opCompositeExtract(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
|
fn opCompositeConstruct(_: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void {
|
||||||
_ = try setupConstant(allocator, rt);
|
_ = 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 {
|
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 id = try rt.it.next();
|
||||||
const base_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 base = &rt.results[base_id];
|
||||||
const values = blk: {
|
var value_ptr = blk: {
|
||||||
if (base.variant) |variant| {
|
if (base.variant) |variant| {
|
||||||
switch (variant) {
|
switch (variant) {
|
||||||
.Variable => |v| break :blk v.values,
|
.Variable => |v| break :blk &v.value,
|
||||||
|
.Constant => |v| break :blk &v,
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return RuntimeError.InvalidSpirV;
|
return RuntimeError.InvalidSpirV;
|
||||||
};
|
};
|
||||||
var value_ptr = &values[0];
|
|
||||||
|
|
||||||
const index_count = word_count - 4;
|
const index_count = word_count - 3;
|
||||||
for (0..index_count) |_| {
|
for (0..index_count) |_| {
|
||||||
const member = &rt.results[try rt.it.next()];
|
const member = &rt.results[try rt.it.next()];
|
||||||
const member_value = switch (member.variant orelse return RuntimeError.InvalidSpirV) {
|
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,
|
else => return RuntimeError.InvalidSpirV,
|
||||||
};
|
};
|
||||||
switch (member_value.*) {
|
switch (member_value.*) {
|
||||||
.Int => |i| {
|
.Int => |i| {
|
||||||
if (i.uint32 > values.len) return RuntimeError.InvalidSpirV;
|
switch (value_ptr.*) {
|
||||||
value_ptr = switch (value_ptr.*) {
|
.Vector => |v| {
|
||||||
.Vector => |v| &v[i.uint32],
|
if (i.uint32 > v.len) return RuntimeError.InvalidSpirV;
|
||||||
.Matrix => |m| &m[i.uint32],
|
value_ptr = &v[i.uint32];
|
||||||
|
},
|
||||||
|
.Matrix => |m| {
|
||||||
|
if (i.uint32 > m.len) return RuntimeError.InvalidSpirV;
|
||||||
|
value_ptr = &m[i.uint32];
|
||||||
|
},
|
||||||
.Array => |_| return RuntimeError.ToDo,
|
.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,
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
else => return RuntimeError.InvalidSpirV,
|
else => return RuntimeError.InvalidSpirV,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
target.variant.?.AccessChain.values = switch (value_ptr.*) {
|
|
||||||
.Vector => |v| v,
|
rt.results[id].variant = .{
|
||||||
.Matrix => |m| m,
|
.AccessChain = .{
|
||||||
.Array => |_| return RuntimeError.ToDo,
|
.target = var_type,
|
||||||
.Structure => |s| s,
|
.value = value_ptr.*,
|
||||||
else => @as([*]Result.Value, @ptrCast(value_ptr))[0..1],
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn opStore(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
|
fn opStore(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
|
||||||
const ptr_id = try rt.it.next();
|
const ptr_id = try rt.it.next();
|
||||||
const val_id = try rt.it.next();
|
const val_id = try rt.it.next();
|
||||||
copyValues(
|
copyValue(
|
||||||
switch (rt.results[ptr_id].variant orelse return RuntimeError.InvalidSpirV) {
|
switch (rt.results[ptr_id].variant orelse return RuntimeError.InvalidSpirV) {
|
||||||
.Variable => |v| v.values,
|
.Variable => |*v| &v.value,
|
||||||
.Constant => |c| c,
|
.Constant => |*c| c,
|
||||||
.AccessChain => |a| a.values,
|
.AccessChain => |*a| &a.value,
|
||||||
else => return RuntimeError.InvalidSpirV,
|
else => return RuntimeError.InvalidSpirV,
|
||||||
},
|
},
|
||||||
switch (rt.results[val_id].variant orelse return RuntimeError.InvalidSpirV) {
|
switch (rt.results[val_id].variant orelse return RuntimeError.InvalidSpirV) {
|
||||||
.Variable => |v| v.values,
|
.Variable => |v| &v.value,
|
||||||
.Constant => |c| c,
|
.Constant => |c| &c,
|
||||||
.AccessChain => |a| a.values,
|
.AccessChain => |a| &a.value,
|
||||||
else => return RuntimeError.InvalidSpirV,
|
else => return RuntimeError.InvalidSpirV,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -557,17 +578,17 @@ fn opLoad(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
|
|||||||
_ = rt.it.skip();
|
_ = rt.it.skip();
|
||||||
const id = try rt.it.next();
|
const id = try rt.it.next();
|
||||||
const ptr_id = try rt.it.next();
|
const ptr_id = try rt.it.next();
|
||||||
copyValues(
|
copyValue(
|
||||||
switch (rt.results[id].variant orelse return RuntimeError.InvalidSpirV) {
|
switch (rt.results[id].variant orelse return RuntimeError.InvalidSpirV) {
|
||||||
.Variable => |v| v.values,
|
.Variable => |*v| &v.value,
|
||||||
.Constant => |c| c,
|
.Constant => |*c| c,
|
||||||
.AccessChain => |a| a.values,
|
.AccessChain => |*a| &a.value,
|
||||||
else => return RuntimeError.InvalidSpirV,
|
else => return RuntimeError.InvalidSpirV,
|
||||||
},
|
},
|
||||||
switch (rt.results[ptr_id].variant orelse return RuntimeError.InvalidSpirV) {
|
switch (rt.results[ptr_id].variant orelse return RuntimeError.InvalidSpirV) {
|
||||||
.Variable => |v| v.values,
|
.Variable => |v| &v.value,
|
||||||
.Constant => |c| c,
|
.Constant => |c| &c,
|
||||||
.AccessChain => |a| a.values,
|
.AccessChain => |a| &a.value,
|
||||||
else => return RuntimeError.InvalidSpirV,
|
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 res_type = try rt.it.next();
|
||||||
const id = try rt.it.next();
|
const id = try rt.it.next();
|
||||||
const target = &rt.mod.results[id];
|
const target = &rt.mod.results[id];
|
||||||
@@ -594,9 +615,7 @@ inline fn setupConstant(allocator: std.mem.Allocator, rt: *Runtime) RuntimeError
|
|||||||
if (member_count == 0) {
|
if (member_count == 0) {
|
||||||
return RuntimeError.InvalidSpirV;
|
return RuntimeError.InvalidSpirV;
|
||||||
}
|
}
|
||||||
target.variant = .{ .Constant = allocator.alloc(Result.Value, member_count) catch return RuntimeError.OutOfMemory };
|
target.variant = .{ .Constant = try Result.initValue(allocator, member_count, rt.mod.results, resolved) };
|
||||||
errdefer allocator.free(target.variant.?.Constant);
|
|
||||||
try Result.initValues(allocator, target.variant.?.Constant, rt.mod.results, resolved);
|
|
||||||
return target;
|
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 {
|
fn copyValue(dst: *Result.Value, src: *const Result.Value) void {
|
||||||
switch (src.*) {
|
if (src.getCompositeDataOrNull()) |src_slice| {
|
||||||
.Vector => |v| copyValues(dst.Vector, v),
|
if (dst.getCompositeDataOrNull()) |dst_slice| {
|
||||||
.Matrix => |m| copyValues(dst.Matrix, m),
|
for (0..@min(dst_slice.len, src_slice.len)) |i| {
|
||||||
.Array => |_| unreachable,
|
copyValue(&dst_slice[i], &src_slice[i]);
|
||||||
.Structure => |s| copyValues(dst.Structure, s),
|
}
|
||||||
else => dst.* = src.*,
|
} else {
|
||||||
}
|
unreachable;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
inline fn copyValues(dst: []Result.Value, src: []const Result.Value) void {
|
dst.* = src.*;
|
||||||
for (dst, src) |*d, *s| {
|
|
||||||
copyValue(d, s);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user