[Flint] adding array_length and some math operations encoding
Mirror Gitea refs to GitHub / mirror (push) Successful in 16s
Build / build (push) Failing after 57s
Test / build_and_test (push) Successful in 3m52s

This commit is contained in:
2026-08-30 19:57:09 +02:00
parent cd4f87a99f
commit d357b722ba
18 changed files with 622 additions and 109 deletions
+1
View File
@@ -101,5 +101,6 @@ pub const ParsedOperation = union(enum) {
store_interface: struct { interface_name: []const u8, value: ValueRef }, store_interface: struct { interface_name: []const u8, value: ValueRef },
load_buffer: struct { resource_name: []const u8, byte_offset: ValueRef }, load_buffer: struct { resource_name: []const u8, byte_offset: ValueRef },
store_buffer: struct { resource_name: []const u8, byte_offset: ValueRef, value: ValueRef }, store_buffer: struct { resource_name: []const u8, byte_offset: ValueRef, value: ValueRef },
array_length: struct { resource_name: []const u8, byte_offset: ValueRef, stride: u32 },
call: struct { function_name: []const u8, arguments: []const ValueRef }, call: struct { function_name: []const u8, arguments: []const ValueRef },
}; };
+13
View File
@@ -285,6 +285,19 @@ fn lowerOperation(
.inferred_type = null, .inferred_type = null,
}; };
}, },
.array_length => |op| blk: {
const resource_id = resources.get(op.resource_name) orelse return error.UnknownResource;
const byte_offset = resolveValue(values, op.byte_offset) orelse return error.UnknownValue;
break :blk .{
.operation = .{ .array_length = .{
.resource = resource_id,
.byte_offset = byte_offset,
.stride = op.stride,
} },
.inferred_type = null,
};
},
.call => |op| blk: { .call => |op| blk: {
const function_id = functions.get(op.function_name) orelse return error.UnknownFunction; const function_id = functions.get(op.function_name) orelse return error.UnknownFunction;
var arguments: std.ArrayList(ids.ValueId) = .empty; var arguments: std.ArrayList(ids.ValueId) = .empty;
+20
View File
@@ -412,6 +412,19 @@ const Parser = struct {
} }; } };
} }
if (std.mem.eql(u8, name, "array_length")) {
const resource_name = (try self.expect(.at_name)).text;
try self.expectDiscard(.comma);
const byte_offset = try self.parseValueRef();
try self.expectDiscard(.comma);
try self.expectIdentifier("stride");
return .{ .array_length = .{
.resource_name = resource_name,
.byte_offset = byte_offset,
.stride = try self.parseUnsigned(u32, .number),
} };
}
if (std.mem.eql(u8, name, "call")) { if (std.mem.eql(u8, name, "call")) {
const function_name = (try self.expect(.at_name)).text; const function_name = (try self.expect(.at_name)).text;
try self.expectDiscard(.left_paren); try self.expectDiscard(.left_paren);
@@ -522,6 +535,13 @@ const Parser = struct {
}); });
} }
if (std.mem.eql(u8, token.text, "runtime_array")) {
try self.expectDiscard(.left_square);
const element_type = try self.parseType();
try self.expectDiscard(.right_square);
return module.internType(.{ .runtime_array = .{ .element_type = element_type } });
}
if (std.mem.eql(u8, token.text, "struct")) { if (std.mem.eql(u8, token.text, "struct")) {
try self.expectDiscard(.left_square); try self.expectDiscard(.left_square);
var members: std.ArrayList(ids.TypeId) = .empty; var members: std.ArrayList(ids.TypeId) = .empty;
+13 -2
View File
@@ -346,7 +346,7 @@ pub fn dispatchBase(interface: *Interface, base_group_x: u32, base_group_y: u32,
var ranges: [gen9_dispatch.max_surfaces]?MemoryRange = @splat(null); var ranges: [gen9_dispatch.max_surfaces]?MemoryRange = @splat(null);
var sizes: [gen9_dispatch.max_surfaces]u64 = @splat(0); var sizes: [gen9_dispatch.max_surfaces]u64 = @splat(0);
for (artifact.resources.bindings) |resource| { for (artifact.resources.bindings) |resource| {
if (resource.set >= base.vulkan_max_descriptor_sets or @as(usize, resource.binding_table_index) >= gen9_dispatch.max_surfaces) if (resource.set >= base.vulkan_max_descriptor_sets or @as(usize, resource.binding_table_index) >= gen9_dispatch.max_storage_surfaces)
return VkError.ValidationFailed; return VkError.ValidationFailed;
const descriptor_set = self.bound_compute_descriptor_sets[resource.set] orelse return VkError.ValidationFailed; const descriptor_set = self.bound_compute_descriptor_sets[resource.set] orelse return VkError.ValidationFailed;
@@ -400,7 +400,7 @@ pub fn dispatchBase(interface: *Interface, base_group_x: u32, base_group_y: u32,
self.gpu_allocations.append(self.interface.host_allocator.allocator(), state) catch return VkError.OutOfHostMemory; self.gpu_allocations.append(self.interface.host_allocator.allocator(), state) catch return VkError.OutOfHostMemory;
state_owned = false; state_owned = false;
for (0..@as(usize, state_layout.surface_count)) |index| { for (0..@as(usize, state_layout.storage_surface_count)) |index| {
const range = ranges[index] orelse return VkError.ValidationFailed; const range = ranges[index] orelse return VkError.ValidationFailed;
if (range.offset > std.math.maxInt(u32)) if (range.offset > std.math.maxInt(u32))
return VkError.FeatureNotPresent; return VkError.FeatureNotPresent;
@@ -415,6 +415,17 @@ pub fn dispatchBase(interface: *Interface, base_group_x: u32, base_group_y: u32,
}) catch return VkError.OutOfHostMemory; }) catch return VkError.OutOfHostMemory;
} }
const size_table_surface = @as(usize, state_layout.storage_surface_count);
self.relocations.append(self.interface.host_allocator.allocator(), .{
.source_handle = state_handle,
.target_handle = state_handle,
.offset = state_layout.surface_address_offsets[size_table_surface],
.delta = state_layout.size_table_offset,
.read = true,
.write = false,
.domain = .render,
}) catch return VkError.OutOfHostMemory;
try self.emitSlice(&gen9_dispatch.pipeControl(gen9_dispatch.pipe_control.cs_stall | try self.emitSlice(&gen9_dispatch.pipeControl(gen9_dispatch.pipe_control.cs_stall |
gen9_dispatch.pipe_control.dc_flush | gen9_dispatch.pipe_control.dc_flush |
gen9_dispatch.pipe_control.render_target_flush | gen9_dispatch.pipe_control.render_target_flush |
-13
View File
@@ -171,19 +171,6 @@ fn lowerToFlint(allocator: std.mem.Allocator, module: *base.ShaderModule.IrModul
const target = device_info orelse return null; const target = device_info orelse return null;
return compiler.targets.compileCompute(allocator, module, target, .{}) catch |err| switch (err) { return compiler.targets.compileCompute(allocator, module, target, .{}) catch |err| switch (err) {
error.OutOfMemory => return VkError.OutOfHostMemory, error.OutOfMemory => return VkError.OutOfHostMemory,
error.UnsupportedGeneration,
error.UnsupportedStage,
error.UnsupportedDispatchWidth,
error.UnsupportedGrfSize,
error.UnsupportedWorkgroupSize,
error.MissingWorkgroupSize,
error.UnsupportedType,
error.UnsupportedOperation,
error.UnsupportedTerminator,
error.TooManyStorageBuffers,
=> return null,
else => { else => {
std.log.scoped(.FlintPipeline).err("compute compilation failed: {s}", .{@errorName(err)}); std.log.scoped(.FlintPipeline).err("compute compilation failed: {s}", .{@errorName(err)});
return VkError.ValidationFailed; return VkError.ValidationFailed;
+20
View File
@@ -28,6 +28,13 @@ pub const StoreBuffer = struct {
source: operand.Source, source: operand.Source,
}; };
pub const ArrayLength = struct {
destination: operand.Destination,
buffer: BufferReference,
byte_offset: operand.Source,
stride: u32,
};
pub const SurfaceRead = struct { pub const SurfaceRead = struct {
destination: operand.Destination, destination: operand.Destination,
binding_table: u8, binding_table: u8,
@@ -93,15 +100,28 @@ pub const Compare = struct {
rhs: operand.Source, rhs: operand.Source,
}; };
pub const MathOpcode = enum {
integer_quotient,
};
pub const Math = struct {
opcode: MathOpcode,
destination: operand.Destination,
lhs: operand.Source,
rhs: operand.Source,
};
pub const Operation = union(enum) { pub const Operation = union(enum) {
load_global_invocation_id: LoadGlobalInvocationId, load_global_invocation_id: LoadGlobalInvocationId,
load_buffer: LoadBuffer, load_buffer: LoadBuffer,
store_buffer: StoreBuffer, store_buffer: StoreBuffer,
array_length: ArrayLength,
surface_read: SurfaceRead, surface_read: SurfaceRead,
surface_write: SurfaceWrite, surface_write: SurfaceWrite,
surface_message: SurfaceMessage, surface_message: SurfaceMessage,
move: Move, move: Move,
binary: Binary, binary: Binary,
math: Math,
compare: Compare, compare: Compare,
parallel_copy: pseudo.ParallelCopy, parallel_copy: pseudo.ParallelCopy,
}; };
+17
View File
@@ -136,6 +136,15 @@ fn writeOperation(program: *const program_ir.Program, writer: *std.Io.Writer, ex
try writer.writeAll(", "); try writer.writeAll(", ");
try writeSource(program, writer, execution_size, op.source); try writeSource(program, writer, execution_size, op.source);
}, },
.array_length => |op| {
try writer.writeAll("array_length ");
try writeDestination(program, writer, execution_size, op.destination);
try writer.writeAll(", ");
try writeBufferReference(program, writer, op.buffer);
try writer.writeAll(", ");
try writeSource(program, writer, execution_size, op.byte_offset);
try writer.print(", stride({d})", .{op.stride});
},
.surface_read => |op| { .surface_read => |op| {
try writer.writeAll("surface_read "); try writer.writeAll("surface_read ");
try writeDestination(program, writer, execution_size, op.destination); try writeDestination(program, writer, execution_size, op.destination);
@@ -185,6 +194,14 @@ fn writeOperation(program: *const program_ir.Program, writer: *std.Io.Writer, ex
try writer.writeAll(", "); try writer.writeAll(", ");
try writeSource(program, writer, execution_size, op.rhs); try writeSource(program, writer, execution_size, op.rhs);
}, },
.math => |op| {
try writer.print("{t} ", .{op.opcode});
try writeDestination(program, writer, execution_size, op.destination);
try writer.writeAll(", ");
try writeSource(program, writer, execution_size, op.lhs);
try writer.writeAll(", ");
try writeSource(program, writer, execution_size, op.rhs);
},
.parallel_copy => |op| try writeParallelCopy(program, writer, execution_size, op), .parallel_copy => |op| try writeParallelCopy(program, writer, execution_size, op),
} }
} }
+55 -31
View File
@@ -5,42 +5,43 @@ const program_ir = @import("program.zig");
const pseudo = @import("pseudo.zig"); const pseudo = @import("pseudo.zig");
pub const Error = error{ pub const Error = error{
MissingEntryBlock,
InvalidBlock,
MissingTerminator,
InvalidInstruction,
InvalidVirtualRegister,
InvalidVirtualFlag,
UnallocatedVirtualFlag,
InvalidPhysicalRegister,
InvalidRegisterSize,
InvalidRegisterAlignment,
InvalidLaneCount,
InvalidRegion,
InvalidDestination,
InvalidImmediateType,
InvalidStorageBuffer,
InvalidBufferReference,
InvalidGlobalInvocationId,
InvalidBufferAccess,
InvalidWorkgroupSize,
EmptyParallelCopy,
InvalidParallelCopyDestination,
ParallelCopyTypeMismatch,
DuplicateParallelCopyDestination,
PredicatedParallelCopy,
UnloweredParallelCopy,
UnloweredSystemValue,
UnloweredResource,
UnloweredMessage,
InvalidMessage,
InvalidPayloadLayout,
EntryBlockHasParameters,
DuplicateBlockParameter, DuplicateBlockParameter,
DuplicateParallelCopyDestination,
EdgeArgumentCountMismatch, EdgeArgumentCountMismatch,
EdgeArgumentKindMismatch, EdgeArgumentKindMismatch,
EdgeArgumentTypeMismatch, EdgeArgumentTypeMismatch,
EmptyParallelCopy,
EntryBlockHasParameters,
InvalidBlock,
InvalidBufferAccess,
InvalidBufferReference,
InvalidDestination,
InvalidGlobalInvocationId,
InvalidImmediateType,
InvalidInstruction,
InvalidLaneCount,
InvalidMath,
InvalidMessage,
InvalidParallelCopyDestination,
InvalidPayloadLayout,
InvalidPhysicalRegister,
InvalidRegion,
InvalidRegisterAlignment,
InvalidRegisterSize,
InvalidStorageBuffer,
InvalidVirtualFlag,
InvalidVirtualRegister,
InvalidWorkgroupSize,
MissingEntryBlock,
MissingTerminator,
ParallelCopyTypeMismatch,
PredicatedParallelCopy,
UnallocatedVirtualFlag,
UnloweredBlockParameter, UnloweredBlockParameter,
UnloweredMessage,
UnloweredParallelCopy,
UnloweredResource,
UnloweredSystemValue,
}; };
pub fn validate(program: *const program_ir.Program) Error!void { pub fn validate(program: *const program_ir.Program) Error!void {
@@ -157,6 +158,13 @@ fn validateInstruction(program: *const program_ir.Program, inst: instruction.Ins
if (!op.source.type.isInitialTargetType()) if (!op.source.type.isInitialTargetType())
return Error.InvalidBufferAccess; return Error.InvalidBufferAccess;
}, },
.array_length => |op| {
try validateBufferReference(program, op.buffer);
try validateDestination(program, op.destination);
try validateBufferOffset(program, op.byte_offset);
if (op.destination.type != .u32 or op.stride == 0)
return Error.InvalidBufferAccess;
},
.surface_read => |op| { .surface_read => |op| {
try validateDestination(program, op.destination); try validateDestination(program, op.destination);
try validateBufferOffset(program, op.address); try validateBufferOffset(program, op.address);
@@ -199,6 +207,22 @@ fn validateInstruction(program: *const program_ir.Program, inst: instruction.Ins
try validateSource(program, op.lhs); try validateSource(program, op.lhs);
try validateSource(program, op.rhs); try validateSource(program, op.rhs);
}, },
.math => |op| {
try validateDestination(program, op.destination);
try validateSource(program, op.lhs);
try validateSource(program, op.rhs);
switch (op.opcode) {
.integer_quotient => {
if (inst.execution_size != .simd8)
return Error.InvalidMath;
if (op.destination.type != .u32 and op.destination.type != .i32)
return Error.InvalidMath;
if (op.lhs.type != op.destination.type or op.rhs.type != op.destination.type)
return Error.InvalidMath;
},
}
},
.parallel_copy => |op| { .parallel_copy => |op| {
if (program.properties.parallel_copies_lowered) if (program.properties.parallel_copies_lowered)
+153 -17
View File
@@ -1,4 +1,5 @@
const std = @import("std"); const std = @import("std");
const base = @import("base");
const shader_compiler = @import("shader_ir"); const shader_compiler = @import("shader_ir");
const shader_ir = shader_compiler.ir; const shader_ir = shader_compiler.ir;
const device = @import("../device.zig"); const device = @import("../device.zig");
@@ -431,7 +432,7 @@ const LoweringState = struct {
.load_buffer => |operation| try self.lowerLoadBuffer(block_id, source_instruction.result, operation), .load_buffer => |operation| try self.lowerLoadBuffer(block_id, source_instruction.result, operation),
.store_buffer => |operation| try self.lowerStoreBuffer(block_id, source_instruction.result, operation), .store_buffer => |operation| try self.lowerStoreBuffer(block_id, source_instruction.result, operation),
.call => return Error.UnsanitizedModule, .call => return Error.UnsanitizedModule,
.array_length => return Error.UnsupportedOperation, .array_length => |operation| try self.lowerArrayLength(block_id, source_instruction.result, operation),
} }
} }
@@ -507,6 +508,32 @@ const LoweringState = struct {
if (lhs_components.len == 0 or lhs_components.len != rhs_components.len or lhs_components.len != result_components.len) if (lhs_components.len == 0 or lhs_components.len != rhs_components.len or lhs_components.len != result_components.len)
return Error.InvalidModule; return Error.InvalidModule;
switch (operation.opcode) {
.unsigned_divide,
.signed_divide,
=> {
const signed = operation.opcode == .signed_divide;
const target_type: operand.DataType = if (signed) .i32 else .u32;
for (lhs_components, rhs_components, result_components) |lhs_value, rhs_value, result_component| {
var dst = try destinationFromSource(result_component);
dst.type = target_type;
try self.appendInstruction(block_id, null, .{
.math = .{
.opcode = .integer_quotient,
.destination = dst,
.lhs = try retypeIntegerSource(lhs_value, target_type),
.rhs = try retypeIntegerSource(rhs_value, target_type),
},
});
}
return;
},
else => {},
}
const data_type = lhs_components[0].type; const data_type = lhs_components[0].type;
const opcode: instruction.BinaryOpcode = switch (operation.opcode) { const opcode: instruction.BinaryOpcode = switch (operation.opcode) {
.integer_add => if (data_type == .u32 or data_type == .i32) .add else return Error.UnsupportedOperation, .integer_add => if (data_type == .u32 or data_type == .i32) .add else return Error.UnsupportedOperation,
@@ -521,8 +548,11 @@ const LoweringState = struct {
.bitwise_and => if (data_type == .u32 or data_type == .i32) .bitwise_and else return Error.UnsupportedOperation, .bitwise_and => if (data_type == .u32 or data_type == .i32) .bitwise_and else return Error.UnsupportedOperation,
.bitwise_or => if (data_type == .u32 or data_type == .i32) .bitwise_or else return Error.UnsupportedOperation, .bitwise_or => if (data_type == .u32 or data_type == .i32) .bitwise_or else return Error.UnsupportedOperation,
.bitwise_xor => if (data_type == .u32 or data_type == .i32) .bitwise_xor else return Error.UnsupportedOperation, .bitwise_xor => if (data_type == .u32 or data_type == .i32) .bitwise_xor else return Error.UnsupportedOperation,
.unsigned_divide, .unsigned_divide,
.signed_divide, .signed_divide,
=> unreachable,
.unsigned_modulo, .unsigned_modulo,
.signed_modulo, .signed_modulo,
.float_divide, .float_divide,
@@ -549,6 +579,47 @@ const LoweringState = struct {
} }
} }
fn retypeIntegerSource(source_value: operand.Source, target_type: operand.DataType) Error!operand.Source {
if (target_type != .u32 and target_type != .i32)
return Error.UnsupportedType;
var result = source_value;
switch (source_value.type) {
.u32, .i32 => {},
else => return Error.UnsupportedType,
}
result.register = switch (source_value.register) {
.immediate => |immediate| .{
.immediate = switch (target_type) {
.u32 => .{
.u32 = switch (immediate) {
.u32 => |v| v,
.i32 => |v| @bitCast(v),
else => return Error.UnsupportedType,
},
},
.i32 => .{
.i32 = switch (immediate) {
.u32 => |v| @bitCast(v),
.i32 => |v| v,
else => return Error.UnsupportedType,
},
},
else => unreachable,
},
},
else => source_value.register,
};
result.type = target_type;
return result;
}
fn lowerCompare(self: *LoweringState, block_id: ids.BlockId, result: ?shader_ir.id.ValueId, operation: shader_ir.instruction.Compare) Error!void { fn lowerCompare(self: *LoweringState, block_id: ids.BlockId, result: ?shader_ir.id.ValueId, operation: shader_ir.instruction.Compare) Error!void {
const result_id = try requireResult(result); const result_id = try requireResult(result);
const result_value = self.lowerer.module.values.get(result_id) orelse return Error.InvalidModule; const result_value = self.lowerer.module.values.get(result_id) orelse return Error.InvalidModule;
@@ -689,21 +760,36 @@ const LoweringState = struct {
const variable = self.lowerer.module.interface_variables.get(operation.variable) orelse return Error.InvalidModule; const variable = self.lowerer.module.interface_variables.get(operation.variable) orelse return Error.InvalidModule;
if (variable.direction != .input) if (variable.direction != .input)
return Error.InvalidModule; return Error.InvalidModule;
switch (variable.semantic) { switch (variable.semantic) {
.builtin => |builtin| if (builtin != .global_invocation_id) .builtin => |builtin| switch (builtin) {
return Error.UnsupportedOperation, .global_invocation_id => try self.lowerGlobalInvocationId(block_id, result_id, variable),
.num_workgroups => try self.lowerNumWorkgroups(result_id),
.workgroup_size => base.unsupported("workgroup size builtin is not yet supported in Flint", .{}),
else => return Error.UnsupportedOperation,
},
.location => return Error.UnsupportedOperation, .location => return Error.UnsupportedOperation,
} }
}
fn lowerGlobalInvocationId(
self: *LoweringState,
block_id: ids.BlockId,
result_id: shader_ir.id.ValueId,
variable: *const shader_ir.module.InterfaceVariable,
) Error!void {
const result_value = self.lowerer.module.values.get(result_id) orelse return Error.InvalidModule; const result_value = self.lowerer.module.values.get(result_id) orelse return Error.InvalidModule;
if (result_value.type != variable.type) if (result_value.type != variable.type)
return Error.InvalidModule; return Error.InvalidModule;
const result_components = try self.addRegisterLocation(result_id, .temporary); const result_components = try self.addRegisterLocation(result_id, .temporary);
if (result_components.len != 3) if (result_components.len != 3)
return Error.UnsupportedOperation; return Error.UnsupportedOperation;
for (result_components, 0..) |result_component, component_index| { for (result_components, 0..) |result_component, component_index| {
if (result_component.type != .u32) if (result_component.type != .u32)
return Error.UnsupportedOperation; return Error.UnsupportedOperation;
try self.appendInstruction(block_id, null, .{ try self.appendInstruction(block_id, null, .{
.load_global_invocation_id = .{ .load_global_invocation_id = .{
.destination = try destinationFromSource(result_component), .destination = try destinationFromSource(result_component),
@@ -713,6 +799,28 @@ const LoweringState = struct {
} }
} }
fn lowerNumWorkgroups(self: *LoweringState, result_id: shader_ir.id.ValueId) Error!void {
const result_value = self.lowerer.module.values.get(result_id) orelse return Error.InvalidModule;
const lowered_type = try self.lowerType(result_value.type);
if (lowered_type.element_type != .u32 or lowered_type.component_count != 3)
return Error.UnsupportedType;
const vec = try self.storage.alloc(operand.Source, 3);
for (self.lowerer.module.execution_modes.workgroup_size.?, vec) |value, *component| {
component.* = .{
.register = .{
.immediate = .{ .u32 = value },
},
.type = .u32,
.region = operand.Region.broadcast(),
};
}
try self.putLocation(result_id, .{ .components = vec });
}
fn lowerStoreInterface(self: *LoweringState, block_id: ids.BlockId, result: ?shader_ir.id.ValueId, operation: shader_ir.instruction.StoreInterface) Error!void { fn lowerStoreInterface(self: *LoweringState, block_id: ids.BlockId, result: ?shader_ir.id.ValueId, operation: shader_ir.instruction.StoreInterface) Error!void {
_ = self; _ = self;
_ = block_id; _ = block_id;
@@ -759,6 +867,26 @@ const LoweringState = struct {
} }
} }
fn lowerArrayLength(self: *LoweringState, block_id: ids.BlockId, result: ?shader_ir.id.ValueId, operation: shader_ir.instruction.ArrayLength) Error!void {
const result_id = try requireResult(result);
const byte_offset = try self.source(operation.byte_offset);
if (byte_offset.type != .u32)
return Error.UnsupportedType;
const result_components = try self.addRegisterLocation(result_id, .temporary);
if (result_components.len != 1 or result_components[0].type != .u32)
return Error.UnsupportedType;
try self.appendInstruction(block_id, null, .{
.array_length = .{
.destination = try destinationFromSource(result_components[0]),
.buffer = .{ .logical = try self.storageBuffer(operation.resource) },
.byte_offset = byte_offset,
.stride = operation.stride,
},
});
}
fn lowerControlAndTerminators(self: *LoweringState, allocator: std.mem.Allocator) Error!void { fn lowerControlAndTerminators(self: *LoweringState, allocator: std.mem.Allocator) Error!void {
const source_entry = try self.sourceEntryFunction(); const source_entry = try self.sourceEntryFunction();
const function = source_entry[1]; const function = source_entry[1];
@@ -1297,6 +1425,28 @@ test "[ir] Lower: vector storage-buffer operations" {
}, &.{}); }, &.{});
} }
test "[ir] Lower: runtime array length" {
const source =
\\shader compute @main
\\{
\\ @storage: runtime_array[u32] = storage_buffer[set(0), binding(3)]
\\ %offset: constant u32 = 16
\\ fn @main() -> void
\\ {
\\ .entry():
\\ %length: u32 = array_length @storage, %offset, stride 4
\\ return
\\ }
\\}
;
try expectLoweredFragments(source, &.{
"@storage = storage_buffer[set(0), binding(3)]",
"%length: vgrf u32[8], class(temporary)",
"[simd8] array_length %length:u32, @storage, 16:u32, stride(4)",
}, &.{});
}
test "[ir] Lower: vector block parameter" { test "[ir] Lower: vector block parameter" {
const source = const source =
\\shader compute @main \\shader compute @main
@@ -1402,20 +1552,6 @@ test "[ir] Lower: boolean block parameter" {
} }
test "[ir] Lower: unsupported operations" { test "[ir] Lower: unsupported operations" {
try expectLoweringError(
\\shader compute @main
\\{
\\ %one: constant u32 = bits(0x1)
\\ %two: constant u32 = bits(0x2)
\\ fn @main() -> void
\\ {
\\ .entry():
\\ %quotient: u32 = unsigned_divide %one, %two
\\ return
\\ }
\\}
, Error.UnsupportedOperation);
try expectLoweringError( try expectLoweringError(
\\shader compute @main \\shader compute @main
\\{ \\{
@@ -0,0 +1,92 @@
const std = @import("std");
const Builder = @import("../../../ir/Builder.zig");
const ids = @import("../../../ir/id.zig");
const instruction = @import("../../../ir/instruction.zig");
const operand = @import("../../../ir/operand.zig");
const program_ir = @import("../../../ir/program.zig");
const resource_layout = @import("resource_layout.zig");
pub const Error = std.mem.Allocator.Error || error{InvalidProgram};
pub fn run(program: *program_ir.Program, layout: *const resource_layout.Layout) Error!void {
if (!program.properties.resources_lowered)
return Error.InvalidProgram;
if (layout.bindings.len >= std.math.maxInt(u8))
return Error.InvalidProgram;
var builder = Builder.init(program);
for (program.blocks.entries.items, 0..) |entry, block_index| {
_ = entry orelse continue;
const block_id = ids.BlockId.fromIndex(block_index);
var instruction_index: usize = 0;
while (true) {
const block = program.blocks.get(block_id) orelse return Error.InvalidProgram;
if (instruction_index >= block.instructions.items.len)
break;
const instruction_id = block.instructions.items[instruction_index];
const inst = program.instructions.get(instruction_id) orelse return Error.InvalidProgram;
const op = switch (inst.operation) {
.array_length => |value| value,
else => {
instruction_index += 1;
continue;
},
};
const resource_index = switch (op.buffer) {
.binding_table => |value| value,
.logical => return Error.InvalidProgram,
};
if (resource_index >= layout.bindings.len or op.stride == 0)
return Error.InvalidProgram;
const execution_size = inst.execution_size;
const predicate = inst.predicate;
const result_source: operand.Source = .{
.register = op.destination.register,
.type = .u32,
.region = operand.Region.contiguous(execution_size),
};
var negated_offset = op.byte_offset;
negated_offset.negate = !negated_offset.negate;
const mutable = program.instructions.getMut(instruction_id) orelse return Error.InvalidProgram;
mutable.operation = .{ .load_buffer = .{
.destination = op.destination,
.buffer = .{ .binding_table = @intCast(layout.bindings.len) },
.byte_offset = immediate(@as(u32, resource_index) * @sizeOf(u32)),
} };
_ = builder.insertInstruction(block_id, instruction_index + 1, execution_size, predicate, .{ .binary = .{
.opcode = .add,
.destination = op.destination,
.lhs = result_source,
.rhs = negated_offset,
} }) catch |err| return mapBuilderError(err);
_ = builder.insertInstruction(block_id, instruction_index + 2, execution_size, predicate, .{ .math = .{
.opcode = .integer_quotient,
.destination = op.destination,
.lhs = result_source,
.rhs = immediate(op.stride),
} }) catch |err| return mapBuilderError(err);
instruction_index += 3;
}
}
}
fn immediate(value: u32) operand.Source {
return .{
.register = .{ .immediate = .{ .u32 = value } },
.type = .u32,
.region = operand.Region.broadcast(),
};
}
fn mapBuilderError(err: Builder.Error) Error {
return switch (err) {
error.OutOfMemory => Error.OutOfMemory,
else => Error.InvalidProgram,
};
}
@@ -1,6 +1,7 @@
const std = @import("std"); const std = @import("std");
pub const max_surfaces: usize = 4; pub const max_storage_surfaces: usize = 4;
pub const max_surfaces: usize = max_storage_surfaces + 1;
pub const page_size: usize = 4096; pub const page_size: usize = 4096;
pub const surface_state_size: usize = 64; pub const surface_state_size: usize = 64;
pub const interface_descriptor_size: usize = 32; pub const interface_descriptor_size: usize = 32;
@@ -22,12 +23,14 @@ pub const StateLayout = struct {
surface_offsets: [max_surfaces]u32, surface_offsets: [max_surfaces]u32,
surface_address_offsets: [max_surfaces]u32, surface_address_offsets: [max_surfaces]u32,
surface_count: u8, surface_count: u8,
storage_surface_count: u8,
size_table_offset: u32,
binding_table_offset: u32, binding_table_offset: u32,
interface_descriptor_offset: u32, interface_descriptor_offset: u32,
}; };
pub fn writeState(destination: []u8, kernel: []const u8, buffer_sizes: []const u64) Error!StateLayout { pub fn writeState(destination: []u8, kernel: []const u8, buffer_sizes: []const u64) Error!StateLayout {
if (buffer_sizes.len > max_surfaces) if (buffer_sizes.len > max_storage_surfaces)
return Error.TooManySurfaces; return Error.TooManySurfaces;
var layout: StateLayout = .{ var layout: StateLayout = .{
@@ -35,7 +38,9 @@ pub fn writeState(destination: []u8, kernel: []const u8, buffer_sizes: []const u
.kernel_offset = 0, .kernel_offset = 0,
.surface_offsets = @splat(0), .surface_offsets = @splat(0),
.surface_address_offsets = @splat(0), .surface_address_offsets = @splat(0),
.surface_count = @intCast(buffer_sizes.len), .surface_count = @intCast(buffer_sizes.len + 1),
.storage_surface_count = @intCast(buffer_sizes.len),
.size_table_offset = 0,
.binding_table_offset = 0, .binding_table_offset = 0,
.interface_descriptor_offset = 0, .interface_descriptor_offset = 0,
}; };
@@ -48,11 +53,23 @@ pub fn writeState(destination: []u8, kernel: []const u8, buffer_sizes: []const u
cursor += surface_state_size; cursor += surface_state_size;
if (size == 0) if (size == 0)
return Error.EmptyBuffer; return Error.EmptyBuffer;
if (size > std.math.maxInt(u32))
return Error.UnsupportedBufferSize;
} }
const size_table_surface = buffer_sizes.len;
cursor = alignForward(cursor, surface_state_size);
layout.surface_offsets[size_table_surface] = @intCast(cursor);
layout.surface_address_offsets[size_table_surface] = @intCast(cursor + 8 * @sizeOf(u32));
cursor += surface_state_size;
cursor = alignForward(cursor, 32); cursor = alignForward(cursor, 32);
layout.binding_table_offset = @intCast(cursor); layout.binding_table_offset = @intCast(cursor);
cursor += buffer_sizes.len * @sizeOf(u32); cursor += layout.surface_count * @sizeOf(u32);
cursor = alignForward(cursor, @alignOf(u32));
layout.size_table_offset = @intCast(cursor);
cursor += @max(buffer_sizes.len, 1) * @sizeOf(u32);
cursor = alignForward(cursor, 64); cursor = alignForward(cursor, 64);
layout.interface_descriptor_offset = @intCast(cursor); layout.interface_descriptor_offset = @intCast(cursor);
@@ -67,7 +84,10 @@ pub fn writeState(destination: []u8, kernel: []const u8, buffer_sizes: []const u
for (buffer_sizes, 0..) |size, index| { for (buffer_sizes, 0..) |size, index| {
_ = try encodeRawBufferSurface(destination, layout.surface_offsets[index], size); _ = try encodeRawBufferSurface(destination, layout.surface_offsets[index], size);
putU32(destination, layout.binding_table_offset + @as(u32, @intCast(index * @sizeOf(u32))), layout.surface_offsets[index]); putU32(destination, layout.binding_table_offset + @as(u32, @intCast(index * @sizeOf(u32))), layout.surface_offsets[index]);
putU32(destination, layout.size_table_offset + @as(u32, @intCast(index * @sizeOf(u32))), @intCast(size));
} }
_ = try encodeRawBufferSurface(destination, layout.surface_offsets[size_table_surface], @max(buffer_sizes.len, 1) * @sizeOf(u32));
putU32(destination, layout.binding_table_offset + @as(u32, @intCast(size_table_surface * @sizeOf(u32))), layout.surface_offsets[size_table_surface]);
const idd = layout.interface_descriptor_offset; const idd = layout.interface_descriptor_offset;
putU32(destination, idd + 0, layout.kernel_offset); putU32(destination, idd + 0, layout.kernel_offset);
@@ -54,28 +54,8 @@ const Grf = struct {
pub fn encodeMove(execution_size: device.ExecutionSize, move: ir_instruction.Move) Error!EncodedInstruction { pub fn encodeMove(execution_size: device.ExecutionSize, move: ir_instruction.Move) Error!EncodedInstruction {
var encoded = try instructionHeader(1, execution_size); var encoded = try instructionHeader(1, execution_size);
const destination = try resolveGrf(move.destination.register, move.destination.region.byte_offset); const destination = try resolveGrf(move.destination.register, move.destination.region.byte_offset);
setDestination(&encoded, .grf, try hardwareType(move.destination.type), destination, try horizontalStride(move.destination.region.horizontal_stride));
setDestination( try setSource0(&encoded, move.source);
&encoded,
.grf,
try hardwareType(move.destination.type),
destination,
try horizontalStride(move.destination.region.horizontal_stride),
);
switch (move.source.register) {
.physical_grf => {
const source = try resolveGrf(move.source.register, move.source.region.byte_offset);
try setSource0Register(&encoded, move.source, source);
},
.immediate => |immediate| {
if (move.source.negate or move.source.absolute)
return Error.UnsupportedOperand;
setSource0Immediate(&encoded, try hardwareType(move.source.type), immediate);
},
else => return Error.UnsupportedOperand,
}
return encoded; return encoded;
} }
@@ -141,6 +121,38 @@ pub fn encodeSurfaceMessage(execution_size: device.ExecutionSize, message: ir_in
return encoded; return encoded;
} }
pub fn encodeBinary(execution_size: device.ExecutionSize, binary: ir_instruction.Binary) Error!EncodedInstruction {
const opcode: u7 = switch (binary.opcode) {
.add => 64,
else => return Error.UnsupportedOperand,
};
var encoded = try instructionHeader(opcode, execution_size);
const destination = try resolveGrf(binary.destination.register, binary.destination.region.byte_offset);
setDestination(&encoded, .grf, try hardwareType(binary.destination.type), destination, try horizontalStride(binary.destination.region.horizontal_stride));
try setSource0(&encoded, binary.lhs);
try setSource1(&encoded, binary.rhs);
return encoded;
}
pub fn encodeMath(execution_size: device.ExecutionSize, math: ir_instruction.Math) Error!EncodedInstruction {
if (execution_size != .simd8)
return Error.UnsupportedExecutionSize;
var encoded = try instructionHeader(56, execution_size);
const destination = try resolveGrf(math.destination.register, math.destination.region.byte_offset);
setDestination(&encoded, .grf, try hardwareType(math.destination.type), destination, try horizontalStride(math.destination.region.horizontal_stride));
try setSource0(&encoded, math.lhs);
try setSource1(&encoded, math.rhs);
encoded.setBits(27, 24, switch (math.opcode) {
.integer_quotient => 12,
});
return encoded;
}
fn instructionHeader(opcode: u7, execution_size: device.ExecutionSize) Error!EncodedInstruction { fn instructionHeader(opcode: u7, execution_size: device.ExecutionSize) Error!EncodedInstruction {
var encoded: EncodedInstruction = .{}; var encoded: EncodedInstruction = .{};
encoded.setBits(6, 0, opcode); encoded.setBits(6, 0, opcode);
@@ -180,6 +192,81 @@ fn setSource0Immediate(encoded: *EncodedInstruction, data_type: HardwareType, im
}); });
} }
fn setSource0(encoded: *EncodedInstruction, source: operand.Source) Error!void {
switch (source.register) {
.physical_grf => {
const register = try resolveGrf(source.register, source.region.byte_offset);
try setSource0Register(encoded, source, register);
},
.immediate => |immediate| {
setSource0Immediate(encoded, try hardwareType(source.type), try applyImmediateModifiers(immediate, source.negate, source.absolute));
},
else => return Error.UnsupportedOperand,
}
}
fn setSource1Register(encoded: *EncodedInstruction, source: operand.Source, register: Grf) Error!void {
encoded.setBits(90, 89, @intFromEnum(RegisterFile.grf));
encoded.setBits(94, 91, @intFromEnum(try hardwareType(source.type)));
// Direct addressing.
encoded.setBits(100, 96, register.byte_offset);
encoded.setBits(108, 101, register.number);
// Source modifiers.
encoded.setBits(109, 109, @intFromBool(source.absolute));
encoded.setBits(110, 110, @intFromBool(source.negate));
// AddressMode = direct.
encoded.setBits(111, 111, 0);
// Align1 region.
encoded.setBits(113, 112, try horizontalStride(source.region.horizontal_stride));
encoded.setBits(116, 114, try regionWidth(source.region.width));
encoded.setBits(120, 117, try verticalStride(source.region.vertical_stride));
}
fn setSource1Immediate(encoded: *EncodedInstruction, data_type: HardwareType, immediate: operand.Immediate) void {
encoded.setBits(90, 89, @intFromEnum(RegisterFile.immediate));
encoded.setBits(94, 91, @intFromEnum(data_type));
encoded.setBits(127, 96, switch (immediate) {
.u32 => |value| value,
.i32 => |value| @as(u32, @bitCast(value)),
.f32 => |value| @as(u32, @bitCast(value)),
});
}
fn setSource1(encoded: *EncodedInstruction, source: operand.Source) Error!void {
switch (source.register) {
.physical_grf => {
const register = try resolveGrf(source.register, source.region.byte_offset);
try setSource1Register(encoded, source, register);
},
.immediate => |immediate| {
setSource1Immediate(encoded, try hardwareType(source.type), try applyImmediateModifiers(immediate, source.negate, source.absolute));
},
else => return Error.UnsupportedOperand,
}
}
fn applyImmediateModifiers(immediate: operand.Immediate, negate: bool, absolute: bool) Error!operand.Immediate {
if (absolute)
return Error.UnsupportedOperand;
if (!negate)
return immediate;
return switch (immediate) {
.u32 => |value| .{ .u32 = 0 -% value },
.i32 => |value| .{ .i32 = 0 -% value },
.f32 => |value| .{ .f32 = -value },
};
}
fn resolveGrf(register: operand.RegisterRef, region_byte_offset: u16) Error!Grf { fn resolveGrf(register: operand.RegisterRef, region_byte_offset: u16) Error!Grf {
const physical = switch (register) { const physical = switch (register) {
.physical_grf => |value| value, .physical_grf => |value| value,
@@ -25,21 +25,33 @@ pub fn encode(allocator: std.mem.Allocator, program: *program_ir.Program) Error!
live_block_count += 1; live_block_count += 1;
} }
if (live_block_count != 1) if (live_block_count != 1) {
std.log.scoped(.FlintEuEncoder).err("cannot encode control flow: program has {d} live blocks; only one is currently supported", .{live_block_count});
return Error.UnsupportedControlFlow; return Error.UnsupportedControlFlow;
}
var kernel: std.ArrayList(u8) = .empty; var kernel: std.ArrayList(u8) = .empty;
errdefer kernel.deinit(allocator); errdefer kernel.deinit(allocator);
for (entry.instructions.items) |instruction_id| { for (entry.instructions.items) |instruction_id| {
const instruction = program.instructions.get(instruction_id) orelse return Error.InvalidProgram; const instruction = program.instructions.get(instruction_id) orelse return Error.InvalidProgram;
if (instruction.predicate != null) if (instruction.predicate != null) {
std.log.scoped(.FlintEuEncoder).err("cannot encode instruction {d} ({t}): predication is not supported", .{ instruction_id.index(), std.meta.activeTag(instruction.operation) });
return Error.UnsupportedPredication; return Error.UnsupportedPredication;
}
const encoded = switch (instruction.operation) { const encoded = switch (instruction.operation) {
.move => |move| try eu.encodeMove(instruction.execution_size, move), .move => |move| eu.encodeMove(instruction.execution_size, move),
.surface_message => |message| try eu.encodeSurfaceMessage(instruction.execution_size, message), .surface_message => |message| eu.encodeSurfaceMessage(instruction.execution_size, message),
else => return Error.UnsupportedOperation, .binary => |binary| eu.encodeBinary(instruction.execution_size, binary),
.math => |math| eu.encodeMath(instruction.execution_size, math),
else => {
std.log.scoped(.FlintEuEncoder).err("cannot encode instruction {d}: unsupported operation {t}", .{ instruction_id.index(), std.meta.activeTag(instruction.operation) });
return Error.UnsupportedOperation;
},
} catch |err| {
std.log.scoped(.FlintEuEncoder).err("failed to encode instruction {d} ({t}): {s}", .{ instruction_id.index(), std.meta.activeTag(instruction.operation), @errorName(err) });
return err;
}; };
try appendInstruction(allocator, &kernel, encoded); try appendInstruction(allocator, &kernel, encoded);
} }
@@ -53,7 +65,10 @@ pub fn encode(allocator: std.mem.Allocator, program: *program_ir.Program) Error!
try appendInstruction(allocator, &kernel, instruction); try appendInstruction(allocator, &kernel, instruction);
program.program_data.total_grf_count = eu.eot_payload_grf + 1; program.program_data.total_grf_count = eu.eot_payload_grf + 1;
}, },
else => return Error.UnsupportedControlFlow, else => {
std.log.scoped(.FlintEuEncoder).err("cannot encode entry-block terminator {t}", .{std.meta.activeTag(terminator)});
return Error.UnsupportedControlFlow;
},
} }
return kernel.toOwnedSlice(allocator); return kernel.toOwnedSlice(allocator);
@@ -11,6 +11,7 @@ const register_allocation = @import("../register_allocation.zig");
const compute = @import("compute.zig"); const compute = @import("compute.zig");
const abi = @import("abi.zig"); const abi = @import("abi.zig");
const array_length_lowering = @import("array_length_lowering.zig");
const kernel_encoder = @import("kernel_encoder.zig"); const kernel_encoder = @import("kernel_encoder.zig");
const message_addresses = @import("message_addresses.zig"); const message_addresses = @import("message_addresses.zig");
const message_lowering = @import("message_lowering.zig"); const message_lowering = @import("message_lowering.zig");
@@ -22,6 +23,7 @@ pub const Error = common_ir.Error ||
block_arguments.Error || block_arguments.Error ||
parallel_copies.Error || parallel_copies.Error ||
abi.Error || abi.Error ||
array_length_lowering.Error ||
kernel_encoder.Error || kernel_encoder.Error ||
message_addresses.Error || message_addresses.Error ||
message_lowering.Error || message_lowering.Error ||
@@ -76,20 +78,20 @@ pub fn compile(allocator: std.mem.Allocator, module: *shader_ir.module.Module, d
try block_arguments.run(allocator, &program); try block_arguments.run(allocator, &program);
try parallel_copies.run(allocator, &program); try parallel_copies.run(allocator, &program);
var resources = try resource_layout.Layout.init( var resources = try resource_layout.Layout.init(allocator, &program);
allocator,
&program,
);
errdefer resources.deinit(allocator); errdefer resources.deinit(allocator);
try resource_lowering.run(&program, &resources); try resource_lowering.run(&program, &resources);
try array_length_lowering.run(&program, &resources);
try message_lowering.run(&program); try message_lowering.run(&program);
try message_addresses.run(&program); try message_addresses.run(&program);
try message_payloads.run(&program); try message_payloads.run(&program);
try flag_allocation.run(allocator, &program); try flag_allocation.run(allocator, &program);
try register_allocation.run(allocator, &program); try register_allocation.run(allocator, &program);
const kernel = kernel_encoder.encode(allocator, &program) catch |err| switch (err) { const kernel = kernel_encoder.encode(allocator, &program) catch |err| encoding_error: {
std.log.scoped(.FlintCompiler).err("Gen9 EU kernel encoding failed: {s}", .{@errorName(err)});
break :encoding_error switch (err) {
error.UnsupportedControlFlow, error.UnsupportedControlFlow,
error.UnsupportedOperation, error.UnsupportedOperation,
error.UnsupportedPredication, error.UnsupportedPredication,
@@ -100,6 +102,7 @@ pub fn compile(allocator: std.mem.Allocator, module: *shader_ir.module.Module, d
=> null, => null,
else => return err, else => return err,
}; };
};
errdefer if (kernel) |bytes| allocator.free(bytes); errdefer if (kernel) |bytes| allocator.free(bytes);
return .{ return .{
@@ -53,6 +53,7 @@ fn bufferReference(operation: instruction.Operation) ?instruction.BufferReferenc
return switch (operation) { return switch (operation) {
.load_buffer => |op| op.buffer, .load_buffer => |op| op.buffer,
.store_buffer => |op| op.buffer, .store_buffer => |op| op.buffer,
.array_length => |op| op.buffer,
else => null, else => null,
}; };
} }
@@ -61,6 +62,7 @@ fn bufferReferenceMut(operation: *instruction.Operation) ?*instruction.BufferRef
return switch (operation.*) { return switch (operation.*) {
.load_buffer => |*op| &op.buffer, .load_buffer => |*op| &op.buffer,
.store_buffer => |*op| &op.buffer, .store_buffer => |*op| &op.buffer,
.array_length => |*op| &op.buffer,
else => null, else => null,
}; };
} }
+37
View File
@@ -150,3 +150,40 @@ test "[gen9] target: lower 256 KiB SSBO copy loop" {
try std.testing.expectEqual(@as(usize, 4), load_count); try std.testing.expectEqual(@as(usize, 4), load_count);
try std.testing.expectEqual(@as(usize, 4), store_count); try std.testing.expectEqual(@as(usize, 4), store_count);
} }
test "[gen9] target: encode runtime array length" {
var module = try shader_ir.parser.parseString(std.testing.allocator,
\\shader compute @main
\\{
\\ @storage: runtime_array[u32] = storage_buffer[set(0), binding(0)]
\\ %offset: constant u32 = 16
\\ fn @main() -> void
\\ {
\\ .entry():
\\ %length: u32 = array_length @storage, %offset, stride 4
\\ return
\\ }
\\}
);
defer module.deinit();
module.execution_modes.workgroup_size = .{ 1, 1, 1 };
const gen9_device: device.DeviceInfo = .{
.generation = .gen9,
.platform = .skylake,
.pci_device_id = 0x1912,
.grf_count = 128,
};
var artifact = try compileCompute(std.testing.allocator, &module, gen9_device, .{});
defer artifact.deinit(std.testing.allocator);
if (artifact.kernel == null) {
const encoded = try compute.kernel_encoder.encode(std.testing.allocator, &artifact.program);
std.testing.allocator.free(encoded);
return error.TestExpectedEncodedKernel;
}
for (artifact.program.instructions.entries.items) |entry| {
const inst = entry orelse continue;
try std.testing.expect(inst.operation != .array_length);
}
}
@@ -68,6 +68,10 @@ fn reserveExistingPhysicalRegisters(program: *const program_ir.Program, initial:
reserveRegister(&next_byte, op.byte_offset.register, grf_size); reserveRegister(&next_byte, op.byte_offset.register, grf_size);
reserveRegister(&next_byte, op.source.register, grf_size); reserveRegister(&next_byte, op.source.register, grf_size);
}, },
.array_length => |op| {
reserveRegister(&next_byte, op.destination.register, grf_size);
reserveRegister(&next_byte, op.byte_offset.register, grf_size);
},
.surface_read => |op| { .surface_read => |op| {
reserveRegister(&next_byte, op.destination.register, grf_size); reserveRegister(&next_byte, op.destination.register, grf_size);
reserveRegister(&next_byte, op.address.register, grf_size); reserveRegister(&next_byte, op.address.register, grf_size);
@@ -94,6 +98,11 @@ fn reserveExistingPhysicalRegisters(program: *const program_ir.Program, initial:
reserveRegister(&next_byte, op.lhs.register, grf_size); reserveRegister(&next_byte, op.lhs.register, grf_size);
reserveRegister(&next_byte, op.rhs.register, grf_size); reserveRegister(&next_byte, op.rhs.register, grf_size);
}, },
.math => |op| {
reserveRegister(&next_byte, op.destination.register, grf_size);
reserveRegister(&next_byte, op.lhs.register, grf_size);
reserveRegister(&next_byte, op.rhs.register, grf_size);
},
.parallel_copy => return Error.ParallelCopiesNotLowered, .parallel_copy => return Error.ParallelCopiesNotLowered,
} }
} }
@@ -125,6 +134,10 @@ fn rewriteProgram(program: *program_ir.Program, allocations: []const ?operand.Ph
try rewriteSource(program, &op.byte_offset, allocations); try rewriteSource(program, &op.byte_offset, allocations);
try rewriteSource(program, &op.source, allocations); try rewriteSource(program, &op.source, allocations);
}, },
.array_length => |*op| {
try rewriteDestination(program, &op.destination, allocations);
try rewriteSource(program, &op.byte_offset, allocations);
},
.surface_read => |*op| { .surface_read => |*op| {
try rewriteDestination(program, &op.destination, allocations); try rewriteDestination(program, &op.destination, allocations);
try rewriteSource(program, &op.address, allocations); try rewriteSource(program, &op.address, allocations);
@@ -151,6 +164,11 @@ fn rewriteProgram(program: *program_ir.Program, allocations: []const ?operand.Ph
try rewriteSource(program, &op.lhs, allocations); try rewriteSource(program, &op.lhs, allocations);
try rewriteSource(program, &op.rhs, allocations); try rewriteSource(program, &op.rhs, allocations);
}, },
.math => |*op| {
try rewriteDestination(program, &op.destination, allocations);
try rewriteSource(program, &op.lhs, allocations);
try rewriteSource(program, &op.rhs, allocations);
},
.parallel_copy => return Error.ParallelCopiesNotLowered, .parallel_copy => return Error.ParallelCopiesNotLowered,
} }
} }
@@ -64,6 +64,11 @@ fn validateInstruction(inst: instruction.Instruction) Error!void {
try validateSource(op.byte_offset); try validateSource(op.byte_offset);
try validateSource(op.source); try validateSource(op.source);
}, },
.array_length => |op| {
try validateBufferReference(op.buffer);
try validateDestination(op.destination);
try validateSource(op.byte_offset);
},
.surface_read => |op| { .surface_read => |op| {
try validateBindingTableIndex(op.binding_table); try validateBindingTableIndex(op.binding_table);
try validateDestination(op.destination); try validateDestination(op.destination);
@@ -89,6 +94,11 @@ fn validateInstruction(inst: instruction.Instruction) Error!void {
try validateSource(op.lhs); try validateSource(op.lhs);
try validateSource(op.rhs); try validateSource(op.rhs);
}, },
.math => |op| {
try validateDestination(op.destination);
try validateSource(op.lhs);
try validateSource(op.rhs);
},
.parallel_copy => |copy| { .parallel_copy => |copy| {
for (copy.register_copies) |item| { for (copy.register_copies) |item| {
try validateDestination(item.destination); try validateDestination(item.destination);