spirv: anon decl refs

This commit is contained in:
Robin Voetter 2023-10-07 14:21:59 +02:00
parent 08ea9a9ff6
commit ab701c3d37
No known key found for this signature in database
3 changed files with 163 additions and 5 deletions

View File

@ -3050,6 +3050,7 @@ pub const Object = struct {
decl_val: InternPool.Index,
llvm_addr_space: Builder.AddrSpace,
) Error!Builder.Variable.Index {
// TODO: Add address space to the anon_decl_map
const gop = try o.anon_decl_map.getOrPut(o.gpa, decl_val);
if (gop.found_existing) return gop.value_ptr.ptr(&o.builder).kind.variable;
errdefer assert(o.anon_decl_map.remove(decl_val));

View File

@ -52,9 +52,12 @@ const Block = struct {
const BlockMap = std.AutoHashMapUnmanaged(Air.Inst.Index, *Block);
/// Maps Zig decl indices to linking SPIR-V linking information.
/// Maps Zig decl indices to SPIR-V linking information.
pub const DeclLinkMap = std.AutoHashMap(Module.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);
/// 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.
@ -77,9 +80,12 @@ 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 globals.
/// 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) = .{},
@ -182,6 +188,7 @@ pub const DeclGen = struct {
module: *Module,
spv: *SpvModule,
decl_link: *DeclLinkMap,
anon_decl_link: *AnonDeclLinkMap,
) DeclGen {
return .{
.gpa = allocator,
@ -191,6 +198,7 @@ pub const DeclGen = struct {
.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,
@ -301,6 +309,89 @@ pub const DeclGen = struct {
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 });
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;
}
const spv_decl_index = try self.spv.allocDecl(.global);
try self.func.decl_deps.put(self.spv.gpa, spv_decl_index, {});
entry.value_ptr.* = spv_decl_index;
break :blk spv_decl_index;
};
const mod = self.module;
const ty = mod.intern_pool.typeOf(val).toType();
const ty_ref = try self.resolveType(ty, .indirect);
const ptr_ty_ref = try self.spv.ptrType(ty_ref, storage_class);
const var_id = self.spv.declPtr(spv_decl_index).result_id;
const section = &self.spv.sections.types_globals_constants;
try section.emit(self.spv.gpa, .OpVariable, .{
.id_result_type = self.typeId(ptr_ty_ref),
.id_result = var_id,
.storage_class = storage_class,
});
// TODO: At some point we will be able to generate this all constant here, but then all of
// constant() will need to be implemented such that it doesn't generate any at-runtime code.
// NOTE: Because this is a global, we really only want to initialize it once. Therefore the
// constant lowering of this value will need to be deferred to some other function, which
// is then added to the list of initializers using endGlobal().
// Save the current state so that we can temporarily generate into a different function.
// TODO: This should probably be made a little more robust.
const func = self.func;
defer self.func = func;
const block_label_id = self.current_block_label_id;
defer self.current_block_label_id = block_label_id;
self.func = .{};
// TODO: Merge this with genDecl?
const begin = self.spv.beginGlobal();
const void_ty_ref = try self.resolveType(Type.void, .direct);
const initializer_proto_ty_ref = try self.spv.resolve(.{ .function_type = .{
.return_type = void_ty_ref,
.parameters = &.{},
} });
const initializer_id = self.spv.allocId();
try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{
.id_result_type = self.typeId(void_ty_ref),
.id_result = initializer_id,
.function_control = .{},
.function_type = self.typeId(initializer_proto_ty_ref),
});
const root_block_id = self.spv.allocId();
try self.func.prologue.emit(self.spv.gpa, .OpLabel, .{
.id_result = root_block_id,
});
self.current_block_label_id = root_block_id;
const val_id = try self.constant(ty, val.toValue(), .indirect);
try self.func.body.emit(self.spv.gpa, .OpStore, .{
.pointer = var_id,
.object = val_id,
});
self.spv.endGlobal(spv_decl_index, begin, var_id, initializer_id);
try self.func.body.emit(self.spv.gpa, .OpReturn, {});
try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {});
try self.spv.addFunction(spv_decl_index, self.func);
try self.spv.debugNameFmt(var_id, "__anon_{d}", .{@intFromEnum(val)});
try self.spv.debugNameFmt(initializer_id, "initializer of __anon_{d}", .{@intFromEnum(val)});
return var_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,7 +858,7 @@ pub const DeclGen = struct {
switch (mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr.addr) {
.decl => |decl| return try self.constantDeclRef(ptr_ty, decl),
.mut_decl => |decl_mut| return try self.constantDeclRef(ptr_ty, decl_mut.decl),
.anon_decl => @panic("TODO"),
.anon_decl => |anon_decl| return try self.constantAnonDeclRef(ptr_ty, anon_decl),
.int => |int| {
const ptr_id = self.spv.allocId();
// TODO: This can probably be an OpSpecConstantOp Bitcast, but
@ -813,6 +904,69 @@ pub const DeclGen = struct {
}
}
fn constantAnonDeclRef(self: *DeclGen, ty: Type, decl_val: InternPool.Index) !IdRef {
// TODO: Merge this function with constantDeclRef.
const mod = self.module;
const ip = &mod.intern_pool;
const ty_ref = try self.resolveType(ty, .direct);
const decl_ty = ip.typeOf(decl_val).toType();
if (decl_val.toValue().getFunction(mod)) |func| {
_ = func;
unreachable; // TODO
} else if (decl_val.toValue().getExternFunc(mod)) |func| {
_ = func;
unreachable;
}
// const is_fn_body = decl_ty.zigTypeTag(mod) == .Fn;
if (!decl_ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) {
// Pointer to nothing - return undefoined
return self.spv.constUndef(ty_ref);
}
if (decl_ty.zigTypeTag(mod) == .Fn) {
unreachable; // TODO
}
const final_storage_class = spvStorageClass(ty.ptrAddressSpace(mod));
const actual_storage_class = switch (final_storage_class) {
.Generic => .CrossWorkgroup,
else => |other| other,
};
const decl_id = try self.resolveAnonDecl(decl_val, actual_storage_class);
const decl_ty_ref = try self.resolveType(decl_ty, .indirect);
const decl_ptr_ty_ref = try self.spv.ptrType(decl_ty_ref, final_storage_class);
const ptr_id = switch (final_storage_class) {
.Generic => blk: {
const result_id = self.spv.allocId();
try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{
.id_result_type = self.typeId(decl_ptr_ty_ref),
.id_result = result_id,
.pointer = decl_id,
});
break :blk result_id;
},
else => decl_id,
};
if (decl_ptr_ty_ref != ty_ref) {
// Differing pointer types, insert a cast.
const casted_ptr_id = self.spv.allocId();
try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
.id_result_type = self.typeId(ty_ref),
.id_result = casted_ptr_id,
.operand = ptr_id,
});
return casted_ptr_id;
} else {
return ptr_id;
}
}
fn constantDeclRef(self: *DeclGen, ty: Type, decl_index: Decl.Index) !IdRef {
const mod = self.module;
const ty_ref = try self.resolveType(ty, .direct);

View File

@ -48,6 +48,7 @@ base: link.File,
spv: SpvModule,
spv_arena: ArenaAllocator,
decl_link: codegen.DeclLinkMap,
anon_decl_link: codegen.AnonDeclLinkMap,
pub fn createEmpty(gpa: Allocator, options: link.Options) !*SpirV {
const self = try gpa.create(SpirV);
@ -61,6 +62,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*SpirV {
.spv = undefined,
.spv_arena = ArenaAllocator.init(gpa),
.decl_link = codegen.DeclLinkMap.init(self.base.allocator),
.anon_decl_link = codegen.AnonDeclLinkMap.init(self.base.allocator),
};
self.spv = SpvModule.init(gpa, self.spv_arena.allocator());
errdefer self.deinit();
@ -102,6 +104,7 @@ pub fn deinit(self: *SpirV) void {
self.spv.deinit();
self.spv_arena.deinit();
self.decl_link.deinit();
self.anon_decl_link.deinit();
}
pub fn updateFunc(self: *SpirV, module: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {
@ -113,7 +116,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);
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| {
@ -129,7 +132,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);
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| {