[Flint] normalize surface addresses and pack Gen9 message payloads
This commit is contained in:
@@ -283,6 +283,8 @@ test "Flint pipeline: lower common compute IR" {
|
|||||||
try std.testing.expect(!program.properties.system_values_lowered);
|
try std.testing.expect(!program.properties.system_values_lowered);
|
||||||
try std.testing.expect(program.properties.resources_lowered);
|
try std.testing.expect(program.properties.resources_lowered);
|
||||||
try std.testing.expect(program.properties.messages_lowered);
|
try std.testing.expect(program.properties.messages_lowered);
|
||||||
|
try std.testing.expect(program.properties.message_addresses_lowered);
|
||||||
|
try std.testing.expect(program.properties.message_payloads_lowered);
|
||||||
try std.testing.expect(program.properties.registers_allocated);
|
try std.testing.expect(program.properties.registers_allocated);
|
||||||
try std.testing.expect(!program.properties.instructions_selected);
|
try std.testing.expect(!program.properties.instructions_selected);
|
||||||
try std.testing.expectEqual([3]u32{ 1, 1, 1 }, program.workgroup_size);
|
try std.testing.expectEqual([3]u32{ 1, 1, 1 }, program.workgroup_size);
|
||||||
@@ -294,5 +296,5 @@ test "Flint pipeline: lower common compute IR" {
|
|||||||
const text = try compiler.printer.allocPrint(std.testing.allocator, program);
|
const text = try compiler.printer.allocPrint(std.testing.allocator, program);
|
||||||
defer std.testing.allocator.free(text);
|
defer std.testing.allocator.free(text);
|
||||||
try std.testing.expect(std.mem.indexOf(u8, text, "load_global_invocation_id r0:u32, component(0)") != null);
|
try std.testing.expect(std.mem.indexOf(u8, text, "load_global_invocation_id r0:u32, component(0)") != null);
|
||||||
try std.testing.expect(std.mem.indexOf(u8, text, "surface_write bti(0), 0:u32, r0:u32") != null);
|
try std.testing.expect(std.mem.indexOf(u8, text, "surface_message write bti(0)") != null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,19 @@ pub const SurfaceWrite = struct {
|
|||||||
data: operand.Source,
|
data: operand.Source,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const SurfaceMessageKind = enum {
|
||||||
|
read,
|
||||||
|
write,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const SurfaceMessage = struct {
|
||||||
|
kind: SurfaceMessageKind,
|
||||||
|
binding_table: u8,
|
||||||
|
payload: operand.RegisterSpan,
|
||||||
|
response: ?operand.RegisterSpan,
|
||||||
|
data_type: operand.DataType,
|
||||||
|
};
|
||||||
|
|
||||||
pub const Move = struct {
|
pub const Move = struct {
|
||||||
destination: operand.Destination,
|
destination: operand.Destination,
|
||||||
source: operand.Source,
|
source: operand.Source,
|
||||||
@@ -86,6 +99,7 @@ pub const Operation = union(enum) {
|
|||||||
store_buffer: StoreBuffer,
|
store_buffer: StoreBuffer,
|
||||||
surface_read: SurfaceRead,
|
surface_read: SurfaceRead,
|
||||||
surface_write: SurfaceWrite,
|
surface_write: SurfaceWrite,
|
||||||
|
surface_message: SurfaceMessage,
|
||||||
move: Move,
|
move: Move,
|
||||||
binary: Binary,
|
binary: Binary,
|
||||||
compare: Compare,
|
compare: Compare,
|
||||||
|
|||||||
@@ -152,6 +152,17 @@ 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.data);
|
try writeSource(program, writer, execution_size, op.data);
|
||||||
},
|
},
|
||||||
|
.surface_message => |op| {
|
||||||
|
try writer.print("surface_message {t} bti({d}), payload(", .{ op.kind, op.binding_table });
|
||||||
|
try writeRegister(program, writer, op.payload.base);
|
||||||
|
try writer.print(", {d})", .{op.payload.register_count});
|
||||||
|
if (op.response) |response| {
|
||||||
|
try writer.writeAll(", response(");
|
||||||
|
try writeRegister(program, writer, response.base);
|
||||||
|
try writer.print(", {d})", .{response.register_count});
|
||||||
|
}
|
||||||
|
try writer.print(", type({t})", .{op.data_type});
|
||||||
|
},
|
||||||
.move => |op| {
|
.move => |op| {
|
||||||
try writer.writeAll("mov ");
|
try writer.writeAll("mov ");
|
||||||
try writeDestination(program, writer, execution_size, op.destination);
|
try writeDestination(program, writer, execution_size, op.destination);
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ pub const Properties = packed struct {
|
|||||||
system_values_lowered: bool = false,
|
system_values_lowered: bool = false,
|
||||||
resources_lowered: bool = false,
|
resources_lowered: bool = false,
|
||||||
messages_lowered: bool = false,
|
messages_lowered: bool = false,
|
||||||
|
message_addresses_lowered: bool = false,
|
||||||
|
message_payloads_lowered: bool = false,
|
||||||
control_flow_lowered: bool = false,
|
control_flow_lowered: bool = false,
|
||||||
|
|
||||||
regions_legalized: bool = false,
|
regions_legalized: bool = false,
|
||||||
@@ -23,7 +25,7 @@ pub const Properties = packed struct {
|
|||||||
flags_allocated: bool = false,
|
flags_allocated: bool = false,
|
||||||
branches_resolved: bool = false,
|
branches_resolved: bool = false,
|
||||||
|
|
||||||
_padding: u19 = 0,
|
_padding: u17 = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const StorageBuffer = struct {
|
pub const StorageBuffer = struct {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ pub const Error = error{
|
|||||||
UnloweredSystemValue,
|
UnloweredSystemValue,
|
||||||
UnloweredResource,
|
UnloweredResource,
|
||||||
UnloweredMessage,
|
UnloweredMessage,
|
||||||
|
InvalidMessage,
|
||||||
InvalidPayloadLayout,
|
InvalidPayloadLayout,
|
||||||
EntryBlockHasParameters,
|
EntryBlockHasParameters,
|
||||||
DuplicateBlockParameter,
|
DuplicateBlockParameter,
|
||||||
@@ -168,6 +169,22 @@ fn validateInstruction(program: *const program_ir.Program, inst: instruction.Ins
|
|||||||
if (!op.data.type.isInitialTargetType())
|
if (!op.data.type.isInitialTargetType())
|
||||||
return Error.InvalidBufferAccess;
|
return Error.InvalidBufferAccess;
|
||||||
},
|
},
|
||||||
|
.surface_message => |op| {
|
||||||
|
try validateRegisterSpan(program, op.payload);
|
||||||
|
if (!op.data_type.isInitialTargetType())
|
||||||
|
return Error.InvalidMessage;
|
||||||
|
switch (op.kind) {
|
||||||
|
.read => {
|
||||||
|
if (op.payload.register_count != 1 or op.response == null)
|
||||||
|
return Error.InvalidMessage;
|
||||||
|
try validateRegisterSpan(program, op.response.?);
|
||||||
|
if (op.response.?.register_count != 1)
|
||||||
|
return Error.InvalidMessage;
|
||||||
|
},
|
||||||
|
.write => if (op.payload.register_count != 2 or op.response != null)
|
||||||
|
return Error.InvalidMessage,
|
||||||
|
}
|
||||||
|
},
|
||||||
.move => |op| {
|
.move => |op| {
|
||||||
try validateDestination(program, op.destination);
|
try validateDestination(program, op.destination);
|
||||||
try validateSource(program, op.source);
|
try validateSource(program, op.source);
|
||||||
@@ -303,6 +320,15 @@ fn validateDestination(program: *const program_ir.Program, destination: operand.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validateRegisterSpan(program: *const program_ir.Program, span: operand.RegisterSpan) Error!void {
|
||||||
|
if (span.register_count == 0)
|
||||||
|
return Error.InvalidMessage;
|
||||||
|
switch (span.base) {
|
||||||
|
.virtual, .physical_grf => try validateRegisterRef(program, span.base),
|
||||||
|
else => return Error.InvalidMessage,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn validateRegisterRef(program: *const program_ir.Program, register: operand.RegisterRef) Error!void {
|
fn validateRegisterRef(program: *const program_ir.Program, register: operand.RegisterRef) Error!void {
|
||||||
switch (register) {
|
switch (register) {
|
||||||
.virtual => |id| if (!program.virtual_registers.isLive(id))
|
.virtual => |id| if (!program.virtual_registers.isLive(id))
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const operand = @import("../ir/operand.zig");
|
|||||||
const program_ir = @import("../ir/program.zig");
|
const program_ir = @import("../ir/program.zig");
|
||||||
const pseudo = @import("../ir/pseudo.zig");
|
const pseudo = @import("../ir/pseudo.zig");
|
||||||
const validator = @import("../ir/validator.zig");
|
const validator = @import("../ir/validator.zig");
|
||||||
|
const device = @import("../device.zig");
|
||||||
|
|
||||||
pub const Error = std.mem.Allocator.Error || error{
|
pub const Error = std.mem.Allocator.Error || error{
|
||||||
InvalidProgram,
|
InvalidProgram,
|
||||||
@@ -128,7 +129,7 @@ fn rewriteEdge(
|
|||||||
return .{ .target = edge_block, .arguments = &.{} };
|
return .{ .target = edge_block, .arguments = &.{} };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn executionSize(dispatch_width: @import("../device.zig").DispatchWidth) @import("../device.zig").ExecutionSize {
|
fn executionSize(dispatch_width: device.DispatchWidth) device.ExecutionSize {
|
||||||
return @enumFromInt(@intFromEnum(dispatch_width));
|
return @enumFromInt(@intFromEnum(dispatch_width));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +141,6 @@ fn mapBuilderError(err: anyerror) Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "[ir] block arguments: lower register and flag parameters" {
|
test "[ir] block arguments: lower register and flag parameters" {
|
||||||
const device = @import("../device.zig");
|
|
||||||
const printer = @import("../ir/printer.zig");
|
const printer = @import("../ir/printer.zig");
|
||||||
|
|
||||||
const device_info: device.DeviceInfo = .{
|
const device_info: device.DeviceInfo = .{
|
||||||
@@ -215,8 +215,6 @@ test "[ir] block arguments: lower register and flag parameters" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "[ir] block arguments: split same-target conditional edges" {
|
test "[ir] block arguments: split same-target conditional edges" {
|
||||||
const device = @import("../device.zig");
|
|
||||||
|
|
||||||
const device_info: device.DeviceInfo = .{
|
const device_info: device.DeviceInfo = .{
|
||||||
.generation = .gen9,
|
.generation = .gen9,
|
||||||
.platform = .skylake,
|
.platform = .skylake,
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub const message_addresses = @import("message_addresses.zig");
|
||||||
pub const message_lowering = @import("message_lowering.zig");
|
pub const message_lowering = @import("message_lowering.zig");
|
||||||
|
pub const message_payloads = @import("message_payloads.zig");
|
||||||
pub const resource_layout = @import("resource_layout.zig");
|
pub const resource_layout = @import("resource_layout.zig");
|
||||||
pub const resource_lowering = @import("resource_lowering.zig");
|
pub const resource_lowering = @import("resource_lowering.zig");
|
||||||
pub const ResourceLayout = resource_layout.Layout;
|
pub const ResourceLayout = resource_layout.Layout;
|
||||||
|
|||||||
@@ -0,0 +1,208 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const Builder = @import("../../../ir/Builder.zig");
|
||||||
|
const ids = @import("../../../ir/id.zig");
|
||||||
|
const operand = @import("../../../ir/operand.zig");
|
||||||
|
const program_ir = @import("../../../ir/program.zig");
|
||||||
|
const instruction = @import("../../../ir/instruction.zig");
|
||||||
|
|
||||||
|
pub const Error = std.mem.Allocator.Error || error{
|
||||||
|
MessagesNotLowered,
|
||||||
|
InvalidProgram,
|
||||||
|
};
|
||||||
|
|
||||||
|
const AddressAdjustment = struct {
|
||||||
|
address: operand.Source,
|
||||||
|
immediate_offset: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn run(program: *program_ir.Program) Error!void {
|
||||||
|
if (!program.properties.messages_lowered)
|
||||||
|
return error.MessagesNotLowered;
|
||||||
|
if (program.properties.message_addresses_lowered)
|
||||||
|
return;
|
||||||
|
|
||||||
|
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 adjustment = addressAdjustment(inst.operation) orelse {
|
||||||
|
instruction_index += 1;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if (adjustment.address.type != .u32)
|
||||||
|
return error.InvalidProgram;
|
||||||
|
|
||||||
|
if (adjustment.immediate_offset == 0) {
|
||||||
|
instruction_index += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (adjustment.address.register) {
|
||||||
|
.immediate => |immediate| {
|
||||||
|
const base = switch (immediate) {
|
||||||
|
.u32 => |value| value,
|
||||||
|
else => return error.InvalidProgram,
|
||||||
|
};
|
||||||
|
const mutable = program.instructions.getMut(instruction_id) orelse return error.InvalidProgram;
|
||||||
|
const address = messageAddressMut(&mutable.operation) orelse return error.InvalidProgram;
|
||||||
|
address.source.register = .{ .immediate = .{ .u32 = base +% adjustment.immediate_offset } };
|
||||||
|
address.immediate_offset.* = 0;
|
||||||
|
instruction_index += 1;
|
||||||
|
},
|
||||||
|
.virtual, .physical_grf, .architecture => {
|
||||||
|
const execution_width: u32 = @intFromEnum(inst.execution_size);
|
||||||
|
const size_bytes = execution_width * @sizeOf(u32);
|
||||||
|
const address_register = builder.addVirtualRegister(.{
|
||||||
|
.size_bytes = size_bytes,
|
||||||
|
.alignment_bytes = @intCast(@min(size_bytes, program.device_info.grf_size_bytes)),
|
||||||
|
.element_type = .u32,
|
||||||
|
.lane_count = @intCast(execution_width),
|
||||||
|
.class = .temporary,
|
||||||
|
}) catch |err| return mapBuilderError(err);
|
||||||
|
|
||||||
|
_ = builder.insertInstruction(block_id, instruction_index, inst.execution_size, inst.predicate, .{
|
||||||
|
.binary = .{
|
||||||
|
.opcode = .add,
|
||||||
|
.destination = .{
|
||||||
|
.register = .{ .virtual = address_register },
|
||||||
|
.type = .u32,
|
||||||
|
},
|
||||||
|
.lhs = adjustment.address,
|
||||||
|
.rhs = immediateSource(adjustment.immediate_offset),
|
||||||
|
},
|
||||||
|
}) catch |err| return mapBuilderError(err);
|
||||||
|
|
||||||
|
const mutable = program.instructions.getMut(instruction_id) orelse return error.InvalidProgram;
|
||||||
|
const address = messageAddressMut(&mutable.operation) orelse return error.InvalidProgram;
|
||||||
|
address.source.* = .{
|
||||||
|
.register = .{ .virtual = address_register },
|
||||||
|
.type = .u32,
|
||||||
|
.region = operand.Region.contiguous(inst.execution_size),
|
||||||
|
};
|
||||||
|
address.immediate_offset.* = 0;
|
||||||
|
instruction_index += 2;
|
||||||
|
},
|
||||||
|
.null => return error.InvalidProgram,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
program.properties.message_addresses_lowered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn addressAdjustment(operation: instruction.Operation) ?AddressAdjustment {
|
||||||
|
return switch (operation) {
|
||||||
|
.surface_read => |op| .{ .address = op.address, .immediate_offset = op.immediate_offset },
|
||||||
|
.surface_write => |op| .{ .address = op.address, .immediate_offset = op.immediate_offset },
|
||||||
|
else => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const MutableAddress = struct {
|
||||||
|
source: *operand.Source,
|
||||||
|
immediate_offset: *u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn messageAddressMut(operation: *instruction.Operation) ?MutableAddress {
|
||||||
|
return switch (operation.*) {
|
||||||
|
.surface_read => |*op| .{ .source = &op.address, .immediate_offset = &op.immediate_offset },
|
||||||
|
.surface_write => |*op| .{ .source = &op.address, .immediate_offset = &op.immediate_offset },
|
||||||
|
else => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn immediateSource(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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const device = @import("../../../device.zig");
|
||||||
|
|
||||||
|
const test_device: device.DeviceInfo = .{
|
||||||
|
.generation = .gen9,
|
||||||
|
.platform = .skylake,
|
||||||
|
.pci_device_id = 0x1912,
|
||||||
|
.grf_count = 128,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn markPrerequisite(program: *program_ir.Program) void {
|
||||||
|
program.properties.messages_lowered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
test "[gen9] message addresses: fold immediate offsets" {
|
||||||
|
var program = program_ir.Program.init(std.testing.allocator, .{ 1, 1, 1 }, test_device, .simd8);
|
||||||
|
defer program.deinit();
|
||||||
|
|
||||||
|
const entry = try program.addBlock("entry");
|
||||||
|
const message = try program.appendInstruction(entry, .simd8, null, .{ .surface_write = .{
|
||||||
|
.binding_table = 0,
|
||||||
|
.address = immediateSource(12),
|
||||||
|
.immediate_offset = 4,
|
||||||
|
.data = immediateSource(7),
|
||||||
|
} });
|
||||||
|
try program.setTerminator(entry, .end_thread);
|
||||||
|
markPrerequisite(&program);
|
||||||
|
|
||||||
|
try run(&program);
|
||||||
|
|
||||||
|
const write = program.instructions.get(message).?.operation.surface_write;
|
||||||
|
try std.testing.expectEqual(@as(u32, 16), write.address.register.immediate.u32);
|
||||||
|
try std.testing.expectEqual(@as(u32, 0), write.immediate_offset);
|
||||||
|
try std.testing.expect(program.properties.message_addresses_lowered);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "[gen9] message addresses: materialize dynamic offsets" {
|
||||||
|
var program = program_ir.Program.init(std.testing.allocator, .{ 1, 1, 1 }, test_device, .simd8);
|
||||||
|
defer program.deinit();
|
||||||
|
|
||||||
|
const base = try program.addVirtualRegister(.{
|
||||||
|
.size_bytes = 32,
|
||||||
|
.alignment_bytes = 32,
|
||||||
|
.element_type = .u32,
|
||||||
|
.lane_count = 8,
|
||||||
|
.class = .temporary,
|
||||||
|
});
|
||||||
|
const entry = try program.addBlock("entry");
|
||||||
|
const message = try program.appendInstruction(entry, .simd8, null, .{ .surface_read = .{
|
||||||
|
.destination = .{ .register = .{ .virtual = base }, .type = .u32 },
|
||||||
|
.binding_table = 0,
|
||||||
|
.address = .{
|
||||||
|
.register = .{ .virtual = base },
|
||||||
|
.type = .u32,
|
||||||
|
.region = operand.Region.contiguous(.simd8),
|
||||||
|
},
|
||||||
|
.immediate_offset = 8,
|
||||||
|
} });
|
||||||
|
try program.setTerminator(entry, .end_thread);
|
||||||
|
markPrerequisite(&program);
|
||||||
|
|
||||||
|
try run(&program);
|
||||||
|
|
||||||
|
const block = program.blocks.get(entry).?;
|
||||||
|
try std.testing.expectEqual(@as(usize, 2), block.instructions.items.len);
|
||||||
|
try std.testing.expect(program.instructions.get(block.instructions.items[0]).?.operation == .binary);
|
||||||
|
const read = program.instructions.get(message).?.operation.surface_read;
|
||||||
|
try std.testing.expect(read.address.register == .virtual);
|
||||||
|
try std.testing.expect(read.address.register.virtual != base);
|
||||||
|
try std.testing.expectEqual(@as(u32, 0), read.immediate_offset);
|
||||||
|
}
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const Builder = @import("../../../ir/Builder.zig");
|
||||||
|
const device = @import("../../../device.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");
|
||||||
|
|
||||||
|
pub const Error = std.mem.Allocator.Error || error{
|
||||||
|
MessageAddressesNotLowered,
|
||||||
|
InvalidProgram,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn run(program: *program_ir.Program) Error!void {
|
||||||
|
if (!program.properties.message_addresses_lowered)
|
||||||
|
return error.MessageAddressesNotLowered;
|
||||||
|
if (program.properties.message_payloads_lowered)
|
||||||
|
return;
|
||||||
|
if (program.device_info.grf_size_bytes != 32)
|
||||||
|
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 execution_size = inst.execution_size;
|
||||||
|
switch (inst.operation) {
|
||||||
|
.surface_read => |op| {
|
||||||
|
if (op.immediate_offset != 0 or op.address.type != .u32)
|
||||||
|
return error.InvalidProgram;
|
||||||
|
const response = try responseSpan(op.destination);
|
||||||
|
const payload = try addPayloadRegister(&builder, execution_size, 1);
|
||||||
|
_ = builder.insertInstruction(block_id, instruction_index, execution_size, null, .{ .move = .{
|
||||||
|
.destination = payloadDestination(payload, 0, .u32),
|
||||||
|
.source = op.address,
|
||||||
|
} }) catch |err| return mapBuilderError(err);
|
||||||
|
|
||||||
|
const mutable = program.instructions.getMut(instruction_id) orelse return error.InvalidProgram;
|
||||||
|
mutable.operation = .{ .surface_message = .{
|
||||||
|
.kind = .read,
|
||||||
|
.binding_table = op.binding_table,
|
||||||
|
.payload = .{ .base = .{ .virtual = payload }, .register_count = 1 },
|
||||||
|
.response = response,
|
||||||
|
.data_type = op.destination.type,
|
||||||
|
} };
|
||||||
|
instruction_index += 2;
|
||||||
|
},
|
||||||
|
.surface_write => |op| {
|
||||||
|
if (op.immediate_offset != 0 or op.address.type != .u32)
|
||||||
|
return error.InvalidProgram;
|
||||||
|
const payload = try addPayloadRegister(&builder, execution_size, 2);
|
||||||
|
_ = builder.insertInstruction(block_id, instruction_index, execution_size, null, .{ .move = .{
|
||||||
|
.destination = payloadDestination(payload, 0, .u32),
|
||||||
|
.source = op.address,
|
||||||
|
} }) catch |err| return mapBuilderError(err);
|
||||||
|
_ = builder.insertInstruction(block_id, instruction_index + 1, execution_size, null, .{ .move = .{
|
||||||
|
.destination = payloadDestination(payload, 32, op.data.type),
|
||||||
|
.source = op.data,
|
||||||
|
} }) catch |err| return mapBuilderError(err);
|
||||||
|
|
||||||
|
const mutable = program.instructions.getMut(instruction_id) orelse return error.InvalidProgram;
|
||||||
|
mutable.operation = .{ .surface_message = .{
|
||||||
|
.kind = .write,
|
||||||
|
.binding_table = op.binding_table,
|
||||||
|
.payload = .{ .base = .{ .virtual = payload }, .register_count = 2 },
|
||||||
|
.response = null,
|
||||||
|
.data_type = op.data.type,
|
||||||
|
} };
|
||||||
|
instruction_index += 3;
|
||||||
|
},
|
||||||
|
else => instruction_index += 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
program.properties.message_payloads_lowered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn addPayloadRegister(builder: *Builder, execution_size: device.ExecutionSize, register_count: u8) Error!ids.VirtualRegisterId {
|
||||||
|
return builder.addVirtualRegister(.{
|
||||||
|
.size_bytes = @as(u32, register_count) * 32,
|
||||||
|
.alignment_bytes = 32,
|
||||||
|
.element_type = .u32,
|
||||||
|
.lane_count = @intFromEnum(execution_size),
|
||||||
|
.class = .temporary,
|
||||||
|
.spillable = false,
|
||||||
|
}) catch |err| return mapBuilderError(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn payloadDestination(register: ids.VirtualRegisterId, byte_offset: u16, data_type: operand.DataType) operand.Destination {
|
||||||
|
return .{
|
||||||
|
.register = .{ .virtual = register },
|
||||||
|
.type = data_type,
|
||||||
|
.region = .{ .byte_offset = byte_offset },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn responseSpan(destination: operand.Destination) Error!operand.RegisterSpan {
|
||||||
|
if (destination.region.byte_offset != 0 or destination.region.horizontal_stride != 1)
|
||||||
|
return error.InvalidProgram;
|
||||||
|
return switch (destination.register) {
|
||||||
|
.virtual, .physical_grf => .{
|
||||||
|
.base = destination.register,
|
||||||
|
.register_count = 1,
|
||||||
|
},
|
||||||
|
else => error.InvalidProgram,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mapBuilderError(err: Builder.Error) Error {
|
||||||
|
return switch (err) {
|
||||||
|
error.OutOfMemory => error.OutOfMemory,
|
||||||
|
else => error.InvalidProgram,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const test_device: device.DeviceInfo = .{
|
||||||
|
.generation = .gen9,
|
||||||
|
.platform = .skylake,
|
||||||
|
.pci_device_id = 0x1912,
|
||||||
|
.grf_count = 128,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn immediate(value: u32) operand.Source {
|
||||||
|
return .{
|
||||||
|
.register = .{ .immediate = .{ .u32 = value } },
|
||||||
|
.type = .u32,
|
||||||
|
.region = operand.Region.broadcast(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
test "[gen9] message payloads: pack surface write address and data" {
|
||||||
|
var program = program_ir.Program.init(std.testing.allocator, .{ 1, 1, 1 }, test_device, .simd8);
|
||||||
|
defer program.deinit();
|
||||||
|
|
||||||
|
const entry = try program.addBlock("entry");
|
||||||
|
const message = try program.appendInstruction(entry, .simd8, null, .{ .surface_write = .{
|
||||||
|
.binding_table = 2,
|
||||||
|
.address = immediate(16),
|
||||||
|
.data = immediate(42),
|
||||||
|
} });
|
||||||
|
try program.setTerminator(entry, .end_thread);
|
||||||
|
program.properties.message_addresses_lowered = true;
|
||||||
|
|
||||||
|
try run(&program);
|
||||||
|
|
||||||
|
const block = program.blocks.get(entry).?;
|
||||||
|
try std.testing.expectEqual(@as(usize, 3), block.instructions.items.len);
|
||||||
|
const address_move = program.instructions.get(block.instructions.items[0]).?.operation.move;
|
||||||
|
const data_move = program.instructions.get(block.instructions.items[1]).?.operation.move;
|
||||||
|
try std.testing.expectEqual(@as(u16, 0), address_move.destination.region.byte_offset);
|
||||||
|
try std.testing.expectEqual(@as(u16, 32), data_move.destination.region.byte_offset);
|
||||||
|
try std.testing.expectEqual(address_move.destination.register.virtual, data_move.destination.register.virtual);
|
||||||
|
|
||||||
|
const send = program.instructions.get(message).?.operation.surface_message;
|
||||||
|
try std.testing.expectEqual(instruction.SurfaceMessageKind.write, send.kind);
|
||||||
|
try std.testing.expectEqual(@as(u8, 2), send.binding_table);
|
||||||
|
try std.testing.expectEqual(@as(u8, 2), send.payload.register_count);
|
||||||
|
try std.testing.expect(send.response == null);
|
||||||
|
try std.testing.expect(program.properties.message_payloads_lowered);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "[gen9] message payloads: prepare surface read response" {
|
||||||
|
var program = program_ir.Program.init(std.testing.allocator, .{ 1, 1, 1 }, test_device, .simd8);
|
||||||
|
defer program.deinit();
|
||||||
|
|
||||||
|
const result = try program.addVirtualRegister(.{
|
||||||
|
.size_bytes = 32,
|
||||||
|
.alignment_bytes = 32,
|
||||||
|
.element_type = .u32,
|
||||||
|
.lane_count = 8,
|
||||||
|
.class = .response,
|
||||||
|
});
|
||||||
|
const entry = try program.addBlock("entry");
|
||||||
|
const message = try program.appendInstruction(entry, .simd8, null, .{ .surface_read = .{
|
||||||
|
.destination = .{ .register = .{ .virtual = result }, .type = .u32 },
|
||||||
|
.binding_table = 1,
|
||||||
|
.address = immediate(0),
|
||||||
|
} });
|
||||||
|
try program.setTerminator(entry, .end_thread);
|
||||||
|
program.properties.message_addresses_lowered = true;
|
||||||
|
|
||||||
|
try run(&program);
|
||||||
|
|
||||||
|
const send = program.instructions.get(message).?.operation.surface_message;
|
||||||
|
try std.testing.expectEqual(instruction.SurfaceMessageKind.read, send.kind);
|
||||||
|
try std.testing.expectEqual(@as(u8, 1), send.payload.register_count);
|
||||||
|
try std.testing.expectEqual(result, send.response.?.base.virtual);
|
||||||
|
try std.testing.expectEqual(@as(u8, 1), send.response.?.register_count);
|
||||||
|
}
|
||||||
@@ -10,12 +10,14 @@ const flag_allocation = @import("../flag_allocation.zig");
|
|||||||
const register_allocation = @import("../register_allocation.zig");
|
const register_allocation = @import("../register_allocation.zig");
|
||||||
|
|
||||||
const compute = @import("compute.zig");
|
const compute = @import("compute.zig");
|
||||||
|
const message_addresses = @import("message_addresses.zig");
|
||||||
const message_lowering = @import("message_lowering.zig");
|
const message_lowering = @import("message_lowering.zig");
|
||||||
|
const message_payloads = @import("message_payloads.zig");
|
||||||
const resource_layout = @import("resource_layout.zig");
|
const resource_layout = @import("resource_layout.zig");
|
||||||
const resource_lowering = @import("resource_lowering.zig");
|
const resource_lowering = @import("resource_lowering.zig");
|
||||||
|
|
||||||
pub const Error = common_ir.Error || block_arguments.Error || parallel_copies.Error ||
|
pub const Error = common_ir.Error || block_arguments.Error || parallel_copies.Error ||
|
||||||
message_lowering.Error || resource_layout.Error || resource_lowering.Error || flag_allocation.Error || register_allocation.Error || compute.Error || error{
|
message_addresses.Error || message_lowering.Error || message_payloads.Error || resource_layout.Error || resource_lowering.Error || flag_allocation.Error || register_allocation.Error || compute.Error || error{
|
||||||
UnsupportedGeneration,
|
UnsupportedGeneration,
|
||||||
UnsupportedStage,
|
UnsupportedStage,
|
||||||
UnsupportedDispatchWidth,
|
UnsupportedDispatchWidth,
|
||||||
@@ -64,6 +66,8 @@ pub fn compile(allocator: std.mem.Allocator, module: *shader_ir.module.Module, d
|
|||||||
|
|
||||||
try resource_lowering.run(&program, &resources);
|
try resource_lowering.run(&program, &resources);
|
||||||
try message_lowering.run(&program);
|
try message_lowering.run(&program);
|
||||||
|
try message_addresses.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);
|
||||||
|
|
||||||
|
|||||||
@@ -106,6 +106,8 @@ test "[gen9] target: lower 256 KiB SSBO copy loop" {
|
|||||||
|
|
||||||
try std.testing.expect(program.properties.resources_lowered);
|
try std.testing.expect(program.properties.resources_lowered);
|
||||||
try std.testing.expect(program.properties.messages_lowered);
|
try std.testing.expect(program.properties.messages_lowered);
|
||||||
|
try std.testing.expect(program.properties.message_addresses_lowered);
|
||||||
|
try std.testing.expect(program.properties.message_payloads_lowered);
|
||||||
try std.testing.expectEqual(@as(usize, 2), resources.bindings.len);
|
try std.testing.expectEqual(@as(usize, 2), resources.bindings.len);
|
||||||
try std.testing.expectEqual(compute.resource_layout.Binding{
|
try std.testing.expectEqual(compute.resource_layout.Binding{
|
||||||
.set = 0,
|
.set = 0,
|
||||||
@@ -118,28 +120,24 @@ test "[gen9] target: lower 256 KiB SSBO copy loop" {
|
|||||||
.binding_table_index = 1,
|
.binding_table_index = 1,
|
||||||
}, resources.bindings[1]);
|
}, resources.bindings[1]);
|
||||||
|
|
||||||
var load_offsets: [4]bool = @splat(false);
|
|
||||||
var store_offsets: [4]bool = @splat(false);
|
|
||||||
var load_count: usize = 0;
|
var load_count: usize = 0;
|
||||||
var store_count: usize = 0;
|
var store_count: usize = 0;
|
||||||
for (program.instructions.entries.items) |instruction_entry| {
|
for (program.instructions.entries.items) |instruction_entry| {
|
||||||
const inst = instruction_entry orelse continue;
|
const inst = instruction_entry orelse continue;
|
||||||
switch (inst.operation) {
|
switch (inst.operation) {
|
||||||
.surface_read => |operation| {
|
.surface_message => |operation| switch (operation.kind) {
|
||||||
try std.testing.expectEqual(@as(u8, 0), operation.binding_table);
|
.read => {
|
||||||
try std.testing.expect(operation.immediate_offset % @sizeOf(u32) == 0);
|
try std.testing.expectEqual(@as(u8, 0), operation.binding_table);
|
||||||
const component = operation.immediate_offset / @sizeOf(u32);
|
try std.testing.expectEqual(@as(u8, 1), operation.payload.register_count);
|
||||||
try std.testing.expect(component < load_offsets.len);
|
try std.testing.expect(operation.response != null);
|
||||||
load_offsets[component] = true;
|
load_count += 1;
|
||||||
load_count += 1;
|
},
|
||||||
},
|
.write => {
|
||||||
.surface_write => |operation| {
|
try std.testing.expectEqual(@as(u8, 1), operation.binding_table);
|
||||||
try std.testing.expectEqual(@as(u8, 1), operation.binding_table);
|
try std.testing.expectEqual(@as(u8, 2), operation.payload.register_count);
|
||||||
try std.testing.expect(operation.immediate_offset % @sizeOf(u32) == 0);
|
try std.testing.expect(operation.response == null);
|
||||||
const component = operation.immediate_offset / @sizeOf(u32);
|
store_count += 1;
|
||||||
try std.testing.expect(component < store_offsets.len);
|
},
|
||||||
store_offsets[component] = true;
|
|
||||||
store_count += 1;
|
|
||||||
},
|
},
|
||||||
.parallel_copy => return error.UnloweredParallelCopy,
|
.parallel_copy => return error.UnloweredParallelCopy,
|
||||||
else => {},
|
else => {},
|
||||||
@@ -148,6 +146,4 @@ 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);
|
||||||
try std.testing.expectEqual([4]bool{ true, true, true, true }, load_offsets);
|
|
||||||
try std.testing.expectEqual([4]bool{ true, true, true, true }, store_offsets);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,6 +76,11 @@ fn reserveExistingPhysicalRegisters(program: *const program_ir.Program, initial:
|
|||||||
reserveRegister(&next_byte, op.address.register, grf_size);
|
reserveRegister(&next_byte, op.address.register, grf_size);
|
||||||
reserveRegister(&next_byte, op.data.register, grf_size);
|
reserveRegister(&next_byte, op.data.register, grf_size);
|
||||||
},
|
},
|
||||||
|
.surface_message => |op| {
|
||||||
|
reserveRegister(&next_byte, op.payload.base, grf_size);
|
||||||
|
if (op.response) |response|
|
||||||
|
reserveRegister(&next_byte, response.base, grf_size);
|
||||||
|
},
|
||||||
.move => |op| {
|
.move => |op| {
|
||||||
reserveRegister(&next_byte, op.destination.register, grf_size);
|
reserveRegister(&next_byte, op.destination.register, grf_size);
|
||||||
reserveRegister(&next_byte, op.source.register, grf_size);
|
reserveRegister(&next_byte, op.source.register, grf_size);
|
||||||
@@ -128,6 +133,11 @@ fn rewriteProgram(program: *program_ir.Program, allocations: []const ?operand.Ph
|
|||||||
try rewriteSource(program, &op.address, allocations);
|
try rewriteSource(program, &op.address, allocations);
|
||||||
try rewriteSource(program, &op.data, allocations);
|
try rewriteSource(program, &op.data, allocations);
|
||||||
},
|
},
|
||||||
|
.surface_message => |*op| {
|
||||||
|
try rewriteRegister(program, &op.payload.base, allocations);
|
||||||
|
if (op.response) |*response|
|
||||||
|
try rewriteRegister(program, &response.base, allocations);
|
||||||
|
},
|
||||||
.move => |*op| {
|
.move => |*op| {
|
||||||
try rewriteDestination(program, &op.destination, allocations);
|
try rewriteDestination(program, &op.destination, allocations);
|
||||||
try rewriteSource(program, &op.source, allocations);
|
try rewriteSource(program, &op.source, allocations);
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ fn validateInstruction(inst: instruction.Instruction) Error!void {
|
|||||||
try validateSource(op.address);
|
try validateSource(op.address);
|
||||||
try validateSource(op.data);
|
try validateSource(op.data);
|
||||||
},
|
},
|
||||||
|
.surface_message => |op| try validateBindingTableIndex(op.binding_table),
|
||||||
.move => |op| {
|
.move => |op| {
|
||||||
try validateDestination(op.destination);
|
try validateDestination(op.destination);
|
||||||
try validateSource(op.source);
|
try validateSource(op.source);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ const SoftPipeline = @import("SoftPipeline.zig");
|
|||||||
const SoftRenderPass = @import("SoftRenderPass.zig");
|
const SoftRenderPass = @import("SoftRenderPass.zig");
|
||||||
|
|
||||||
const ExecutionDevice = @import("device/Device.zig");
|
const ExecutionDevice = @import("device/Device.zig");
|
||||||
|
const Renderer = @import("device/Renderer.zig");
|
||||||
const blitter = @import("device/blitter.zig");
|
const blitter = @import("device/blitter.zig");
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
@@ -1375,7 +1376,7 @@ pub fn setDepthBias(interface: *Interface, constant_factor: f32, clamp: f32, slo
|
|||||||
const CommandImpl = struct {
|
const CommandImpl = struct {
|
||||||
const Impl = @This();
|
const Impl = @This();
|
||||||
|
|
||||||
depth_bias: @import("device/Renderer.zig").DepthBias,
|
depth_bias: Renderer.DepthBias,
|
||||||
|
|
||||||
pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void {
|
pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void {
|
||||||
const impl: *Impl = @ptrCast(@alignCast(context));
|
const impl: *Impl = @ptrCast(@alignCast(context));
|
||||||
@@ -1402,7 +1403,7 @@ pub fn setDepthBounds(interface: *Interface, min: f32, max: f32) VkError!void {
|
|||||||
const CommandImpl = struct {
|
const CommandImpl = struct {
|
||||||
const Impl = @This();
|
const Impl = @This();
|
||||||
|
|
||||||
depth_bounds: @import("device/Renderer.zig").DepthBounds,
|
depth_bounds: Renderer.DepthBounds,
|
||||||
|
|
||||||
pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void {
|
pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void {
|
||||||
const impl: *Impl = @ptrCast(@alignCast(context));
|
const impl: *Impl = @ptrCast(@alignCast(context));
|
||||||
|
|||||||
Reference in New Issue
Block a user