adding real runtime
All checks were successful
Test / build (push) Successful in 19s
Build / build (push) Successful in 1m30s

This commit is contained in:
2026-01-11 04:22:57 +01:00
parent 5b9f5c93fb
commit 10da5ee648
6 changed files with 582 additions and 185 deletions

View File

@@ -13,10 +13,13 @@ pub fn main() !void {
var module = try spv.Module.init(allocator, @ptrCast(@alignCast(shader_source))); var module = try spv.Module.init(allocator, @ptrCast(@alignCast(shader_source)));
defer module.deinit(allocator); defer module.deinit(allocator);
var rt = spv.Runtime.init(&module); var rt = try spv.Runtime.init(allocator, &module);
defer rt.deinit(); defer rt.deinit(allocator);
try rt.callEntryPoint(0); 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("Successfully executed", .{}); std.log.info("Successfully executed", .{});
} }

View File

@@ -4,8 +4,6 @@ const lib = @import("lib.zig");
const spv = @import("spv.zig"); const spv = @import("spv.zig");
const op = @import("opcodes.zig"); const op = @import("opcodes.zig");
const pretty = @import("pretty");
const SpvVoid = spv.SpvVoid; const SpvVoid = spv.SpvVoid;
const SpvByte = spv.SpvByte; const SpvByte = spv.SpvByte;
const SpvWord = spv.SpvWord; const SpvWord = spv.SpvWord;
@@ -60,7 +58,7 @@ memory_model: spv.SpvMemoryModel,
files: std.ArrayList(SpvSource), files: std.ArrayList(SpvSource),
extensions: std.ArrayList([]const u8), extensions: std.ArrayList([]const u8),
results: std.ArrayList(Result), results: []Result,
entry_points: std.ArrayList(SpvEntryPoint), entry_points: std.ArrayList(SpvEntryPoint),
capabilities: std.EnumSet(spv.SpvCapability), capabilities: std.EnumSet(spv.SpvCapability),
@@ -74,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.AutoHashMap(SpvWord, []Value),
output_locations: std.AutoHashMap(SpvWord, *Value), output_locations: std.AutoHashMap(SpvWord, []Value),
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 {
@@ -84,15 +82,14 @@ pub fn init(allocator: std.mem.Allocator, source: []const SpvWord) ModuleError!S
.code = allocator.dupe(SpvWord, source) catch return ModuleError.OutOfMemory, .code = allocator.dupe(SpvWord, source) catch return ModuleError.OutOfMemory,
.files = std.ArrayList(SpvSource).empty, .files = std.ArrayList(SpvSource).empty,
.extensions = std.ArrayList([]const u8).empty, .extensions = std.ArrayList([]const u8).empty,
.results = std.ArrayList(Result).empty,
.entry_points = std.ArrayList(SpvEntryPoint).empty, .entry_points = std.ArrayList(SpvEntryPoint).empty,
.capabilities = std.EnumSet(spv.SpvCapability).initEmpty(), .capabilities = std.EnumSet(spv.SpvCapability).initEmpty(),
.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.AutoHashMap(SpvWord, []Value).init(allocator),
.output_locations = std.AutoHashMap(SpvWord, *Value).init(allocator), .output_locations = std.AutoHashMap(SpvWord, []Value).init(allocator),
.bindings = std.AutoHashMap(SpvBinding, *Value).init(allocator), .bindings = std.AutoHashMap(SpvBinding, []Value).init(allocator),
}); });
errdefer self.deinit(allocator); errdefer self.deinit(allocator);
@@ -115,18 +112,15 @@ pub fn init(allocator: std.mem.Allocator, source: []const SpvWord) ModuleError!S
self.generator_version = @intCast(generator & 0x0000FFFF); self.generator_version = @intCast(generator & 0x0000FFFF);
self.bound = self.it.next() catch return ModuleError.InvalidSpirV; self.bound = self.it.next() catch return ModuleError.InvalidSpirV;
self.results.resize(allocator, self.bound) catch return ModuleError.OutOfMemory; self.results = allocator.alloc(Result, self.bound) catch return ModuleError.OutOfMemory;
for (self.results.items) |*result| { for (self.results) |*result| {
result.* = Result.init(); result.* = Result.init();
} }
_ = self.it.skip(); // Skip schema _ = self.it.skip(); // Skip schema
const prepassOps = std.EnumSet(spv.SpvOp).initMany(&[_]spv.SpvOp{ try self.pass(allocator); // Setup pass
spv.SpvOp.Name, try self.populateMaps();
});
try self.pass(allocator, prepassOps); // Pre-pass
try self.pass(allocator, prepassOps.complement()); // Setup pass
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;
@@ -166,7 +160,7 @@ pub fn init(allocator: std.mem.Allocator, source: []const SpvWord) ModuleError!S
entry_points, entry_points,
}); });
pretty.print(allocator, self.results, .{ .tab_size = 4, .max_depth = 0 }) catch return ModuleError.OutOfMemory; //@import("pretty").print(allocator, self.results, .{ .tab_size = 4, .max_depth = 0 }) catch return ModuleError.OutOfMemory;
} }
return self; return self;
@@ -183,19 +177,18 @@ fn checkEndiannessFromSpvMagic(magic: SpvWord) bool {
return false; return false;
} }
fn pass(self: *Self, allocator: std.mem.Allocator, opcodes: std.EnumSet(spv.SpvOp)) ModuleError!void { fn pass(self: *Self, allocator: std.mem.Allocator) ModuleError!void {
var rt = Runtime.init(self); var rt = Runtime.init(allocator, self) catch return ModuleError.OutOfMemory;
defer rt.deinit(); defer rt.deinit(allocator);
while (rt.it.nextOrNull()) |opcode_data| { while (rt.it.nextOrNull()) |opcode_data| {
const word_count = ((opcode_data & (~spv.SpvOpCodeMask)) >> spv.SpvWordCountShift) - 1; const word_count = ((opcode_data & (~spv.SpvOpCodeMask)) >> spv.SpvWordCountShift) - 1;
const opcode = (opcode_data & spv.SpvOpCodeMask); const opcode = (opcode_data & spv.SpvOpCodeMask);
var it_tmp = rt.it; // Save because operations may iter on this iterator var it_tmp = rt.it; // Save because operations may iter on this iterator
if (std.enums.fromInt(spv.SpvOp, opcode)) |spv_op| { if (std.enums.fromInt(spv.SpvOp, opcode)) |spv_op| {
if (opcodes.contains(spv_op)) { if (op.SetupDispatcher.get(spv_op)) |pfn| {
if (op.SetupDispatcher.get(spv_op)) |pfn| { pfn(allocator, word_count, &rt) catch return ModuleError.InvalidSpirV;
pfn(allocator, word_count, &rt) catch return ModuleError.InvalidSpirV;
}
} }
} }
_ = it_tmp.skipN(word_count); _ = it_tmp.skipN(word_count);
@@ -203,6 +196,20 @@ fn pass(self: *Self, allocator: std.mem.Allocator, opcodes: std.EnumSet(spv.SpvO
} }
} }
fn populateMaps(self: *Self) 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) {
.Output => for (result.decorations.items) |decoration| switch (decoration.rtype) {
.Location => self.output_locations.put(@intCast(id), variable.values) catch return ModuleError.OutOfMemory,
else => {},
},
else => {},
}
}
}
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();
@@ -220,8 +227,8 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
} }
self.extensions.deinit(allocator); self.extensions.deinit(allocator);
for (self.results.items) |*result| { for (self.results) |*result| {
result.deinit(allocator); result.deinit(allocator);
} }
self.results.deinit(allocator); allocator.free(self.results);
} }

