aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/spirv/Module.zig
diff options
context:
space:
mode:
authorRobin Voetter <robin@voetter.nl>2024-04-05 00:30:06 +0200
committerRobin Voetter <robin@voetter.nl>2024-04-06 13:37:39 +0200
commit97a67762ba1fcc363656a59af10a3031332cbd62 (patch)
tree7e333b5d505b6886dae97ca1f1a734690c588712 /src/codegen/spirv/Module.zig
parent188922a5448417d1939023b1eab7f70fa1953dde (diff)
downloadzig-97a67762ba1fcc363656a59af10a3031332cbd62.tar.gz
zig-97a67762ba1fcc363656a59af10a3031332cbd62.zip
spirv: remove cache usage for types
Diffstat (limited to 'src/codegen/spirv/Module.zig')
-rw-r--r--src/codegen/spirv/Module.zig173
1 files changed, 123 insertions, 50 deletions
diff --git a/src/codegen/spirv/Module.zig b/src/codegen/spirv/Module.zig
index eb1b2d41a0..73374389b7 100644
--- a/src/codegen/spirv/Module.zig
+++ b/src/codegen/spirv/Module.zig
@@ -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,
- .component_count = len,
- } });
+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 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,
- } });
+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,
+ });
+ 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,
});
}