mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 12:27:41 +00:00
spirv: generate code directly in updateFunc/updateDecl
This cloneAir/cloneLiveness idea used to ignore Zig's internals has proven buggy. Instead, just generate the code directly from updateFunc and updateDecl as the other backends do, but pretend that Zig is not an incremental compiler. The SPIR-V backend will for the time being not support this.
This commit is contained in:
parent
34b98ee372
commit
c9db6e43af
@ -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);
|
||||
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user