diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index cae6b4cd8b..f44e894a29 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -37,6 +37,8 @@ pub const BlockMap = std.AutoHashMapUnmanaged(Air.Inst.Index, struct { incoming_blocks: *std.ArrayListUnmanaged(IncomingBlock), }); +pub const DeclMap = std.AutoHashMap(Module.Decl.Index, IdResult); + /// This structure is used to compile a declaration, and contains all relevant meta-information to deal with that. pub const DeclGen = struct { /// A general-purpose allocator that can be used for any allocations for this DeclGen. @@ -59,7 +61,8 @@ pub const DeclGen = struct { /// Note: If the declaration is not a function, this value will be undefined! liveness: Liveness, - ids: *const std.AutoHashMap(Decl.Index, IdResult), + /// Maps Zig Decl indices to SPIR-V result indices. + decl_ids: *DeclMap, /// An array of function argument result-ids. Each index corresponds with the /// function argument of the same index. @@ -149,7 +152,7 @@ pub const DeclGen = struct { allocator: Allocator, module: *Module, spv: *SpvModule, - ids: *const std.AutoHashMap(Decl.Index, IdResult), + decl_ids: *DeclMap, ) DeclGen { return .{ .gpa = allocator, @@ -158,7 +161,7 @@ pub const DeclGen = struct { .decl_index = undefined, .air = undefined, .liveness = undefined, - .ids = ids, + .decl_ids = decl_ids, .next_arg_index = undefined, .current_block_label_id = undefined, .error_msg = undefined, @@ -232,9 +235,7 @@ pub const DeclGen = struct { .function => val.castTag(.function).?.data.owner_decl, else => unreachable, }; - const decl = self.module.declPtr(fn_decl_index); - self.module.markDeclAlive(decl); - return self.ids.get(fn_decl_index).?; + return try self.resolveDecl(fn_decl_index); } const result_id = self.spv.allocId(); @@ -245,6 +246,21 @@ 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) !IdResult { + const decl = self.module.declPtr(decl_index); + self.module.markDeclAlive(decl); + + const entry = try self.decl_ids.getOrPut(decl_index); + if (entry.found_existing) { + return entry.value_ptr.*; + } + const result_id = self.spv.allocId(); + entry.value_ptr.* = result_id; + return result_id; + } + /// Start a new SPIR-V block, Emits the label of the new block, and stores which /// block we are currently generating. /// Note that there is no such thing as nested blocks like in ZIR or AIR, so we don't need to @@ -767,8 +783,8 @@ pub const DeclGen = struct { } fn genDecl(self: *DeclGen) !void { - const result_id = self.ids.get(self.decl_index).?; const decl = self.module.declPtr(self.decl_index); + const result_id = try self.resolveDecl(self.decl_index); if (decl.val.castTag(.function)) |_| { assert(decl.ty.zigTypeTag() == .Fn); diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index 561775a757..c63e4a20af 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -44,34 +44,25 @@ const IdResult = spec.IdResult; base: link.File, -/// This linker backend does not try to incrementally link output SPIR-V code. -/// Instead, it tracks all declarations in this table, and iterates over it -/// in the flush function. -decl_table: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclGenContext) = .{}, - -const DeclGenContext = struct { - air: Air, - air_arena: ArenaAllocator.State, - liveness: Liveness, - - fn deinit(self: *DeclGenContext, gpa: Allocator) void { - self.air.deinit(gpa); - self.liveness.deinit(gpa); - self.air_arena.promote(gpa).deinit(); - self.* = undefined; - } -}; +spv: SpvModule, +spv_arena: ArenaAllocator, +decl_ids: codegen.DeclMap, pub fn createEmpty(gpa: Allocator, options: link.Options) !*SpirV { - const spirv = try gpa.create(SpirV); - spirv.* = .{ + const self = try gpa.create(SpirV); + self.* = .{ .base = .{ .tag = .spirv, .options = options, .file = null, .allocator = gpa, }, + .spv = undefined, + .spv_arena = ArenaAllocator.init(gpa), + .decl_ids = codegen.DeclMap.init(self.base.allocator), }; + self.spv = SpvModule.init(gpa, self.spv_arena.allocator()); + errdefer self.deinit(); // TODO: Figure out where to put all of these switch (options.target.cpu.arch) { @@ -88,7 +79,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*SpirV { return error.TODOAbiNotSupported; } - return spirv; + return self; } pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*SpirV { @@ -107,44 +98,35 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option } pub fn deinit(self: *SpirV) void { - self.decl_table.deinit(self.base.allocator); + self.spv.deinit(); + self.spv_arena.deinit(); + self.decl_ids.deinit(); } pub fn updateFunc(self: *SpirV, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { if (build_options.skip_non_native) { @panic("Attempted to compile for architecture that was disabled by build configuration"); } - _ = module; - // Keep track of all decls so we can iterate over them on flush(). - const result = try self.decl_table.getOrPut(self.base.allocator, func.owner_decl); - if (result.found_existing) { - result.value_ptr.deinit(self.base.allocator); + var decl_gen = codegen.DeclGen.init(self.base.allocator, module, &self.spv, &self.decl_ids); + 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); } - - var arena = ArenaAllocator.init(self.base.allocator); - errdefer arena.deinit(); - - var new_air = try cloneAir(air, self.base.allocator, arena.allocator()); - errdefer new_air.deinit(self.base.allocator); - - var new_liveness = try cloneLiveness(liveness, self.base.allocator); - errdefer new_liveness.deinit(self.base.allocator); - - result.value_ptr.* = .{ - .air = new_air, - .air_arena = arena.state, - .liveness = new_liveness, - }; } pub fn updateDecl(self: *SpirV, module: *Module, decl_index: Module.Decl.Index) !void { if (build_options.skip_non_native) { @panic("Attempted to compile for architecture that was disabled by build configuration"); } - _ = module; - // Keep track of all decls so we can iterate over them on flush(). - _ = try self.decl_table.getOrPut(self.base.allocator, decl_index); + + var decl_gen = codegen.DeclGen.init(self.base.allocator, module, &self.spv, &self.decl_ids); + defer decl_gen.deinit(); + + if (try decl_gen.gen(decl_index, undefined, undefined)) |msg| { + try module.failed_decls.put(module.gpa, decl_index, msg); + } } pub fn updateDeclExports( @@ -160,13 +142,8 @@ pub fn updateDeclExports( } pub fn freeDecl(self: *SpirV, decl_index: Module.Decl.Index) void { - if (self.decl_table.getIndex(decl_index)) |index| { - const module = self.base.options.module.?; - const decl = module.declPtr(decl_index); - if (decl.val.tag() == .function) { - self.decl_table.values()[index].deinit(self.base.allocator); - } - } + _ = self; + _ = decl_index; } pub fn flush(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { @@ -189,56 +166,11 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No sub_prog_node.activate(); defer sub_prog_node.end(); - const module = self.base.options.module.?; const target = comp.getTarget(); + try writeCapabilities(&self.spv, target); + try writeMemoryModel(&self.spv, target); - var arena = std.heap.ArenaAllocator.init(self.base.allocator); - defer arena.deinit(); - - var spv = SpvModule.init(self.base.allocator, arena.allocator()); - defer spv.deinit(); - - // Allocate an ID for every declaration before generating code, - // so that we can access them before processing them. - // TODO: We're allocating an ID unconditionally now, are there - // declarations which don't generate a result? - var ids = std.AutoHashMap(Module.Decl.Index, IdResult).init(self.base.allocator); - defer ids.deinit(); - try ids.ensureTotalCapacity(@intCast(u32, self.decl_table.count())); - - for (self.decl_table.keys()) |decl_index| { - const decl = module.declPtr(decl_index); - if (decl.has_tv) { - ids.putAssumeCapacityNoClobber(decl_index, spv.allocId()); - } - } - - // Now, actually generate the code for all declarations. - var decl_gen = codegen.DeclGen.init(self.base.allocator, module, &spv, &ids); - defer decl_gen.deinit(); - - var it = self.decl_table.iterator(); - while (it.next()) |entry| { - const decl_index = entry.key_ptr.*; - const decl = module.declPtr(decl_index); - if (!decl.has_tv) continue; - - const air = entry.value_ptr.air; - const liveness = entry.value_ptr.liveness; - - log.debug("generating code for {s}", .{decl.name}); - - // Note, if `decl` is not a function, air/liveness may be undefined. - if (try decl_gen.gen(decl_index, air, liveness)) |msg| { - try module.failed_decls.put(module.gpa, decl_index, msg); - return; // TODO: Attempt to generate more decls? - } - } - - try writeCapabilities(&spv, target); - try writeMemoryModel(&spv, target); - - try spv.flush(self.base.file.?); + try self.spv.flush(self.base.file.?); } fn writeCapabilities(spv: *SpvModule, target: std.Target) !void { @@ -281,45 +213,3 @@ fn writeMemoryModel(spv: *SpvModule, target: std.Target) !void { .memory_model = memory_model, }); } - -fn cloneLiveness(l: Liveness, gpa: Allocator) !Liveness { - const tomb_bits = try gpa.dupe(usize, l.tomb_bits); - errdefer gpa.free(tomb_bits); - - const extra = try gpa.dupe(u32, l.extra); - errdefer gpa.free(extra); - - return Liveness{ - .tomb_bits = tomb_bits, - .extra = extra, - .special = try l.special.clone(gpa), - }; -} - -fn cloneAir(air: Air, gpa: Allocator, air_arena: Allocator) !Air { - const values = try gpa.alloc(Value, air.values.len); - errdefer gpa.free(values); - - for (values, 0..) |*value, i| { - value.* = try air.values[i].copy(air_arena); - } - - var instructions = try air.instructions.toMultiArrayList().clone(gpa); - errdefer instructions.deinit(gpa); - - const air_tags = instructions.items(.tag); - const air_datas = instructions.items(.data); - - for (air_tags, 0..) |tag, i| { - switch (tag) { - .alloc, .ret_ptr, .const_ty => air_datas[i].ty = try air_datas[i].ty.copy(air_arena), - else => {}, - } - } - - return Air{ - .instructions = instructions.slice(), - .extra = try gpa.dupe(u32, air.extra), - .values = values, - }; -}