diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2025-08-07 20:55:50 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-07 20:55:50 -0700 |
| commit | 3fb86841cc65437c65a6d599117833e260ea797c (patch) | |
| tree | 2f4df8b5da41e8df00ba0989ca72beb26566b6c1 /src/codegen/spirv/Section.zig | |
| parent | 5998a8cebe3973d70c258b2a1440c5c3252d3539 (diff) | |
| parent | cd4b03c5ed1bc5b48ad9c353679b309ead75551d (diff) | |
| download | zig-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.zig | 251 |
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); + }; } |
