fixing bit insert and extract
All checks were successful
Build / build (push) Successful in 4m58s
Test / build (push) Successful in 11m48s

This commit is contained in:
2026-03-23 04:59:38 +01:00
parent 2d0d3b40fd
commit d1bf1c23f2
5 changed files with 276 additions and 99 deletions

View File

@@ -23,7 +23,7 @@ pub fn main() !void {
try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main")); try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main"));
var output: [4]f32 = undefined; var output: [4]f32 = undefined;
try rt.readOutput(f32, output[0..output.len], try rt.getResultByName("color")); try rt.readOutput(std.mem.asBytes(output[0..output.len]), try rt.getResultByName("color"));
std.log.info("Output: Vec4{any}", .{output}); std.log.info("Output: Vec4{any}", .{output});
} }
std.log.info("Successfully executed", .{}); std.log.info("Successfully executed", .{});

View File

@@ -167,7 +167,7 @@ pub fn init(allocator: std.mem.Allocator, source: []const SpvWord, options: Modu
_ = self.it.skip(); // Skip schema _ = self.it.skip(); // Skip schema
try self.pass(allocator); // Setup pass try self.pass(allocator); // Setup pass
try self.populateMaps(); try self.applyDecorations();
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;
@@ -254,37 +254,49 @@ fn pass(self: *Self, allocator: std.mem.Allocator) ModuleError!void {
self.needed_runtime_bytes += wrapped_allocator.total_bytes_allocated; self.needed_runtime_bytes += wrapped_allocator.total_bytes_allocated;
} }
fn populateMaps(self: *Self) ModuleError!void { fn applyDecorations(self: *Self) ModuleError!void {
for (self.results, 0..) |result, id| { for (self.results, 0..) |result, id| {
if (result.variant == null or std.meta.activeTag(result.variant.?) != .Variable) if (result.variant == null)
continue; continue;
var set: ?usize = null; var set: ?usize = null;
var binding: ?usize = null; var binding: ?usize = null;
for (result.decorations.items) |decoration| { for (result.decorations.items) |decoration| {
switch (result.variant.?.Variable.storage_class) { switch (result.variant.?) {
.Input => { .Variable => |v| {
switch (decoration.rtype) { switch (v.storage_class) {
.BuiltIn => self.builtins.put( .Input => {
std.enums.fromInt(spv.SpvBuiltIn, decoration.literal_1) orelse return ModuleError.InvalidSpirV, switch (decoration.rtype) {
@intCast(id), .BuiltIn => self.builtins.put(
), std.enums.fromInt(spv.SpvBuiltIn, decoration.literal_1) orelse return ModuleError.InvalidSpirV,
.Location => self.input_locations[decoration.literal_1] = @intCast(id), @intCast(id),
),
.Location => self.input_locations[decoration.literal_1] = @intCast(id),
else => {},
}
},
.Output => {
if (decoration.rtype == .Location)
self.output_locations[decoration.literal_1] = @intCast(id);
},
.StorageBuffer, .Uniform, .UniformConstant => {
switch (decoration.rtype) {
.Binding => binding = decoration.literal_1,
.DescriptorSet => set = decoration.literal_1,
else => {},
}
},
else => {}, else => {},
} }
}, },
.Output => { .Type => |t| {
if (decoration.rtype == .Location) switch (t) {
self.output_locations[decoration.literal_1] = @intCast(id); .Structure => |*s| {
}, if (decoration.rtype == .Offset) {
.StorageBuffer, s.members_offsets[decoration.index] = decoration.literal_1;
.Uniform, }
.UniformConstant, },
=> {
switch (decoration.rtype) {
.Binding => binding = decoration.literal_1,
.DescriptorSet => set = decoration.literal_1,
else => {}, else => {},
} }
}, },

View File

@@ -120,6 +120,7 @@ pub const TypeData = union(Type) {
}, },
Structure: struct { Structure: struct {
members_type_word: []const SpvWord, members_type_word: []const SpvWord,
members_offsets: []?SpvWord,
member_names: std.ArrayList([]const u8), member_names: std.ArrayList([]const u8),
}, },
Function: struct { Function: struct {
@@ -146,7 +147,13 @@ pub const TypeData = union(Type) {
.RuntimeArray => |a| a.stride, .RuntimeArray => |a| a.stride,
.Structure => |s| blk: { .Structure => |s| blk: {
var total: usize = 0; var total: usize = 0;
for (s.members_type_word) |type_word| { for (s.members_type_word, 0..) |type_word, i| {
if (i + 1 < s.members_offsets.len) {
if (s.members_offsets[i + 1]) |offset| {
total = offset;
continue;
}
}
total += results[type_word].variant.?.Type.getSize(results); total += results[type_word].variant.?.Type.getSize(results);
} }
break :blk total; break :blk total;
@@ -224,6 +231,7 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
for (data.member_names.items) |name| { for (data.member_names.items) |name| {
allocator.free(name); allocator.free(name);
} }
allocator.free(data.members_offsets);
data.member_names.deinit(allocator); data.member_names.deinit(allocator);
}, },
else => {}, else => {},
@@ -301,6 +309,7 @@ pub fn dupe(self: *const Self, allocator: std.mem.Allocator) RuntimeError!Self {
.Type = .{ .Type = .{
.Structure = .{ .Structure = .{
.members_type_word = allocator.dupe(SpvWord, s.members_type_word) catch return RuntimeError.OutOfMemory, .members_type_word = allocator.dupe(SpvWord, s.members_type_word) catch return RuntimeError.OutOfMemory,
.members_offsets = allocator.dupe(?SpvWord, s.members_offsets) catch return RuntimeError.OutOfMemory,
.member_names = blk2: { .member_names = blk2: {
const member_names = s.member_names.clone(allocator) catch return RuntimeError.OutOfMemory; const member_names = s.member_names.clone(allocator) catch return RuntimeError.OutOfMemory;
for (member_names.items, s.member_names.items) |*new_name, name| { for (member_names.items, s.member_names.items) |*new_name, name| {

View File

@@ -85,7 +85,10 @@ pub const Value = union(Type) {
return @divTrunc(self.data.len, self.stride); return @divTrunc(self.data.len, self.stride);
} }
}, },
Structure: []Self, Structure: struct {
offsets: []const ?SpvWord,
values: []Self,
},
Function: noreturn, Function: noreturn,
Image: struct {}, Image: struct {},
Sampler: struct {}, Sampler: struct {},
@@ -97,12 +100,14 @@ pub const Value = union(Type) {
i32_ptr: *i32, //< For vector specializations i32_ptr: *i32, //< For vector specializations
u32_ptr: *u32, u32_ptr: *u32,
}, },
is_owner_of_uniform_slice: bool = false,
uniform_slice_window: ?[]u8 = null, uniform_slice_window: ?[]u8 = null,
}, },
pub inline fn getCompositeDataOrNull(self: *const Self) ?[]Self { pub inline fn getCompositeDataOrNull(self: *const Self) ?[]Self {
return switch (self.*) { return switch (self.*) {
.Vector, .Matrix, .Array, .Structure => |v| v, .Structure => |*s| s.values,
.Vector, .Matrix, .Array => |v| v,
else => null, else => null,
}; };
} }
@@ -160,11 +165,16 @@ pub const Value = union(Type) {
break :blk self; break :blk self;
}, },
.Structure => |s| blk: { .Structure => |s| blk: {
var self: Self = .{ .Structure = allocator.alloc(Self, member_count) catch return RuntimeError.OutOfMemory }; var self: Self = .{
.Structure = .{
.offsets = allocator.dupe(?SpvWord, s.members_offsets) catch return RuntimeError.OutOfMemory,
.values = allocator.alloc(Self, member_count) catch return RuntimeError.OutOfMemory,
},
};
errdefer self.deinit(allocator); errdefer self.deinit(allocator);
for (self.Structure, s.members_type_word) |*value, member_type_word| { for (self.Structure.values, s.members_type_word) |*value, type_word| {
value.* = try Self.init(allocator, results, member_type_word); value.* = try Self.init(allocator, results, type_word);
} }
break :blk self; break :blk self;
}, },
@@ -210,9 +220,13 @@ pub const Value = union(Type) {
}, },
.Structure => |s| .{ .Structure => |s| .{
.Structure = blk: { .Structure = blk: {
const values = allocator.dupe(Self, s) catch return RuntimeError.OutOfMemory; const offsets = allocator.dupe(?SpvWord, s.offsets) catch return RuntimeError.OutOfMemory;
for (values, s) |*new_value, value| new_value.* = try value.dupe(allocator); const values = allocator.dupe(Self, s.values) catch return RuntimeError.OutOfMemory;
break :blk values; for (values, s.values) |*new_value, value| new_value.* = try value.dupe(allocator);
break :blk .{
.offsets = offsets,
.values = values,
};
}, },
}, },
else => self.*, else => self.*,
@@ -301,7 +315,6 @@ pub const Value = union(Type) {
.Vector, .Vector,
.Matrix, .Matrix,
.Array, .Array,
.Structure,
=> |values| { => |values| {
var offset: usize = 0; var offset: usize = 0;
for (values) |v| { for (values) |v| {
@@ -309,6 +322,20 @@ pub const Value = union(Type) {
} }
return offset; return offset;
}, },
.Structure => |s| {
var offset: usize = 0;
for (s.values, 0..) |v, i| {
const read_size = try v.read(output[offset..]);
if (i + 1 < s.offsets.len) {
if (s.offsets[i + 1]) |o| {
offset = o;
continue;
}
}
offset += read_size;
}
return offset;
},
else => return RuntimeError.InvalidValueType, else => return RuntimeError.InvalidValueType,
} }
return 0; return 0;
@@ -427,7 +454,6 @@ pub const Value = union(Type) {
.Vector, .Vector,
.Matrix, .Matrix,
.Array, .Array,
.Structure,
=> |*values| { => |*values| {
var offset: usize = 0; var offset: usize = 0;
for (values.*) |*v| { for (values.*) |*v| {
@@ -435,6 +461,20 @@ pub const Value = union(Type) {
} }
return offset; return offset;
}, },
.Structure => |s| {
var offset: usize = 0;
for (s.values, 0..) |*v, i| {
const write_size = try v.write(input[offset..]);
if (i + 1 < s.offsets.len) {
if (s.offsets[i + 1]) |o| {
offset = o;
continue;
}
}
offset += write_size;
}
return offset;
},
.RuntimeArray => |*arr| arr.data = input[0..], .RuntimeArray => |*arr| arr.data = input[0..],
else => return RuntimeError.InvalidValueType, else => return RuntimeError.InvalidValueType,
} }
@@ -449,13 +489,26 @@ pub const Value = union(Type) {
.Vector4f32, .Vector4i32, .Vector4u32 => 4 * 4, .Vector4f32, .Vector4i32, .Vector4u32 => 4 * 4,
.Vector3f32, .Vector3i32, .Vector3u32 => 3 * 4, .Vector3f32, .Vector3i32, .Vector3u32 => 3 * 4,
.Vector2f32, .Vector2i32, .Vector2u32 => 2 * 4, .Vector2f32, .Vector2i32, .Vector2u32 => 2 * 4,
.Vector, .Matrix, .Array, .Structure => |values| blk: { .Vector, .Matrix, .Array => |values| blk: {
var size: usize = 0; var size: usize = 0;
for (values) |v| { for (values) |v| {
size += try v.getPlainMemorySize(); size += try v.getPlainMemorySize();
} }
break :blk size; break :blk size;
}, },
.Structure => |s| blk: {
var size: usize = 0;
for (s.values, 0..) |v, i| {
if (i + 1 < s.offsets.len) {
if (s.offsets[i + 1]) |o| {
size = o;
continue;
}
}
size += try v.getPlainMemorySize();
}
break :blk size;
},
.RuntimeArray => |arr| arr.getLen(), .RuntimeArray => |arr| arr.getLen(),
else => return RuntimeError.InvalidValueType, else => return RuntimeError.InvalidValueType,
}; };
@@ -504,12 +557,13 @@ pub const Value = union(Type) {
.common => |ptr| { .common => |ptr| {
_ = try ptr.read(window); _ = try ptr.read(window);
ptr.deinit(allocator); ptr.deinit(allocator);
allocator.destroy(ptr); if (p.is_owner_of_uniform_slice)
allocator.destroy(ptr);
}, },
else => {}, else => {},
} }
p.uniform_slice_window = null;
} }
p.uniform_slice_window = null;
}, },
else => {}, else => {},
} }
@@ -517,10 +571,15 @@ pub const Value = union(Type) {
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
switch (self.*) { switch (self.*) {
.Vector, .Matrix, .Array, .Structure => |values| { .Vector, .Matrix, .Array => |values| {
for (values) |*value| value.deinit(allocator); for (values) |*value| value.deinit(allocator);
allocator.free(values); allocator.free(values);
}, },
.Structure => |s| {
for (s.values) |*value| value.deinit(allocator);
allocator.free(s.values);
allocator.free(s.offsets);
},
else => {}, else => {},
} }
} }

View File

@@ -303,6 +303,8 @@ fn BitOperator(comptime T: ValueType, comptime Op: BitOp) type {
if (T == .Float) @compileError("Invalid value type"); if (T == .Float) @compileError("Invalid value type");
} }
const max_operator_count: usize = 4;
inline fn isUnaryOp() bool { inline fn isUnaryOp() bool {
return comptime switch (Op) { return comptime switch (Op) {
.Not, .BitCount, .BitReverse => true, .Not, .BitCount, .BitReverse => true,
@@ -310,17 +312,88 @@ fn BitOperator(comptime T: ValueType, comptime Op: BitOp) type {
}; };
} }
inline fn bitMask(bits: u64) u64 { inline fn isBinaryOp() bool {
return if (bits >= 32) ~@as(u64, 0) else (@as(u64, 0x1) << @intCast(bits)) - 1; return !isUnaryOp() and !isTernaryOp() and !isQuaternaryOp(); // flemme d'ajouter les opérateurs à chaque fois
}
inline fn isTernaryOp() bool {
return comptime switch (Op) {
.BitFieldUExtract, .BitFieldSExtract => true,
else => false,
};
}
inline fn isQuaternaryOp() bool {
return comptime switch (Op) {
.BitFieldInsert => true,
else => false,
};
}
inline fn getOperatorsCount() usize {
return if (isUnaryOp())
1
else if (isBinaryOp())
2
else if (isTernaryOp())
3
else
4;
} }
inline fn bitInsert(comptime TT: type, base: TT, insert: TT, offset: u64, count: u64) TT { inline fn bitInsert(comptime TT: type, base: TT, insert: TT, offset: u64, count: u64) TT {
const mask: TT = @intCast(bitMask(count) << @intCast(offset)); const info = @typeInfo(TT);
return @as(TT, @intCast((base & ~mask) | ((insert << @intCast(offset)) & mask))); if (info != .int) @compileError("must be an integer type");
const bits: u32 = info.int.bits;
const U = std.meta.Int(.unsigned, bits);
if (count == 0) return base;
const base_u: U = @bitCast(base);
const insert_u: U = @bitCast(insert);
const field_mask: U = if (count == bits)
~@as(U, 0)
else
(@as(U, 1) << @intCast(count)) - 1;
const shift: std.math.Log2Int(U) = @truncate(offset);
const positioned_mask: U = @shlWithOverflow(field_mask, shift)[0];
const positioned_insert: U = @shlWithOverflow(insert_u & field_mask, shift)[0];
return @bitCast((base_u & ~positioned_mask) | positioned_insert);
} }
inline fn bitExtract(comptime TT: type, v: TT, offset: TT, count: u64) TT { inline fn bitExtract(comptime TT: type, comptime signed_result: bool, base: TT, offset: u64, count: u64) TT {
return (v >> @intCast(offset)) & @as(TT, @bitCast(@as(std.meta.Int(.unsigned, @bitSizeOf(TT)), @truncate(bitMask(count))))); const info = @typeInfo(TT);
if (info != .int) @compileError("must be an integer type");
const bits: u32 = info.int.bits;
if (count == 0) return @as(TT, 0);
const U = std.meta.Int(.unsigned, bits);
const base_u: U = @bitCast(base);
const field: U = if (count == bits)
base_u
else
(base_u >> @intCast(offset)) &
((@as(U, 1) << @intCast(count)) - 1);
const result: U = if (!signed_result or count == bits) blk: {
break :blk field;
} else blk: {
const sign_bit: U = @as(U, 1) << @intCast(count - 1);
if ((field & sign_bit) != 0) {
break :blk field | (~@as(U, 0) << @intCast(count));
}
break :blk field;
};
return @bitCast(result);
} }
inline fn operationUnary(comptime TT: type, op1: TT) RuntimeError!TT { inline fn operationUnary(comptime TT: type, op1: TT) RuntimeError!TT {
@@ -337,24 +410,8 @@ fn BitOperator(comptime T: ValueType, comptime Op: BitOp) type {
}; };
} }
inline fn operationBinary(comptime TT: type, rt: *Runtime, op1: TT, op2: TT) RuntimeError!TT { inline fn operationBinary(comptime TT: type, op1: TT, op2: TT) RuntimeError!TT {
return switch (Op) { return switch (Op) {
.BitFieldInsert => blk: {
const offset = try rt.results[try rt.it.next()].getValue();
const count = try rt.results[try rt.it.next()].getValue();
break :blk bitInsert(TT, op1, op2, offset.Int.value.uint64, count.Int.value.uint64);
},
.BitFieldSExtract => blk: {
if (T != .SInt) return RuntimeError.InvalidSpirV;
const count = try rt.results[try rt.it.next()].getValue();
break :blk bitExtract(TT, op1, op2, count.Int.value.uint64);
},
.BitFieldUExtract => blk: {
if (T != .UInt) return RuntimeError.InvalidSpirV;
const count = try rt.results[try rt.it.next()].getValue();
break :blk bitExtract(TT, op1, op2, count.Int.value.uint64);
},
.BitwiseAnd => op1 & op2, .BitwiseAnd => op1 & op2,
.BitwiseOr => op1 | op2, .BitwiseOr => op1 | op2,
.BitwiseXor => op1 ^ op2, .BitwiseXor => op1 ^ op2,
@@ -365,6 +422,27 @@ fn BitOperator(comptime T: ValueType, comptime Op: BitOp) type {
}; };
} }
inline fn operationTernary(comptime TT: type, op1: TT, op2: TT, op3: *const Value) RuntimeError!TT {
return switch (Op) {
.BitFieldSExtract => blk: {
if (T != .SInt) return RuntimeError.InvalidSpirV;
break :blk bitExtract(TT, true, op1, @intCast(op2), op3.Int.value.uint64);
},
.BitFieldUExtract => blk: {
if (T != .UInt) return RuntimeError.InvalidSpirV;
break :blk bitExtract(TT, false, op1, @intCast(op2), op3.Int.value.uint64);
},
else => RuntimeError.InvalidSpirV,
};
}
inline fn operationQuaternary(comptime TT: type, op1: TT, op2: TT, op3: *const Value, op4: *const Value) RuntimeError!TT {
return switch (Op) {
.BitFieldInsert => bitInsert(TT, op1, op2, op3.Int.value.uint64, op4.Int.value.uint64),
else => RuntimeError.InvalidSpirV,
};
}
inline fn readLane(comptime bits: u32, v: *const Value, lane_index: usize) RuntimeError!getValuePrimitiveFieldType(T, bits) { inline fn readLane(comptime bits: u32, v: *const Value, lane_index: usize) RuntimeError!getValuePrimitiveFieldType(T, bits) {
const TT = getValuePrimitiveFieldType(T, bits); const TT = getValuePrimitiveFieldType(T, bits);
@@ -431,18 +509,21 @@ fn BitOperator(comptime T: ValueType, comptime Op: BitOp) type {
} }
} }
fn applyScalarBits(rt: *Runtime, bit_count: SpvWord, dst: *Value, op1_v: *const Value, op2_v: ?*const Value) RuntimeError!void { fn applyScalarBits(bit_count: SpvWord, dst: *Value, ops: [max_operator_count]?*const Value) RuntimeError!void {
switch (bit_count) { switch (bit_count) {
inline 8, 16, 32, 64 => |bits| { inline 8, 16, 32, 64 => |bits| {
const TT = getValuePrimitiveFieldType(T, bits); const TT = getValuePrimitiveFieldType(T, bits);
const a = try readLane(bits, op1_v, 0);
const out: TT = if (comptime isUnaryOp()) const out: TT = blk: {
try operationUnary(TT, a) const a = try readLane(bits, ops[0].?, 0);
else blk: {
const rhs = op2_v orelse return RuntimeError.InvalidSpirV; if (comptime isUnaryOp()) break :blk try operationUnary(TT, a);
const b = try readLane(bits, rhs, 0);
break :blk try operationBinary(TT, rt, a, b); const b = try readLane(bits, ops[1].?, 0);
if (comptime isBinaryOp()) break :blk try operationBinary(TT, a, b);
if (comptime isTernaryOp()) break :blk try operationTernary(TT, a, b, ops[2].?);
if (comptime isQuaternaryOp()) break :blk try operationQuaternary(TT, a, b, ops[2].?, ops[3].?);
}; };
try writeLane(bits, dst, 0, out); try writeLane(bits, dst, 0, out);
@@ -451,30 +532,24 @@ fn BitOperator(comptime T: ValueType, comptime Op: BitOp) type {
} }
} }
fn applyVectorBits(rt: *Runtime, lane_bits: SpvWord, dst: *Value, op1_v: *const Value, op2_v: ?*const Value) RuntimeError!void { fn applyVectorBits(lane_bits: SpvWord, dst: *Value, ops: [max_operator_count]?*const Value) RuntimeError!void {
const dst_len = try dst.getLaneCount(); const dst_len = try dst.getLaneCount();
const op1_len = try op1_v.getLaneCount();
if (op1_v.isVector() and dst_len != op1_len) return RuntimeError.InvalidSpirV;
if (!comptime isUnaryOp()) {
const rhs = op2_v orelse return RuntimeError.InvalidSpirV;
const op2_len = try rhs.getLaneCount();
if (op2_v.?.isVector() and dst_len != op2_len) return RuntimeError.InvalidSpirV;
}
switch (lane_bits) { switch (lane_bits) {
inline 8, 16, 32, 64 => |bits| { inline 8, 16, 32, 64 => |bits| {
const TT = getValuePrimitiveFieldType(T, bits); const TT = getValuePrimitiveFieldType(T, bits);
for (0..dst_len) |i| { for (0..dst_len) |i| {
const a = try readLane(bits, op1_v, if (op1_v.isVector()) i else 0); const out: TT = blk: {
const a = try readLane(bits, ops[0].?, if (ops[0].?.isVector()) i else 0);
const out: TT = if (comptime isUnaryOp()) if (comptime isUnaryOp()) break :blk try operationUnary(TT, a);
try operationUnary(TT, a)
else blk: { const b = try readLane(bits, ops[1].?, if (ops[1].?.isVector()) i else 0);
const rhs = op2_v orelse return RuntimeError.InvalidSpirV;
const b = try readLane(bits, rhs, if (op2_v.?.isVector()) i else 0); if (comptime isBinaryOp()) break :blk try operationBinary(TT, a, b);
break :blk try operationBinary(TT, rt, a, b); if (comptime isTernaryOp()) break :blk try operationTernary(TT, a, b, ops[2].?);
if (comptime isQuaternaryOp()) break :blk try operationQuaternary(TT, a, b, ops[2].?, ops[3].?);
}; };
try writeLane(bits, dst, i, out); try writeLane(bits, dst, i, out);
@@ -489,18 +564,22 @@ fn BitOperator(comptime T: ValueType, comptime Op: BitOp) type {
fn BitEngine(comptime T: ValueType, comptime Op: BitOp) type { fn BitEngine(comptime T: ValueType, comptime Op: BitOp) type {
return struct { return struct {
fn op(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { fn op(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
const target_type = (try rt.results[try rt.it.next()].getVariant()).Type;
const dst = try rt.results[try rt.it.next()].getValue();
const op1 = try rt.results[try rt.it.next()].getValue();
const operator = BitOperator(T, Op); const operator = BitOperator(T, Op);
const op2_value: ?*Value = if (comptime operator.isUnaryOp()) null else try rt.results[try rt.it.next()].getValue(); const target_type = (try rt.results[try rt.it.next()].getVariant()).Type;
const dst = try rt.results[try rt.it.next()].getValue();
var ops = [_]?*Value{null} ** operator.max_operator_count;
ops[0] = try rt.results[try rt.it.next()].getValue();
if (comptime operator.getOperatorsCount() >= 2) ops[1] = try rt.results[try rt.it.next()].getValue();
if (comptime operator.getOperatorsCount() >= 3) ops[2] = try rt.results[try rt.it.next()].getValue();
if (comptime operator.getOperatorsCount() >= 4) ops[3] = try rt.results[try rt.it.next()].getValue();
const lane_bits = try Result.resolveLaneBitWidth(target_type, rt); const lane_bits = try Result.resolveLaneBitWidth(target_type, rt);
switch (dst.*) { switch (dst.*) {
.Int => try operator.applyScalarBits(rt, lane_bits, dst, op1, op2_value), .Int => try operator.applyScalarBits(lane_bits, dst, ops),
.Vector, .Vector,
.Vector2i32, .Vector2i32,
@@ -509,7 +588,7 @@ fn BitEngine(comptime T: ValueType, comptime Op: BitOp) type {
.Vector2u32, .Vector2u32,
.Vector3u32, .Vector3u32,
.Vector4u32, .Vector4u32,
=> try operator.applyVectorBits(rt, lane_bits, dst, op1, op2_value), => try operator.applyVectorBits(lane_bits, dst, ops),
else => return RuntimeError.InvalidSpirV, else => return RuntimeError.InvalidSpirV,
} }
@@ -1069,7 +1148,8 @@ fn copyValue(dst: *Value, src: *const Value) void {
inline fn getDstSlice(v: *Value) ?[]Value { inline fn getDstSlice(v: *Value) ?[]Value {
return switch (v.*) { return switch (v.*) {
.Vector, .Matrix, .Array, .Structure => |s| s, .Vector, .Matrix, .Array => |s| s,
.Structure => |s| s.values,
else => null, else => null,
}; };
} }
@@ -1163,10 +1243,14 @@ fn copyValue(dst: *Value, src: *const Value) void {
} }
switch (src.*) { switch (src.*) {
.Vector, .Matrix, .Array, .Structure => |src_slice| { .Vector, .Matrix, .Array => |src_slice| {
const dst_slice = helpers.getDstSlice(dst); const dst_slice = helpers.getDstSlice(dst);
helpers.copySlice(dst_slice.?, src_slice); helpers.copySlice(dst_slice.?, src_slice);
}, },
.Structure => |s| {
const dst_slice = helpers.getDstSlice(dst);
helpers.copySlice(dst_slice.?, s.values);
},
.Pointer => |ptr| switch (ptr.ptr) { .Pointer => |ptr| switch (ptr.ptr) {
.common => |src_val_ptr| copyValue(dst, src_val_ptr), .common => |src_val_ptr| copyValue(dst, src_val_ptr),
.f32_ptr => |src_f32_ptr| helpers.readF32(dst, src_f32_ptr), .f32_ptr => |src_f32_ptr| helpers.readF32(dst, src_f32_ptr),
@@ -1236,9 +1320,11 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime
.AccessChain = .{ .AccessChain = .{
.target = var_type, .target = var_type,
.value = blk: { .value = blk: {
var is_owner_of_uniform_slice = false;
var uniform_slice_window: ?[]u8 = null; var uniform_slice_window: ?[]u8 = null;
for (0..index_count) |_| { for (0..index_count) |index| {
const is_last = (index == index_count - 1);
const member = &rt.results[try rt.it.next()]; const member = &rt.results[try rt.it.next()];
const member_value = switch ((try member.getVariant()).*) { const member_value = switch ((try member.getVariant()).*) {
.Constant => |c| &c.value, .Constant => |c| &c.value,
@@ -1253,13 +1339,19 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime
} }
switch (value_ptr.*) { switch (value_ptr.*) {
.Vector, .Matrix, .Array, .Structure => |v| { .Vector, .Matrix, .Array => |v| {
if (i.value.uint32 >= v.len) return RuntimeError.OutOfBounds; if (i.value.uint32 >= v.len) return RuntimeError.OutOfBounds;
value_ptr = &v[i.value.uint32]; value_ptr = &v[i.value.uint32];
}, },
.Structure => |s| {
if (i.value.uint32 >= s.values.len) return RuntimeError.OutOfBounds;
value_ptr = &s.values[i.value.uint32];
},
.RuntimeArray => |*arr| { .RuntimeArray => |*arr| {
if (i.value.uint32 >= arr.getLen()) return RuntimeError.OutOfBounds; if (i.value.uint32 >= arr.getLen()) return RuntimeError.OutOfBounds;
value_ptr = try arr.createValueFromIndex(allocator, rt.results, i.value.uint32); value_ptr = try arr.createValueFromIndex(if (is_last) allocator else arena.allocator(), rt.results, i.value.uint32);
if (is_last)
is_owner_of_uniform_slice = true;
uniform_slice_window = arr.data[arr.getOffsetOfIndex(i.value.uint32)..]; uniform_slice_window = arr.data[arr.getOffsetOfIndex(i.value.uint32)..];
}, },
.Vector4f32 => |*v| { .Vector4f32 => |*v| {
@@ -1307,6 +1399,7 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime
break :blk .{ break :blk .{
.Pointer = .{ .Pointer = .{
.ptr = .{ .common = value_ptr }, .ptr = .{ .common = value_ptr },
.is_owner_of_uniform_slice = is_owner_of_uniform_slice,
.uniform_slice_window = uniform_slice_window, .uniform_slice_window = uniform_slice_window,
}, },
}; };
@@ -1363,6 +1456,7 @@ fn opCompositeConstruct(_: std.mem.Allocator, word_count: SpvWord, rt: *Runtime)
var offset: usize = 0; var offset: usize = 0;
for (0..index_count) |_| { for (0..index_count) |_| {
// DOES NOT WORK : FIXME
const elem_value = (try rt.results[try rt.it.next()].getVariant()).Constant.value; const elem_value = (try rt.results[try rt.it.next()].getVariant()).Constant.value;
std.mem.copyForwards(u8, arr.data[offset..(offset + arr.stride)], std.mem.asBytes(&elem_value)); std.mem.copyForwards(u8, arr.data[offset..(offset + arr.stride)], std.mem.asBytes(&elem_value));
offset += arr.stride; offset += arr.stride;
@@ -1753,6 +1847,7 @@ fn opMemberName(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime)
.Type = .{ .Type = .{
.Structure = .{ .Structure = .{
.members_type_word = undefined, .members_type_word = undefined,
.members_offsets = undefined,
.member_names = .empty, .member_names = .empty,
}, },
}, },
@@ -1800,8 +1895,6 @@ fn opReturnValue(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!vo
if (rt.function_stack.getLastOrNull()) |function| { if (rt.function_stack.getLastOrNull()) |function| {
var ret_res = rt.results[try rt.it.next()]; var ret_res = rt.results[try rt.it.next()];
copyValue(try function.ret.getValue(), try ret_res.getValue()); copyValue(try function.ret.getValue(), try ret_res.getValue());
} else {
return RuntimeError.InvalidSpirV; // No current function ???
} }
_ = rt.function_stack.pop(); _ = rt.function_stack.pop();
@@ -2060,12 +2153,15 @@ fn opTypeStruct(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime)
} }
break :blk members_type_word; break :blk members_type_word;
}; };
const members_offsets = allocator.alloc(?SpvWord, word_count - 1) catch return RuntimeError.OutOfMemory;
@memset(members_offsets, null);
if (rt.mod.results[id].variant) |*variant| { if (rt.mod.results[id].variant) |*variant| {
switch (variant.*) { switch (variant.*) {
.Type => |*t| switch (t.*) { .Type => |*t| switch (t.*) {
.Structure => |*s| { .Structure => |*s| {
s.members_type_word = members_type_word; s.members_type_word = members_type_word;
s.members_offsets = members_offsets;
}, },
else => unreachable, else => unreachable,
}, },
@@ -2076,6 +2172,7 @@ fn opTypeStruct(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime)
.Type = .{ .Type = .{
.Structure = .{ .Structure = .{
.members_type_word = members_type_word, .members_type_word = members_type_word,
.members_offsets = members_offsets,
.member_names = .empty, .member_names = .empty,
}, },
}, },