aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/spirv/Section.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2025-08-07 20:55:50 -0700
committerGitHub <noreply@github.com>2025-08-07 20:55:50 -0700
commit3fb86841cc65437c65a6d599117833e260ea797c (patch)
tree2f4df8b5da41e8df00ba0989ca72beb26566b6c1 /src/codegen/spirv/Section.zig
parent5998a8cebe3973d70c258b2a1440c5c3252d3539 (diff)
parentcd4b03c5ed1bc5b48ad9c353679b309ead75551d (diff)
downloadzig-3fb86841cc65437c65a6d599117833e260ea797c.tar.gz
zig-3fb86841cc65437c65a6d599117833e260ea797c.zip
Merge pull request #24661 from alichraghi/spv4
spirv: refactor and remove deduplication ISel
Diffstat (limited to 'src/codegen/spirv/Section.zig')
-rw-r--r--src/codegen/spirv/Section.zig251
1 files changed, 46 insertions, 205 deletions
diff --git a/src/codegen/spirv/Section.zig b/src/codegen/spirv/Section.zig
index 53fbe66764..d5748afa39 100644
--- a/src/codegen/spirv/Section.zig
+++ b/src/codegen/spirv/Section.zig
@@ -13,8 +13,6 @@ const Log2Word = std.math.Log2Int(Word);
const Opcode = spec.Opcode;
-/// The instructions in this section. Memory is owned by the Module
-/// externally associated to this Section.
instructions: std.ArrayListUnmanaged(Word) = .empty,
pub fn deinit(section: *Section, allocator: Allocator) void {
@@ -22,9 +20,8 @@ pub fn deinit(section: *Section, allocator: Allocator) void {
section.* = undefined;
}
-/// Clear the instructions in this section
pub fn reset(section: *Section) void {
- section.instructions.items.len = 0;
+ section.instructions.clearRetainingCapacity();
}
pub fn toWords(section: Section) []Word {
@@ -36,9 +33,12 @@ pub fn append(section: *Section, allocator: Allocator, other_section: Section) !
try section.instructions.appendSlice(allocator, other_section.instructions.items);
}
-/// Ensure capacity of at least `capacity` more words in this section.
-pub fn ensureUnusedCapacity(section: *Section, allocator: Allocator, capacity: usize) !void {
- try section.instructions.ensureUnusedCapacity(allocator, capacity);
+pub fn ensureUnusedCapacity(
+ section: *Section,
+ allocator: Allocator,
+ words: usize,
+) !void {
+ try section.instructions.ensureUnusedCapacity(allocator, words);
}
/// Write an instruction and size, operands are to be inserted manually.
@@ -46,7 +46,7 @@ pub fn emitRaw(
section: *Section,
allocator: Allocator,
opcode: Opcode,
- operand_words: usize, // opcode itself not included
+ operand_words: usize,
) !void {
const word_count = 1 + operand_words;
try section.instructions.ensureUnusedCapacity(allocator, word_count);
@@ -64,45 +64,26 @@ pub fn emitRawInstruction(
section.writeWords(operands);
}
-pub fn emit(
+pub fn emitAssumeCapacity(
section: *Section,
- allocator: Allocator,
comptime opcode: spec.Opcode,
operands: opcode.Operands(),
) !void {
const word_count = instructionSize(opcode, operands);
- try section.instructions.ensureUnusedCapacity(allocator, word_count);
section.writeWord(@as(Word, @intCast(word_count << 16)) | @intFromEnum(opcode));
section.writeOperands(opcode.Operands(), operands);
}
-pub fn emitBranch(
- section: *Section,
- allocator: Allocator,
- target_label: spec.Id,
-) !void {
- try section.emit(allocator, .OpBranch, .{
- .target_label = target_label,
- });
-}
-
-pub fn emitSpecConstantOp(
+pub fn emit(
section: *Section,
allocator: Allocator,
comptime opcode: spec.Opcode,
operands: opcode.Operands(),
) !void {
- const word_count = operandsSize(opcode.Operands(), operands);
- try section.emitRaw(allocator, .OpSpecConstantOp, 1 + word_count);
- section.writeOperand(spec.Id, operands.id_result_type);
- section.writeOperand(spec.Id, operands.id_result);
- section.writeOperand(Opcode, opcode);
-
- const fields = @typeInfo(opcode.Operands()).@"struct".fields;
- // First 2 fields are always id_result_type and id_result.
- inline for (fields[2..]) |field| {
- section.writeOperand(field.type, @field(operands, field.name));
- }
+ const word_count = instructionSize(opcode, operands);
+ try section.instructions.ensureUnusedCapacity(allocator, word_count);
+ section.writeWord(@as(Word, @intCast(word_count << 16)) | @intFromEnum(opcode));
+ section.writeOperands(opcode.Operands(), operands);
}
pub fn writeWord(section: *Section, word: Word) void {
@@ -126,7 +107,6 @@ fn writeOperands(section: *Section, comptime Operands: type, operands: Operands)
.void => return,
else => unreachable,
};
-
inline for (fields) |field| {
section.writeOperand(field.type, @field(operands, field.name));
}
@@ -134,30 +114,18 @@ fn writeOperands(section: *Section, comptime Operands: type, operands: Operands)
pub fn writeOperand(section: *Section, comptime Operand: type, operand: Operand) void {
switch (Operand) {
+ spec.LiteralSpecConstantOpInteger => unreachable,
spec.Id => section.writeWord(@intFromEnum(operand)),
-
spec.LiteralInteger => section.writeWord(operand),
-
spec.LiteralString => section.writeString(operand),
-
spec.LiteralContextDependentNumber => section.writeContextDependentNumber(operand),
-
spec.LiteralExtInstInteger => section.writeWord(operand.inst),
-
- // TODO: Where this type is used (OpSpecConstantOp) is currently not correct in the spec json,
- // so it most likely needs to be altered into something that can actually describe the entire
- // instruction in which it is used.
- spec.LiteralSpecConstantOpInteger => section.writeWord(@intFromEnum(operand.opcode)),
-
spec.PairLiteralIntegerIdRef => section.writeWords(&.{ operand.value, @enumFromInt(operand.label) }),
spec.PairIdRefLiteralInteger => section.writeWords(&.{ @intFromEnum(operand.target), operand.member }),
spec.PairIdRefIdRef => section.writeWords(&.{ @intFromEnum(operand[0]), @intFromEnum(operand[1]) }),
-
else => switch (@typeInfo(Operand)) {
.@"enum" => section.writeWord(@intFromEnum(operand)),
- .optional => |info| if (operand) |child| {
- section.writeOperand(info.child, child);
- },
+ .optional => |info| if (operand) |child| section.writeOperand(info.child, child),
.pointer => |info| {
std.debug.assert(info.size == .slice); // Should be no other pointer types in the spec.
for (operand) |item| {
@@ -178,18 +146,14 @@ pub fn writeOperand(section: *Section, comptime Operand: type, operand: Operand)
}
fn writeString(section: *Section, str: []const u8) void {
- // TODO: Not actually sure whether this is correct for big-endian.
- // See https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#Literal
const zero_terminated_len = str.len + 1;
var i: usize = 0;
while (i < zero_terminated_len) : (i += @sizeOf(Word)) {
var word: Word = 0;
-
var j: usize = 0;
while (j < @sizeOf(Word) and i + j < str.len) : (j += 1) {
word |= @as(Word, str[i + j]) << @as(Log2Word, @intCast(j * @bitSizeOf(u8)));
}
-
section.instructions.appendAssumeCapacity(word);
}
}
@@ -233,20 +197,19 @@ fn writeExtendedMask(section: *Section, comptime Operand: type, operand: Operand
}
fn writeExtendedUnion(section: *Section, comptime Operand: type, operand: Operand) void {
- const tag = std.meta.activeTag(operand);
- section.writeWord(@intFromEnum(tag));
-
- inline for (@typeInfo(Operand).@"union".fields) |field| {
- if (@field(Operand, field.name) == tag) {
- section.writeOperands(field.type, @field(operand, field.name));
- return;
- }
- }
- unreachable;
+ return switch (operand) {
+ inline else => |op, tag| {
+ section.writeWord(@intFromEnum(tag));
+ section.writeOperands(
+ @FieldType(Operand, @tagName(tag)),
+ op,
+ );
+ },
+ };
}
fn instructionSize(comptime opcode: spec.Opcode, operands: opcode.Operands()) usize {
- return 1 + operandsSize(opcode.Operands(), operands);
+ return operandsSize(opcode.Operands(), operands) + 1;
}
fn operandsSize(comptime Operands: type, operands: Operands) usize {
@@ -266,28 +229,14 @@ fn operandsSize(comptime Operands: type, operands: Operands) usize {
fn operandSize(comptime Operand: type, operand: Operand) usize {
return switch (Operand) {
- spec.Id,
- spec.LiteralInteger,
- spec.LiteralExtInstInteger,
- => 1,
-
- spec.LiteralString => std.math.divCeil(usize, operand.len + 1, @sizeOf(Word)) catch unreachable, // Add one for zero-terminator
-
+ spec.LiteralSpecConstantOpInteger => unreachable,
+ spec.Id, spec.LiteralInteger, spec.LiteralExtInstInteger => 1,
+ spec.LiteralString => std.math.divCeil(usize, operand.len + 1, @sizeOf(Word)) catch unreachable,
spec.LiteralContextDependentNumber => switch (operand) {
.int32, .uint32, .float32 => 1,
.int64, .uint64, .float64 => 2,
},
-
- // TODO: Where this type is used (OpSpecConstantOp) is currently not correct in the spec
- // json, so it most likely needs to be altered into something that can actually
- // describe the entire insturction in which it is used.
- spec.LiteralSpecConstantOpInteger => 1,
-
- spec.PairLiteralIntegerIdRef,
- spec.PairIdRefLiteralInteger,
- spec.PairIdRefIdRef,
- => 2,
-
+ spec.PairLiteralIntegerIdRef, spec.PairIdRefLiteralInteger, spec.PairIdRefIdRef => 2,
else => switch (@typeInfo(Operand)) {
.@"enum" => 1,
.optional => |info| if (operand) |child| operandSize(info.child, child) else 0,
@@ -299,133 +248,25 @@ fn operandSize(comptime Operand: type, operand: Operand) usize {
}
break :blk total;
},
- .@"struct" => |info| if (info.layout == .@"packed") 1 else extendedMaskSize(Operand, operand),
- .@"union" => extendedUnionSize(Operand, operand),
- else => unreachable,
- },
- };
-}
+ .@"struct" => |struct_info| {
+ if (struct_info.layout == .@"packed") return 1;
-fn extendedMaskSize(comptime Operand: type, operand: Operand) usize {
- var total: usize = 0;
- var any_set = false;
- inline for (@typeInfo(Operand).@"struct".fields) |field| {
- switch (@typeInfo(field.type)) {
- .optional => |info| if (@field(operand, field.name)) |child| {
- total += operandsSize(info.child, child);
- any_set = true;
+ var total: usize = 0;
+ inline for (@typeInfo(Operand).@"struct".fields) |field| {
+ switch (@typeInfo(field.type)) {
+ .optional => |info| if (@field(operand, field.name)) |child| {
+ total += operandsSize(info.child, child);
+ },
+ .bool => {},
+ else => unreachable,
+ }
+ }
+ return total + 1; // Add one for the mask itself.
},
- .bool => if (@field(operand, field.name)) {
- any_set = true;
+ .@"union" => switch (operand) {
+ inline else => |op, tag| operandsSize(@FieldType(Operand, @tagName(tag)), op) + 1,
},
else => unreachable,
- }
- }
- return total + 1; // Add one for the mask itself.
-}
-
-fn extendedUnionSize(comptime Operand: type, operand: Operand) usize {
- const tag = std.meta.activeTag(operand);
- inline for (@typeInfo(Operand).@"union".fields) |field| {
- if (@field(Operand, field.name) == tag) {
- // Add one for the tag itself.
- return 1 + operandsSize(field.type, @field(operand, field.name));
- }
- }
- unreachable;
-}
-
-test "SPIR-V Section emit() - no operands" {
- var section = Section{};
- defer section.deinit(std.testing.allocator);
-
- try section.emit(std.testing.allocator, .OpNop, {});
-
- try testing.expect(section.instructions.items[0] == (@as(Word, 1) << 16) | @intFromEnum(Opcode.OpNop));
-}
-
-test "SPIR-V Section emit() - simple" {
- var section = Section{};
- defer section.deinit(std.testing.allocator);
-
- try section.emit(std.testing.allocator, .OpUndef, .{
- .id_result_type = @enumFromInt(0),
- .id_result = @enumFromInt(1),
- });
-
- try testing.expectEqualSlices(Word, &.{
- (@as(Word, 3) << 16) | @intFromEnum(Opcode.OpUndef),
- 0,
- 1,
- }, section.instructions.items);
-}
-
-test "SPIR-V Section emit() - string" {
- var section = Section{};
- defer section.deinit(std.testing.allocator);
-
- try section.emit(std.testing.allocator, .OpSource, .{
- .source_language = .Unknown,
- .version = 123,
- .file = @enumFromInt(256),
- .source = "pub fn main() void {}",
- });
-
- try testing.expectEqualSlices(Word, &.{
- (@as(Word, 10) << 16) | @intFromEnum(Opcode.OpSource),
- @intFromEnum(spec.SourceLanguage.Unknown),
- 123,
- 456,
- std.mem.bytesToValue(Word, "pub "),
- std.mem.bytesToValue(Word, "fn m"),
- std.mem.bytesToValue(Word, "ain("),
- std.mem.bytesToValue(Word, ") vo"),
- std.mem.bytesToValue(Word, "id {"),
- std.mem.bytesToValue(Word, "}\x00\x00\x00"),
- }, section.instructions.items);
-}
-
-test "SPIR-V Section emit() - extended mask" {
- var section = Section{};
- defer section.deinit(std.testing.allocator);
-
- try section.emit(std.testing.allocator, .OpLoopMerge, .{
- .merge_block = @enumFromInt(10),
- .continue_target = @enumFromInt(20),
- .loop_control = .{
- .Unroll = true,
- .DependencyLength = .{
- .literal_integer = 2,
- },
- },
- });
-
- try testing.expectEqualSlices(Word, &.{
- (@as(Word, 5) << 16) | @intFromEnum(Opcode.OpLoopMerge),
- 10,
- 20,
- @as(Word, @bitCast(spec.LoopControl{ .Unroll = true, .DependencyLength = true })),
- 2,
- }, section.instructions.items);
-}
-
-test "SPIR-V Section emit() - extended union" {
- var section = Section{};
- defer section.deinit(std.testing.allocator);
-
- try section.emit(std.testing.allocator, .OpExecutionMode, .{
- .entry_point = @enumFromInt(888),
- .mode = .{
- .LocalSize = .{ .x_size = 4, .y_size = 8, .z_size = 16 },
},
- });
-
- try testing.expectEqualSlices(Word, &.{
- (@as(Word, 6) << 16) | @intFromEnum(Opcode.OpExecutionMode),
- 888,
- @intFromEnum(spec.ExecutionMode.LocalSize),
- 4,
- 8,
- 16,
- }, section.instructions.items);
+ };
}