spirv: remove cache usage for types

This commit is contained in:
Robin Voetter 2024-04-05 00:30:06 +02:00
parent 188922a544
commit 97a67762ba
No known key found for this signature in database
4 changed files with 556 additions and 497 deletions

File diff suppressed because it is too large Load Diff

View File

@ -296,18 +296,19 @@ fn processTypeInstruction(self: *Assembler) !AsmValue {
.OpTypeVoid => try self.spv.resolve(.void_type),
.OpTypeBool => try self.spv.resolve(.bool_type),
.OpTypeInt => blk: {
const signedness: std.builtin.Signedness = switch (operands[2].literal32) {
0 => .unsigned,
1 => .signed,
else => {
// TODO: Improve source location.
return self.fail(0, "{} is not a valid signedness (expected 0 or 1)", .{operands[2].literal32});
},
};
const width = std.math.cast(u16, operands[1].literal32) orelse {
return self.fail(0, "int type of {} bits is too large", .{operands[1].literal32});
};
break :blk try self.spv.intType(signedness, width);
// const signedness: std.builtin.Signedness = switch (operands[2].literal32) {
// 0 => .unsigned,
// 1 => .signed,
// else => {
// // TODO: Improve source location.
// return self.fail(0, "{} is not a valid signedness (expected 0 or 1)", .{operands[2].literal32});
// },
// };
// const width = std.math.cast(u16, operands[1].literal32) orelse {
// return self.fail(0, "int type of {} bits is too large", .{operands[1].literal32});
// };
// break :blk try self.spv.intType(signedness, width);
break :blk @as(CacheRef, @enumFromInt(0)); // TODO(robin): fix
},
.OpTypeFloat => blk: {
const bits = operands[1].literal32;

View File

@ -23,7 +23,6 @@ const Section = @import("Section.zig");
const Cache = @import("Cache.zig");
pub const CacheKey = Cache.Key;
pub const CacheRef = Cache.Ref;
pub const CacheString = Cache.String;
/// This structure represents a function that isc in-progress of being emitted.
/// Commonly, the contents of this structure will be merged with the appropriate
@ -98,7 +97,7 @@ pub const EntryPoint = struct {
/// The declaration that should be exported.
decl_index: Decl.Index,
/// The name of the kernel to be exported.
name: CacheString,
name: []const u8,
/// Calling Convention
execution_model: spec.ExecutionModel,
};
@ -106,6 +105,9 @@ pub const EntryPoint = struct {
/// A general-purpose allocator which may be used to allocate resources for this module
gpa: Allocator,
/// Arena for things that need to live for the length of this program.
arena: std.heap.ArenaAllocator,
/// Module layout, according to SPIR-V Spec section 2.4, "Logical Layout of a Module".
sections: struct {
/// Capability instructions
@ -143,15 +145,26 @@ sections: struct {
/// SPIR-V instructions return result-ids. This variable holds the module-wide counter for these.
next_result_id: Word,
/// Cache for results of OpString instructions for module file names fed to OpSource.
/// Since OpString is pretty much only used for those, we don't need to keep track of all strings,
/// just the ones for OpLine. Note that OpLine needs the result of OpString, and not that of OpSource.
source_file_names: std.AutoArrayHashMapUnmanaged(CacheString, IdRef) = .{},
/// 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 {
bool_type: ?IdRef = null,
void_type: ?IdRef = null,
int_types: std.AutoHashMapUnmanaged(std.builtin.Type.Int, IdRef) = .{},
float_types: std.AutoHashMapUnmanaged(std.builtin.Type.Float, IdRef) = .{},
} = .{},
/// Set of Decls, referred to by Decl.Index.
decls: std.ArrayListUnmanaged(Decl) = .{},
@ -168,6 +181,7 @@ extended_instruction_set: std.AutoHashMapUnmanaged(spec.InstructionSet, IdRef) =
pub fn init(gpa: Allocator) Module {
return .{
.gpa = gpa,
.arena = std.heap.ArenaAllocator.init(gpa),
.next_result_id = 1, // 0 is an invalid SPIR-V result id, so start counting at 1.
};
}
@ -184,15 +198,19 @@ pub fn deinit(self: *Module) void {
self.sections.types_globals_constants.deinit(self.gpa);
self.sections.functions.deinit(self.gpa);
self.source_file_names.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.decls.deinit(self.gpa);
self.decl_deps.deinit(self.gpa);
self.entry_points.deinit(self.gpa);
self.extended_instruction_set.deinit(self.gpa);
self.arena.deinit();
self.* = undefined;
}
@ -235,10 +253,6 @@ pub fn resolveId(self: *Module, key: CacheKey) !IdResult {
return self.resultId(try self.resolve(key));
}
pub fn resolveString(self: *Module, str: []const u8) !CacheString {
return try self.cache.addString(self, str);
}
fn addEntryPointDeps(
self: *Module,
decl_index: Decl.Index,
@ -283,7 +297,7 @@ fn entryPoints(self: *Module) !Section {
try entry_points.emit(self.gpa, .OpEntryPoint, .{
.execution_model = entry_point.execution_model,
.entry_point = entry_point_id,
.name = self.cache.getString(entry_point.name).?,
.name = entry_point.name,
.interface = interface.items,
});
}
@ -388,51 +402,110 @@ pub fn importInstructionSet(self: *Module, set: spec.InstructionSet) !IdRef {
return result_id;
}
/// Fetch the result-id of an OpString instruction that encodes the path of the source
/// file of the decl. This function may also emit an OpSource with source-level information regarding
/// the decl.
pub fn resolveSourceFileName(self: *Module, path: []const u8) !IdRef {
const path_ref = try self.resolveString(path);
const result = try self.source_file_names.getOrPut(self.gpa, path_ref);
if (!result.found_existing) {
const file_result_id = self.allocId();
result.value_ptr.* = file_result_id;
try self.sections.debug_strings.emit(self.gpa, .OpString, .{
.id_result = file_result_id,
.string = path,
});
/// Fetch the result-id of an instruction corresponding to a string.
pub fn resolveString(self: *Module, string: []const u8) !IdRef {
if (self.strings.get(string)) |id| {
return id;
}
return result.value_ptr.*;
const id = self.allocId();
try self.strings.put(self.gpa, try self.arena.allocator().dupe(u8, string), id);
try self.sections.debug_strings.emit(self.gpa, .OpString, .{
.id_result = id,
.string = string,
});
return id;
}
pub fn intType(self: *Module, signedness: std.builtin.Signedness, bits: u16) !CacheRef {
return try self.resolve(.{ .int_type = .{
.signedness = signedness,
.bits = bits,
} });
pub fn structType(self: *Module, types: []const IdRef, maybe_names: ?[]const []const u8) !IdRef {
const result_id = self.allocId();
try self.sections.types_globals_constants.emit(self.gpa, .OpTypeStruct, .{
.id_result = result_id,
.id_ref = types,
});
if (maybe_names) |names| {
assert(names.len == types.len);
for (names, 0..) |name, i| {
try self.memberDebugName(result_id, @intCast(i), name);
}
}
return result_id;
}
pub fn vectorType(self: *Module, len: u32, elem_ty_ref: CacheRef) !CacheRef {
return try self.resolve(.{ .vector_type = .{
.component_type = elem_ty_ref,
pub fn boolType(self: *Module) !IdRef {
if (self.cache2.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;
return result_id;
}
pub fn voidType(self: *Module) !IdRef {
if (self.cache2.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;
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 });
if (!entry.found_existing) {
const result_id = self.allocId();
entry.value_ptr.* = result_id;
try self.sections.types_globals_constants.emit(self.gpa, .OpTypeInt, .{
.id_result = result_id,
.width = bits,
.signedness = switch (signedness) {
.signed => 1,
.unsigned => 0,
},
});
switch (signedness) {
.signed => try self.debugNameFmt(result_id, "i{}", .{bits}),
.unsigned => try self.debugNameFmt(result_id, "u{}", .{bits}),
}
}
return entry.value_ptr.*;
}
pub fn floatType(self: *Module, bits: u16) !IdRef {
assert(bits > 0);
const entry = try self.cache2.float_types.getOrPut(self.gpa, .{ .bits = bits });
if (!entry.found_existing) {
const result_id = self.allocId();
entry.value_ptr.* = result_id;
try self.sections.types_globals_constants.emit(self.gpa, .OpTypeFloat, .{
.id_result = result_id,
.width = bits,
});
try self.debugNameFmt(result_id, "f{}", .{bits});
}
return entry.value_ptr.*;
}
pub fn vectorType(self: *Module, len: u32, child_id: IdRef) !IdRef {
const result_id = self.allocId();
try self.sections.types_globals_constants.emit(self.gpa, .OpTypeVector, .{
.id_result = result_id,
.component_type = child_id,
.component_count = len,
} });
}
pub fn arrayType(self: *Module, len: u32, elem_ty_ref: CacheRef) !CacheRef {
const len_ty_ref = try self.resolve(.{ .int_type = .{
.signedness = .unsigned,
.bits = 32,
} });
const len_ref = try self.resolve(.{ .int = .{
.ty = len_ty_ref,
.value = .{ .uint64 = len },
} });
return try self.resolve(.{ .array_type = .{
.element_type = elem_ty_ref,
.length = len_ref,
} });
});
return result_id;
}
pub fn constUndef(self: *Module, ty_id: IdRef) !IdRef {
@ -526,7 +599,7 @@ pub fn declareEntryPoint(
) !void {
try self.entry_points.append(self.gpa, .{
.decl_index = decl_index,
.name = try self.resolveString(name),
.name = try self.arena.allocator().dupe(u8, name),
.execution_model = execution_model,
});
}

View File

@ -116,7 +116,8 @@ pub const Instruction = struct {
const instruction_len = self.words[self.offset] >> 16;
defer self.offset += instruction_len;
defer self.index += 1;
assert(instruction_len != 0 and self.offset < self.words.len); // Verified in BinaryModule.parse.
assert(instruction_len != 0);
assert(self.offset < self.words.len);
return Instruction{
.opcode = @enumFromInt(self.words[self.offset] & 0xFFFF),