Commit ab701c3d37
src/codegen/llvm.zig
@@ -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));
src/codegen/spirv.zig
@@ -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);
src/link/SpirV.zig
@@ -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| {