Commit 28dda3bf89
src/codegen/spirv.zig
@@ -53,20 +53,141 @@ const Block = struct {
const BlockMap = std.AutoHashMapUnmanaged(Air.Inst.Index, *Block);
/// Maps Zig decl indices to SPIR-V linking information.
-pub const DeclLinkMap = std.AutoHashMap(Module.Decl.Index, SpvModule.Decl.Index);
+pub const DeclLinkMap = std.AutoHashMapUnmanaged(Decl.Index, SpvModule.Decl.Index);
/// Maps anon decl indices to SPIR-V linking information.
-pub const AnonDeclLinkMap = std.AutoHashMap(struct { InternPool.Index, StorageClass }, SpvModule.Decl.Index);
+pub const AnonDeclLinkMap = std.AutoHashMapUnmanaged(struct { InternPool.Index, StorageClass }, SpvModule.Decl.Index);
+
+/// This structure holds information that is relevant to the entire compilation,
+/// in contrast to `DeclGen`, which only holds relevant information about a
+/// single decl.
+pub const Object = struct {
+ /// A general-purpose allocator that can be used for any allocation for this Object.
+ gpa: Allocator,
+
+ /// the SPIR-V module that represents the final binary.
+ spv: SpvModule,
+
+ /// The Zig module that this object file is generated for.
+ /// A map of Zig decl indices to SPIR-V decl indices.
+ decl_link: DeclLinkMap = .{},
+
+ /// A map of Zig InternPool indices for anonymous decls to SPIR-V decl indices.
+ anon_decl_link: AnonDeclLinkMap = .{},
+
+ /// A map that maps AIR intern pool indices to SPIR-V cache references (which
+ /// is basically the same thing except for SPIR-V).
+ /// This map is typically only used for structures that are deemed heavy enough
+ /// that it is worth to store them here. The SPIR-V module also interns types,
+ /// and so the main purpose of this map is to avoid recomputation and to
+ /// cache extra information about the type rather than to aid in validity
+ /// of the SPIR-V module.
+ type_map: TypeMap = .{},
+
+ pub fn init(gpa: Allocator) Object {
+ return .{
+ .gpa = gpa,
+ .spv = SpvModule.init(gpa),
+ };
+ }
+
+ pub fn deinit(self: *Object) void {
+ self.spv.deinit();
+ self.decl_link.deinit(self.gpa);
+ self.anon_decl_link.deinit(self.gpa);
+ self.type_map.deinit(self.gpa);
+ }
+
+ fn genDecl(
+ self: *Object,
+ mod: *Module,
+ decl_index: Decl.Index,
+ air: Air,
+ liveness: Liveness,
+ ) !void {
+ var decl_gen = DeclGen{
+ .gpa = self.gpa,
+ .object = self,
+ .module = mod,
+ .spv = &self.spv,
+ .decl_index = decl_index,
+ .air = air,
+ .liveness = liveness,
+ .type_map = &self.type_map,
+ .current_block_label_id = undefined,
+ };
+ defer decl_gen.deinit();
+
+ decl_gen.genDecl() catch |err| switch (err) {
+ error.CodegenFail => {
+ try mod.failed_decls.put(mod.gpa, decl_index, decl_gen.error_msg.?);
+ },
+ else => |other| {
+ // There might be an error that happened *after* self.error_msg
+ // was already allocated, so be sure to free it.
+ if (decl_gen.error_msg) |error_msg| {
+ error_msg.deinit(mod.gpa);
+ }
+
+ return other;
+ },
+ };
+ }
+
+ pub fn updateFunc(
+ self: *Object,
+ mod: *Module,
+ func_index: InternPool.Index,
+ air: Air,
+ liveness: Liveness,
+ ) !void {
+ const decl_index = mod.funcInfo(func_index).owner_decl;
+ // TODO: Separate types for generating decls and functions?
+ try self.genDecl(mod, decl_index, air, liveness);
+ }
+
+ pub fn updateDecl(
+ self: *Object,
+ mod: *Module,
+ decl_index: Decl.Index,
+ ) !void {
+ try self.genDecl(mod, decl_index, undefined, undefined);
+ }
+
+ /// Fetch or allocate a result id for decl index. This function also marks the decl as alive.
+ /// Note: Function does not actually generate the decl, it just allocates an index.
+ pub fn resolveDecl(self: *Object, mod: *Module, decl_index: Decl.Index) !SpvModule.Decl.Index {
+ const decl = mod.declPtr(decl_index);
+ try mod.markDeclAlive(decl);
+
+ const entry = try self.decl_link.getOrPut(self.gpa, decl_index);
+ if (!entry.found_existing) {
+ // TODO: Extern fn?
+ const kind: SpvModule.DeclKind = if (decl.val.isFuncBody(mod))
+ .func
+ else
+ .global;
+
+ entry.value_ptr.* = try self.spv.allocDecl(kind);
+ }
+
+ return entry.value_ptr.*;
+ }
+};
/// This structure is used to compile a declaration, and contains all relevant meta-information to deal with that.
-pub const DeclGen = struct {
+const DeclGen = struct {
/// A general-purpose allocator that can be used for any allocations for this DeclGen.
gpa: Allocator,
+ /// The object that this decl is generated into.
+ object: *Object,
+
/// The Zig module that we are generating decls for.
module: *Module,
/// The SPIR-V module that instructions should be emitted into.
+ /// This is the same as `self.object.spv`, repeated here for brevity.
spv: *SpvModule,
/// The decl we are currently generating code for.
@@ -80,30 +201,19 @@ pub const DeclGen = struct {
/// Note: If the declaration is not a function, this value will be undefined!
liveness: Liveness,
- /// Maps Zig Decl indices to SPIR-V decl indices.
- decl_link: *DeclLinkMap,
-
- /// Maps Zig anon decl indices to SPIR-V decl indices.
- anon_decl_link: *AnonDeclLinkMap,
-
/// An array of function argument result-ids. Each index corresponds with the
/// function argument of the same index.
args: std.ArrayListUnmanaged(IdRef) = .{},
/// A counter to keep track of how many `arg` instructions we've seen yet.
- next_arg_index: u32,
+ next_arg_index: u32 = 0,
/// A map keeping track of which instruction generated which result-id.
inst_results: InstMap = .{},
- /// A map that maps AIR intern pool indices to SPIR-V cache references (which
- /// is basically the same thing except for SPIR-V).
- /// This map is typically only used for structures that are deemed heavy enough
- /// that it is worth to store them here. The SPIR-V module also interns types,
- /// and so the main purpose of this map is to avoid recomputation and to
- /// cache extra information about the type rather than to aid in validity
- /// of the SPIR-V module.
- type_map: TypeMap = .{},
+ /// A map that maps AIR intern pool indices to SPIR-V cache references.
+ /// See Object.type_map
+ type_map: *TypeMap,
/// We need to keep track of result ids for block labels, as well as the 'incoming'
/// blocks for a block.
@@ -121,7 +231,7 @@ pub const DeclGen = struct {
/// If `gen` returned `Error.CodegenFail`, this contains an explanatory message.
/// Memory is owned by `module.gpa`.
- error_msg: ?*Module.ErrorMsg,
+ error_msg: ?*Module.ErrorMsg = null,
/// Possible errors the `genDecl` function may return.
const Error = error{ CodegenFail, OutOfMemory };
@@ -181,67 +291,10 @@ pub const DeclGen = struct {
indirect,
};
- /// Initialize the common resources of a DeclGen. Some fields are left uninitialized,
- /// only set when `gen` is called.
- pub fn init(
- allocator: Allocator,
- module: *Module,
- spv: *SpvModule,
- decl_link: *DeclLinkMap,
- anon_decl_link: *AnonDeclLinkMap,
- ) DeclGen {
- return .{
- .gpa = allocator,
- .module = module,
- .spv = spv,
- .decl_index = undefined,
- .air = undefined,
- .liveness = undefined,
- .decl_link = decl_link,
- .anon_decl_link = anon_decl_link,
- .next_arg_index = undefined,
- .current_block_label_id = undefined,
- .error_msg = undefined,
- };
- }
-
- /// Generate the code for `decl`. If a reportable error occurred during code generation,
- /// a message is returned by this function. Callee owns the memory. If this function
- /// returns such a reportable error, it is valid to be called again for a different decl.
- pub fn gen(self: *DeclGen, decl_index: Decl.Index, air: Air, liveness: Liveness) !?*Module.ErrorMsg {
- // Reset internal resources, we don't want to re-allocate these.
- self.decl_index = decl_index;
- self.air = air;
- self.liveness = liveness;
- self.args.items.len = 0;
- self.next_arg_index = 0;
- self.inst_results.clearRetainingCapacity();
- self.blocks.clearRetainingCapacity();
- self.current_block_label_id = undefined;
- self.func.reset();
- self.base_line_stack.items.len = 0;
- self.error_msg = null;
-
- self.genDecl() catch |err| switch (err) {
- error.CodegenFail => return self.error_msg,
- else => |others| {
- // There might be an error that happened *after* self.error_msg
- // was already allocated, so be sure to free it.
- if (self.error_msg) |error_msg| {
- error_msg.deinit(self.module.gpa);
- }
- return others;
- },
- };
-
- return null;
- }
-
/// Free resources owned by the DeclGen.
pub fn deinit(self: *DeclGen) void {
self.args.deinit(self.gpa);
self.inst_results.deinit(self.gpa);
- self.type_map.deinit(self.gpa);
self.blocks.deinit(self.gpa);
self.func.deinit(self.gpa);
self.base_line_stack.deinit(self.gpa);
@@ -277,7 +330,7 @@ pub const DeclGen = struct {
.func => |func| func.owner_decl,
else => unreachable,
};
- const spv_decl_index = try self.resolveDecl(fn_decl_index);
+ const spv_decl_index = try self.object.resolveDecl(mod, fn_decl_index);
try self.func.decl_deps.put(self.spv.gpa, spv_decl_index, {});
return self.spv.declPtr(spv_decl_index).result_id;
}
@@ -288,31 +341,10 @@ pub const DeclGen = struct {
return self.inst_results.get(index).?; // Assertion means instruction does not dominate usage.
}
- /// Fetch or allocate a result id for decl index. This function also marks the decl as alive.
- /// Note: Function does not actually generate the decl.
- fn resolveDecl(self: *DeclGen, decl_index: Module.Decl.Index) !SpvModule.Decl.Index {
- const mod = self.module;
- const decl = mod.declPtr(decl_index);
- try mod.markDeclAlive(decl);
-
- const entry = try self.decl_link.getOrPut(decl_index);
- if (!entry.found_existing) {
- // TODO: Extern fn?
- const kind: SpvModule.DeclKind = if (decl.val.isFuncBody(mod))
- .func
- else
- .global;
-
- entry.value_ptr.* = try self.spv.allocDecl(kind);
- }
-
- return entry.value_ptr.*;
- }
-
fn resolveAnonDecl(self: *DeclGen, val: InternPool.Index, storage_class: StorageClass) !IdRef {
// TODO: This cannot be a function at this point, but it should probably be handled anyway.
const spv_decl_index = blk: {
- const entry = try self.anon_decl_link.getOrPut(.{ val, storage_class });
+ const entry = try self.object.anon_decl_link.getOrPut(self.object.gpa, .{ val, storage_class });
if (entry.found_existing) {
try self.func.decl_deps.put(self.spv.gpa, entry.value_ptr.*, {});
return self.spv.declPtr(entry.value_ptr.*).result_id;
@@ -988,7 +1020,7 @@ pub const DeclGen = struct {
return self.spv.constUndef(ty_ref);
}
- const spv_decl_index = try self.resolveDecl(decl_index);
+ const spv_decl_index = try self.object.resolveDecl(mod, decl_index);
const decl_id = self.spv.declPtr(spv_decl_index).result_id;
try self.func.decl_deps.put(self.spv.gpa, spv_decl_index, {});
@@ -1624,7 +1656,7 @@ pub const DeclGen = struct {
const mod = self.module;
const ip = &mod.intern_pool;
const decl = mod.declPtr(self.decl_index);
- const spv_decl_index = try self.resolveDecl(self.decl_index);
+ const spv_decl_index = try self.object.resolveDecl(mod, self.decl_index);
const decl_id = self.spv.declPtr(spv_decl_index).result_id;
src/link/SpirV.zig
@@ -45,9 +45,7 @@ const IdResult = spec.IdResult;
base: link.File,
-spv: SpvModule,
-decl_link: codegen.DeclLinkMap,
-anon_decl_link: codegen.AnonDeclLinkMap,
+object: codegen.Object,
pub fn createEmpty(gpa: Allocator, options: link.Options) !*SpirV {
const self = try gpa.create(SpirV);
@@ -58,11 +56,8 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*SpirV {
.file = null,
.allocator = gpa,
},
- .spv = undefined,
- .decl_link = codegen.DeclLinkMap.init(self.base.allocator),
- .anon_decl_link = codegen.AnonDeclLinkMap.init(self.base.allocator),
+ .object = codegen.Object.init(gpa),
};
- self.spv = SpvModule.init(gpa);
errdefer self.deinit();
// TODO: Figure out where to put all of these
@@ -99,9 +94,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
}
pub fn deinit(self: *SpirV) void {
- self.spv.deinit();
- self.decl_link.deinit();
- self.anon_decl_link.deinit();
+ self.object.deinit();
}
pub fn updateFunc(self: *SpirV, module: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {
@@ -113,12 +106,7 @@ pub fn updateFunc(self: *SpirV, module: *Module, func_index: InternPool.Index, a
const decl = module.declPtr(func.owner_decl);
log.debug("lowering function {s}", .{module.intern_pool.stringToSlice(decl.name)});
- var decl_gen = codegen.DeclGen.init(self.base.allocator, module, &self.spv, &self.decl_link, &self.anon_decl_link);
- defer decl_gen.deinit();
-
- if (try decl_gen.gen(func.owner_decl, air, liveness)) |msg| {
- try module.failed_decls.put(module.gpa, func.owner_decl, msg);
- }
+ try self.object.updateFunc(module, func_index, air, liveness);
}
pub fn updateDecl(self: *SpirV, module: *Module, decl_index: Module.Decl.Index) !void {
@@ -129,12 +117,7 @@ pub fn updateDecl(self: *SpirV, module: *Module, decl_index: Module.Decl.Index)
const decl = module.declPtr(decl_index);
log.debug("lowering declaration {s}", .{module.intern_pool.stringToSlice(decl.name)});
- var decl_gen = codegen.DeclGen.init(self.base.allocator, module, &self.spv, &self.decl_link, &self.anon_decl_link);
- defer decl_gen.deinit();
-
- if (try decl_gen.gen(decl_index, undefined, undefined)) |msg| {
- try module.failed_decls.put(module.gpa, decl_index, msg);
- }
+ try self.object.updateDecl(module, decl_index);
}
pub fn updateDeclExports(
@@ -145,15 +128,9 @@ pub fn updateDeclExports(
) !void {
const decl = mod.declPtr(decl_index);
if (decl.val.isFuncBody(mod) and decl.ty.fnCallingConvention(mod) == .Kernel) {
- // TODO: Unify with resolveDecl in spirv.zig.
- const entry = try self.decl_link.getOrPut(decl_index);
- if (!entry.found_existing) {
- entry.value_ptr.* = try self.spv.allocDecl(.func);
- }
- const spv_decl_index = entry.value_ptr.*;
-
+ const spv_decl_index = try self.object.resolveDecl(mod, decl_index);
for (exports) |exp| {
- try self.spv.declareEntryPoint(spv_decl_index, mod.intern_pool.stringToSlice(exp.opts.name));
+ try self.object.spv.declareEntryPoint(spv_decl_index, mod.intern_pool.stringToSlice(exp.opts.name));
}
}
@@ -185,15 +162,17 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No
sub_prog_node.activate();
defer sub_prog_node.end();
+ const spv = &self.object.spv;
+
const target = comp.getTarget();
- try writeCapabilities(&self.spv, target);
- try writeMemoryModel(&self.spv, target);
+ try writeCapabilities(spv, target);
+ try writeMemoryModel(spv, target);
// We need to export the list of error names somewhere so that we can pretty-print them in the
// executor. This is not really an important thing though, so we can just dump it in any old
// nonsemantic instruction. For now, just put it in OpSourceExtension with a special name.
- var error_info = std.ArrayList(u8).init(self.spv.gpa);
+ var error_info = std.ArrayList(u8).init(self.object.gpa);
defer error_info.deinit();
try error_info.appendSlice("zig_errors");
@@ -209,11 +188,11 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No
defer self.base.allocator.free(escaped_name);
try error_info.writer().print(":{s}", .{escaped_name});
}
- try self.spv.sections.debug_strings.emit(self.spv.gpa, .OpSourceExtension, .{
+ try spv.sections.debug_strings.emit(spv.gpa, .OpSourceExtension, .{
.extension = error_info.items,
});
- try self.spv.flush(self.base.file.?);
+ try spv.flush(self.base.file.?);
}
fn writeCapabilities(spv: *SpvModule, target: std.Target) !void {