Commit 9d069d98e3

Andrew Kelley <andrew@ziglang.org>
2023-09-28 06:47:35
C backend: start handling anonymous decls
Start keeping track of dependencies on anon decls for dependency ordering during flush() Currently this causes use of undefined symbols because these dependencies need to get rendered into the output.
1 parent c0b5512
Changed files (3)
src
src/codegen/c.zig
@@ -528,6 +528,9 @@ pub const DeclGen = struct {
     fwd_decl: std.ArrayList(u8),
     error_msg: ?*Module.ErrorMsg,
     ctypes: CType.Store,
+    /// Keeps track of anonymous decls that need to be rendered before this
+    /// (named) Decl in the output C code.
+    anon_decl_deps: std.AutoArrayHashMapUnmanaged(InternPool.Index, void),
 
     fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
         @setCold(true);
@@ -540,6 +543,57 @@ pub const DeclGen = struct {
         return error.AnalysisFail;
     }
 
+    fn renderAnonDeclValue(
+        dg: *DeclGen,
+        writer: anytype,
+        ty: Type,
+        ptr_val: Value,
+        decl_val: InternPool.Index,
+        location: ValueRenderLocation,
+    ) error{ OutOfMemory, AnalysisFail }!void {
+        const mod = dg.module;
+        const ip = &mod.intern_pool;
+        const decl_ty = ip.typeOf(decl_val).toType();
+
+        // Render an undefined pointer if we have a pointer to a zero-bit or comptime type.
+        if (ty.isPtrAtRuntime(mod) and !decl_ty.isFnOrHasRuntimeBits(mod)) {
+            return dg.writeCValue(writer, .{ .undef = ty });
+        }
+
+        // Chase function values in order to be able to reference the original function.
+        if (decl_val.toValue().getFunction(mod)) |func| {
+            _ = func;
+            _ = ptr_val;
+            _ = location;
+            @panic("TODO");
+        }
+        if (decl_val.toValue().getExternFunc(mod)) |extern_func| {
+            _ = extern_func;
+            _ = ptr_val;
+            _ = location;
+            @panic("TODO");
+        }
+
+        assert(decl_val.toValue().getVariable(mod) == null);
+
+        // We shouldn't cast C function pointers as this is UB (when you call
+        // them).  The analysis until now should ensure that the C function
+        // pointers are compatible.  If they are not, then there is a bug
+        // somewhere and we should let the C compiler tell us about it.
+        const need_typecast = if (ty.castPtrToFn(mod)) |_| false else !ty.childType(mod).eql(decl_ty, mod);
+        if (need_typecast) {
+            try writer.writeAll("((");
+            try dg.renderType(writer, ty);
+            try writer.writeByte(')');
+        }
+        try writer.print("&__anon_{d}", .{@intFromEnum(decl_val)});
+        if (need_typecast) try writer.writeByte(')');
+
+        // Indicate that the anon decl should be rendered to the output so that
+        // our reference above is not undefined.
+        try dg.anon_decl_deps.put(dg.gpa, decl_val, {});
+    }
+
     fn renderDeclValue(
         dg: *DeclGen,
         writer: anytype,
@@ -593,18 +647,9 @@ pub const DeclGen = struct {
         const ptr_cty = try dg.typeToIndex(ptr_ty, .complete);
         const ptr = mod.intern_pool.indexToKey(ptr_val).ptr;
         switch (ptr.addr) {
-            .decl, .mut_decl => try dg.renderDeclValue(
-                writer,
-                ptr_ty,
-                ptr_val.toValue(),
-                switch (ptr.addr) {
-                    .decl => |decl| decl,
-                    .mut_decl => |mut_decl| mut_decl.decl,
-                    else => unreachable,
-                },
-                location,
-            ),
-            .anon_decl => @panic("TODO"),
+            .decl => |d| try dg.renderDeclValue(writer, ptr_ty, ptr_val.toValue(), d, location),
+            .mut_decl => |md| try dg.renderDeclValue(writer, ptr_ty, ptr_val.toValue(), md.decl, location),
+            .anon_decl => |decl_val| try dg.renderAnonDeclValue(writer, ptr_ty, ptr_val.toValue(), decl_val, location),
             .int => |int| {
                 try writer.writeByte('(');
                 try dg.renderCType(writer, ptr_cty);
@@ -1145,18 +1190,9 @@ pub const DeclGen = struct {
                     else => val.slicePtr(mod),
                 };
                 switch (ptr.addr) {
-                    .decl, .mut_decl => try dg.renderDeclValue(
-                        writer,
-                        ptr_ty,
-                        ptr_val,
-                        switch (ptr.addr) {
-                            .decl => |decl| decl,
-                            .mut_decl => |mut_decl| mut_decl.decl,
-                            else => unreachable,
-                        },
-                        ptr_location,
-                    ),
-                    .anon_decl => @panic("TODO"),
+                    .decl => |d| try dg.renderDeclValue(writer, ptr_ty, ptr_val, d, ptr_location),
+                    .mut_decl => |md| try dg.renderDeclValue(writer, ptr_ty, ptr_val, md.decl, ptr_location),
+                    .anon_decl => |decl_val| try dg.renderAnonDeclValue(writer, ptr_ty, ptr_val, decl_val, ptr_location),
                     .int => |int| {
                         try writer.writeAll("((");
                         try dg.renderType(writer, ptr_ty);
src/link/C.zig
@@ -34,6 +34,9 @@ fwd_decl_buf: std.ArrayListUnmanaged(u8) = .{},
 /// Optimization, `updateDecl` reuses this buffer rather than creating a new
 /// one with every call.
 code_buf: std.ArrayListUnmanaged(u8) = .{},
+/// Optimization, `updateDecl` reuses this table rather than creating a new one
+/// with every call.
+scratch_anon_decl_deps: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{},
 /// Optimization, `flush` reuses this buffer rather than creating a new
 /// one with every call.
 lazy_fwd_decl_buf: std.ArrayListUnmanaged(u8) = .{},
@@ -51,7 +54,6 @@ const String = struct {
         .len = 0,
     };
 };
-
 /// Per-declaration data.
 const DeclBlock = struct {
     code: String = String.empty,
@@ -98,7 +100,7 @@ pub fn openPath(gpa: Allocator, sub_path: []const u8, options: link.Options) !*C
     var c_file = try gpa.create(C);
     errdefer gpa.destroy(c_file);
 
-    c_file.* = C{
+    c_file.* = .{
         .base = .{
             .tag = .c,
             .options = options,
@@ -121,6 +123,7 @@ pub fn deinit(self: *C) void {
     self.string_bytes.deinit(gpa);
     self.fwd_decl_buf.deinit(gpa);
     self.code_buf.deinit(gpa);
+    self.scratch_anon_decl_deps.deinit(gpa);
 }
 
 pub fn freeDecl(self: *C, decl_index: Module.Decl.Index) void {
@@ -131,10 +134,13 @@ pub fn freeDecl(self: *C, decl_index: Module.Decl.Index) void {
     }
 }
 
-pub fn updateFunc(self: *C, module: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {
-    const tracy = trace(@src());
-    defer tracy.end();
-
+pub fn updateFunc(
+    self: *C,
+    module: *Module,
+    func_index: InternPool.Index,
+    air: Air,
+    liveness: Liveness,
+) !void {
     const gpa = self.base.allocator;
 
     const func = module.funcInfo(func_index);
@@ -152,52 +158,57 @@ pub fn updateFunc(self: *C, module: *Module, func_index: InternPool.Index, air:
     lazy_fns.clearRetainingCapacity();
     fwd_decl.clearRetainingCapacity();
     code.clearRetainingCapacity();
+    self.scratch_anon_decl_deps.clearRetainingCapacity();
 
-    var function: codegen.Function = .{
-        .value_map = codegen.CValueMap.init(gpa),
-        .air = air,
-        .liveness = liveness,
-        .func_index = func_index,
-        .object = .{
-            .dg = .{
-                .gpa = gpa,
-                .module = module,
-                .error_msg = null,
-                .decl_index = decl_index.toOptional(),
-                .is_naked_fn = decl.ty.fnCallingConvention(module) == .Naked,
-                .fwd_decl = fwd_decl.toManaged(gpa),
-                .ctypes = ctypes.*,
+    {
+        var function: codegen.Function = .{
+            .value_map = codegen.CValueMap.init(gpa),
+            .air = air,
+            .liveness = liveness,
+            .func_index = func_index,
+            .object = .{
+                .dg = .{
+                    .gpa = gpa,
+                    .module = module,
+                    .error_msg = null,
+                    .decl_index = decl_index.toOptional(),
+                    .is_naked_fn = decl.ty.fnCallingConvention(module) == .Naked,
+                    .fwd_decl = fwd_decl.toManaged(gpa),
+                    .ctypes = ctypes.*,
+                    .anon_decl_deps = self.scratch_anon_decl_deps,
+                },
+                .code = code.toManaged(gpa),
+                .indent_writer = undefined, // set later so we can get a pointer to object.code
             },
-            .code = code.toManaged(gpa),
-            .indent_writer = undefined, // set later so we can get a pointer to object.code
-        },
-        .lazy_fns = lazy_fns.*,
-    };
+            .lazy_fns = lazy_fns.*,
+        };
 
-    function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() };
-    defer {
-        fwd_decl.* = function.object.dg.fwd_decl.moveToUnmanaged();
-        code.* = function.object.code.moveToUnmanaged();
-        function.deinit();
-    }
+        function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() };
+        defer {
+            self.scratch_anon_decl_deps = function.object.dg.anon_decl_deps;
+            fwd_decl.* = function.object.dg.fwd_decl.moveToUnmanaged();
+            code.* = function.object.code.moveToUnmanaged();
+            function.deinit();
+        }
 
-    codegen.genFunc(&function) catch |err| switch (err) {
-        error.AnalysisFail => {
-            try module.failed_decls.put(gpa, decl_index, function.object.dg.error_msg.?);
-            return;
-        },
-        else => |e| return e,
-    };
+        codegen.genFunc(&function) catch |err| switch (err) {
+            error.AnalysisFail => {
+                try module.failed_decls.put(gpa, decl_index, function.object.dg.error_msg.?);
+                return;
+            },
+            else => |e| return e,
+        };
 
-    ctypes.* = function.object.dg.ctypes.move();
-    lazy_fns.* = function.lazy_fns.move();
+        ctypes.* = function.object.dg.ctypes.move();
+        lazy_fns.* = function.lazy_fns.move();
 
-    // Free excess allocated memory for this Decl.
-    ctypes.shrinkAndFree(gpa, ctypes.count());
-    lazy_fns.shrinkAndFree(gpa, lazy_fns.count());
+        // Free excess allocated memory for this Decl.
+        ctypes.shrinkAndFree(gpa, ctypes.count());
+        lazy_fns.shrinkAndFree(gpa, lazy_fns.count());
 
-    gop.value_ptr.code = try self.addString(function.object.code.items);
-    gop.value_ptr.fwd_decl = try self.addString(function.object.dg.fwd_decl.items);
+        gop.value_ptr.code = try self.addString(function.object.code.items);
+        gop.value_ptr.fwd_decl = try self.addString(function.object.dg.fwd_decl.items);
+    }
 }
 
 pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !void {
@@ -216,6 +227,7 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi
     ctypes.clearRetainingCapacity(gpa);
     fwd_decl.clearRetainingCapacity();
     code.clearRetainingCapacity();
+    self.scratch_anon_decl_deps.clearRetainingCapacity();
 
     var object: codegen.Object = .{
         .dg = .{
@@ -226,12 +238,14 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi
             .is_naked_fn = false,
             .fwd_decl = fwd_decl.toManaged(gpa),
             .ctypes = ctypes.*,
+            .anon_decl_deps = self.scratch_anon_decl_deps,
         },
         .code = code.toManaged(gpa),
         .indent_writer = undefined, // set later so we can get a pointer to object.code
     };
     object.indent_writer = .{ .underlying_writer = object.code.writer() };
     defer {
+        self.scratch_anon_decl_deps = object.dg.anon_decl_deps;
         object.dg.ctypes.deinit(object.dg.gpa);
         fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged();
         code.* = object.code.moveToUnmanaged();
@@ -512,12 +526,16 @@ fn flushErrDecls(self: *C, ctypes: *codegen.CType.Store) FlushDeclError!void {
             .is_naked_fn = false,
             .fwd_decl = fwd_decl.toManaged(gpa),
             .ctypes = ctypes.*,
+            .anon_decl_deps = .{},
         },
         .code = code.toManaged(gpa),
         .indent_writer = undefined, // set later so we can get a pointer to object.code
     };
     object.indent_writer = .{ .underlying_writer = object.code.writer() };
     defer {
+        // If this assert trips just handle the anon_decl_deps the same as
+        // `updateFunc()` does.
+        assert(object.dg.anon_decl_deps.count() == 0);
         object.dg.ctypes.deinit(gpa);
         fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged();
         code.* = object.code.moveToUnmanaged();
@@ -531,7 +549,11 @@ fn flushErrDecls(self: *C, ctypes: *codegen.CType.Store) FlushDeclError!void {
     ctypes.* = object.dg.ctypes.move();
 }
 
-fn flushLazyFn(self: *C, ctypes: *codegen.CType.Store, lazy_fn: codegen.LazyFnMap.Entry) FlushDeclError!void {
+fn flushLazyFn(
+    self: *C,
+    ctypes: *codegen.CType.Store,
+    lazy_fn: codegen.LazyFnMap.Entry,
+) FlushDeclError!void {
     const gpa = self.base.allocator;
 
     const fwd_decl = &self.lazy_fwd_decl_buf;
@@ -546,12 +568,16 @@ fn flushLazyFn(self: *C, ctypes: *codegen.CType.Store, lazy_fn: codegen.LazyFnMa
             .is_naked_fn = false,
             .fwd_decl = fwd_decl.toManaged(gpa),
             .ctypes = ctypes.*,
+            .anon_decl_deps = .{},
         },
         .code = code.toManaged(gpa),
         .indent_writer = undefined, // set later so we can get a pointer to object.code
     };
     object.indent_writer = .{ .underlying_writer = object.code.writer() };
     defer {
+        // If this assert trips just handle the anon_decl_deps the same as
+        // `updateFunc()` does.
+        assert(object.dg.anon_decl_deps.count() == 0);
         object.dg.ctypes.deinit(gpa);
         fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged();
         code.* = object.code.moveToUnmanaged();
src/Compilation.zig
@@ -3499,6 +3499,7 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v
                         .is_naked_fn = false,
                         .fwd_decl = fwd_decl.toManaged(gpa),
                         .ctypes = .{},
+                        .anon_decl_deps = .{},
                     };
                     defer {
                         dg.ctypes.deinit(gpa);