Commit 9d069d98e3
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);