View File

@@ -55,7 +55,7 @@ const Decoration = struct {
}; };
pub const Value = union(Type) { pub const Value = union(Type) {
Void, Void: struct {},
Bool: bool, Bool: bool,
Int: extern union { Int: extern union {
sint8: i8, sint8: i8,
@@ -77,11 +77,11 @@ pub const Value = union(Type) {
Array: struct {}, Array: struct {},
RuntimeArray: struct {}, RuntimeArray: struct {},
Structure: []Value, Structure: []Value,
Function, Function: struct {},
Image: struct {}, Image: struct {},
Sampler: struct {}, Sampler: struct {},
SampledImage: struct {}, SampledImage: struct {},
Pointer, Pointer: struct {},
fn initMembers(self: *Value, allocator: std.mem.Allocator, results: []const Self, target: SpvWord) RuntimeError!void { fn initMembers(self: *Value, allocator: std.mem.Allocator, results: []const Self, target: SpvWord) RuntimeError!void {
const resolved = results[target].resolveType(results); const resolved = results[target].resolveType(results);
@@ -91,16 +91,19 @@ pub const Value = union(Type) {
.Type => |t| switch (t) { .Type => |t| switch (t) {
.Bool, .Int, .Float => std.debug.assert(member_count == 1), .Bool, .Int, .Float => std.debug.assert(member_count == 1),
.Structure => |s| { .Structure => |s| {
_ = s; self.* = .{
//self.Structure = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory; .Structure = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory,
//for (self.Structure, s.members) |*value, member_id| { };
// value.* = switch (results[member_id].variant.?.Type) { // wtf ? for (self.Structure, s.members) |*value, member| {
// inline else => |tag| @unionInit(Value, @tagName(tag), undefined), value.* = switch (member) {
// }; inline else => |tag| @unionInit(Value, @tagName(tag), undefined),
//} };
}
}, },
.Matrix => |m| { .Matrix => |m| {
self.Matrix = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory; self.* = .{
.Matrix = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory,
};
for (self.Matrix) |*value| { for (self.Matrix) |*value| {
value.* = switch (m.column_type) { value.* = switch (m.column_type) {
inline else => |tag| @unionInit(Value, @tagName(tag), undefined), inline else => |tag| @unionInit(Value, @tagName(tag), undefined),
@@ -111,7 +114,9 @@ pub const Value = union(Type) {
_ = a; _ = a;
}, },
.Vector => |v| { .Vector => |v| {
self.Vector = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory; 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.* = switch (v.components_type) {
inline else => |tag| @unionInit(Value, @tagName(tag), undefined), inline else => |tag| @unionInit(Value, @tagName(tag), undefined),
@@ -123,47 +128,32 @@ pub const Value = union(Type) {
else => {}, else => {},
} }
} }
};
//void spvm_member_allocate_typed_value(spvm_member_t val, spvm_result* results, spvm_word type) /// Performs a deep copy
//{ fn dupe(self: *const Value, allocator: std.mem.Allocator) RuntimeError!Value {
// spvm_result_t type_info = spvm_state_get_type_info(results, &results[type]); return switch (self.*) {
// assert(type != 0); .Vector => |v| .{
// .Vector = allocator.dupe(Value, v) catch return RuntimeError.OutOfMemory,
// if (type_info->value_type == spvm_value_type_void || },
// type_info->value_type == spvm_value_type_int || .Matrix => |m| .{
// type_info->value_type == spvm_value_type_float || .Matrix = allocator.dupe(Value, m) catch return RuntimeError.OutOfMemory,
// type_info->value_type == spvm_value_type_bool) { },
// assert(type_info->member_count == 1u); .Structure => |s| .{
// } else { .Structure = allocator.dupe(Value, s) catch return RuntimeError.OutOfMemory,
// spvm_member_allocate_value(val, type_info->member_count); },
// } else => self.*,
// };
// val->type = type; }
//
// if (type_info->value_type == spvm_value_type_struct) { fn deinit(self: *Value, allocator: std.mem.Allocator) void {
// for (spvm_word i = 0; i < val->member_count; i++) { switch (self.*) {
// spvm_member_allocate_typed_value(&val->members[i], results, type_info->params[i]); .Structure => |values| allocator.free(values),
// } .Matrix => |values| allocator.free(values),
// } .Vector => |values| allocator.free(values),
// else if (type_info->value_type == spvm_value_type_matrix) { else => {},
// for (spvm_word i = 0; i < val->member_count; i++) }
// spvm_member_allocate_typed_value(&val->members[i], results, type_info->pointer); }
// } };
// else if (type_info->value_type == spvm_value_type_array) {
// if (results[type_info->pointer].member_count > 0)
// for (spvm_word i = 0; i < val->member_count; i++)
// spvm_member_allocate_typed_value(&val->members[i], results, type_info->pointer);
// } else if (type_info->value_type == spvm_value_type_vector) {
// for (spvm_word i = 0; i < val->member_count; ++i)
// val->members[i].type = type_info->pointer;
// } else {
// // having nested images/samplers is not supported
// assert(type_info->value_type != spvm_value_type_sampled_image);
// assert(type_info->value_type != spvm_value_type_image);
// assert(type_info->value_type != spvm_value_type_sampler);
// }
//}
const Self = @This(); const Self = @This();
@@ -187,6 +177,7 @@ variant: ?union(Variant) {
bit_length: SpvWord, bit_length: SpvWord,
}, },
Vector: struct { Vector: struct {
components_type_word: SpvWord,
components_type: Type, components_type: Type,
member_count: SpvWord, member_count: SpvWord,
}, },
@@ -198,10 +189,12 @@ variant: ?union(Variant) {
Array: struct {}, Array: struct {},
RuntimeArray: struct {}, RuntimeArray: struct {},
Structure: struct { Structure: struct {
members: []const SpvWord, members_type_word: []const SpvWord,
members: []Type,
member_names: std.ArrayList([]const u8), member_names: std.ArrayList([]const u8),
}, },
Function: struct { Function: struct {
source_location: usize,
return_type: SpvWord, return_type: SpvWord,
params: []const SpvWord, params: []const SpvWord,
}, },
@@ -219,13 +212,19 @@ variant: ?union(Variant) {
}, },
Constant: []Value, Constant: []Value,
Function: struct { Function: struct {
source_location: usize,
return_type: SpvWord, return_type: SpvWord,
function_type: SpvWord, function_type: SpvWord,
params: []const SpvWord, params: []const SpvWord,
}, },
AccessChain: struct {}, AccessChain: struct {
target: SpvWord,
values: []Value,
},
FunctionParameter: struct {}, FunctionParameter: struct {},
Label: struct {}, Label: struct {
source_location: usize,
},
}, },
pub fn init() Self { pub fn init() Self {
@@ -246,6 +245,7 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
.Type => |*t| switch (t.*) { .Type => |*t| switch (t.*) {
.Function => |data| allocator.free(data.params), .Function => |data| allocator.free(data.params),
.Structure => |*data| { .Structure => |*data| {
allocator.free(data.members_type_word);
allocator.free(data.members); allocator.free(data.members);
for (data.member_names.items) |name| { for (data.member_names.items) |name| {
allocator.free(name); allocator.free(name);
@@ -254,13 +254,97 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
}, },
else => {}, else => {},
}, },
.Constant => |values| allocator.free(values), .Constant => |values| {
for (values) |*value| value.deinit(allocator);
allocator.free(values);
},
.Variable => |v| {
for (v.values) |*value| value.deinit(allocator);
allocator.free(v.values);
},
else => {}, else => {},
} }
} }
self.decorations.deinit(allocator); self.decorations.deinit(allocator);
} }
/// Performs a deep copy
pub fn dupe(self: *const Self, allocator: std.mem.Allocator) RuntimeError!Self {
return .{
.name = if (self.name) |name| allocator.dupe(u8, name) catch return RuntimeError.OutOfMemory else null,
.decorations = self.decorations.clone(allocator) catch return RuntimeError.OutOfMemory,
.parent = self.parent,
.variant = blk: {
if (self.variant) |variant| {
switch (variant) {
.String => |s| break :blk .{
.String = allocator.dupe(u8, s) catch return RuntimeError.OutOfMemory,
},
.Type => |t| switch (t) {
.Structure => |s| break :blk .{
.Type = .{
.Structure = .{
.members_type_word = allocator.dupe(SpvWord, s.members_type_word) catch return RuntimeError.OutOfMemory,
.members = allocator.dupe(Type, s.members) catch return RuntimeError.OutOfMemory,
.member_names = blk2: {
const member_names = s.member_names.clone(allocator) catch return RuntimeError.OutOfMemory;
for (member_names.items, s.member_names.items) |*new_name, name| {
new_name.* = allocator.dupe(u8, name) catch return RuntimeError.OutOfMemory;
}
break :blk2 member_names;
},
},
},
},
.Function => |f| break :blk .{
.Type = .{
.Function = .{
.source_location = f.source_location,
.return_type = f.return_type,
.params = allocator.dupe(SpvWord, f.params) catch return RuntimeError.OutOfMemory,
},
},
},
else => {},
},
.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;
},
},
.Function => |f| break :blk .{
.Function = .{
.source_location = f.source_location,
.return_type = f.return_type,
.function_type = f.function_type,
.params = allocator.dupe(SpvWord, f.params) catch return RuntimeError.OutOfMemory,
},
},
else => {},
}
break :blk variant;
}
break :blk null;
},
};
}
pub fn resolveType(self: *const Self, results: []const Self) *const Self { pub fn resolveType(self: *const Self, results: []const Self) *const Self {
return if (self.variant) |variant| return if (self.variant) |variant|
switch (variant) { switch (variant) {
@@ -292,19 +376,7 @@ pub fn getMemberCounts(self: *const Self) usize {
return 0; return 0;
} }
pub fn initConstantValue(self: *Self, allocator: std.mem.Allocator, results: []const Self, target: SpvWord) RuntimeError!void { pub fn initValues(allocator: std.mem.Allocator, values: []Value, results: []const Self, resolved: *const Self) RuntimeError!void {
const resolved = results[target].resolveType(results);
const member_count = resolved.getMemberCounts();
if (member_count == 0) return;
self.variant = .{ .Constant = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory };
errdefer switch (self.variant.?) {
.Constant => |c| allocator.free(c),
else => unreachable,
};
const values = self.variant.?.Constant;
switch (resolved.variant.?) { switch (resolved.variant.?) {
.Type => |t| switch (t) { .Type => |t| switch (t) {
.Bool => values[0] = .{ .Bool = undefined }, .Bool => values[0] = .{ .Bool = undefined },
@@ -322,17 +394,17 @@ pub fn initConstantValue(self: *Self, allocator: std.mem.Allocator, results: []c
try value.initMembers(allocator, results, m.column_type_word); try value.initMembers(allocator, results, m.column_type_word);
} }
}, },
.Array => |a| { .Array => |a| { // TODO
_ = a; _ = a;
}, },
.Structure => |s| { .Structure => |s| {
for (values, s.members) |*value, member_type| { for (values, s.members_type_word) |*value, member_type_word| {
try value.initMembers(allocator, results, member_type); try value.initMembers(allocator, results, member_type_word);
} }
}, },
.Image => {}, .Image => {}, // TODO
.Sampler => {}, // No op .Sampler => {}, // No op
.SampledImage => {}, .SampledImage => {}, // TODO
else => return RuntimeError.InvalidSpirV, else => return RuntimeError.InvalidSpirV,
}, },
else => return RuntimeError.InvalidSpirV, else => return RuntimeError.InvalidSpirV,

View File

@@ -19,26 +19,157 @@ pub const RuntimeError = error{
OutOfMemory, OutOfMemory,
Unreachable, Unreachable,
Killed, Killed,
InvalidEntryPoint,
ToDo,
};
pub const Function = struct {
source_location: usize,
result: *Result,
}; };
mod: *Module, mod: *Module,
it: WordIterator, it: WordIterator,
current_function: ?*Result, /// Local deep copy of module's results to be able to run multiple runtimes concurrently
results: []Result,
pub fn init(module: *Module) Self { current_function: ?*Result,
return std.mem.zeroInit(Self, .{ function_stack: std.ArrayList(Function),
pub fn init(allocator: std.mem.Allocator, module: *Module) RuntimeError!Self {
return .{
.mod = module, .mod = module,
.it = module.it, .it = module.it,
.results = blk: {
const results = allocator.dupe(Result, module.results) catch return RuntimeError.OutOfMemory;
for (results, module.results) |*new_result, result| {
new_result.* = result.dupe(allocator) catch return RuntimeError.OutOfMemory;
}
break :blk results;
},
.current_function = null, .current_function = null,
}); .function_stack = .empty,
};
} }
pub fn deinit(self: *const Self) void { pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
_ = self; for (self.results) |*result| {
result.deinit(allocator);
}
allocator.free(self.results);
self.function_stack.deinit(allocator);
} }
pub fn callEntryPoint(self: *Self, entry: SpvWord) RuntimeError!void { pub fn getEntryPointByName(self: *const Self, name: []const u8) error{NotFound}!SpvWord {
_ = self; for (self.mod.entry_points.items, 0..) |entry_point, i| {
_ = entry; if (blk: {
// Not using std.mem.eql as entry point names may have longer size than their content
for (0..@min(name.len, entry_point.name.len)) |j| {
if (name[j] != entry_point.name[j]) break :blk false;
}
break :blk true;
}) return @intCast(i);
}
return error.NotFound;
}
pub fn getResultByName(self: *const Self, name: []const u8) error{NotFound}!SpvWord {
for (self.results, 0..) |result, i| {
if (result.name) |result_name| {
if (blk: {
// Same as entry points
for (0..@min(name.len, result_name.len)) |j| {
if (name[j] != result_name[j]) break :blk false;
}
break :blk true;
}) return @intCast(i);
}
}
return error.NotFound;
}
/// Calls an entry point, `entry_point_index` being the index of the entry point ordered by declaration in the bytecode
pub fn callEntryPoint(self: *Self, allocator: std.mem.Allocator, entry_point_index: SpvWord) RuntimeError!void {
self.reset();
if (entry_point_index > self.mod.entry_points.items.len) return RuntimeError.InvalidEntryPoint;
{
const entry_point_desc = &self.mod.entry_points.items[entry_point_index];
const entry_point_result = &self.mod.results[entry_point_desc.id];
if (entry_point_result.variant) |variant| {
switch (variant) {
.Function => |f| {
if (!self.it.jumpToSourceLocation(f.source_location)) return RuntimeError.InvalidEntryPoint;
},
else => return RuntimeError.InvalidEntryPoint,
}
} else {
return RuntimeError.InvalidEntryPoint;
}
}
while (self.it.nextOrNull()) |opcode_data| {
const word_count = ((opcode_data & (~spv.SpvOpCodeMask)) >> spv.SpvWordCountShift) - 1;
const opcode = (opcode_data & spv.SpvOpCodeMask);
var it_tmp = self.it; // Save because operations may iter on this iterator
if (std.enums.fromInt(spv.SpvOp, opcode)) |spv_op| {
if (op.RuntimeDispatcher.get(spv_op)) |pfn| {
try pfn(allocator, word_count, self);
}
}
_ = it_tmp.skipN(word_count);
self.it = it_tmp;
}
}
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]);
} else {
return error.NotFound;
}
}
fn reset(self: *Self) void {
self.function_stack.clearRetainingCapacity();
self.current_function = null;
}
fn readValue(self: *const Self, comptime T: type, output: []T, value: *const Result.Value) void {
switch (value.*) {
.Bool => |b| {
if (T == bool) {
output[0] = b;
}
},
.Int => |i| {
switch (T) {
i8 => output[0] = i.sint8,
i16 => output[0] = i.sint16,
i32 => output[0] = i.sint32,
i64 => output[0] = i.sint64,
u8 => output[0] = i.uint8,
u16 => output[0] = i.uint16,
u32 => output[0] = i.uint32,
u64 => output[0] = i.uint64,
inline else => unreachable,
}
},
.Float => |f| {
switch (T) {
f16 => output[0] = f.float16,
f32 => output[0] = f.float32,
f64 => output[0] = f.float64,
inline else => unreachable,
}
},
.Vector => |values| for (values, 0..) |v, i| self.readValue(T, output[i..], &v),
.Matrix => |values| for (values, 0..) |v, i| self.readValue(T, output[i..], &v),
.Array => unreachable, // TODO
.Structure => |values| for (values, 0..) |v, i| self.readValue(T, output[i..], &v),
else => unreachable,
}
} }

View File

@@ -54,3 +54,17 @@ pub fn skipN(self: *Self, count: usize) bool {
self.index += count; self.index += count;
return true; return true;
} }
pub fn skipToEnd(self: *Self) void {
self.index = self.buffer.len;
}
pub inline fn emitSourceLocation(self: *const Self) usize {
return self.index;
}
pub inline fn jumpToSourceLocation(self: *Self, source_location: usize) bool {
if (source_location > self.buffer.len) return false;
self.index = source_location;
return true;
}

View File

@@ -13,25 +13,22 @@ const SpvByte = spv.SpvByte;
const SpvWord = spv.SpvWord; const SpvWord = spv.SpvWord;
const SpvBool = spv.SpvBool; const SpvBool = spv.SpvBool;
// DUMB INDEV OPCODES TODO :
// OpVariable X
// OpAccessChain X
// OpStore X
// OpLoad X
// OpCompositeExtract X
// OpReturn X
// OpFunctionEnd X
pub const OpCodeFunc = *const fn (std.mem.Allocator, SpvWord, *Runtime) RuntimeError!void; pub const OpCodeFunc = *const fn (std.mem.Allocator, SpvWord, *Runtime) RuntimeError!void;
pub const SetupDispatcher = block: { 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,
.CompositeExtract = opCompositeExtract,
.Constant = opConstant, .Constant = opConstant,
.Decorate = opDecorate, .Decorate = opDecorate,
.EntryPoint = opEntryPoint, .EntryPoint = opEntryPoint,
.ExecutionMode = opExecutionMode, .ExecutionMode = opExecutionMode,
.Function = opFunction,
.FunctionEnd = opFunctionEnd,
.Label = opLabel,
.Load = opLoadSetup,
.MemberDecorate = opDecorateMember, .MemberDecorate = opDecorateMember,
.MemberName = opMemberName, .MemberName = opMemberName,
.MemoryModel = opMemoryModel, .MemoryModel = opMemoryModel,
@@ -48,9 +45,16 @@ pub const SetupDispatcher = block: {
.TypeVector = opTypeVector, .TypeVector = opTypeVector,
.TypeVoid = opTypeVoid, .TypeVoid = opTypeVoid,
.Variable = opVariable, .Variable = opVariable,
.Function = opFunction, });
.Label = opLabel, };
.CompositeConstruct = opCompositeConstruct,
pub const RuntimeDispatcher = block: {
@setEvalBranchQuota(65535);
break :block std.EnumMap(spv.SpvOp, OpCodeFunc).init(.{
.AccessChain = opAccessChain,
.Load = opLoad,
.Return = opReturn,
.Store = opStore,
}); });
}; };
@@ -59,7 +63,7 @@ fn opCapability(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!voi
} }
fn addDecoration(allocator: std.mem.Allocator, rt: *Runtime, target: SpvWord, decoration_type: spv.SpvDecoration, member: ?SpvWord) RuntimeError!void { fn addDecoration(allocator: std.mem.Allocator, rt: *Runtime, target: SpvWord, decoration_type: spv.SpvDecoration, member: ?SpvWord) RuntimeError!void {
var decoration = rt.mod.results.items[target].decorations.addOne(allocator) catch return RuntimeError.OutOfMemory; var decoration = rt.mod.results[target].decorations.addOne(allocator) catch return RuntimeError.OutOfMemory;
decoration.rtype = decoration_type; decoration.rtype = decoration_type;
decoration.index = if (member) |memb| memb else 0; decoration.index = if (member) |memb| memb else 0;
@@ -155,12 +159,13 @@ fn opMemberName(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime)
const id = try rt.it.next(); const id = try rt.it.next();
const memb = try rt.it.next(); const memb = try rt.it.next();
var result = &rt.mod.results.items[id]; var result = &rt.mod.results[id];
if (result.variant == null) { if (result.variant == null) {
result.variant = .{ result.variant = .{
.Type = .{ .Type = .{
.Structure = .{ .Structure = .{
.members_type_word = undefined,
.members = undefined, .members = undefined,
.member_names = .empty, .member_names = .empty,
}, },
@@ -190,8 +195,8 @@ 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.items.len) return RuntimeError.InvalidSpirV; if (id >= rt.mod.results.len) return RuntimeError.InvalidSpirV;
var result = &rt.mod.results.items[id]; var result = &rt.mod.results[id];
result.* = Result.init(); result.* = Result.init();
result.name = try readStringN(allocator, &rt.it, word_count - 1); result.name = try readStringN(allocator, &rt.it, word_count - 1);
} }
@@ -202,15 +207,15 @@ fn opSource(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) Run
file.lang_version = try rt.it.next(); file.lang_version = try rt.it.next();
if (word_count > 2) { if (word_count > 2) {
const id = try rt.it.next(); const id = try rt.it.next();
if (id >= rt.mod.results.items.len) return RuntimeError.InvalidSpirV; if (id >= rt.mod.results.len) return RuntimeError.InvalidSpirV;
if (rt.mod.results.items[id].name) |name| { if (rt.mod.results[id].name) |name| {
file.file_name = name; file.file_name = name;
} }
} }
if (word_count > 3) { if (word_count > 3) {
const id = try rt.it.next(); const id = try rt.it.next();
if (id >= rt.mod.results.items.len) return RuntimeError.InvalidSpirV; if (id >= rt.mod.results.len) return RuntimeError.InvalidSpirV;
if (rt.mod.results.items[id].name) |name| { if (rt.mod.results[id].name) |name| {
file.source = name; file.source = name;
} }
} }
@@ -222,7 +227,7 @@ fn opSourceExtension(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Run
fn opTypeVoid(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { fn opTypeVoid(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
const id = try rt.it.next(); const id = try rt.it.next();
rt.mod.results.items[id].variant = .{ rt.mod.results[id].variant = .{
.Type = .{ .Type = .{
.Void = .{}, .Void = .{},
}, },
@@ -231,7 +236,7 @@ fn opTypeVoid(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void
fn opTypeBool(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { fn opTypeBool(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
const id = try rt.it.next(); const id = try rt.it.next();
rt.mod.results.items[id].variant = .{ rt.mod.results[id].variant = .{
.Type = .{ .Type = .{
.Bool = .{}, .Bool = .{},
}, },
@@ -240,7 +245,7 @@ fn opTypeBool(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void
fn opTypeInt(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { fn opTypeInt(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
const id = try rt.it.next(); const id = try rt.it.next();
rt.mod.results.items[id].variant = .{ rt.mod.results[id].variant = .{
.Type = .{ .Type = .{
.Int = .{ .Int = .{
.bit_length = try rt.it.next(), .bit_length = try rt.it.next(),
@@ -252,7 +257,7 @@ fn opTypeInt(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
fn opTypeFloat(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { fn opTypeFloat(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
const id = try rt.it.next(); const id = try rt.it.next();
rt.mod.results.items[id].variant = .{ rt.mod.results[id].variant = .{
.Type = .{ .Type = .{
.Float = .{ .Float = .{
.bit_length = try rt.it.next(), .bit_length = try rt.it.next(),
@@ -263,10 +268,12 @@ fn opTypeFloat(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void
fn opTypeVector(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { fn opTypeVector(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
const id = try rt.it.next(); const id = try rt.it.next();
rt.mod.results.items[id].variant = .{ const components_type_word = try rt.it.next();
rt.mod.results[id].variant = .{
.Type = .{ .Type = .{
.Vector = .{ .Vector = .{
.components_type = switch (rt.mod.results.items[try rt.it.next()].variant.?) { .components_type_word = components_type_word,
.components_type = switch (rt.mod.results[components_type_word].variant.?) {
.Type => |t| @as(Result.Type, t), .Type => |t| @as(Result.Type, t),
else => return RuntimeError.InvalidSpirV, else => return RuntimeError.InvalidSpirV,
}, },
@@ -279,11 +286,11 @@ fn opTypeVector(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!voi
fn opTypeMatrix(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { fn opTypeMatrix(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
const id = try rt.it.next(); const id = try rt.it.next();
const column_type_word = try rt.it.next(); const column_type_word = try rt.it.next();
rt.mod.results.items[id].variant = .{ rt.mod.results[id].variant = .{
.Type = .{ .Type = .{
.Matrix = .{ .Matrix = .{
.column_type_word = column_type_word, .column_type_word = column_type_word,
.column_type = switch (rt.mod.results.items[column_type_word].variant.?) { .column_type = switch (rt.mod.results[column_type_word].variant.?) {
.Type => |t| @as(Result.Type, t), .Type => |t| @as(Result.Type, t),
else => return RuntimeError.InvalidSpirV, else => return RuntimeError.InvalidSpirV,
}, },
@@ -295,7 +302,7 @@ fn opTypeMatrix(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!voi
fn opTypePointer(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { fn opTypePointer(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
const id = try rt.it.next(); const id = try rt.it.next();
rt.mod.results.items[id].variant = .{ rt.mod.results[id].variant = .{
.Type = .{ .Type = .{
.Pointer = .{ .Pointer = .{
.storage_class = try rt.it.nextAs(spv.SpvStorageClass), .storage_class = try rt.it.nextAs(spv.SpvStorageClass),
@@ -307,27 +314,36 @@ fn opTypePointer(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!vo
fn opTypeStruct(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { fn opTypeStruct(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void {
const id = try rt.it.next(); const id = try rt.it.next();
const members = blk: { const members_type_word, const members = blk: {
const members = allocator.alloc(SpvWord, word_count - 1) catch return RuntimeError.OutOfMemory; const members_type_word = allocator.alloc(SpvWord, word_count - 1) catch return RuntimeError.OutOfMemory;
errdefer allocator.free(members_type_word);
const members = allocator.alloc(Result.Type, word_count - 1) catch return RuntimeError.OutOfMemory;
errdefer allocator.free(members); errdefer allocator.free(members);
for (members) |*member| {
member.* = try rt.it.next(); for (members_type_word, members) |*member_type_word, *member| {
member_type_word.* = try rt.it.next();
member.* = rt.mod.results[member_type_word.*].variant.?.Type;
} }
break :blk members; break :blk .{ members_type_word, members };
}; };
if (rt.mod.results.items[id].variant) |*variant| { if (rt.mod.results[id].variant) |*variant| {
switch (variant.*) { switch (variant.*) {
.Type => |*t| switch (t.*) { .Type => |*t| switch (t.*) {
.Structure => |*s| s.members = members, .Structure => |*s| {
s.members_type_word = members_type_word;
s.members = members;
},
else => unreachable, else => unreachable,
}, },
else => unreachable, else => unreachable,
} }
} else { } else {
rt.mod.results.items[id].variant = .{ rt.mod.results[id].variant = .{
.Type = .{ .Type = .{
.Structure = .{ .Structure = .{
.members_type_word = members_type_word,
.members = members, .members = members,
.member_names = .empty, .member_names = .empty,
}, },
@@ -338,9 +354,10 @@ fn opTypeStruct(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime)
fn opTypeFunction(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { fn opTypeFunction(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void {
const id = try rt.it.next(); const id = try rt.it.next();
rt.mod.results.items[id].variant = .{ rt.mod.results[id].variant = .{
.Type = .{ .Type = .{
.Function = .{ .Function = .{
.source_location = 0,
.return_type = try rt.it.next(), .return_type = try rt.it.next(),
.params = blk: { .params = blk: {
const params = allocator.alloc(SpvWord, word_count - 2) catch return RuntimeError.OutOfMemory; const params = allocator.alloc(SpvWord, word_count - 2) catch return RuntimeError.OutOfMemory;
@@ -356,12 +373,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 var_type = try rt.it.next(); const target = try setupConstant(allocator, rt);
const id = try rt.it.next();
const target = &rt.mod.results.items[id];
try target.initConstantValue(allocator, rt.mod.results.items, var_type);
// 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[0]) {
.Int => |*i| { .Int => |*i| {
@@ -382,26 +394,29 @@ fn opConstant(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) R
} }
} }
fn opVariable(_: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { fn opVariable(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void {
const var_type = try rt.it.next(); const var_type = try rt.it.next();
const id = try rt.it.next(); const id = try rt.it.next();
const storage_class = try rt.it.nextAs(spv.SpvStorageClass); const storage_class = try rt.it.nextAs(spv.SpvStorageClass);
const initializer: ?SpvWord = if (word_count >= 4) try rt.it.next() else null; const initializer: ?SpvWord = if (word_count >= 4) try rt.it.next() else null;
_ = var_type;
_ = id;
_ = storage_class;
_ = initializer;
//rt.mod.results.items[id].variant = .{ const target = &rt.mod.results[id];
// .Variable = .{
// .storage_class = storage_class, const resolved = rt.mod.results[var_type].resolveType(rt.mod.results);
// .value = value: { const member_count = resolved.getMemberCounts();
// const resolved = rt.mod.results.items[var_type].resolveType(rt.mod.results.items); if (member_count == 0) {
// _ = resolved; return RuntimeError.InvalidSpirV;
// break :value undefined; }
// }, target.variant = .{
// }, .Variable = .{
//}; .storage_class = storage_class,
.values = allocator.alloc(Result.Value, member_count) catch return RuntimeError.OutOfMemory,
},
};
errdefer allocator.free(target.variant.?.Variable.values);
try Result.initValues(allocator, target.variant.?.Variable.values, rt.mod.results, resolved);
_ = initializer;
} }
fn opFunction(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { fn opFunction(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
@@ -410,12 +425,15 @@ fn opFunction(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeErr
_ = rt.it.skip(); // Skip function control _ = rt.it.skip(); // Skip function control
const function_type_id = try rt.it.next(); const function_type_id = try rt.it.next();
rt.mod.results.items[id].variant = .{ const source_location = rt.it.emitSourceLocation();
rt.mod.results[id].variant = .{
.Function = .{ .Function = .{
.source_location = source_location,
.return_type = return_type, .return_type = return_type,
.function_type = function_type_id, .function_type = function_type_id,
.params = params: { .params = params: {
if (rt.mod.results.items[function_type_id].variant) |variant| { if (rt.mod.results[function_type_id].variant) |variant| {
const params_count = switch (variant) { const params_count = switch (variant) {
.Type => |t| switch (t) { .Type => |t| switch (t) {
.Function => |f| f.params.len, .Function => |f| f.params.len,
@@ -430,30 +448,167 @@ fn opFunction(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeErr
}, },
}; };
rt.current_function = &rt.mod.results.items[id]; rt.mod.results[function_type_id].variant.?.Type.Function.source_location = source_location;
rt.current_function = &rt.mod.results[id];
} }
fn opLabel(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { fn opLabel(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
const id = try rt.it.next(); const id = try rt.it.next();
rt.mod.results.items[id].variant = .{ rt.mod.results[id].variant = .{
.Label = .{}, .Label = .{
.source_location = rt.it.emitSourceLocation() - 2, // Original label location
},
}; };
} }
fn opCompositeConstruct(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { fn opCompositeConstruct(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
_ = rt; _ = try setupConstant(allocator, rt);
}
fn opCompositeExtract(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
_ = try setupConstant(allocator, rt);
}
fn opFunctionEnd(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
rt.current_function = null;
}
fn opAccessChain(_: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void {
const var_type = try rt.it.next();
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: {
if (base.variant) |variant| {
switch (variant) {
.Variable => |v| break :blk v.values,
else => {},
}
}
return RuntimeError.InvalidSpirV;
};
var value_ptr = &values[0];
const index_count = word_count - 4;
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],
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],
.Array => |_| return RuntimeError.ToDo,
.Structure => |s| &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],
};
}
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(
switch (rt.results[ptr_id].variant orelse return RuntimeError.InvalidSpirV) {
.Variable => |v| v.values,
.Constant => |c| c,
.AccessChain => |a| a.values,
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,
else => return RuntimeError.InvalidSpirV,
},
);
}
fn opLoadSetup(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
_ = try setupConstant(allocator, rt);
}
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(
switch (rt.results[id].variant orelse return RuntimeError.InvalidSpirV) {
.Variable => |v| v.values,
.Constant => |c| c,
.AccessChain => |a| a.values,
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,
else => return RuntimeError.InvalidSpirV,
},
);
}
fn opReturn(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
_ = rt.function_stack.pop();
if (rt.function_stack.getLastOrNull()) |function| {
_ = rt.it.jumpToSourceLocation(function.source_location);
rt.current_function = function.result;
} else {
rt.current_function = null;
rt.it.skipToEnd();
}
}
inline 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];
const resolved = rt.mod.results[res_type].resolveType(rt.mod.results);
const member_count = resolved.getMemberCounts();
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);
return target;
} }
fn readString(allocator: std.mem.Allocator, it: *WordIterator) RuntimeError![]const u8 { fn readString(allocator: std.mem.Allocator, it: *WordIterator) RuntimeError![]const u8 {
var str: std.ArrayList(u8) = .empty; var str: std.ArrayList(u8) = .empty;
while (it.nextOrNull()) |word| { while (it.nextOrNull()) |word| {
if (word == 0) break;
(str.addOne(allocator) catch return RuntimeError.OutOfMemory).* = @truncate(word & 0x000000FF); (str.addOne(allocator) catch return RuntimeError.OutOfMemory).* = @truncate(word & 0x000000FF);
(str.addOne(allocator) catch return RuntimeError.OutOfMemory).* = @truncate((word & 0x0000FF00) >> 8); (str.addOne(allocator) catch return RuntimeError.OutOfMemory).* = @truncate((word & 0x0000FF00) >> 8);
(str.addOne(allocator) catch return RuntimeError.OutOfMemory).* = @truncate((word & 0x00FF0000) >> 16); (str.addOne(allocator) catch return RuntimeError.OutOfMemory).* = @truncate((word & 0x00FF0000) >> 16);
(str.addOne(allocator) catch return RuntimeError.OutOfMemory).* = @truncate((word & 0xFF000000) >> 24); (str.addOne(allocator) catch return RuntimeError.OutOfMemory).* = @truncate((word & 0xFF000000) >> 24);
if (str.getLast() == 0) { if (str.getLast() == 0) break;
break;
}
} }
return str.toOwnedSlice(allocator); return str.toOwnedSlice(allocator);
} }
@@ -462,14 +617,29 @@ fn readStringN(allocator: std.mem.Allocator, it: *WordIterator, n: usize) Runtim
var str = std.ArrayList(u8).initCapacity(allocator, n * 4) catch return RuntimeError.OutOfMemory; var str = std.ArrayList(u8).initCapacity(allocator, n * 4) catch return RuntimeError.OutOfMemory;
for (0..n) |_| { for (0..n) |_| {
if (it.nextOrNull()) |word| { if (it.nextOrNull()) |word| {
if (word == 0) break;
str.addOneAssumeCapacity().* = @truncate(word & 0x000000FF); str.addOneAssumeCapacity().* = @truncate(word & 0x000000FF);
str.addOneAssumeCapacity().* = @truncate((word & 0x0000FF00) >> 8); str.addOneAssumeCapacity().* = @truncate((word & 0x0000FF00) >> 8);
str.addOneAssumeCapacity().* = @truncate((word & 0x00FF0000) >> 16); str.addOneAssumeCapacity().* = @truncate((word & 0x00FF0000) >> 16);
str.addOneAssumeCapacity().* = @truncate((word & 0xFF000000) >> 24); str.addOneAssumeCapacity().* = @truncate((word & 0xFF000000) >> 24);
if (str.getLast() == 0) { if (str.getLast() == 0) break;
break;
}
} }
} }
return str.toOwnedSlice(allocator); return str.toOwnedSlice(allocator);
} }
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);
}
}