authorgravatar for ian@ianjohnson.devIan Johnson <ian@ianjohnson.dev> 2023-10-27 22:20:07-04:00
committergravatar for ian@ianjohnson.devIan Johnson <ian@ianjohnson.dev> 2024-01-29 19:02:18-05:00
log2b2c9c5db893c7bc0352d1738c1658a5fcd32d62
tree0d044a1eb182c5705b4998e1a875e260571e0cd2
parentcc25756b3f33578774b557bdf09dcc9e918c1e5a

Introduce type-erased writers to C backend codegen

This reduces generic instantiations of several write functions. Before: ``` @as(type, io.writer.Writer(*array_list.ArrayListAligned(u8,null),error{OutOfMemory},(function 'appendWrite'))) @as(type, io.writer.Writer(*codegen.c.IndentWriter(io.writer.Writer(*array_list.ArrayListAligned(u8,null),error{OutOfMemory},(function 'appendWrite'))),error{OutOfMemory},(function 'write'))) ``` After: ``` @as(type, io.GenericWriter(io.Writer,error{OutOfMemory},(function 'write'))) ```

1 files changed, 53 insertions(+), 11 deletions(-)

src/codegen/c.zig+53-11
...@@ -306,7 +306,7 @@ pub const Function = struct {...@@ -306,7 +306,7 @@ pub const Function = struct {
306 const ty = f.typeOf(ref);306 const ty = f.typeOf(ref);
307307
308 const result: CValue = if (lowersToArray(ty, mod)) result: {308 const result: CValue = if (lowersToArray(ty, mod)) result: {
309 const writer = f.object.code_header.writer();309 const writer = f.object.codeHeaderWriter();
310 const alignment: Alignment = .none;310 const alignment: Alignment = .none;
311 const decl_c_value = try f.allocLocalValue(ty, alignment);311 const decl_c_value = try f.allocLocalValue(ty, alignment);
312 const gpa = f.object.dg.gpa;312 const gpa = f.object.dg.gpa;
...@@ -534,6 +534,10 @@ pub const Object = struct {...@@ -534,6 +534,10 @@ pub const Object = struct {
534 fn writer(o: *Object) IndentWriter(std.ArrayList(u8).Writer).Writer {534 fn writer(o: *Object) IndentWriter(std.ArrayList(u8).Writer).Writer {
535 return o.indent_writer.writer();535 return o.indent_writer.writer();
536 }536 }
537
538 fn codeHeaderWriter(o: *Object) ArrayListWriter {
539 return arrayListWriter(&o.code_header);
540 }
537};541};
538542
539/// This data is available both when outputting .c code and when outputting an .h file.543/// This data is available both when outputting .c code and when outputting an .h file.
...@@ -557,6 +561,10 @@ pub const DeclGen = struct {...@@ -557,6 +561,10 @@ pub const DeclGen = struct {
557 flush,561 flush,
558 };562 };
559563
564 fn fwdDeclWriter(dg: *DeclGen) ArrayListWriter {
565 return arrayListWriter(&dg.fwd_decl);
566 }
567
560 fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {568 fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
561 @setCold(true);569 @setCold(true);
562 const mod = dg.module;570 const mod = dg.module;
...@@ -1997,7 +2005,7 @@ pub const DeclGen = struct {...@@ -1997,7 +2005,7 @@ pub const DeclGen = struct {
1997 fwd_kind: enum { tentative, final },2005 fwd_kind: enum { tentative, final },
1998 ) !void {2006 ) !void {
1999 const decl = dg.module.declPtr(decl_index);2007 const decl = dg.module.declPtr(decl_index);
2000 const fwd = dg.fwd_decl.writer();2008 const fwd = dg.fwdDeclWriter();
2001 const is_global = variable.is_extern or dg.declIsGlobal(.{ .ty = decl.ty, .val = decl.val });2009 const is_global = variable.is_extern or dg.declIsGlobal(.{ .ty = decl.ty, .val = decl.val });
2002 try fwd.writeAll(if (is_global) "zig_extern " else "static ");2010 try fwd.writeAll(if (is_global) "zig_extern " else "static ");
2003 const maybe_exports = dg.module.decl_exports.get(decl_index);2011 const maybe_exports = dg.module.decl_exports.get(decl_index);
...@@ -2683,7 +2691,7 @@ fn genExports(o: *Object) !void {...@@ -2683,7 +2691,7 @@ fn genExports(o: *Object) !void {
2683 };2691 };
2684 const decl = mod.declPtr(decl_index);2692 const decl = mod.declPtr(decl_index);
2685 const tv: TypedValue = .{ .ty = decl.ty, .val = Value.fromInterned((try decl.internValue(mod))) };2693 const tv: TypedValue = .{ .ty = decl.ty, .val = Value.fromInterned((try decl.internValue(mod))) };
2686 const fwd = o.dg.fwd_decl.writer();2694 const fwd = o.dg.fwdDeclWriter();
26872695
2688 const exports = mod.decl_exports.get(decl_index) orelse return;2696 const exports = mod.decl_exports.get(decl_index) orelse return;
2689 if (exports.items.len < 2) return;2697 if (exports.items.len < 2) return;
...@@ -2797,7 +2805,7 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void {...@@ -2797,7 +2805,7 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void {
2797 const fn_cty = try o.dg.typeToCType(fn_decl.ty, .complete);2805 const fn_cty = try o.dg.typeToCType(fn_decl.ty, .complete);
2798 const fn_info = fn_cty.cast(CType.Payload.Function).?.data;2806 const fn_info = fn_cty.cast(CType.Payload.Function).?.data;
27992807
2800 const fwd_decl_writer = o.dg.fwd_decl.writer();2808 const fwd_decl_writer = o.dg.fwdDeclWriter();
2801 try fwd_decl_writer.print("static zig_{s} ", .{@tagName(key)});2809 try fwd_decl_writer.print("static zig_{s} ", .{@tagName(key)});
2802 try o.dg.renderFunctionSignature(2810 try o.dg.renderFunctionSignature(
2803 fwd_decl_writer,2811 fwd_decl_writer,
...@@ -2839,7 +2847,7 @@ pub fn genFunc(f: *Function) !void {...@@ -2839,7 +2847,7 @@ pub fn genFunc(f: *Function) !void {
2839 defer o.code_header.deinit();2847 defer o.code_header.deinit();
28402848
2841 const is_global = o.dg.declIsGlobal(tv);2849 const is_global = o.dg.declIsGlobal(tv);
2842 const fwd_decl_writer = o.dg.fwd_decl.writer();2850 const fwd_decl_writer = o.dg.fwdDeclWriter();
2843 try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static ");2851 try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static ");
28442852
2845 if (mod.decl_exports.get(decl_index)) |exports|2853 if (mod.decl_exports.get(decl_index)) |exports|
...@@ -2894,7 +2902,7 @@ pub fn genFunc(f: *Function) !void {...@@ -2894,7 +2902,7 @@ pub fn genFunc(f: *Function) !void {
2894 };2902 };
2895 free_locals.sort(SortContext{ .keys = free_locals.keys() });2903 free_locals.sort(SortContext{ .keys = free_locals.keys() });
28962904
2897 const w = o.code_header.writer();2905 const w = o.codeHeaderWriter();
2898 for (free_locals.values()) |list| {2906 for (free_locals.values()) |list| {
2899 for (list.keys()) |local_index| {2907 for (list.keys()) |local_index| {
2900 const local = f.locals.items[local_index];2908 const local = f.locals.items[local_index];
...@@ -2922,7 +2930,7 @@ pub fn genDecl(o: *Object) !void {...@@ -2922,7 +2930,7 @@ pub fn genDecl(o: *Object) !void {
29222930
2923 if (!tv.ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) return;2931 if (!tv.ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) return;
2924 if (tv.val.getExternFunc(mod)) |_| {2932 if (tv.val.getExternFunc(mod)) |_| {
2925 const fwd_decl_writer = o.dg.fwd_decl.writer();2933 const fwd_decl_writer = o.dg.fwdDeclWriter();
2926 try fwd_decl_writer.writeAll("zig_extern ");2934 try fwd_decl_writer.writeAll("zig_extern ");
2927 try o.dg.renderFunctionSignature(fwd_decl_writer, decl_index, .forward, .{ .export_index = 0 });2935 try o.dg.renderFunctionSignature(fwd_decl_writer, decl_index, .forward, .{ .export_index = 0 });
2928 try fwd_decl_writer.writeAll(";\n");2936 try fwd_decl_writer.writeAll(";\n");
...@@ -2963,7 +2971,7 @@ pub fn genDeclValue(...@@ -2963,7 +2971,7 @@ pub fn genDeclValue(
2963 link_section: InternPool.OptionalNullTerminatedString,2971 link_section: InternPool.OptionalNullTerminatedString,
2964) !void {2972) !void {
2965 const mod = o.dg.module;2973 const mod = o.dg.module;
2966 const fwd_decl_writer = o.dg.fwd_decl.writer();2974 const fwd_decl_writer = o.dg.fwdDeclWriter();
29672975
2968 try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static ");2976 try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static ");
2969 try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, Const, alignment, .complete);2977 try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, Const, alignment, .complete);
...@@ -3007,7 +3015,7 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void {...@@ -3007,7 +3015,7 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void {
3007 .ty = decl.ty,3015 .ty = decl.ty,
3008 .val = decl.val,3016 .val = decl.val,
3009 };3017 };
3010 const writer = dg.fwd_decl.writer();3018 const writer = dg.fwdDeclWriter();
30113019
3012 switch (tv.ty.zigTypeTag(mod)) {3020 switch (tv.ty.zigTypeTag(mod)) {
3013 .Fn => if (dg.declIsGlobal(tv)) {3021 .Fn => if (dg.declIsGlobal(tv)) {
...@@ -7531,11 +7539,25 @@ fn toAtomicRmwSuffix(order: std.builtin.AtomicRmwOp) []const u8 {...@@ -7531,11 +7539,25 @@ fn toAtomicRmwSuffix(order: std.builtin.AtomicRmwOp) []const u8 {
7531 };7539 };
7532}7540}
75337541
7542const ArrayListWriter = ErrorOnlyGenericWriter(std.ArrayList(u8).Writer.Error);
7543
7544fn arrayListWriter(list: *std.ArrayList(u8)) ArrayListWriter {
7545 return .{ .context = .{
7546 .context = list,
7547 .writeFn = struct {
7548 fn write(context: *const anyopaque, bytes: []const u8) anyerror!usize {
7549 const l: *std.ArrayList(u8) = @alignCast(@constCast(@ptrCast(context)));
7550 return l.writer().write(bytes);
7551 }
7552 }.write,
7553 } };
7554}
7555
7534fn IndentWriter(comptime UnderlyingWriter: type) type {7556fn IndentWriter(comptime UnderlyingWriter: type) type {
7535 return struct {7557 return struct {
7536 const Self = @This();7558 const Self = @This();
7537 pub const Error = UnderlyingWriter.Error;7559 pub const Error = UnderlyingWriter.Error;
7538 pub const Writer = std.io.Writer(*Self, Error, write);7560 pub const Writer = ErrorOnlyGenericWriter(Error);
75397561
7540 pub const indent_delta = 1;7562 pub const indent_delta = 1;
75417563
...@@ -7544,7 +7566,10 @@ fn IndentWriter(comptime UnderlyingWriter: type) type {...@@ -7544,7 +7566,10 @@ fn IndentWriter(comptime UnderlyingWriter: type) type {
7544 current_line_empty: bool = true,7566 current_line_empty: bool = true,
75457567
7546 pub fn writer(self: *Self) Writer {7568 pub fn writer(self: *Self) Writer {
7547 return .{ .context = self };7569 return .{ .context = .{
7570 .context = self,
7571 .writeFn = writeAny,
7572 } };
7548 }7573 }
75497574
7550 pub fn write(self: *Self, bytes: []const u8) Error!usize {7575 pub fn write(self: *Self, bytes: []const u8) Error!usize {
...@@ -7559,6 +7584,11 @@ fn IndentWriter(comptime UnderlyingWriter: type) type {...@@ -7559,6 +7584,11 @@ fn IndentWriter(comptime UnderlyingWriter: type) type {
7559 return self.writeNoIndent(bytes);7584 return self.writeNoIndent(bytes);
7560 }7585 }
75617586
7587 fn writeAny(context: *const anyopaque, bytes: []const u8) anyerror!usize {
7588 const self: *Self = @alignCast(@constCast(@ptrCast(context)));
7589 return self.write(bytes);
7590 }
7591
7562 pub fn insertNewline(self: *Self) Error!void {7592 pub fn insertNewline(self: *Self) Error!void {
7563 _ = try self.writeNoIndent("\n");7593 _ = try self.writeNoIndent("\n");
7564 }7594 }
...@@ -7584,6 +7614,18 @@ fn IndentWriter(comptime UnderlyingWriter: type) type {...@@ -7584,6 +7614,18 @@ fn IndentWriter(comptime UnderlyingWriter: type) type {
7584 };7614 };
7585}7615}
75867616
7617/// A wrapper around `std.io.AnyWriter` that maintains a generic error set while
7618/// erasing the rest of the implementation. This is intended to avoid duplicate
7619/// generic instantiations for writer types which share the same error set, while
7620/// maintaining ease of error handling.
7621fn ErrorOnlyGenericWriter(comptime Error: type) type {
7622 return std.io.GenericWriter(std.io.AnyWriter, Error, struct {
7623 fn write(context: std.io.AnyWriter, bytes: []const u8) Error!usize {
7624 return @errorCast(context.write(bytes));
7625 }
7626 }.write);
7627}
7628
7587fn toCIntBits(zig_bits: u32) ?u32 {7629fn toCIntBits(zig_bits: u32) ?u32 {
7588 for (&[_]u8{ 8, 16, 32, 64, 128 }) |c_bits| {7630 for (&[_]u8{ 8, 16, 32, 64, 128 }) |c_bits| {
7589 if (zig_bits <= c_bits) {7631 if (zig_bits <= c_bits) {