Files
VulkanDriver/src/intel/compiler/targets/gen9/validator.zig
T
kbz_8 b2f5fce3a0
Mirror Gitea refs to GitHub / mirror (push) Successful in 12s
Build / build (push) Failing after 1m4s
Test / build_and_test (push) Successful in 1m56s
[Flint] adding multiple registers/blocks support
2026-09-05 14:04:05 +02:00

220 lines
7.6 KiB
Zig

const std = @import("std");
const compute = @import("compute/compute.zig");
const shared = @import("../../ir/validator.zig");
const instruction = @import("../../ir/instruction.zig");
const operand = @import("../../ir/operand.zig");
const program_ir = @import("../../ir/program.zig");
pub const Error = shared.Error || compute.Error || error{
UnsupportedGeneration,
UnsupportedDispatchWidth,
UnsupportedGrfSize,
UnsupportedExecutionSize,
UnsupportedDataType,
InvalidPhysicalFlag,
InvalidBindingTableIndex,
InvalidPayloadLayout,
};
pub fn validate(program: *const program_ir.Program) Error!void {
try shared.validate(program);
if (program.device_info.generation != .gen9)
return Error.UnsupportedGeneration;
try compute.validateWorkgroupSize(program.workgroup_size);
if (program.dispatch_width != .simd8 or !program.device_info.supportsDispatch(.simd8))
return Error.UnsupportedDispatchWidth;
if (program.device_info.grf_size_bytes != 32)
return Error.UnsupportedGrfSize;
for (program.blocks.entries.items) |block_entry| {
const block = block_entry orelse continue;
for (block.instructions.items) |instruction_id| {
const inst = program.instructions.get(instruction_id) orelse return Error.InvalidInstruction;
switch (inst.execution_size) {
.simd1, .simd8 => {},
else => return Error.UnsupportedExecutionSize,
}
try validateInstruction(inst.*);
}
try validateTerminator(block.terminator.?);
}
for (program.virtual_registers.entries.items) |entry| {
const register = entry orelse continue;
if (!register.element_type.isInitialTargetType())
return Error.UnsupportedDataType;
}
try validatePayload(program);
}
fn validateInstruction(inst: instruction.Instruction) Error!void {
if (inst.predicate) |predicate|
try validateFlag(predicate.flag);
switch (inst.operation) {
.load_global_invocation_id, .load_num_workgroups => |op| try validateDestination(op.destination),
.load_buffer => |op| {
try validateBufferReference(op.buffer);
try validateDestination(op.destination);
try validateSource(op.byte_offset);
},
.store_buffer => |op| {
try validateBufferReference(op.buffer);
try validateSource(op.byte_offset);
try validateSource(op.source);
},
.array_length => |op| {
try validateBufferReference(op.buffer);
try validateDestination(op.destination);
try validateSource(op.byte_offset);
},
.surface_read => |op| {
try validateBindingTableIndex(op.binding_table);
try validateDestination(op.destination);
try validateSource(op.address);
},
.surface_write => |op| {
try validateBindingTableIndex(op.binding_table);
try validateSource(op.address);
try validateSource(op.data);
},
.surface_message => |op| try validateBindingTableIndex(op.binding_table),
.move => |op| {
try validateDestination(op.destination);
try validateSource(op.source);
},
.binary => |op| {
try validateDestination(op.destination);
try validateSource(op.lhs);
try validateSource(op.rhs);
},
.compare => |op| {
try validateFlag(op.destination);
try validateSource(op.lhs);
try validateSource(op.rhs);
},
.math => |op| {
try validateDestination(op.destination);
try validateSource(op.lhs);
try validateSource(op.rhs);
},
.parallel_copy => |copy| {
for (copy.register_copies) |item| {
try validateDestination(item.destination);
try validateSource(item.source);
}
for (copy.flag_copies) |item| switch (item.source) {
.constant => {},
.dynamic => |predicate| try validateFlag(predicate.flag),
};
},
}
}
fn validateBufferReference(reference: instruction.BufferReference) Error!void {
switch (reference) {
.logical => {},
.binding_table => |index| try validateBindingTableIndex(index),
}
}
fn validateBindingTableIndex(index: u8) Error!void {
if (index >= compute.resource_layout.max_storage_buffers)
return Error.InvalidBindingTableIndex;
}
fn validateSource(source: operand.Source) Error!void {
try validateType(source.type);
switch (source.register) {
.immediate => |immediate| try validateImmediate(immediate),
else => {},
}
}
fn validateDestination(destination: operand.Destination) Error!void {
try validateType(destination.type);
}
fn validateType(data_type: operand.DataType) Error!void {
if (!data_type.isInitialTargetType())
return Error.UnsupportedDataType;
}
fn validateImmediate(immediate: operand.Immediate) Error!void {
switch (immediate) {
.u32, .i32, .f32 => {},
}
}
fn validateTerminator(terminator: instruction.Terminator) Error!void {
switch (terminator) {
.conditional_branch => |branch| {
try validateFlag(branch.predicate.flag);
try validateEdge(branch.true_edge);
try validateEdge(branch.false_edge);
},
.jump => |edge| try validateEdge(edge),
else => {},
}
}
fn validateEdge(edge: instruction.Edge) Error!void {
for (edge.arguments) |argument| switch (argument) {
.source => {},
.predicate => |predicate_value| switch (predicate_value) {
.constant => {},
.dynamic => |predicate| try validateFlag(predicate.flag),
},
};
}
fn validateFlag(flag: operand.FlagRef) Error!void {
switch (flag) {
.virtual => {},
.physical => |physical| if (physical.register > 1 or physical.subregister > 1)
return Error.InvalidPhysicalFlag,
}
}
test "[gen9] validator: accept all four flag halves and reject out of range flags" {
for (0..2) |register| {
for (0..2) |subregister| {
try validateFlag(.{ .physical = .{ .register = @intCast(register), .subregister = @intCast(subregister) } });
}
}
try std.testing.expectError(Error.InvalidPhysicalFlag, validateFlag(.{ .physical = .{ .register = 2 } }));
try std.testing.expectError(Error.InvalidPhysicalFlag, validateFlag(.{ .physical = .{ .register = 1, .subregister = 2 } }));
}
fn validatePayload(program: *const program_ir.Program) Error!void {
if (program.payload.header_grf) |header| {
if (header.number != 0 or header.byte_offset != 0)
return Error.InvalidPayloadLayout;
}
}
test "[gen9] validator: layer target legality over shared structural validation" {
const Builder = @import("../../ir/Builder.zig");
const device = @import("../../device.zig");
const gen11_device: device.DeviceInfo = .{
.generation = .gen11,
.platform = .ice_lake,
.pci_device_id = 0x8a52,
.grf_count = 128,
.supports_simd16 = true,
};
var program = program_ir.Program.init(std.testing.allocator, .{ 1, 1, 1 }, gen11_device, .simd16);
defer program.deinit();
var builder = Builder.init(&program);
const entry = try builder.addBlock("entry");
try builder.setTerminator(entry, .end_thread);
try shared.validate(&program);
try std.testing.expectError(Error.UnsupportedGeneration, validate(&program));
program.device_info.generation = .gen9;
try std.testing.expectError(Error.UnsupportedDispatchWidth, validate(&program));
}