220 lines
7.6 KiB
Zig
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));
|
|
}
|