Commit ab701c3d37

Robin Voetter <robin@voetter.nl>
2023-10-07 14:21:59
spirv: anon decl refs
1 parent 08ea9a9
Changed files (3)
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| {