diff options
| author | Robin Voetter <robin@voetter.nl> | 2024-04-06 03:14:36 +0200 |
|---|---|---|
| committer | Robin Voetter <robin@voetter.nl> | 2024-04-06 13:37:40 +0200 |
| commit | 3e388faecdb7100403d466df20086eb6781409e2 (patch) | |
| tree | 2152d3f032e71c09d23ed1ac34d5e44d96990e06 /src/codegen/spirv | |
| parent | ef638502d472e3c3430044c6ed908f30f17d2796 (diff) | |
| download | zig-3e388faecdb7100403d466df20086eb6781409e2.tar.gz zig-3e388faecdb7100403d466df20086eb6781409e2.zip | |
spirv: yeet cache
Diffstat (limited to 'src/codegen/spirv')
| -rw-r--r-- | src/codegen/spirv/Assembler.zig | 4 | ||||
| -rw-r--r-- | src/codegen/spirv/Cache.zig | 1125 | ||||
| -rw-r--r-- | src/codegen/spirv/Module.zig | 53 |
3 files changed, 11 insertions, 1171 deletions
diff --git a/src/codegen/spirv/Assembler.zig b/src/codegen/spirv/Assembler.zig index 9037eb71cc..2cbb873d30 100644 --- a/src/codegen/spirv/Assembler.zig +++ b/src/codegen/spirv/Assembler.zig @@ -716,7 +716,7 @@ fn parseContextDependentNumber(self: *Assembler) !void { // TODO: Count be improved to be a little bit more efficent. { - var it = self.spv.cache2.int_types.iterator(); + var it = self.spv.cache.int_types.iterator(); while (it.next()) |entry| { const id = entry.value_ptr.*; if (id != result_id) continue; @@ -726,7 +726,7 @@ fn parseContextDependentNumber(self: *Assembler) !void { } { - var it = self.spv.cache2.float_types.iterator(); + var it = self.spv.cache.float_types.iterator(); while (it.next()) |entry| { const id = entry.value_ptr.*; if (id != result_id) continue; diff --git a/src/codegen/spirv/Cache.zig b/src/codegen/spirv/Cache.zig deleted file mode 100644 index e8460e1d79..0000000000 --- a/src/codegen/spirv/Cache.zig +++ /dev/null @@ -1,1125 +0,0 @@ -//! This file implements an InternPool-like structure that caches -//! SPIR-V types and constants. Instead of generating type and -//! constant instructions directly, we first keep a representation -//! in a compressed database. This is then only later turned into -//! actual SPIR-V instructions. -//! Note: This cache is insertion-ordered. This means that we -//! can materialize the SPIR-V instructions in the proper order, -//! as SPIR-V requires that the type is emitted before use. -//! Note: According to SPIR-V spec section 2.8, Types and Variables, -//! non-pointer non-aggrerate types (which includes matrices and -//! vectors) must have a _unique_ representation in the final binary. - -const std = @import("std"); -const assert = std.debug.assert; -const Allocator = std.mem.Allocator; - -const Section = @import("Section.zig"); -const Module = @import("Module.zig"); - -const spec = @import("spec.zig"); -const Opcode = spec.Opcode; -const IdResult = spec.IdResult; -const StorageClass = spec.StorageClass; - -const InternPool = @import("../../InternPool.zig"); - -const Self = @This(); - -map: std.AutoArrayHashMapUnmanaged(void, void) = .{}, -items: std.MultiArrayList(Item) = .{}, -extra: std.ArrayListUnmanaged(u32) = .{}, - -string_bytes: std.ArrayListUnmanaged(u8) = .{}, -strings: std.AutoArrayHashMapUnmanaged(void, u32) = .{}, - -recursive_ptrs: std.AutoHashMapUnmanaged(Ref, void) = .{}, - -const Item = struct { - tag: Tag, - /// The result-id that this item uses. - result_id: IdResult, - /// The Tag determines how this should be interpreted. - data: u32, -}; - -const Tag = enum { - // -- Types - /// Simple type that has no additional data. - /// data is SimpleType. - type_simple, - /// Signed integer type - /// data is number of bits - type_int_signed, - /// Unsigned integer type - /// data is number of bits - type_int_unsigned, - /// Floating point type - /// data is number of bits - type_float, - /// Vector type - /// data is payload to VectorType - type_vector, - /// Array type - /// data is payload to ArrayType - type_array, - /// Function (proto)type - /// data is payload to FunctionType - type_function, - // /// Pointer type in the CrossWorkgroup storage class - // /// data is child type - // type_ptr_generic, - // /// Pointer type in the CrossWorkgroup storage class - // /// data is child type - // type_ptr_crosswgp, - // /// Pointer type in the Function storage class - // /// data is child type - // type_ptr_function, - /// Simple pointer type that does not have any decorations. - /// data is payload to SimplePointerType - type_ptr_simple, - /// A forward declaration for a pointer. - /// data is ForwardPointerType - type_fwd_ptr, - /// Simple structure type that does not have any decorations. - /// data is payload to SimpleStructType - type_struct_simple, - /// Simple structure type that does not have any decorations, but does - /// have member names trailing. - /// data is payload to SimpleStructType - type_struct_simple_with_member_names, - /// Opaque type. - /// data is name string. - type_opaque, - - // -- Values - /// Value of type u8 - /// data is value - uint8, - /// Value of type u32 - /// data is value - uint32, - // TODO: More specialized tags here. - /// Integer value for signed values that are smaller than 32 bits. - /// data is pointer to Int32 - int_small, - /// Integer value for unsigned values that are smaller than 32 bits. - /// data is pointer to UInt32 - uint_small, - /// Integer value for signed values that are beteen 32 and 64 bits. - /// data is pointer to Int64 - int_large, - /// Integer value for unsinged values that are beteen 32 and 64 bits. - /// data is pointer to UInt64 - uint_large, - /// Value of type f16 - /// data is value - float16, - /// Value of type f32 - /// data is value - float32, - /// Value of type f64 - /// data is payload to Float16 - float64, - /// Undefined value - /// data is type - undef, - /// Null value - /// data is type - null, - /// Bool value that is true - /// data is (bool) type - bool_true, - /// Bool value that is false - /// data is (bool) type - bool_false, - - const SimpleType = enum { - void, - bool, - }; - - const VectorType = Key.VectorType; - const ArrayType = Key.ArrayType; - - // Trailing: - // - [param_len]Ref: parameter types. - const FunctionType = struct { - param_len: u32, - return_type: Ref, - }; - - const SimplePointerType = struct { - storage_class: StorageClass, - child_type: Ref, - fwd: Ref, - }; - - const ForwardPointerType = struct { - storage_class: StorageClass, - zig_child_type: InternPool.Index, - }; - - /// Trailing: - /// - [members_len]Ref: Member types. - /// - [members_len]String: Member names, -- ONLY if the tag is type_struct_simple_with_member_names - const SimpleStructType = struct { - /// (optional) The name of the struct. - name: String, - /// Number of members that this struct has. - members_len: u32, - }; - - const Float64 = struct { - // Low-order 32 bits of the value. - low: u32, - // High-order 32 bits of the value. - high: u32, - - fn encode(value: f64) Float64 { - const bits = @as(u64, @bitCast(value)); - return .{ - .low = @truncate(bits), - .high = @truncate(bits >> 32), - }; - } - - fn decode(self: Float64) f64 { - const bits = @as(u64, self.low) | (@as(u64, self.high) << 32); - return @bitCast(bits); - } - }; - - const Int32 = struct { - ty: Ref, - value: i32, - }; - - const UInt32 = struct { - ty: Ref, - value: u32, - }; - - const UInt64 = struct { - ty: Ref, - low: u32, - high: u32, - - fn encode(ty: Ref, value: u64) Int64 { - return .{ - .ty = ty, - .low = @truncate(value), - .high = @truncate(value >> 32), - }; - } - - fn decode(self: UInt64) u64 { - return @as(u64, self.low) | (@as(u64, self.high) << 32); - } - }; - - const Int64 = struct { - ty: Ref, - low: u32, - high: u32, - - fn encode(ty: Ref, value: i64) Int64 { - return .{ - .ty = ty, - .low = @truncate(@as(u64, @bitCast(value))), - .high = @truncate(@as(u64, @bitCast(value)) >> 32), - }; - } - - fn decode(self: Int64) i64 { - return @as(i64, @bitCast(@as(u64, self.low) | (@as(u64, self.high) << 32))); - } - }; -}; - -pub const Ref = enum(u32) { _ }; - -/// This union represents something that can be interned. This includes -/// types and constants. This structure is used for interfacing with the -/// database: Values described for this structure are ephemeral and stored -/// in a more memory-efficient manner internally. -pub const Key = union(enum) { - // -- Types - void_type, - bool_type, - int_type: IntType, - float_type: FloatType, - vector_type: VectorType, - array_type: ArrayType, - function_type: FunctionType, - ptr_type: PointerType, - fwd_ptr_type: ForwardPointerType, - struct_type: StructType, - opaque_type: OpaqueType, - - // -- values - int: Int, - float: Float, - undef: Undef, - null: Null, - bool: Bool, - - pub const IntType = std.builtin.Type.Int; - pub const FloatType = std.builtin.Type.Float; - - pub const VectorType = struct { - component_type: Ref, - component_count: u32, - }; - - pub const ArrayType = struct { - /// Child type of this array. - element_type: Ref, - /// Reference to a constant. - length: Ref, - /// Type has the 'ArrayStride' decoration. - /// If zero, no stride is present. - stride: u32 = 0, - }; - - pub const FunctionType = struct { - return_type: Ref, - parameters: []const Ref, - }; - - pub const PointerType = struct { - storage_class: StorageClass, - child_type: Ref, - /// Ref to a .fwd_ptr_type. - fwd: Ref, - // TODO: Decorations: - // - Alignment - // - ArrayStride - // - MaxByteOffset - }; - - pub const ForwardPointerType = struct { - zig_child_type: InternPool.Index, - storage_class: StorageClass, - }; - - pub const StructType = struct { - // TODO: Decorations. - /// The name of the structure. Can be `.none`. - name: String = .none, - /// The type of each member. - member_types: []const Ref, - /// Name for each member. May be omitted. - member_names: ?[]const String = null, - - fn memberNames(self: @This()) []const String { - return if (self.member_names) |member_names| member_names else &.{}; - } - }; - - pub const OpaqueType = struct { - name: String = .none, - }; - - pub const Int = struct { - /// The type: any bitness integer. - ty: Ref, - /// The actual value. Only uint64 and int64 types - /// are available here: Smaller types should use these - /// fields. - value: Value, - - pub const Value = union(enum) { - uint64: u64, - int64: i64, - }; - - /// Turns this value into the corresponding 32-bit literal, 2s complement signed. - fn toBits32(self: Int) u32 { - return switch (self.value) { - .uint64 => |val| @intCast(val), - .int64 => |val| if (val < 0) @bitCast(@as(i32, @intCast(val))) else @intCast(val), - }; - } - - fn toBits64(self: Int) u64 { - return switch (self.value) { - .uint64 => |val| val, - .int64 => |val| @bitCast(val), - }; - } - - fn to(self: Int, comptime T: type) T { - return switch (self.value) { - inline else => |val| @intCast(val), - }; - } - }; - - /// Represents a numberic value of some type. - pub const Float = struct { - /// The type: 16, 32, or 64-bit float. - ty: Ref, - /// The actual value. - value: Value, - - pub const Value = union(enum) { - float16: f16, - float32: f32, - float64: f64, - }; - }; - - pub const Undef = struct { - ty: Ref, - }; - - pub const Null = struct { - ty: Ref, - }; - - pub const Bool = struct { - ty: Ref, - value: bool, - }; - - fn hash(self: Key) u32 { - var hasher = std.hash.Wyhash.init(0); - switch (self) { - .float => |float| { - std.hash.autoHash(&hasher, float.ty); - switch (float.value) { - .float16 => |value| std.hash.autoHash(&hasher, @as(u16, @bitCast(value))), - .float32 => |value| std.hash.autoHash(&hasher, @as(u32, @bitCast(value))), - .float64 => |value| std.hash.autoHash(&hasher, @as(u64, @bitCast(value))), - } - }, - .function_type => |func| { - std.hash.autoHash(&hasher, func.return_type); - for (func.parameters) |param_type| { - std.hash.autoHash(&hasher, param_type); - } - }, - .struct_type => |struct_type| { - std.hash.autoHash(&hasher, struct_type.name); - for (struct_type.member_types) |member_type| { - std.hash.autoHash(&hasher, member_type); - } - for (struct_type.memberNames()) |member_name| { - std.hash.autoHash(&hasher, member_name); - } - }, - inline else => |key| std.hash.autoHash(&hasher, key), - } - return @truncate(hasher.final()); - } - - fn eql(a: Key, b: Key) bool { - const KeyTag = @typeInfo(Key).Union.tag_type.?; - const a_tag: KeyTag = a; - const b_tag: KeyTag = b; - if (a_tag != b_tag) { - return false; - } - return switch (a) { - .function_type => |a_func| { - const b_func = b.function_type; - return a_func.return_type == b_func.return_type and - std.mem.eql(Ref, a_func.parameters, b_func.parameters); - }, - .struct_type => |a_struct| { - const b_struct = b.struct_type; - return a_struct.name == b_struct.name and - std.mem.eql(Ref, a_struct.member_types, b_struct.member_types) and - std.mem.eql(String, a_struct.memberNames(), b_struct.memberNames()); - }, - // TODO: Unroll? - else => std.meta.eql(a, b), - }; - } - - pub const Adapter = struct { - self: *const Self, - - pub fn eql(ctx: @This(), a: Key, b_void: void, b_index: usize) bool { - _ = b_void; - return ctx.self.lookup(@enumFromInt(b_index)).eql(a); - } - - pub fn hash(ctx: @This(), a: Key) u32 { - _ = ctx; - return a.hash(); - } - }; - - fn toSimpleType(self: Key) Tag.SimpleType { - return switch (self) { - .void_type => .void, - .bool_type => .bool, - else => unreachable, - }; - } - - pub fn isNumericalType(self: Key) bool { - return switch (self) { - .int_type, .float_type => true, - else => false, - }; - } -}; - -pub fn deinit(self: *Self, spv: *const Module) void { - self.map.deinit(spv.gpa); - self.items.deinit(spv.gpa); - self.extra.deinit(spv.gpa); - self.string_bytes.deinit(spv.gpa); - self.strings.deinit(spv.gpa); - self.recursive_ptrs.deinit(spv.gpa); -} - -/// Actually materialize the database into spir-v instructions. -/// This function returns a spir-v section of (only) constant and type instructions. -/// Additionally, decorations, debug names, etc, are all directly emitted into the -/// `spv` module. The section is allocated with `spv.gpa`. -pub fn materialize(self: *const Self, spv: *Module) !Section { - var section = Section{}; - errdefer section.deinit(spv.gpa); - for (self.items.items(.result_id), 0..) |result_id, index| { - try self.emit(spv, result_id, @enumFromInt(index), §ion); - } - return section; -} - -fn emit( - self: *const Self, - spv: *Module, - result_id: IdResult, - ref: Ref, - section: *Section, -) !void { - const key = self.lookup(ref); - const Lit = spec.LiteralContextDependentNumber; - switch (key) { - .void_type => { - try section.emit(spv.gpa, .OpTypeVoid, .{ .id_result = result_id }); - try spv.debugName(result_id, "void"); - }, - .bool_type => { - try section.emit(spv.gpa, .OpTypeBool, .{ .id_result = result_id }); - try spv.debugName(result_id, "bool"); - }, - .int_type => |int| { - try section.emit(spv.gpa, .OpTypeInt, .{ - .id_result = result_id, - .width = int.bits, - .signedness = switch (int.signedness) { - .unsigned => @as(spec.Word, 0), - .signed => 1, - }, - }); - const ui: []const u8 = switch (int.signedness) { - .unsigned => "u", - .signed => "i", - }; - try spv.debugNameFmt(result_id, "{s}{}", .{ ui, int.bits }); - }, - .float_type => |float| { - try section.emit(spv.gpa, .OpTypeFloat, .{ - .id_result = result_id, - .width = float.bits, - }); - try spv.debugNameFmt(result_id, "f{}", .{float.bits}); - }, - .vector_type => |vector| { - try section.emit(spv.gpa, .OpTypeVector, .{ - .id_result = result_id, - .component_type = self.resultId(vector.component_type), - .component_count = vector.component_count, - }); - }, - .array_type => |array| { - try section.emit(spv.gpa, .OpTypeArray, .{ - .id_result = result_id, - .element_type = self.resultId(array.element_type), - .length = self.resultId(array.length), - }); - if (array.stride != 0) { - try spv.decorate(result_id, .{ .ArrayStride = .{ .array_stride = array.stride } }); - } - }, - .function_type => |function| { - try section.emitRaw(spv.gpa, .OpTypeFunction, 2 + function.parameters.len); - section.writeOperand(IdResult, result_id); - section.writeOperand(IdResult, self.resultId(function.return_type)); - for (function.parameters) |param_type| { - section.writeOperand(IdResult, self.resultId(param_type)); - } - }, - .ptr_type => |ptr| { - try section.emit(spv.gpa, .OpTypePointer, .{ - .id_result = result_id, - .storage_class = ptr.storage_class, - .type = self.resultId(ptr.child_type), - }); - // TODO: Decorations? - }, - .fwd_ptr_type => |fwd| { - // Only emit the OpTypeForwardPointer if its actually required. - if (self.recursive_ptrs.contains(ref)) { - try section.emit(spv.gpa, .OpTypeForwardPointer, .{ - .pointer_type = result_id, - .storage_class = fwd.storage_class, - }); - } - }, - .struct_type => |struct_type| { - try section.emitRaw(spv.gpa, .OpTypeStruct, 1 + struct_type.member_types.len); - section.writeOperand(IdResult, result_id); - for (struct_type.member_types) |member_type| { - section.writeOperand(IdResult, self.resultId(member_type)); - } - if (self.getString(struct_type.name)) |name| { - try spv.debugName(result_id, name); - } - for (struct_type.memberNames(), 0..) |member_name, i| { - if (self.getString(member_name)) |name| { - try spv.memberDebugName(result_id, @intCast(i), name); - } - } - // TODO: Decorations? - }, - .opaque_type => |opaque_type| { - const name = if (self.getString(opaque_type.name)) |name| name else ""; - try section.emit(spv.gpa, .OpTypeOpaque, .{ - .id_result = result_id, - .literal_string = name, - }); - }, - .int => |int| { - const int_type = self.lookup(int.ty).int_type; - const ty_id = self.resultId(int.ty); - const lit: Lit = switch (int_type.bits) { - 1...32 => .{ .uint32 = int.toBits32() }, - 33...64 => .{ .uint64 = int.toBits64() }, - else => unreachable, - }; - - try section.emit(spv.gpa, .OpConstant, .{ - .id_result_type = ty_id, - .id_result = result_id, - .value = lit, - }); - }, - .float => |float| { - const ty_id = self.resultId(float.ty); - const lit: Lit = switch (float.value) { - .float16 => |value| .{ .uint32 = @as(u16, @bitCast(value)) }, - .float32 => |value| .{ .float32 = value }, - .float64 => |value| .{ .float64 = value }, - }; - try section.emit(spv.gpa, .OpConstant, .{ - .id_result_type = ty_id, - .id_result = result_id, - .value = lit, - }); - }, - .undef => |undef| { - try section.emit(spv.gpa, .OpUndef, .{ - .id_result_type = self.resultId(undef.ty), - .id_result = result_id, - }); - }, - .null => |null_info| { - try section.emit(spv.gpa, .OpConstantNull, .{ - .id_result_type = self.resultId(null_info.ty), - .id_result = result_id, - }); - }, - .bool => |bool_info| switch (bool_info.value) { - true => { - try section.emit(spv.gpa, .OpConstantTrue, .{ - .id_result_type = self.resultId(bool_info.ty), - .id_result = result_id, - }); - }, - false => { - try section.emit(spv.gpa, .OpConstantFalse, .{ - .id_result_type = self.resultId(bool_info.ty), - .id_result = result_id, - }); - }, - }, - } -} - -/// Add a key to this cache. Returns a reference to the key that -/// was added. The corresponding result-id can be queried using -/// self.resultId with the result. -pub fn resolve(self: *Self, spv: *Module, key: Key) !Ref { - const adapter: Key.Adapter = .{ .self = self }; - const entry = try self.map.getOrPutAdapted(spv.gpa, key, adapter); - if (entry.found_existing) { - return @enumFromInt(entry.index); - } - const item: Item = switch (key) { - inline .void_type, .bool_type => .{ - .tag = .type_simple, - .result_id = spv.allocId(), - .data = @intFromEnum(key.toSimpleType()), - }, - .int_type => |int| blk: { - const t: Tag = switch (int.signedness) { - .signed => .type_int_signed, - .unsigned => .type_int_unsigned, - }; - break :blk .{ - .tag = t, - .result_id = spv.allocId(), - .data = int.bits, - }; - }, - .float_type => |float| .{ - .tag = .type_float, - .result_id = spv.allocId(), - .data = float.bits, - }, - .vector_type => |vector| .{ - .tag = .type_vector, - .result_id = spv.allocId(), - .data = try self.addExtra(spv, vector), - }, - .array_type => |array| .{ - .tag = .type_array, - .result_id = spv.allocId(), - .data = try self.addExtra(spv, array), - }, - .function_type => |function| blk: { - const extra = try self.addExtra(spv, Tag.FunctionType{ - .param_len = @intCast(function.parameters.len), - .return_type = function.return_type, - }); - try self.extra.appendSlice(spv.gpa, @ptrCast(function.parameters)); - break :blk .{ - .tag = .type_function, - .result_id = spv.allocId(), - .data = extra, - }; - }, - // .ptr_type => |ptr| switch (ptr.storage_class) { - // .Generic => Item{ - // .tag = .type_ptr_generic, - // .result_id = spv.allocId(), - // .data = @intFromEnum(ptr.child_type), - // }, - // .CrossWorkgroup => Item{ - // .tag = .type_ptr_crosswgp, - // .result_id = spv.allocId(), - // .data = @intFromEnum(ptr.child_type), - // }, - // .Function => Item{ - // .tag = .type_ptr_function, - // .result_id = spv.allocId(), - // .data = @intFromEnum(ptr.child_type), - // }, - // else => |storage_class| Item{ - // .tag = .type_ptr_simple, - // .result_id = spv.allocId(), - // .data = try self.addExtra(spv, Tag.SimplePointerType{ - // .storage_class = storage_class, - // .child_type = ptr.child_type, - // }), - // }, - // }, - .ptr_type => |ptr| Item{ - .tag = .type_ptr_simple, - // For this variant we need to steal the ID of the forward-declaration, instead - // of allocating one manually. This will make sure that we get a single result-id - // any possibly forward declared pointer type. - .result_id = self.resultId(ptr.fwd), - .data = try self.addExtra(spv, Tag.SimplePointerType{ - .storage_class = ptr.storage_class, - .child_type = ptr.child_type, - .fwd = ptr.fwd, - }), - }, - .fwd_ptr_type => |fwd| Item{ - .tag = .type_fwd_ptr, - .result_id = spv.allocId(), - .data = try self.addExtra(spv, Tag.ForwardPointerType{ - .zig_child_type = fwd.zig_child_type, - .storage_class = fwd.storage_class, - }), - }, - .struct_type => |struct_type| blk: { - const extra = try self.addExtra(spv, Tag.SimpleStructType{ - .name = struct_type.name, - .members_len = @intCast(struct_type.member_types.len), - }); - try self.extra.appendSlice(spv.gpa, @ptrCast(struct_type.member_types)); - - if (struct_type.member_names) |member_names| { - try self.extra.appendSlice(spv.gpa, @ptrCast(member_names)); - break :blk Item{ - .tag = .type_struct_simple_with_member_names, - .result_id = spv.allocId(), - .data = extra, - }; - } else { - break :blk Item{ - .tag = .type_struct_simple, - .result_id = spv.allocId(), - .data = extra, - }; - } - }, - .opaque_type => |opaque_type| Item{ - .tag = .type_opaque, - .result_id = spv.allocId(), - .data = @intFromEnum(opaque_type.name), - }, - .int => |int| blk: { - const int_type = self.lookup(int.ty).int_type; - if (int_type.signedness == .unsigned and int_type.bits == 8) { - break :blk .{ - .tag = .uint8, - .result_id = spv.allocId(), - .data = int.to(u8), - }; - } else if (int_type.signedness == .unsigned and int_type.bits == 32) { - break :blk .{ - .tag = .uint32, - .result_id = spv.allocId(), - .data = int.to(u32), - }; - } - - switch (int.value) { - inline else => |val| { - if (val >= 0 and val <= std.math.maxInt(u32)) { - break :blk .{ - .tag = .uint_small, - .result_id = spv.allocId(), - .data = try self.addExtra(spv, Tag.UInt32{ - .ty = int.ty, - .value = @intCast(val), - }), - }; - } else if (val >= std.math.minInt(i32) and val <= std.math.maxInt(i32)) { - break :blk .{ - .tag = .int_small, - .result_id = spv.allocId(), - .data = try self.addExtra(spv, Tag.Int32{ - .ty = int.ty, - .value = @intCast(val), - }), - }; - } else if (val < 0) { - break :blk .{ - .tag = .int_large, - .result_id = spv.allocId(), - .data = try self.addExtra(spv, Tag.Int64.encode(int.ty, @intCast(val))), - }; - } else { - break :blk .{ - .tag = .uint_large, - .result_id = spv.allocId(), - .data = try self.addExtra(spv, Tag.UInt64.encode(int.ty, @intCast(val))), - }; - } - }, - } - }, - .float => |float| switch (self.lookup(float.ty).float_type.bits) { - 16 => .{ - .tag = .float16, - .result_id = spv.allocId(), - .data = @as(u16, @bitCast(float.value.float16)), - }, - 32 => .{ - .tag = .float32, - .result_id = spv.allocId(), - .data = @as(u32, @bitCast(float.value.float32)), - }, - 64 => .{ - .tag = .float64, - .result_id = spv.allocId(), - .data = try self.addExtra(spv, Tag.Float64.encode(float.value.float64)), - }, - else => unreachable, - }, - .undef => |undef| .{ - .tag = .undef, - .result_id = spv.allocId(), - .data = @intFromEnum(undef.ty), - }, - .null => |null_info| .{ - .tag = .null, - .result_id = spv.allocId(), - .data = @intFromEnum(null_info.ty), - }, - .bool => |bool_info| .{ - .tag = switch (bool_info.value) { - true => Tag.bool_true, - false => Tag.bool_false, - }, - .result_id = spv.allocId(), - .data = @intFromEnum(bool_info.ty), - }, - }; - try self.items.append(spv.gpa, item); - - return @enumFromInt(entry.index); -} - -/// Turn a Ref back into a Key. -/// The Key is valid until the next call to resolve(). -pub fn lookup(self: *const Self, ref: Ref) Key { - const item = self.items.get(@intFromEnum(ref)); - const data = item.data; - return switch (item.tag) { - .type_simple => switch (@as(Tag.SimpleType, @enumFromInt(data))) { - .void => .void_type, - .bool => .bool_type, - }, - .type_int_signed => .{ .int_type = .{ - .signedness = .signed, - .bits = @intCast(data), - } }, - .type_int_unsigned => .{ .int_type = .{ - .signedness = .unsigned, - .bits = @intCast(data), - } }, - .type_float => .{ .float_type = .{ - .bits = @intCast(data), - } }, - .type_vector => .{ .vector_type = self.extraData(Tag.VectorType, data) }, - .type_array => .{ .array_type = self.extraData(Tag.ArrayType, data) }, - .type_function => { - const payload = self.extraDataTrail(Tag.FunctionType, data); - return .{ - .function_type = .{ - .return_type = payload.data.return_type, - .parameters = @ptrCast(self.extra.items[payload.trail..][0..payload.data.param_len]), - }, - }; - }, - .type_ptr_simple => { - const payload = self.extraData(Tag.SimplePointerType, data); - return .{ - .ptr_type = .{ - .storage_class = payload.storage_class, - .child_type = payload.child_type, - .fwd = payload.fwd, - }, - }; - }, - .type_fwd_ptr => { - const payload = self.extraData(Tag.ForwardPointerType, data); - return .{ - .fwd_ptr_type = .{ - .zig_child_type = payload.zig_child_type, - .storage_class = payload.storage_class, - }, - }; - }, - .type_struct_simple => { - const payload = self.extraDataTrail(Tag.SimpleStructType, data); - const member_types: []const Ref = @ptrCast(self.extra.items[payload.trail..][0..payload.data.members_len]); - return .{ - .struct_type = .{ - .name = payload.data.name, - .member_types = member_types, - .member_names = null, - }, - }; - }, - .type_struct_simple_with_member_names => { - const payload = self.extraDataTrail(Tag.SimpleStructType, data); - const trailing = self.extra.items[payload.trail..]; - const member_types: []const Ref = @ptrCast(trailing[0..payload.data.members_len]); - const member_names: []const String = @ptrCast(trailing[payload.data.members_len..][0..payload.data.members_len]); - return .{ - .struct_type = .{ - .name = payload.data.name, - .member_types = member_types, - .member_names = member_names, - }, - }; - }, - .type_opaque => .{ - .opaque_type = .{ - .name = @enumFromInt(data), - }, - }, - .float16 => .{ .float = .{ - .ty = self.get(.{ .float_type = .{ .bits = 16 } }), - .value = .{ .float16 = @bitCast(@as(u16, @intCast(data))) }, - } }, - .float32 => .{ .float = .{ - .ty = self.get(.{ .float_type = .{ .bits = 32 } }), - .value = .{ .float32 = @bitCast(data) }, - } }, - .float64 => .{ .float = .{ - .ty = self.get(.{ .float_type = .{ .bits = 64 } }), - .value = .{ .float64 = self.extraData(Tag.Float64, data).decode() }, - } }, - .uint8 => .{ .int = .{ - .ty = self.get(.{ .int_type = .{ .signedness = .unsigned, .bits = 8 } }), - .value = .{ .uint64 = data }, - } }, - .uint32 => .{ .int = .{ - .ty = self.get(.{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }), - .value = .{ .uint64 = data }, - } }, - .int_small => { - const payload = self.extraData(Tag.Int32, data); - return .{ .int = .{ - .ty = payload.ty, - .value = .{ .int64 = payload.value }, - } }; - }, - .uint_small => { - const payload = self.extraData(Tag.UInt32, data); - return .{ .int = .{ - .ty = payload.ty, - .value = .{ .uint64 = payload.value }, - } }; - }, - .int_large => { - const payload = self.extraData(Tag.Int64, data); - return .{ .int = .{ - .ty = payload.ty, - .value = .{ .int64 = payload.decode() }, - } }; - }, - .uint_large => { - const payload = self.extraData(Tag.UInt64, data); - return .{ .int = .{ - .ty = payload.ty, - .value = .{ .uint64 = payload.decode() }, - } }; - }, - .undef => .{ .undef = .{ - .ty = @enumFromInt(data), - } }, - .null => .{ .null = .{ - .ty = @enumFromInt(data), - } }, - .bool_true => .{ .bool = .{ - .ty = @enumFromInt(data), - .value = true, - } }, - .bool_false => .{ .bool = .{ - .ty = @enumFromInt(data), - .value = false, - } }, - }; -} - -/// Look op the result-id that corresponds to a particular -/// ref. -pub fn resultId(self: Self, ref: Ref) IdResult { - return self.items.items(.result_id)[@intFromEnum(ref)]; -} - -/// Get the ref for a key that has already been added to the cache. -fn get(self: *const Self, key: Key) Ref { - const adapter: Key.Adapter = .{ .self = self }; - const index = self.map.getIndexAdapted(key, adapter).?; - return @enumFromInt(index); -} - -fn addExtra(self: *Self, spv: *Module, extra: anytype) !u32 { - const fields = @typeInfo(@TypeOf(extra)).Struct.fields; - try self.extra.ensureUnusedCapacity(spv.gpa, fields.len); - return try self.addExtraAssumeCapacity(extra); -} - -fn addExtraAssumeCapacity(self: *Self, extra: anytype) !u32 { - const payload_offset: u32 = @intCast(self.extra.items.len); - inline for (@typeInfo(@TypeOf(extra)).Struct.fields) |field| { - const field_val = @field(extra, field.name); - const word: u32 = switch (field.type) { - u32 => field_val, - i32 => @bitCast(field_val), - Ref => @intFromEnum(field_val), - StorageClass => @intFromEnum(field_val), - String => @intFromEnum(field_val), - InternPool.Index => @intFromEnum(field_val), - else => @compileError("Invalid type: " ++ @typeName(field.type)), - }; - self.extra.appendAssumeCapacity(word); - } - return payload_offset; -} - -fn extraData(self: Self, comptime T: type, offset: u32) T { - return self.extraDataTrail(T, offset).data; -} - -fn extraDataTrail(self: Self, comptime T: type, offset: u32) struct { data: T, trail: u32 } { - var result: T = undefined; - const fields = @typeInfo(T).Struct.fields; - inline for (fields, 0..) |field, i| { - const word = self.extra.items[offset + i]; - @field(result, field.name) = switch (field.type) { - u32 => word, - i32 => @bitCast(word), - Ref => @enumFromInt(word), - StorageClass => @enumFromInt(word), - String => @enumFromInt(word), - InternPool.Index => @enumFromInt(word), - else => @compileError("Invalid type: " ++ @typeName(field.type)), - }; - } - return .{ - .data = result, - .trail = offset + @as(u32, @intCast(fields.len)), - }; -} - -/// Represents a reference to some null-terminated string. -pub const String = enum(u32) { - none = std.math.maxInt(u32), - _, - - pub const Adapter = struct { - self: *const Self, - - pub fn eql(ctx: @This(), a: []const u8, _: void, b_index: usize) bool { - const offset = ctx.self.strings.values()[b_index]; - const b = std.mem.sliceTo(ctx.self.string_bytes.items[offset..], 0); - return std.mem.eql(u8, a, b); - } - - pub fn hash(ctx: @This(), a: []const u8) u32 { - _ = ctx; - var hasher = std.hash.Wyhash.init(0); - hasher.update(a); - return @truncate(hasher.final()); - } - }; -}; - -/// Add a string to the cache. Must not contain any 0 values. -pub fn addString(self: *Self, spv: *Module, str: []const u8) !String { - assert(std.mem.indexOfScalar(u8, str, 0) == null); - const adapter = String.Adapter{ .self = self }; - const entry = try self.strings.getOrPutAdapted(spv.gpa, str, adapter); - if (!entry.found_existing) { - const offset = self.string_bytes.items.len; - try self.string_bytes.ensureUnusedCapacity(spv.gpa, 1 + str.len); - self.string_bytes.appendSliceAssumeCapacity(str); - self.string_bytes.appendAssumeCapacity(0); - entry.value_ptr.* = @intCast(offset); - } - - return @enumFromInt(entry.index); -} - -pub fn getString(self: *const Self, ref: String) ?[]const u8 { - return switch (ref) { - .none => null, - else => std.mem.sliceTo(self.string_bytes.items[self.strings.values()[@intFromEnum(ref)]..], 0), - }; -} diff --git a/src/codegen/spirv/Module.zig b/src/codegen/spirv/Module.zig index 73374389b7..88fe677345 100644 --- a/src/codegen/spirv/Module.zig +++ b/src/codegen/spirv/Module.zig @@ -20,10 +20,6 @@ const IdResultType = spec.IdResultType; const Section = @import("Section.zig"); -const Cache = @import("Cache.zig"); -pub const CacheKey = Cache.Key; -pub const CacheRef = Cache.Ref; - /// This structure represents a function that isc in-progress of being emitted. /// Commonly, the contents of this structure will be merged with the appropriate /// sections of the module and re-used. Note that the SPIR-V module system makes @@ -148,17 +144,13 @@ next_result_id: Word, /// Cache for results of OpString instructions. strings: std.StringArrayHashMapUnmanaged(IdRef) = .{}, -/// SPIR-V type- and constant cache. This structure is used to store information about these in a more -/// efficient manner. -cache: Cache = .{}, - /// Some types shouldn't be emitted more than one time, but cannot be caught by /// the `intern_map` during codegen. Sometimes, IDs are compared to check if /// types are the same, so we can't delay until the dedup pass. Therefore, /// this is an ad-hoc structure to cache types where required. /// According to the SPIR-V specification, section 2.8, this includes all non-aggregate /// non-pointer types. -cache2: struct { +cache: struct { bool_type: ?IdRef = null, void_type: ?IdRef = null, int_types: std.AutoHashMapUnmanaged(std.builtin.Type.Int, IdRef) = .{}, @@ -199,10 +191,9 @@ pub fn deinit(self: *Module) void { self.sections.functions.deinit(self.gpa); self.strings.deinit(self.gpa); - self.cache.deinit(self); - self.cache2.int_types.deinit(self.gpa); - self.cache2.float_types.deinit(self.gpa); + self.cache.int_types.deinit(self.gpa); + self.cache.float_types.deinit(self.gpa); self.decls.deinit(self.gpa); self.decl_deps.deinit(self.gpa); @@ -241,18 +232,6 @@ pub fn idBound(self: Module) Word { return self.next_result_id; } -pub fn resolve(self: *Module, key: CacheKey) !CacheRef { - return self.cache.resolve(self, key); -} - -pub fn resultId(self: *const Module, ref: CacheRef) IdResult { - return self.cache.resultId(ref); -} - -pub fn resolveId(self: *Module, key: CacheKey) !IdResult { - return self.resultId(try self.resolve(key)); -} - fn addEntryPointDeps( self: *Module, decl_index: Decl.Index, @@ -312,9 +291,6 @@ pub fn finalize(self: *Module, a: Allocator, target: std.Target) ![]Word { var entry_points = try self.entryPoints(); defer entry_points.deinit(self.gpa); - var types_constants = try self.cache.materialize(self); - defer types_constants.deinit(self.gpa); - const header = [_]Word{ spec.magic_number, // TODO: From cpu features @@ -357,7 +333,6 @@ pub fn finalize(self: *Module, a: Allocator, target: std.Target) ![]Word { self.sections.debug_strings.toWords(), self.sections.debug_names.toWords(), self.sections.annotations.toWords(), - types_constants.toWords(), self.sections.types_globals_constants.toWords(), self.sections.functions.toWords(), }; @@ -438,31 +413,31 @@ pub fn structType(self: *Module, types: []const IdRef, maybe_names: ?[]const []c } pub fn boolType(self: *Module) !IdRef { - if (self.cache2.bool_type) |id| return id; + if (self.cache.bool_type) |id| return id; const result_id = self.allocId(); try self.sections.types_globals_constants.emit(self.gpa, .OpTypeBool, .{ .id_result = result_id, }); - self.cache2.bool_type = result_id; + self.cache.bool_type = result_id; return result_id; } pub fn voidType(self: *Module) !IdRef { - if (self.cache2.void_type) |id| return id; + if (self.cache.void_type) |id| return id; const result_id = self.allocId(); try self.sections.types_globals_constants.emit(self.gpa, .OpTypeVoid, .{ .id_result = result_id, }); - self.cache2.void_type = result_id; + self.cache.void_type = result_id; try self.debugName(result_id, "void"); return result_id; } pub fn intType(self: *Module, signedness: std.builtin.Signedness, bits: u16) !IdRef { assert(bits > 0); - const entry = try self.cache2.int_types.getOrPut(self.gpa, .{ .signedness = signedness, .bits = bits }); + const entry = try self.cache.int_types.getOrPut(self.gpa, .{ .signedness = signedness, .bits = bits }); if (!entry.found_existing) { const result_id = self.allocId(); entry.value_ptr.* = result_id; @@ -485,7 +460,7 @@ pub fn intType(self: *Module, signedness: std.builtin.Signedness, bits: u16) !Id pub fn floatType(self: *Module, bits: u16) !IdRef { assert(bits > 0); - const entry = try self.cache2.float_types.getOrPut(self.gpa, .{ .bits = bits }); + const entry = try self.cache.float_types.getOrPut(self.gpa, .{ .bits = bits }); if (!entry.found_existing) { const result_id = self.allocId(); entry.value_ptr.* = result_id; @@ -526,16 +501,6 @@ pub fn constNull(self: *Module, ty_id: IdRef) !IdRef { return result_id; } -pub fn constComposite(self: *Module, ty_ref: CacheRef, members: []const IdRef) !IdRef { - const result_id = self.allocId(); - try self.sections.types_globals_constants.emit(self.gpa, .OpSpecConstantComposite, .{ - .id_result_type = self.resultId(ty_ref), - .id_result = result_id, - .constituents = members, - }); - return result_id; -} - /// Decorate a result-id. pub fn decorate( self: *Module, |
