adding real runtime
This commit is contained in:
@@ -13,10 +13,13 @@ pub fn main() !void {
|
||||
var module = try spv.Module.init(allocator, @ptrCast(@alignCast(shader_source)));
|
||||
defer module.deinit(allocator);
|
||||
|
||||
var rt = spv.Runtime.init(&module);
|
||||
defer rt.deinit();
|
||||
var rt = try spv.Runtime.init(allocator, &module);
|
||||
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", .{});
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ const lib = @import("lib.zig");
|
||||
const spv = @import("spv.zig");
|
||||
const op = @import("opcodes.zig");
|
||||
|
||||
const pretty = @import("pretty");
|
||||
|
||||
const SpvVoid = spv.SpvVoid;
|
||||
const SpvByte = spv.SpvByte;
|
||||
const SpvWord = spv.SpvWord;
|
||||
@@ -60,7 +58,7 @@ memory_model: spv.SpvMemoryModel,
|
||||
files: std.ArrayList(SpvSource),
|
||||
extensions: std.ArrayList([]const u8),
|
||||
|
||||
results: std.ArrayList(Result),
|
||||
results: []Result,
|
||||
|
||||
entry_points: std.ArrayList(SpvEntryPoint),
|
||||
capabilities: std.EnumSet(spv.SpvCapability),
|
||||
@@ -74,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.AutoHashMap(SpvWord, []Value),
|
||||
output_locations: std.AutoHashMap(SpvWord, []Value),
|
||||
bindings: std.AutoHashMap(SpvBinding, []Value),
|
||||
push_constants: []Value,
|
||||
|
||||
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,
|
||||
.files = std.ArrayList(SpvSource).empty,
|
||||
.extensions = std.ArrayList([]const u8).empty,
|
||||
.results = std.ArrayList(Result).empty,
|
||||
.entry_points = std.ArrayList(SpvEntryPoint).empty,
|
||||
.capabilities = std.EnumSet(spv.SpvCapability).initEmpty(),
|
||||
.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.AutoHashMap(SpvWord, []Value).init(allocator),
|
||||
.output_locations = std.AutoHashMap(SpvWord, []Value).init(allocator),
|
||||
.bindings = std.AutoHashMap(SpvBinding, []Value).init(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.bound = self.it.next() catch return ModuleError.InvalidSpirV;
|
||||
self.results.resize(allocator, self.bound) catch return ModuleError.OutOfMemory;
|
||||
for (self.results.items) |*result| {
|
||||
self.results = allocator.alloc(Result, self.bound) catch return ModuleError.OutOfMemory;
|
||||
for (self.results) |*result| {
|
||||
result.* = Result.init();
|
||||
}
|
||||
|
||||
_ = self.it.skip(); // Skip schema
|
||||
|
||||
const prepassOps = std.EnumSet(spv.SpvOp).initMany(&[_]spv.SpvOp{
|
||||
spv.SpvOp.Name,
|
||||
});
|
||||
try self.pass(allocator, prepassOps); // Pre-pass
|
||||
try self.pass(allocator, prepassOps.complement()); // Setup pass
|
||||
try self.pass(allocator); // Setup pass
|
||||
try self.populateMaps();
|
||||
|
||||
if (std.process.hasEnvVarConstant("SPIRV_INTERPRETER_DEBUG_LOGS")) {
|
||||
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,
|
||||
});
|
||||
|
||||
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;
|
||||
@@ -183,19 +177,18 @@ fn checkEndiannessFromSpvMagic(magic: SpvWord) bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
fn pass(self: *Self, allocator: std.mem.Allocator, opcodes: std.EnumSet(spv.SpvOp)) ModuleError!void {
|
||||
var rt = Runtime.init(self);
|
||||
defer rt.deinit();
|
||||
fn pass(self: *Self, allocator: std.mem.Allocator) ModuleError!void {
|
||||
var rt = Runtime.init(allocator, self) catch return ModuleError.OutOfMemory;
|
||||
defer rt.deinit(allocator);
|
||||
|
||||
while (rt.it.nextOrNull()) |opcode_data| {
|
||||
const word_count = ((opcode_data & (~spv.SpvOpCodeMask)) >> spv.SpvWordCountShift) - 1;
|
||||
const opcode = (opcode_data & spv.SpvOpCodeMask);
|
||||
|
||||
var it_tmp = rt.it; // Save because operations may iter on this iterator
|
||||
if (std.enums.fromInt(spv.SpvOp, opcode)) |spv_op| {
|
||||
if (opcodes.contains(spv_op)) {
|
||||
if (op.SetupDispatcher.get(spv_op)) |pfn| {
|
||||
pfn(allocator, word_count, &rt) catch return ModuleError.InvalidSpirV;
|
||||
}
|
||||
if (op.SetupDispatcher.get(spv_op)) |pfn| {
|
||||
pfn(allocator, word_count, &rt) catch return ModuleError.InvalidSpirV;
|
||||
}
|
||||
}
|
||||
_ = 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 {
|
||||
allocator.free(self.code);
|
||||
self.input_locations.deinit();
|
||||
@@ -220,8 +227,8 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
||||
}
|
||||
self.extensions.deinit(allocator);
|
||||
|
||||
for (self.results.items) |*result| {
|
||||
for (self.results) |*result| {
|
||||
result.deinit(allocator);
|
||||
}
|
||||
self.results.deinit(allocator);
|
||||
allocator.free(self.results);
|
||||
}
|
||||
|
||||
220
src/Result.zig
220
src/Result.zig
@@ -55,7 +55,7 @@ const Decoration = struct {
|
||||
};
|
||||
|
||||
pub const Value = union(Type) {
|
||||
Void,
|
||||
Void: struct {},
|
||||
Bool: bool,
|
||||
Int: extern union {
|
||||
sint8: i8,
|
||||
@@ -77,11 +77,11 @@ pub const Value = union(Type) {
|
||||
Array: struct {},
|
||||
RuntimeArray: struct {},
|
||||
Structure: []Value,
|
||||
Function,
|
||||
Function: struct {},
|
||||
Image: struct {},
|
||||
Sampler: struct {},
|
||||
SampledImage: struct {},
|
||||
Pointer,
|
||||
Pointer: struct {},
|
||||
|
||||
fn initMembers(self: *Value, allocator: std.mem.Allocator, results: []const Self, target: SpvWord) RuntimeError!void {
|
||||
const resolved = results[target].resolveType(results);
|
||||
@@ -91,16 +91,19 @@ pub const Value = union(Type) {
|
||||
.Type => |t| switch (t) {
|
||||
.Bool, .Int, .Float => std.debug.assert(member_count == 1),
|
||||
.Structure => |s| {
|
||||
_ = s;
|
||||
//self.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 ?
|
||||
// inline else => |tag| @unionInit(Value, @tagName(tag), undefined),
|
||||
// };
|
||||
//}
|
||||
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;
|
||||
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),
|
||||
@@ -111,7 +114,9 @@ pub const Value = union(Type) {
|
||||
_ = a;
|
||||
},
|
||||
.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| {
|
||||
value.* = switch (v.components_type) {
|
||||
inline else => |tag| @unionInit(Value, @tagName(tag), undefined),
|
||||
@@ -123,47 +128,32 @@ pub const Value = union(Type) {
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//void spvm_member_allocate_typed_value(spvm_member_t val, spvm_result* results, spvm_word type)
|
||||
//{
|
||||
// spvm_result_t type_info = spvm_state_get_type_info(results, &results[type]);
|
||||
// assert(type != 0);
|
||||
//
|
||||
// if (type_info->value_type == spvm_value_type_void ||
|
||||
// type_info->value_type == spvm_value_type_int ||
|
||||
// type_info->value_type == spvm_value_type_float ||
|
||||
// type_info->value_type == spvm_value_type_bool) {
|
||||
// assert(type_info->member_count == 1u);
|
||||
// } else {
|
||||
// spvm_member_allocate_value(val, type_info->member_count);
|
||||
// }
|
||||
//
|
||||
// val->type = type;
|
||||
//
|
||||
// if (type_info->value_type == spvm_value_type_struct) {
|
||||
// for (spvm_word i = 0; i < val->member_count; i++) {
|
||||
// spvm_member_allocate_typed_value(&val->members[i], results, type_info->params[i]);
|
||||
// }
|
||||
// }
|
||||
// else if (type_info->value_type == spvm_value_type_matrix) {
|
||||
// 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);
|
||||
// }
|
||||
//}
|
||||
/// Performs a deep copy
|
||||
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,
|
||||
},
|
||||
.Matrix => |m| .{
|
||||
.Matrix = allocator.dupe(Value, m) catch return RuntimeError.OutOfMemory,
|
||||
},
|
||||
.Structure => |s| .{
|
||||
.Structure = allocator.dupe(Value, s) catch return RuntimeError.OutOfMemory,
|
||||
},
|
||||
else => self.*,
|
||||
};
|
||||
}
|
||||
|
||||
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),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const Self = @This();
|
||||
|
||||
@@ -187,6 +177,7 @@ variant: ?union(Variant) {
|
||||
bit_length: SpvWord,
|
||||
},
|
||||
Vector: struct {
|
||||
components_type_word: SpvWord,
|
||||
components_type: Type,
|
||||
member_count: SpvWord,
|
||||
},
|
||||
@@ -198,10 +189,12 @@ variant: ?union(Variant) {
|
||||
Array: struct {},
|
||||
RuntimeArray: struct {},
|
||||
Structure: struct {
|
||||
members: []const SpvWord,
|
||||
members_type_word: []const SpvWord,
|
||||
members: []Type,
|
||||
member_names: std.ArrayList([]const u8),
|
||||
},
|
||||
Function: struct {
|
||||
source_location: usize,
|
||||
return_type: SpvWord,
|
||||
params: []const SpvWord,
|
||||
},
|
||||
@@ -219,13 +212,19 @@ variant: ?union(Variant) {
|
||||
},
|
||||
Constant: []Value,
|
||||
Function: struct {
|
||||
source_location: usize,
|
||||
return_type: SpvWord,
|
||||
function_type: SpvWord,
|
||||
params: []const SpvWord,
|
||||
},
|
||||
AccessChain: struct {},
|
||||
AccessChain: struct {
|
||||
target: SpvWord,
|
||||
values: []Value,
|
||||
},
|
||||
FunctionParameter: struct {},
|
||||
Label: struct {},
|
||||
Label: struct {
|
||||
source_location: usize,
|
||||
},
|
||||
},
|
||||
|
||||
pub fn init() Self {
|
||||
@@ -246,6 +245,7 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
||||
.Type => |*t| switch (t.*) {
|
||||
.Function => |data| allocator.free(data.params),
|
||||
.Structure => |*data| {
|
||||
allocator.free(data.members_type_word);
|
||||
allocator.free(data.members);
|
||||
for (data.member_names.items) |name| {
|
||||
allocator.free(name);
|
||||
@@ -254,13 +254,97 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
||||
},
|
||||
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 => {},
|
||||
}
|
||||
}
|
||||
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 {
|
||||
return if (self.variant) |variant|
|
||||
switch (variant) {
|
||||
@@ -292,19 +376,7 @@ pub fn getMemberCounts(self: *const Self) usize {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pub fn initConstantValue(self: *Self, allocator: std.mem.Allocator, results: []const Self, target: SpvWord) 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;
|
||||
|
||||
pub fn initValues(allocator: std.mem.Allocator, values: []Value, results: []const Self, resolved: *const Self) RuntimeError!void {
|
||||
switch (resolved.variant.?) {
|
||||
.Type => |t| switch (t) {
|
||||
.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);
|
||||
}
|
||||
},
|
||||
.Array => |a| {
|
||||
.Array => |a| { // TODO
|
||||
_ = a;
|
||||
},
|
||||
.Structure => |s| {
|
||||
for (values, s.members) |*value, member_type| {
|
||||
try value.initMembers(allocator, results, member_type);
|
||||
for (values, s.members_type_word) |*value, member_type_word| {
|
||||
try value.initMembers(allocator, results, member_type_word);
|
||||
}
|
||||
},
|
||||
.Image => {},
|
||||
.Image => {}, // TODO
|
||||
.Sampler => {}, // No op
|
||||
.SampledImage => {},
|
||||
.SampledImage => {}, // TODO
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
},
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
|
||||
149
src/Runtime.zig
149
src/Runtime.zig
@@ -19,26 +19,157 @@ pub const RuntimeError = error{
|
||||
OutOfMemory,
|
||||
Unreachable,
|
||||
Killed,
|
||||
InvalidEntryPoint,
|
||||
ToDo,
|
||||
};
|
||||
|
||||
pub const Function = struct {
|
||||
source_location: usize,
|
||||
result: *Result,
|
||||
};
|
||||
|
||||
mod: *Module,
|
||||
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 {
|
||||
return std.mem.zeroInit(Self, .{
|
||||
current_function: ?*Result,
|
||||
function_stack: std.ArrayList(Function),
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, module: *Module) RuntimeError!Self {
|
||||
return .{
|
||||
.mod = module,
|
||||
.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,
|
||||
});
|
||||
.function_stack = .empty,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *const Self) void {
|
||||
_ = self;
|
||||
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
||||
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 {
|
||||
_ = self;
|
||||
_ = entry;
|
||||
pub fn getEntryPointByName(self: *const Self, name: []const u8) error{NotFound}!SpvWord {
|
||||
for (self.mod.entry_points.items, 0..) |entry_point, i| {
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,3 +54,17 @@ pub fn skipN(self: *Self, count: usize) bool {
|
||||
self.index += count;
|
||||
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;
|
||||
}
|
||||
|
||||
314
src/opcodes.zig
314
src/opcodes.zig
@@ -13,25 +13,22 @@ const SpvByte = spv.SpvByte;
|
||||
const SpvWord = spv.SpvWord;
|
||||
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 SetupDispatcher = block: {
|
||||
@setEvalBranchQuota(65535);
|
||||
break :block std.EnumMap(spv.SpvOp, OpCodeFunc).init(.{
|
||||
.Capability = opCapability,
|
||||
.CompositeConstruct = opCompositeConstruct,
|
||||
.CompositeExtract = opCompositeExtract,
|
||||
.Constant = opConstant,
|
||||
.Decorate = opDecorate,
|
||||
.EntryPoint = opEntryPoint,
|
||||
.ExecutionMode = opExecutionMode,
|
||||
.Function = opFunction,
|
||||
.FunctionEnd = opFunctionEnd,
|
||||
.Label = opLabel,
|
||||
.Load = opLoadSetup,
|
||||
.MemberDecorate = opDecorateMember,
|
||||
.MemberName = opMemberName,
|
||||
.MemoryModel = opMemoryModel,
|
||||
@@ -48,9 +45,16 @@ pub const SetupDispatcher = block: {
|
||||
.TypeVector = opTypeVector,
|
||||
.TypeVoid = opTypeVoid,
|
||||
.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 {
|
||||
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.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 memb = try rt.it.next();
|
||||
|
||||
var result = &rt.mod.results.items[id];
|
||||
var result = &rt.mod.results[id];
|
||||
|
||||
if (result.variant == null) {
|
||||
result.variant = .{
|
||||
.Type = .{
|
||||
.Structure = .{
|
||||
.members_type_word = undefined,
|
||||
.members = undefined,
|
||||
.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 {
|
||||
const id = try rt.it.next();
|
||||
if (id >= rt.mod.results.items.len) return RuntimeError.InvalidSpirV;
|
||||
var result = &rt.mod.results.items[id];
|
||||
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);
|
||||
}
|
||||
@@ -202,15 +207,15 @@ fn opSource(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) Run
|
||||
file.lang_version = try rt.it.next();
|
||||
if (word_count > 2) {
|
||||
const id = try rt.it.next();
|
||||
if (id >= rt.mod.results.items.len) return RuntimeError.InvalidSpirV;
|
||||
if (rt.mod.results.items[id].name) |name| {
|
||||
if (id >= rt.mod.results.len) return RuntimeError.InvalidSpirV;
|
||||
if (rt.mod.results[id].name) |name| {
|
||||
file.file_name = name;
|
||||
}
|
||||
}
|
||||
if (word_count > 3) {
|
||||
const id = try rt.it.next();
|
||||
if (id >= rt.mod.results.items.len) return RuntimeError.InvalidSpirV;
|
||||
if (rt.mod.results.items[id].name) |name| {
|
||||
if (id >= rt.mod.results.len) return RuntimeError.InvalidSpirV;
|
||||
if (rt.mod.results[id].name) |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 {
|
||||
const id = try rt.it.next();
|
||||
rt.mod.results.items[id].variant = .{
|
||||
rt.mod.results[id].variant = .{
|
||||
.Type = .{
|
||||
.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 {
|
||||
const id = try rt.it.next();
|
||||
rt.mod.results.items[id].variant = .{
|
||||
rt.mod.results[id].variant = .{
|
||||
.Type = .{
|
||||
.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 {
|
||||
const id = try rt.it.next();
|
||||
rt.mod.results.items[id].variant = .{
|
||||
rt.mod.results[id].variant = .{
|
||||
.Type = .{
|
||||
.Int = .{
|
||||
.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 {
|
||||
const id = try rt.it.next();
|
||||
rt.mod.results.items[id].variant = .{
|
||||
rt.mod.results[id].variant = .{
|
||||
.Type = .{
|
||||
.Float = .{
|
||||
.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 {
|
||||
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 = .{
|
||||
.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),
|
||||
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 {
|
||||
const id = try rt.it.next();
|
||||
const column_type_word = try rt.it.next();
|
||||
rt.mod.results.items[id].variant = .{
|
||||
rt.mod.results[id].variant = .{
|
||||
.Type = .{
|
||||
.Matrix = .{
|
||||
.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),
|
||||
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 {
|
||||
const id = try rt.it.next();
|
||||
rt.mod.results.items[id].variant = .{
|
||||
rt.mod.results[id].variant = .{
|
||||
.Type = .{
|
||||
.Pointer = .{
|
||||
.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 {
|
||||
const id = try rt.it.next();
|
||||
const members = blk: {
|
||||
const members = allocator.alloc(SpvWord, word_count - 1) catch return RuntimeError.OutOfMemory;
|
||||
const members_type_word, const members = blk: {
|
||||
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);
|
||||
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.*) {
|
||||
.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 {
|
||||
rt.mod.results.items[id].variant = .{
|
||||
rt.mod.results[id].variant = .{
|
||||
.Type = .{
|
||||
.Structure = .{
|
||||
.members_type_word = members_type_word,
|
||||
.members = members,
|
||||
.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 {
|
||||
const id = try rt.it.next();
|
||||
rt.mod.results.items[id].variant = .{
|
||||
rt.mod.results[id].variant = .{
|
||||
.Type = .{
|
||||
.Function = .{
|
||||
.source_location = 0,
|
||||
.return_type = try rt.it.next(),
|
||||
.params = blk: {
|
||||
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 {
|
||||
const var_type = try rt.it.next();
|
||||
const id = try rt.it.next();
|
||||
|
||||
const target = &rt.mod.results.items[id];
|
||||
try target.initConstantValue(allocator, rt.mod.results.items, var_type);
|
||||
|
||||
const target = try setupConstant(allocator, rt);
|
||||
// No check on null and sizes, absolute trust in this shit
|
||||
switch (target.variant.?.Constant[0]) {
|
||||
.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 id = try rt.it.next();
|
||||
const storage_class = try rt.it.nextAs(spv.SpvStorageClass);
|
||||
const initializer: ?SpvWord = if (word_count >= 4) try rt.it.next() else null;
|
||||
_ = var_type;
|
||||
_ = id;
|
||||
_ = storage_class;
|
||||
_ = initializer;
|
||||
|
||||
//rt.mod.results.items[id].variant = .{
|
||||
// .Variable = .{
|
||||
// .storage_class = storage_class,
|
||||
// .value = value: {
|
||||
// const resolved = rt.mod.results.items[var_type].resolveType(rt.mod.results.items);
|
||||
// _ = resolved;
|
||||
// break :value undefined;
|
||||
// },
|
||||
// },
|
||||
//};
|
||||
const target = &rt.mod.results[id];
|
||||
|
||||
const resolved = rt.mod.results[var_type].resolveType(rt.mod.results);
|
||||
const member_count = resolved.getMemberCounts();
|
||||
if (member_count == 0) {
|
||||
return RuntimeError.InvalidSpirV;
|
||||
}
|
||||
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 {
|
||||
@@ -410,12 +425,15 @@ fn opFunction(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeErr
|
||||
_ = rt.it.skip(); // Skip function control
|
||||
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 = .{
|
||||
.source_location = source_location,
|
||||
.return_type = return_type,
|
||||
.function_type = function_type_id,
|
||||
.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) {
|
||||
.Type => |t| switch (t) {
|
||||
.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 {
|
||||
const id = try rt.it.next();
|
||||
rt.mod.results.items[id].variant = .{
|
||||
.Label = .{},
|
||||
rt.mod.results[id].variant = .{
|
||||
.Label = .{
|
||||
.source_location = rt.it.emitSourceLocation() - 2, // Original label location
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn opCompositeConstruct(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
|
||||
_ = rt;
|
||||
fn opCompositeConstruct(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 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 {
|
||||
var str: std.ArrayList(u8) = .empty;
|
||||
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 & 0x0000FF00) >> 8);
|
||||
(str.addOne(allocator) catch return RuntimeError.OutOfMemory).* = @truncate((word & 0x00FF0000) >> 16);
|
||||
(str.addOne(allocator) catch return RuntimeError.OutOfMemory).* = @truncate((word & 0xFF000000) >> 24);
|
||||
if (str.getLast() == 0) {
|
||||
break;
|
||||
}
|
||||
if (str.getLast() == 0) break;
|
||||
}
|
||||
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;
|
||||
for (0..n) |_| {
|
||||
if (it.nextOrNull()) |word| {
|
||||
if (word == 0) break;
|
||||
str.addOneAssumeCapacity().* = @truncate(word & 0x000000FF);
|
||||
str.addOneAssumeCapacity().* = @truncate((word & 0x0000FF00) >> 8);
|
||||
str.addOneAssumeCapacity().* = @truncate((word & 0x00FF0000) >> 16);
|
||||
str.addOneAssumeCapacity().* = @truncate((word & 0xFF000000) >> 24);
|
||||
if (str.getLast() == 0) {
|
||||
break;
|
||||
}
|
||||
if (str.getLast() == 0) break;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user