Commit 3e388faecd
Changed files (3)
src
codegen
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;
src/codegen/spirv/Cache.zig
@@ -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),
- };
-}
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,