diff --git a/src/codegen/c.zig b/src/codegen/c.zig index a8a8151060..c8681ca525 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -56,6 +56,7 @@ pub const Mir = struct { /// less than the natural alignment. uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment), // These remaining fields are essentially just an owned version of `link.C.AvBlock`. + code_header: []u8, code: []u8, fwd_decl: []u8, ctype_pool: CType.Pool, @@ -63,6 +64,7 @@ pub const Mir = struct { pub fn deinit(mir: *Mir, gpa: Allocator) void { mir.uavs.deinit(gpa); + gpa.free(mir.code_header); gpa.free(mir.code); gpa.free(mir.fwd_decl); mir.ctype_pool.deinit(gpa); @@ -345,23 +347,23 @@ fn isReservedIdent(ident: []const u8) bool { fn formatIdent( ident: []const u8, - writer: *Writer, + w: *Writer, comptime fmt_str: []const u8, ) Writer.Error!void { const solo = fmt_str.len != 0 and fmt_str[0] == ' '; // space means solo; not part of a bigger ident. if (solo and isReservedIdent(ident)) { - try writer.writeAll("zig_e_"); + try w.writeAll("zig_e_"); } for (ident, 0..) |c, i| { switch (c) { - 'a'...'z', 'A'...'Z', '_' => try writer.writeByte(c), - '.' => try writer.writeByte('_'), + 'a'...'z', 'A'...'Z', '_' => try w.writeByte(c), + '.' => try w.writeByte('_'), '0'...'9' => if (i == 0) { - try writer.print("_{x:2}", .{c}); + try w.print("_{x:2}", .{c}); } else { - try writer.writeByte(c); + try w.writeByte(c); }, - else => try writer.print("_{x:2}", .{c}), + else => try w.print("_{x:2}", .{c}), } } } @@ -375,13 +377,13 @@ const CTypePoolStringFormatData = struct { }; fn formatCTypePoolString( data: CTypePoolStringFormatData, - writer: *Writer, + w: *Writer, comptime fmt_str: []const u8, ) Writer.Error!void { if (data.ctype_pool_string.toSlice(data.ctype_pool)) |slice| - try formatIdent(slice, writer, fmt_str) + try formatIdent(slice, w, fmt_str) else - try writer.print("{f}", .{data.ctype_pool_string.fmt(data.ctype_pool)}); + try w.print("{f}", .{data.ctype_pool_string.fmt(data.ctype_pool)}); } pub fn fmtCTypePoolString( ctype_pool_string: CType.Pool.String, @@ -441,18 +443,18 @@ pub const Function = struct { const ty = f.typeOf(ref); const result: CValue = if (lowersToArray(ty, pt)) result: { - const writer = &f.object.code_header.buffered_writer; + const ch = &f.object.code_header.buffered_writer; const decl_c_value = try f.allocLocalValue(.{ .ctype = try f.ctypeFromType(ty, .complete), .alignas = CType.AlignAs.fromAbiAlignment(ty.abiAlignment(pt.zcu)), }); const gpa = f.object.dg.gpa; try f.allocs.put(gpa, decl_c_value.new_local, false); - try writer.writeAll("static "); - try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, Const, .none, .complete); - try writer.writeAll(" = "); - try f.object.dg.renderValue(writer, val, .StaticInitializer); - try writer.writeAll(";\n "); + try ch.writeAll("static "); + try f.object.dg.renderTypeAndName(ch, ty, decl_c_value, Const, .none, .complete); + try ch.writeAll(" = "); + try f.object.dg.renderValue(ch, val, .StaticInitializer); + try ch.writeAll(";\n "); break :result .{ .local = decl_c_value.new_local }; } else .{ .constant = val }; @@ -979,7 +981,7 @@ pub const DeclGen = struct { fn renderValue( dg: *DeclGen, - writer: *Writer, + w: *Writer, val: Value, location: ValueRenderLocation, ) Error!void { @@ -995,7 +997,7 @@ pub const DeclGen = struct { }; const ty = val.typeOf(zcu); - if (val.isUndefDeep(zcu)) return dg.renderUndefValue(writer, ty, location); + if (val.isUndefDeep(zcu)) return dg.renderUndefValue(w, ty, location); const ctype = try dg.ctypeFromType(ty, location.toCTypeKind()); switch (ip.indexToKey(val.toIntern())) { // types, not values @@ -1028,8 +1030,8 @@ pub const DeclGen = struct { .empty_tuple => unreachable, .@"unreachable" => unreachable, - .false => try writer.writeAll("false"), - .true => try writer.writeAll("true"), + .false => try w.writeAll("false"), + .true => try w.writeAll("true"), }, .variable, .@"extern", @@ -1038,45 +1040,45 @@ pub const DeclGen = struct { .empty_enum_value, => unreachable, // non-runtime values .int => |int| switch (int.storage) { - .u64, .i64, .big_int => try writer.print("{f}", .{try dg.fmtIntLiteral(val, location)}), + .u64, .i64, .big_int => try w.print("{f}", .{try dg.fmtIntLiteral(val, location)}), .lazy_align, .lazy_size => { - try writer.writeAll("(("); - try dg.renderCType(writer, ctype); - try writer.print("){fx})", .{try dg.fmtIntLiteral( + try w.writeAll("(("); + try dg.renderCType(w, ctype); + try w.print("){fx})", .{try dg.fmtIntLiteral( try pt.intValue(.usize, val.toUnsignedInt(zcu)), .Other, )}); }, }, - .err => |err| try dg.renderErrorName(writer, err.name), + .err => |err| try dg.renderErrorName(w, err.name), .error_union => |error_union| switch (ctype.info(ctype_pool)) { .basic => switch (error_union.val) { - .err_name => |err_name| try dg.renderErrorName(writer, err_name), - .payload => try writer.writeByte('0'), + .err_name => |err_name| try dg.renderErrorName(w, err_name), + .payload => try w.writeByte('0'), }, .pointer, .aligned, .array, .vector, .fwd_decl, .function => unreachable, .aggregate => |aggregate| { if (!location.isInitializer()) { - try writer.writeByte('('); - try dg.renderCType(writer, ctype); - try writer.writeByte(')'); + try w.writeByte('('); + try dg.renderCType(w, ctype); + try w.writeByte(')'); } - try writer.writeByte('{'); + try w.writeByte('{'); for (0..aggregate.fields.len) |field_index| { - if (field_index > 0) try writer.writeByte(','); + if (field_index > 0) try w.writeByte(','); switch (aggregate.fields.at(field_index, ctype_pool).name.index) { .@"error" => switch (error_union.val) { - .err_name => |err_name| try dg.renderErrorName(writer, err_name), - .payload => try writer.writeByte('0'), + .err_name => |err_name| try dg.renderErrorName(w, err_name), + .payload => try w.writeByte('0'), }, .payload => switch (error_union.val) { .err_name => try dg.renderUndefValue( - writer, + w, ty.errorUnionPayload(zcu), initializer_type, ), .payload => |payload| try dg.renderValue( - writer, + w, Value.fromInterned(payload), initializer_type, ), @@ -1084,10 +1086,10 @@ pub const DeclGen = struct { else => unreachable, } } - try writer.writeByte('}'); + try w.writeByte('}'); }, }, - .enum_tag => |enum_tag| try dg.renderValue(writer, Value.fromInterned(enum_tag.int), location), + .enum_tag => |enum_tag| try dg.renderValue(w, Value.fromInterned(enum_tag.int), location), .float => { const bits = ty.floatBits(target); const f128_val = val.toFloat(f128, zcu); @@ -1114,18 +1116,18 @@ pub const DeclGen = struct { var empty = true; if (std.math.isFinite(f128_val)) { - try writer.writeAll("zig_make_"); - try dg.renderTypeForBuiltinFnName(writer, ty); - try writer.writeByte('('); + try w.writeAll("zig_make_"); + try dg.renderTypeForBuiltinFnName(w, ty); + try w.writeByte('('); switch (bits) { - 16 => try writer.print("{x}", .{val.toFloat(f16, zcu)}), - 32 => try writer.print("{x}", .{val.toFloat(f32, zcu)}), - 64 => try writer.print("{x}", .{val.toFloat(f64, zcu)}), - 80 => try writer.print("{x}", .{val.toFloat(f80, zcu)}), - 128 => try writer.print("{x}", .{f128_val}), + 16 => try w.print("{x}", .{val.toFloat(f16, zcu)}), + 32 => try w.print("{x}", .{val.toFloat(f32, zcu)}), + 64 => try w.print("{x}", .{val.toFloat(f64, zcu)}), + 80 => try w.print("{x}", .{val.toFloat(f80, zcu)}), + 128 => try w.print("{x}", .{f128_val}), else => unreachable, } - try writer.writeAll(", "); + try w.writeAll(", "); empty = false; } else { // isSignalNan is equivalent to isNan currently, and MSVC doesn't have nans, so prefer nan @@ -1149,45 +1151,45 @@ pub const DeclGen = struct { // return dg.fail("Only quiet nans are supported in global variable initializers", .{}); } - try writer.writeAll("zig_"); - try writer.writeAll(if (location == .StaticInitializer) "init" else "make"); - try writer.writeAll("_special_"); - try dg.renderTypeForBuiltinFnName(writer, ty); - try writer.writeByte('('); - if (std.math.signbit(f128_val)) try writer.writeByte('-'); - try writer.writeAll(", "); - try writer.writeAll(operation); - try writer.writeAll(", "); + try w.writeAll("zig_"); + try w.writeAll(if (location == .StaticInitializer) "init" else "make"); + try w.writeAll("_special_"); + try dg.renderTypeForBuiltinFnName(w, ty); + try w.writeByte('('); + if (std.math.signbit(f128_val)) try w.writeByte('-'); + try w.writeAll(", "); + try w.writeAll(operation); + try w.writeAll(", "); if (std.math.isNan(f128_val)) switch (bits) { // We only actually need to pass the significand, but it will get // properly masked anyway, so just pass the whole value. - 16 => try writer.print("\"0x{x}\"", .{@as(u16, @bitCast(val.toFloat(f16, zcu)))}), - 32 => try writer.print("\"0x{x}\"", .{@as(u32, @bitCast(val.toFloat(f32, zcu)))}), - 64 => try writer.print("\"0x{x}\"", .{@as(u64, @bitCast(val.toFloat(f64, zcu)))}), - 80 => try writer.print("\"0x{x}\"", .{@as(u80, @bitCast(val.toFloat(f80, zcu)))}), - 128 => try writer.print("\"0x{x}\"", .{@as(u128, @bitCast(f128_val))}), + 16 => try w.print("\"0x{x}\"", .{@as(u16, @bitCast(val.toFloat(f16, zcu)))}), + 32 => try w.print("\"0x{x}\"", .{@as(u32, @bitCast(val.toFloat(f32, zcu)))}), + 64 => try w.print("\"0x{x}\"", .{@as(u64, @bitCast(val.toFloat(f64, zcu)))}), + 80 => try w.print("\"0x{x}\"", .{@as(u80, @bitCast(val.toFloat(f80, zcu)))}), + 128 => try w.print("\"0x{x}\"", .{@as(u128, @bitCast(f128_val))}), else => unreachable, }; - try writer.writeAll(", "); + try w.writeAll(", "); empty = false; } - try writer.print("{fx}", .{try dg.fmtIntLiteral( + try w.print("{fx}", .{try dg.fmtIntLiteral( try pt.intValue_big(repr_ty, repr_val_big.toConst()), location, )}); - if (!empty) try writer.writeByte(')'); + if (!empty) try w.writeByte(')'); }, .slice => |slice| { const aggregate = ctype.info(ctype_pool).aggregate; if (!location.isInitializer()) { - try writer.writeByte('('); - try dg.renderCType(writer, ctype); - try writer.writeByte(')'); + try w.writeByte('('); + try dg.renderCType(w, ctype); + try w.writeByte(')'); } - try writer.writeByte('{'); + try w.writeByte('{'); for (0..aggregate.fields.len) |field_index| { - if (field_index > 0) try writer.writeByte(','); - try dg.renderValue(writer, Value.fromInterned( + if (field_index > 0) try w.writeByte(','); + try dg.renderValue(w, Value.fromInterned( switch (aggregate.fields.at(field_index, ctype_pool).name.index) { .ptr => slice.ptr, .len => slice.len, @@ -1195,33 +1197,33 @@ pub const DeclGen = struct { }, ), initializer_type); } - try writer.writeByte('}'); + try w.writeByte('}'); }, .ptr => { var arena = std.heap.ArenaAllocator.init(zcu.gpa); defer arena.deinit(); const derivation = try val.pointerDerivation(arena.allocator(), pt); - try dg.renderPointer(writer, derivation, location); + try dg.renderPointer(w, derivation, location); }, .opt => |opt| switch (ctype.info(ctype_pool)) { - .basic => if (ctype.isBool()) try writer.writeAll(switch (opt.val) { + .basic => if (ctype.isBool()) try w.writeAll(switch (opt.val) { .none => "true", else => "false", }) else switch (opt.val) { - .none => try writer.writeByte('0'), + .none => try w.writeByte('0'), else => |payload| switch (ip.indexToKey(payload)) { .undef => |err_ty| try dg.renderUndefValue( - writer, + w, .fromInterned(err_ty), location, ), - .err => |err| try dg.renderErrorName(writer, err.name), + .err => |err| try dg.renderErrorName(w, err.name), else => unreachable, }, }, .pointer => switch (opt.val) { - .none => try writer.writeAll("NULL"), - else => |payload| try dg.renderValue(writer, Value.fromInterned(payload), location), + .none => try w.writeAll("NULL"), + else => |payload| try dg.renderValue(w, Value.fromInterned(payload), location), }, .aligned, .array, .vector, .fwd_decl, .function => unreachable, .aggregate => |aggregate| { @@ -1230,7 +1232,7 @@ pub const DeclGen = struct { else => |payload| switch (aggregate.fields.at(0, ctype_pool).name.index) { .is_null, .payload => {}, .ptr, .len => return dg.renderValue( - writer, + w, Value.fromInterned(payload), location, ), @@ -1238,48 +1240,48 @@ pub const DeclGen = struct { }, } if (!location.isInitializer()) { - try writer.writeByte('('); - try dg.renderCType(writer, ctype); - try writer.writeByte(')'); + try w.writeByte('('); + try dg.renderCType(w, ctype); + try w.writeByte(')'); } - try writer.writeByte('{'); + try w.writeByte('{'); for (0..aggregate.fields.len) |field_index| { - if (field_index > 0) try writer.writeByte(','); + if (field_index > 0) try w.writeByte(','); switch (aggregate.fields.at(field_index, ctype_pool).name.index) { - .is_null => try writer.writeAll(switch (opt.val) { + .is_null => try w.writeAll(switch (opt.val) { .none => "true", else => "false", }), .payload => switch (opt.val) { .none => try dg.renderUndefValue( - writer, + w, ty.optionalChild(zcu), initializer_type, ), else => |payload| try dg.renderValue( - writer, + w, Value.fromInterned(payload), initializer_type, ), }, - .ptr => try writer.writeAll("NULL"), - .len => try dg.renderUndefValue(writer, .usize, initializer_type), + .ptr => try w.writeAll("NULL"), + .len => try dg.renderUndefValue(w, .usize, initializer_type), else => unreachable, } } - try writer.writeByte('}'); + try w.writeByte('}'); }, }, .aggregate => switch (ip.indexToKey(ty.toIntern())) { .array_type, .vector_type => { if (location == .FunctionArgument) { - try writer.writeByte('('); - try dg.renderCType(writer, ctype); - try writer.writeByte(')'); + try w.writeByte('('); + try dg.renderCType(w, ctype); + try w.writeByte(')'); } const ai = ty.arrayInfo(zcu); if (ai.elem_type.eql(.u8, zcu)) { - var literal: StringLiteral = .init(writer, ty.arrayLenIncludingSentinel(zcu)); + var literal: StringLiteral = .init(w, ty.arrayLenIncludingSentinel(zcu)); try literal.start(); var index: usize = 0; while (index < ai.len) : (index += 1) { @@ -1296,28 +1298,28 @@ pub const DeclGen = struct { } try literal.end(); } else { - try writer.writeByte('{'); + try w.writeByte('{'); var index: usize = 0; while (index < ai.len) : (index += 1) { - if (index != 0) try writer.writeByte(','); + if (index != 0) try w.writeByte(','); const elem_val = try val.elemValue(pt, index); - try dg.renderValue(writer, elem_val, initializer_type); + try dg.renderValue(w, elem_val, initializer_type); } if (ai.sentinel) |s| { - if (index != 0) try writer.writeByte(','); - try dg.renderValue(writer, s, initializer_type); + if (index != 0) try w.writeByte(','); + try dg.renderValue(w, s, initializer_type); } - try writer.writeByte('}'); + try w.writeByte('}'); } }, .tuple_type => |tuple| { if (!location.isInitializer()) { - try writer.writeByte('('); - try dg.renderCType(writer, ctype); - try writer.writeByte(')'); + try w.writeByte('('); + try dg.renderCType(w, ctype); + try w.writeByte(')'); } - try writer.writeByte('{'); + try w.writeByte('{'); var empty = true; for (0..tuple.types.len) |field_index| { const comptime_val = tuple.values.get(ip)[field_index]; @@ -1325,7 +1327,7 @@ pub const DeclGen = struct { const field_ty: Type = .fromInterned(tuple.types.get(ip)[field_index]); if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; - if (!empty) try writer.writeByte(','); + if (!empty) try w.writeByte(','); const field_val = Value.fromInterned( switch (ip.indexToKey(val.toIntern()).aggregate.storage) { @@ -1337,30 +1339,30 @@ pub const DeclGen = struct { .repeated_elem => |elem| elem, }, ); - try dg.renderValue(writer, field_val, initializer_type); + try dg.renderValue(w, field_val, initializer_type); empty = false; } - try writer.writeByte('}'); + try w.writeByte('}'); }, .struct_type => { const loaded_struct = ip.loadStructType(ty.toIntern()); switch (loaded_struct.layout) { .auto, .@"extern" => { if (!location.isInitializer()) { - try writer.writeByte('('); - try dg.renderCType(writer, ctype); - try writer.writeByte(')'); + try w.writeByte('('); + try dg.renderCType(w, ctype); + try w.writeByte(')'); } - try writer.writeByte('{'); + try w.writeByte('{'); var field_it = loaded_struct.iterateRuntimeOrder(ip); var need_comma = false; while (field_it.next()) |field_index| { const field_ty: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; - if (need_comma) try writer.writeByte(','); + if (need_comma) try w.writeByte(','); need_comma = true; const field_val = switch (ip.indexToKey(val.toIntern()).aggregate.storage) { .bytes => |bytes| try pt.intern(.{ .int = .{ @@ -1370,9 +1372,9 @@ pub const DeclGen = struct { .elems => |elems| elems[field_index], .repeated_elem => |elem| elem, }; - try dg.renderValue(writer, Value.fromInterned(field_val), initializer_type); + try dg.renderValue(w, Value.fromInterned(field_val), initializer_type); } - try writer.writeByte('}'); + try w.writeByte('}'); }, .@"packed" => { const int_info = ty.intInfo(zcu); @@ -1390,16 +1392,16 @@ pub const DeclGen = struct { } if (eff_num_fields == 0) { - try writer.writeByte('('); - try dg.renderUndefValue(writer, ty, location); - try writer.writeByte(')'); + try w.writeByte('('); + try dg.renderUndefValue(w, ty, location); + try w.writeByte(')'); } else if (ty.bitSize(zcu) > 64) { // zig_or_u128(zig_or_u128(zig_shl_u128(a, a_off), zig_shl_u128(b, b_off)), zig_shl_u128(c, c_off)) var num_or = eff_num_fields - 1; while (num_or > 0) : (num_or -= 1) { - try writer.writeAll("zig_or_"); - try dg.renderTypeForBuiltinFnName(writer, ty); - try writer.writeByte('('); + try w.writeAll("zig_or_"); + try dg.renderTypeForBuiltinFnName(w, ty); + try w.writeByte('('); } var eff_index: usize = 0; @@ -1418,36 +1420,36 @@ pub const DeclGen = struct { }; const cast_context = IntCastContext{ .value = .{ .value = Value.fromInterned(field_val) } }; if (bit_offset != 0) { - try writer.writeAll("zig_shl_"); - try dg.renderTypeForBuiltinFnName(writer, ty); - try writer.writeByte('('); - try dg.renderIntCast(writer, ty, cast_context, field_ty, .FunctionArgument); - try writer.writeAll(", "); - try dg.renderValue(writer, try pt.intValue(bit_offset_ty, bit_offset), .FunctionArgument); - try writer.writeByte(')'); + try w.writeAll("zig_shl_"); + try dg.renderTypeForBuiltinFnName(w, ty); + try w.writeByte('('); + try dg.renderIntCast(w, ty, cast_context, field_ty, .FunctionArgument); + try w.writeAll(", "); + try dg.renderValue(w, try pt.intValue(bit_offset_ty, bit_offset), .FunctionArgument); + try w.writeByte(')'); } else { - try dg.renderIntCast(writer, ty, cast_context, field_ty, .FunctionArgument); + try dg.renderIntCast(w, ty, cast_context, field_ty, .FunctionArgument); } - if (needs_closing_paren) try writer.writeByte(')'); - if (eff_index != eff_num_fields - 1) try writer.writeAll(", "); + if (needs_closing_paren) try w.writeByte(')'); + if (eff_index != eff_num_fields - 1) try w.writeAll(", "); bit_offset += field_ty.bitSize(zcu); needs_closing_paren = true; eff_index += 1; } } else { - try writer.writeByte('('); + try w.writeByte('('); // a << a_off | b << b_off | c << c_off var empty = true; for (0..loaded_struct.field_types.len) |field_index| { const field_ty: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; - if (!empty) try writer.writeAll(" | "); - try writer.writeByte('('); - try dg.renderCType(writer, ctype); - try writer.writeByte(')'); + if (!empty) try w.writeAll(" | "); + try w.writeByte('('); + try dg.renderCType(w, ctype); + try w.writeByte(')'); const field_val = switch (ip.indexToKey(val.toIntern()).aggregate.storage) { .bytes => |bytes| try pt.intern(.{ .int = .{ @@ -1464,24 +1466,24 @@ pub const DeclGen = struct { .{ .signedness = .unsigned, .bits = undefined }; switch (field_int_info.signedness) { .signed => { - try writer.writeByte('('); - try dg.renderValue(writer, Value.fromInterned(field_val), .Other); - try writer.writeAll(" & "); + try w.writeByte('('); + try dg.renderValue(w, Value.fromInterned(field_val), .Other); + try w.writeAll(" & "); const field_uint_ty = try pt.intType(.unsigned, field_int_info.bits); - try dg.renderValue(writer, try field_uint_ty.maxIntScalar(pt, field_uint_ty), .Other); - try writer.writeByte(')'); + try dg.renderValue(w, try field_uint_ty.maxIntScalar(pt, field_uint_ty), .Other); + try w.writeByte(')'); }, - .unsigned => try dg.renderValue(writer, Value.fromInterned(field_val), .Other), + .unsigned => try dg.renderValue(w, Value.fromInterned(field_val), .Other), } if (bit_offset != 0) { - try writer.writeAll(" << "); - try dg.renderValue(writer, try pt.intValue(bit_offset_ty, bit_offset), .FunctionArgument); + try w.writeAll(" << "); + try dg.renderValue(w, try pt.intValue(bit_offset_ty, bit_offset), .FunctionArgument); } bit_offset += field_ty.bitSize(zcu); empty = false; } - try writer.writeByte(')'); + try w.writeByte(')'); } }, } @@ -1495,11 +1497,11 @@ pub const DeclGen = struct { switch (loaded_union.flagsUnordered(ip).layout) { .@"packed" => { if (!location.isInitializer()) { - try writer.writeByte('('); - try dg.renderType(writer, backing_ty); - try writer.writeByte(')'); + try w.writeByte('('); + try dg.renderType(w, backing_ty); + try w.writeByte(')'); } - try dg.renderValue(writer, Value.fromInterned(un.val), location); + try dg.renderValue(w, Value.fromInterned(un.val), location); }, .@"extern" => { if (location == .StaticInitializer) { @@ -1507,21 +1509,21 @@ pub const DeclGen = struct { } const ptr_ty = try pt.singleConstPtrType(ty); - try writer.writeAll("*(("); - try dg.renderType(writer, ptr_ty); - try writer.writeAll(")("); - try dg.renderType(writer, backing_ty); - try writer.writeAll("){"); - try dg.renderValue(writer, Value.fromInterned(un.val), location); - try writer.writeAll("})"); + try w.writeAll("*(("); + try dg.renderType(w, ptr_ty); + try w.writeAll(")("); + try dg.renderType(w, backing_ty); + try w.writeAll("){"); + try dg.renderValue(w, Value.fromInterned(un.val), location); + try w.writeAll("})"); }, else => unreachable, } } else { if (!location.isInitializer()) { - try writer.writeByte('('); - try dg.renderCType(writer, ctype); - try writer.writeByte(')'); + try w.writeByte('('); + try dg.renderCType(w, ctype); + try w.writeByte(')'); } const field_index = zcu.unionTagFieldIndex(loaded_union, Value.fromInterned(un.tag)).?; @@ -1530,57 +1532,57 @@ pub const DeclGen = struct { if (loaded_union.flagsUnordered(ip).layout == .@"packed") { if (field_ty.hasRuntimeBits(zcu)) { if (field_ty.isPtrAtRuntime(zcu)) { - try writer.writeByte('('); - try dg.renderCType(writer, ctype); - try writer.writeByte(')'); + try w.writeByte('('); + try dg.renderCType(w, ctype); + try w.writeByte(')'); } else if (field_ty.zigTypeTag(zcu) == .float) { - try writer.writeByte('('); - try dg.renderCType(writer, ctype); - try writer.writeByte(')'); + try w.writeByte('('); + try dg.renderCType(w, ctype); + try w.writeByte(')'); } - try dg.renderValue(writer, Value.fromInterned(un.val), location); - } else try writer.writeByte('0'); + try dg.renderValue(w, Value.fromInterned(un.val), location); + } else try w.writeByte('0'); return; } const has_tag = loaded_union.hasTag(ip); - if (has_tag) try writer.writeByte('{'); + if (has_tag) try w.writeByte('{'); const aggregate = ctype.info(ctype_pool).aggregate; for (0..if (has_tag) aggregate.fields.len else 1) |outer_field_index| { - if (outer_field_index > 0) try writer.writeByte(','); + if (outer_field_index > 0) try w.writeByte(','); switch (if (has_tag) aggregate.fields.at(outer_field_index, ctype_pool).name.index else .payload) { .tag => try dg.renderValue( - writer, + w, Value.fromInterned(un.tag), initializer_type, ), .payload => { - try writer.writeByte('{'); + try w.writeByte('{'); if (field_ty.hasRuntimeBits(zcu)) { - try writer.print(" .{f } = ", .{fmtIdent(field_name.toSlice(ip))}); + try w.print(" .{f } = ", .{fmtIdent(field_name.toSlice(ip))}); try dg.renderValue( - writer, + w, Value.fromInterned(un.val), initializer_type, ); - try writer.writeByte(' '); + try w.writeByte(' '); } else for (0..loaded_union.field_types.len) |inner_field_index| { const inner_field_ty: Type = .fromInterned( loaded_union.field_types.get(ip)[inner_field_index], ); if (!inner_field_ty.hasRuntimeBits(zcu)) continue; - try dg.renderUndefValue(writer, inner_field_ty, initializer_type); + try dg.renderUndefValue(w, inner_field_ty, initializer_type); break; } - try writer.writeByte('}'); + try w.writeByte('}'); }, else => unreachable, } } - if (has_tag) try writer.writeByte('}'); + if (has_tag) try w.writeByte('}'); } }, } @@ -2999,18 +3001,20 @@ pub fn generate( .pass = .{ .nav = func.owner_nav }, .is_naked_fn = Type.fromInterned(func.ty).fnCallingConvention(zcu) == .naked, .expected_block = null, - .fwd_decl = .init(gpa), + .fwd_decl = undefined, .ctype_pool = .empty, .scratch = .empty, .uavs = .empty, }, - .code = .init(gpa), - .indent_writer = undefined, // set later so we can get a pointer to object.code + .code_header = undefined, + .code = undefined, + .indent_counter = 0, }, .lazy_fns = .empty, }; defer { - function.object.code.deinit(); + function.object.code_header.init(gpa); + function.object.code.init(gpa); function.object.dg.fwd_decl.deinit(); function.object.dg.ctype_pool.deinit(gpa); function.object.dg.scratch.deinit(gpa); @@ -3018,7 +3022,9 @@ pub fn generate( function.deinit(); } try function.object.dg.ctype_pool.init(gpa); - function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() }; + function.object.dg.fwd_decl.init(gpa); + function.object.code_header.init(gpa); + function.object.code.init(gpa); genFunc(&function) catch |err| switch (err) { error.AnalysisFail => return zcu.codegenFailMsg(func.owner_nav, function.object.dg.error_msg.?), @@ -3034,6 +3040,7 @@ pub fn generate( }; errdefer mir.deinit(gpa); mir.uavs = function.object.dg.uavs.move(); + mir.code_header = try function.object.code_header.toOwnedSlice(); mir.code = try function.object.code.toOwnedSlice(); mir.fwd_decl = try function.object.dg.fwd_decl.toOwnedSlice(); mir.ctype_pool = function.object.dg.ctype_pool.move(); @@ -3041,7 +3048,7 @@ pub fn generate( return mir; } -fn genFunc(f: *Function) !void { +pub fn genFunc(f: *Function) Error!void { const tracy = trace(@src()); defer tracy.end(); @@ -4033,7 +4040,7 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !void { try f.writeCValueDeref(w, ret_val) else try f.writeCValue(w, ret_val, .Other); - try w.writeAll(";\n"); + try w.write(";\n"); if (is_array) { try freeLocal(f, inst, ret_val.new_local, null); } @@ -4347,7 +4354,8 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: try f.writeCValue(w, rhs, .FunctionArgument); if (f.typeOf(bin_op.rhs).isVector(zcu)) try v.elem(f, w); try f.object.dg.renderBuiltinInfo(w, scalar_ty, info); - try w.writeAll(");\n"); + try w.writeAll(");"); + try f.object.newline(); try v.end(f, inst, w); return local; @@ -4884,8 +4892,9 @@ fn airDbgInlineBlock(f: *Function, inst: Air.Inst.Index) !CValue { const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = f.air.extraData(Air.DbgInlineBlock, ty_pl.payload); const owner_nav = ip.getNav(zcu.funcInfo(extra.data.func).owner_nav); - const writer = f.object.writer(); - try writer.print("/* inline:{f} */\n", .{owner_nav.fqn.fmt(&zcu.intern_pool)}); + const w = &f.object.code.buffered_writer; + try w.print("/* inline:{f} */", .{owner_nav.fqn.fmt(&zcu.intern_pool)}); + try f.object.newline(); return lowerBlock(f, inst, @ptrCast(f.air.extra.items[extra.end..][0..extra.data.body_len])); } @@ -7415,30 +7424,31 @@ fn airShuffleTwo(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = unwrapped.result_ty; const elem_ty = inst_ty.childType(zcu); - const writer = &f.object.code.buffered_writer; + const w = &f.object.code.buffered_writer; const local = try f.allocLocal(inst, inst_ty); try reap(f, inst, &.{ unwrapped.operand_a, unwrapped.operand_b }); // local cannot alias operands for (mask, 0..) |mask_elem, out_idx| { - try f.writeCValue(writer, local, .Other); - try writer.writeByte('['); - try f.object.dg.renderValue(writer, try pt.intValue(.usize, out_idx), .Other); - try writer.writeAll("] = "); + try f.writeCValue(w, local, .Other); + try w.writeByte('['); + try f.object.dg.renderValue(w, try pt.intValue(.usize, out_idx), .Other); + try w.writeAll("] = "); switch (mask_elem.unwrap()) { .a_elem => |src_idx| { - try f.writeCValue(writer, operand_a, .Other); - try writer.writeByte('['); - try f.object.dg.renderValue(writer, try pt.intValue(.usize, src_idx), .Other); - try writer.writeByte(']'); + try f.writeCValue(w, operand_a, .Other); + try w.writeByte('['); + try f.object.dg.renderValue(w, try pt.intValue(.usize, src_idx), .Other); + try w.writeByte(']'); }, .b_elem => |src_idx| { - try f.writeCValue(writer, operand_b, .Other); - try writer.writeByte('['); - try f.object.dg.renderValue(writer, try pt.intValue(.usize, src_idx), .Other); - try writer.writeByte(']'); + try f.writeCValue(w, operand_b, .Other); + try w.writeByte('['); + try f.object.dg.renderValue(w, try pt.intValue(.usize, src_idx), .Other); + try w.writeByte(']'); }, - .undef => try f.object.dg.renderUndefValue(writer, elem_ty, .Other), + .undef => try f.object.dg.renderUndefValue(w, elem_ty, .Other), } - try writer.writeAll(";\n"); + try w.writeByte(';'); + try f.object.newline(); } return local; @@ -7889,12 +7899,13 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { fn airRuntimeNavPtr(f: *Function, inst: Air.Inst.Index) !CValue { const ty_nav = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav; - const writer = f.object.writer(); + const w = &f.object.code.buffered_writer; const local = try f.allocLocal(inst, .fromInterned(ty_nav.ty)); - try f.writeCValue(writer, local, .Other); - try writer.writeAll(" = "); - try f.object.dg.renderNav(writer, ty_nav.nav, .Other); - try writer.writeAll(";\n"); + try f.writeCValue(w, local, .Other); + try w.writeAll(" = "); + try f.object.dg.renderNav(w, ty_nav.nav, .Other); + try w.writeByte(';'); + try f.object.newline(); return local; } @@ -8480,19 +8491,19 @@ const Assignment = struct { const Vectorize = struct { index: CValue = .none, - pub fn start(f: *Function, inst: Air.Inst.Index, writer: *Writer, ty: Type) !Vectorize { + pub fn start(f: *Function, inst: Air.Inst.Index, w: *Writer, ty: Type) !Vectorize { const pt = f.object.dg.pt; const zcu = pt.zcu; return if (ty.zigTypeTag(zcu) == .vector) index: { const local = try f.allocLocal(inst, .usize); - try writer.writeAll("for ("); - try f.writeCValue(writer, local, .Other); - try writer.print(" = {fd}; ", .{try f.fmtIntLiteral(.zero_usize)}); - try f.writeCValue(writer, local, .Other); - try writer.print(" < {fd}; ", .{try f.fmtIntLiteral(try pt.intValue(.usize, ty.vectorLen(zcu)))}); - try f.writeCValue(writer, local, .Other); - try writer.print(" += {fd}) {{\n", .{try f.fmtIntLiteral(.one_usize)}); + try w.writeAll("for ("); + try f.writeCValue(w, local, .Other); + try w.print(" = {fd}; ", .{try f.fmtIntLiteral(.zero_usize)}); + try f.writeCValue(w, local, .Other); + try w.print(" < {fd}; ", .{try f.fmtIntLiteral(try pt.intValue(.usize, ty.vectorLen(zcu)))}); + try f.writeCValue(w, local, .Other); + try w.print(" += {fd}) {{\n", .{try f.fmtIntLiteral(.one_usize)}); f.object.indent(); try f.object.newline(); diff --git a/src/link/C.zig b/src/link/C.zig index 1ea130f6b1..0c282dbc4a 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -25,34 +25,34 @@ base: link.File, /// This linker backend does not try to incrementally link output C source code. /// Instead, it tracks all declarations in this table, and iterates over it /// in the flush function, stitching pre-rendered pieces of C code together. -navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, AvBlock) = .empty, +navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, AvBlock), /// All the string bytes of rendered C code, all squished into one array. /// While in progress, a separate buffer is used, and then when finished, the /// buffer is copied into this one. -string_bytes: std.ArrayListUnmanaged(u8) = .empty, +string_bytes: std.ArrayListUnmanaged(u8), /// Tracks all the anonymous decls that are used by all the decls so they can /// be rendered during flush(). -uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, AvBlock) = .empty, +uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, AvBlock), /// Sparse set of uavs that are overaligned. Underaligned anon decls are /// lowered the same as ABI-aligned anon decls. The keys here are a subset of /// the keys of `uavs`. -aligned_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment) = .empty, +aligned_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment), -exported_navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, ExportedBlock) = .empty, -exported_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, ExportedBlock) = .empty, +exported_navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, ExportedBlock), +exported_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, ExportedBlock), /// Optimization, `updateDecl` reuses this buffer rather than creating a new /// one with every call. -fwd_decl_buf: std.ArrayListUnmanaged(u8) = .empty, +fwd_decl_buf: []u8, /// Optimization, `updateDecl` reuses this buffer rather than creating a new /// one with every call. -code_buf: std.ArrayListUnmanaged(u8) = .empty, +code_header_buf: []u8, +/// Optimization, `updateDecl` reuses this buffer rather than creating a new +/// one with every call. +code_buf: []u8, /// Optimization, `flush` reuses this buffer rather than creating a new /// one with every call. -lazy_fwd_decl_buf: std.ArrayListUnmanaged(u8) = .empty, -/// Optimization, `flush` reuses this buffer rather than creating a new -/// one with every call. -lazy_code_buf: std.ArrayListUnmanaged(u8) = .empty, +scratch_buf: []u32, /// A reference into `string_bytes`. const String = extern struct { @@ -67,11 +67,11 @@ const String = extern struct { /// Per-declaration data. pub const AvBlock = struct { - code: String = String.empty, - fwd_decl: String = String.empty, + fwd_decl: String = .empty, + code: String = .empty, /// Each `Decl` stores a set of used `CType`s. In `flush()`, we iterate /// over each `Decl` and generate the definition for each used `CType` once. - ctype_pool: codegen.CType.Pool = codegen.CType.Pool.empty, + ctype_pool: codegen.CType.Pool = .empty, /// May contain string references to ctype_pool lazy_fns: codegen.LazyFnMap = .{}, @@ -84,20 +84,21 @@ pub const AvBlock = struct { /// Per-exported-symbol data. pub const ExportedBlock = struct { - fwd_decl: String = String.empty, + fwd_decl: String = .empty, }; pub fn getString(this: C, s: String) []const u8 { return this.string_bytes.items[s.start..][0..s.len]; } -pub fn addString(this: *C, s: []const u8) Allocator.Error!String { +pub fn addString(this: *C, writers: []const *std.io.AllocatingWriter) Allocator.Error!String { const comp = this.base.comp; const gpa = comp.gpa; - try this.string_bytes.appendSlice(gpa, s); + const start = this.string_bytes.items.len; + for (writers) |writer| try this.string_bytes.appendSlice(gpa, writer.getWritten()); return .{ - .start = @intCast(this.string_bytes.items.len - s.len), - .len = @intCast(s.len), + .start = @intCast(start), + .len = @intCast(this.string_bytes.items.len - start), }; } @@ -147,6 +148,16 @@ pub fn createEmpty( .file = file, .build_id = options.build_id, }, + .navs = .empty, + .string_bytes = .empty, + .uavs = .empty, + .aligned_uavs = .empty, + .exported_navs = .empty, + .exported_uavs = .empty, + .fwd_decl_buf = &.{}, + .code_header_buf = &.{}, + .code_buf = &.{}, + .scratch_buf = &.{}, }; return c_file; @@ -170,10 +181,10 @@ pub fn deinit(self: *C) void { self.exported_uavs.deinit(gpa); self.string_bytes.deinit(gpa); - self.fwd_decl_buf.deinit(gpa); - self.code_buf.deinit(gpa); - self.lazy_fwd_decl_buf.deinit(gpa); - self.lazy_code_buf.deinit(gpa); + gpa.free(self.fwd_decl_buf); + gpa.free(self.code_header_buf); + gpa.free(self.code_buf); + gpa.free(self.scratch_buf); } pub fn updateFunc( @@ -194,20 +205,15 @@ pub fn updateFunc( .ctype_pool = mir.c.ctype_pool.move(), .lazy_fns = mir.c.lazy_fns.move(), }; - gop.value_ptr.code = try self.addString(mir.c.code); - gop.value_ptr.fwd_decl = try self.addString(mir.c.fwd_decl); + gop.value_ptr.fwd_decl = try self.addString(&.{&function.object.dg.fwd_decl}); + gop.value_ptr.code = try self.addString(&.{ &function.object.code_header, &function.object.code }); try self.addUavsFromCodegen(&mir.c.uavs); } -fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) !void { +fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) link.File.FlushError!void { const gpa = self.base.comp.gpa; const uav = self.uavs.keys()[i]; - const fwd_decl = &self.fwd_decl_buf; - const code = &self.code_buf; - fwd_decl.clearRetainingCapacity(); - code.clearRetainingCapacity(); - var object: codegen.Object = .{ .dg = .{ .gpa = gpa, @@ -217,21 +223,24 @@ fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) !void { .pass = .{ .uav = uav }, .is_naked_fn = false, .expected_block = null, - .fwd_decl = fwd_decl.toManaged(gpa), - .ctype_pool = codegen.CType.Pool.empty, - .scratch = .{}, + .fwd_decl = undefined, + .ctype_pool = .empty, + .scratch = .initBuffer(self.scratch_buf), .uavs = .empty, }, - .code = code.toManaged(gpa), - .indent_writer = undefined, // set later so we can get a pointer to object.code + .code_header = undefined, + .code = undefined, + .indent_counter = 0, }; - object.indent_writer = .{ .underlying_writer = object.code.writer() }; + object.dg.fwd_decl.initOwnedSlice(gpa, self.fwd_decl_buf); + object.code.initOwnedSlice(gpa, self.code_buf); defer { object.dg.uavs.deinit(gpa); - fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); object.dg.ctype_pool.deinit(object.dg.gpa); - object.dg.scratch.deinit(gpa); - code.* = object.code.moveToUnmanaged(); + + self.fwd_decl_buf = object.dg.fwd_decl.toArrayList().allocatedSlice(); + self.code_buf = object.code.toArrayList().allocatedSlice(); + self.scratch_buf = object.dg.scratch.allocatedSlice(); } try object.dg.ctype_pool.init(gpa); @@ -243,15 +252,15 @@ fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) !void { //try zcu.failed_decls.put(gpa, decl_index, object.dg.error_msg.?); //return; }, - else => |e| return e, + error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, }; try self.addUavsFromCodegen(&object.dg.uavs); object.dg.ctype_pool.freeUnusedCapacity(gpa); self.uavs.values()[i] = .{ - .code = try self.addString(object.code.items), - .fwd_decl = try self.addString(object.dg.fwd_decl.items), + .fwd_decl = try self.addString(&.{&object.dg.fwd_decl}), + .code = try self.addString(&.{&object.code}), .ctype_pool = object.dg.ctype_pool.move(), }; } @@ -277,12 +286,8 @@ pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) l errdefer _ = self.navs.pop(); if (!gop.found_existing) gop.value_ptr.* = .{}; const ctype_pool = &gop.value_ptr.ctype_pool; - const fwd_decl = &self.fwd_decl_buf; - const code = &self.code_buf; try ctype_pool.init(gpa); ctype_pool.clearRetainingCapacity(); - fwd_decl.clearRetainingCapacity(); - code.clearRetainingCapacity(); var object: codegen.Object = .{ .dg = .{ @@ -293,22 +298,25 @@ pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) l .pass = .{ .nav = nav_index }, .is_naked_fn = false, .expected_block = null, - .fwd_decl = fwd_decl.toManaged(gpa), + .fwd_decl = undefined, .ctype_pool = ctype_pool.*, - .scratch = .{}, + .scratch = .initBuffer(self.scratch_buf), .uavs = .empty, }, - .code = code.toManaged(gpa), - .indent_writer = undefined, // set later so we can get a pointer to object.code + .code_header = undefined, + .code = undefined, + .indent_counter = 0, }; - object.indent_writer = .{ .underlying_writer = object.code.writer() }; + object.dg.fwd_decl.initOwnedSlice(gpa, self.fwd_decl_buf); + object.code.initOwnedSlice(gpa, self.code_buf); defer { object.dg.uavs.deinit(gpa); - fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); ctype_pool.* = object.dg.ctype_pool.move(); ctype_pool.freeUnusedCapacity(gpa); - object.dg.scratch.deinit(gpa); - code.* = object.code.moveToUnmanaged(); + + self.fwd_decl_buf = object.dg.fwd_decl.toArrayList().allocatedSlice(); + self.code_buf = object.code.toArrayList().allocatedSlice(); + self.scratch_buf = object.dg.scratch.allocatedSlice(); } codegen.genDecl(&object) catch |err| switch (err) { @@ -316,10 +324,10 @@ pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) l error.CodegenFail => return, error.OutOfMemory => |e| return e, }, - else => |e| return e, + error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, }; - gop.value_ptr.code = try self.addString(object.code.items); - gop.value_ptr.fwd_decl = try self.addString(object.dg.fwd_decl.items); + gop.value_ptr.fwd_decl = try self.addString(&.{&object.dg.fwd_decl}); + gop.value_ptr.code = try self.addString(&.{&object.code}); try self.addUavsFromCodegen(&object.dg.uavs); } @@ -331,19 +339,14 @@ pub fn updateLineNumber(self: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedIn _ = ti_id; } -fn abiDefines(self: *C, target: *const std.Target) !std.ArrayList(u8) { - const gpa = self.base.comp.gpa; - var defines = std.ArrayList(u8).init(gpa); - errdefer defines.deinit(); - const writer = defines.writer(); +fn abiDefines(bw: *std.io.BufferedWriter, target: std.Target) !void { switch (target.abi) { - .msvc, .itanium => try writer.writeAll("#define ZIG_TARGET_ABI_MSVC\n"), + .msvc, .itanium => try bw.writeAll("#define ZIG_TARGET_ABI_MSVC\n"), else => {}, } - try writer.print("#define ZIG_TARGET_MAX_INT_ALIGNMENT {d}\n", .{ + try bw.print("#define ZIG_TARGET_MAX_INT_ALIGNMENT {d}\n", .{ target.cMaxIntAlignment(), }); - return defines; } pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { @@ -374,37 +377,49 @@ pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.P // emit-h is in `flushEmitH` below. var f: Flush = .{ - .ctype_pool = codegen.CType.Pool.empty, - .lazy_ctype_pool = codegen.CType.Pool.empty, + .ctype_pool = .empty, + .ctype_global_from_decl_map = .empty, + .ctypes = .empty, + + .lazy_ctype_pool = .empty, + .lazy_fns = .empty, + .lazy_fwd_decl = .empty, + .lazy_code = .empty, + + .all_buffers = .empty, + .file_size = 0, }; defer f.deinit(gpa); - const abi_defines = try self.abiDefines(zcu.getTarget()); - defer abi_defines.deinit(); + var abi_defines_aw: std.io.AllocatingWriter = undefined; + abi_defines_aw.init(gpa); + defer abi_defines_aw.deinit(); + abiDefines(&abi_defines_aw.buffered_writer, zcu.getTarget()) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + }; // Covers defines, zig.h, ctypes, asm, lazy fwd. try f.all_buffers.ensureUnusedCapacity(gpa, 5); - f.appendBufAssumeCapacity(abi_defines.items); + f.appendBufAssumeCapacity(abi_defines_aw.getWritten()); f.appendBufAssumeCapacity(zig_h); const ctypes_index = f.all_buffers.items.len; f.all_buffers.items.len += 1; - { - var asm_buf = f.asm_buf.toManaged(gpa); - defer f.asm_buf = asm_buf.moveToUnmanaged(); - try codegen.genGlobalAsm(zcu, asm_buf.writer()); - f.appendBufAssumeCapacity(asm_buf.items); - } + var asm_aw: std.io.AllocatingWriter = undefined; + asm_aw.init(gpa); + defer asm_aw.deinit(); + codegen.genGlobalAsm(zcu, &asm_aw.buffered_writer) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + }; + f.appendBufAssumeCapacity(asm_aw.getWritten()); const lazy_index = f.all_buffers.items.len; f.all_buffers.items.len += 1; - self.lazy_fwd_decl_buf.clearRetainingCapacity(); - self.lazy_code_buf.clearRetainingCapacity(); try f.lazy_ctype_pool.init(gpa); - try self.flushErrDecls(pt, &f.lazy_ctype_pool); + try self.flushErrDecls(pt, &f); // Unlike other backends, the .c code we are emitting has order-dependent decls. // `CType`s, forward decls, and non-functions first. @@ -462,22 +477,15 @@ pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.P } } - f.all_buffers.items[ctypes_index] = .{ - .base = if (f.ctypes_buf.items.len > 0) f.ctypes_buf.items.ptr else "", - .len = f.ctypes_buf.items.len, - }; - f.file_size += f.ctypes_buf.items.len; + f.all_buffers.items[ctypes_index] = f.ctypes.items; + f.file_size += f.ctypes.items.len; - const lazy_fwd_decl_len = self.lazy_fwd_decl_buf.items.len; - f.all_buffers.items[lazy_index] = .{ - .base = if (lazy_fwd_decl_len > 0) self.lazy_fwd_decl_buf.items.ptr else "", - .len = lazy_fwd_decl_len, - }; - f.file_size += lazy_fwd_decl_len; + f.all_buffers.items[lazy_index] = f.lazy_fwd_decl.items; + f.file_size += f.lazy_fwd_decl.items.len; // Now the code. try f.all_buffers.ensureUnusedCapacity(gpa, 1 + (self.uavs.count() + self.navs.count()) * 2); - f.appendBufAssumeCapacity(self.lazy_code_buf.items); + f.appendBufAssumeCapacity(f.lazy_code.items); for (self.uavs.keys(), self.uavs.values()) |uav, av_block| f.appendCodeAssumeCapacity( if (self.exported_uavs.contains(uav)) .default else switch (ip.indexToKey(uav)) { .@"extern" => .zig_extern, @@ -493,31 +501,35 @@ pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.P const file = self.base.file.?; file.setEndPos(f.file_size) catch |err| return diags.fail("failed to allocate file: {s}", .{@errorName(err)}); - file.pwritevAll(f.all_buffers.items, 0) catch |err| return diags.fail("failed to write to '{'}': {s}", .{ - self.base.emit, @errorName(err), - }); + var fw = file.writer(); + var bw = fw.interface().unbuffered(); + bw.writeVecAll(f.all_buffers.items) catch |err| switch (err) { + error.WriteFailed => return diags.fail("failed to write to '{f'}': {s}", .{ + self.base.emit, @errorName(fw.err.?), + }), + }; } const Flush = struct { ctype_pool: codegen.CType.Pool, - ctype_global_from_decl_map: std.ArrayListUnmanaged(codegen.CType) = .empty, - ctypes_buf: std.ArrayListUnmanaged(u8) = .empty, + ctype_global_from_decl_map: std.ArrayListUnmanaged(codegen.CType), + ctypes: std.ArrayListUnmanaged(u8), lazy_ctype_pool: codegen.CType.Pool, - lazy_fns: LazyFns = .{}, - - asm_buf: std.ArrayListUnmanaged(u8) = .empty, + lazy_fns: LazyFns, + lazy_fwd_decl: std.ArrayListUnmanaged(u8), + lazy_code: std.ArrayListUnmanaged(u8), /// We collect a list of buffers to write, and write them all at once with pwritev 😎 - all_buffers: std.ArrayListUnmanaged(std.posix.iovec_const) = .empty, + all_buffers: std.ArrayListUnmanaged([]const u8), /// Keeps track of the total bytes of `all_buffers`. - file_size: u64 = 0, + file_size: u64, const LazyFns = std.AutoHashMapUnmanaged(codegen.LazyFnKey, void); fn appendBufAssumeCapacity(f: *Flush, buf: []const u8) void { if (buf.len == 0) return; - f.all_buffers.appendAssumeCapacity(.{ .base = buf.ptr, .len = buf.len }); + f.all_buffers.appendAssumeCapacity(buf); f.file_size += buf.len; } @@ -532,14 +544,15 @@ const Flush = struct { } fn deinit(f: *Flush, gpa: Allocator) void { - f.all_buffers.deinit(gpa); - f.asm_buf.deinit(gpa); - f.lazy_fns.deinit(gpa); - f.lazy_ctype_pool.deinit(gpa); - f.ctypes_buf.deinit(gpa); + f.ctype_pool.deinit(gpa); assert(f.ctype_global_from_decl_map.items.len == 0); f.ctype_global_from_decl_map.deinit(gpa); - f.ctype_pool.deinit(gpa); + f.ctypes.deinit(gpa); + f.lazy_ctype_pool.deinit(gpa); + f.lazy_fns.deinit(gpa); + f.lazy_fwd_decl.deinit(gpa); + f.lazy_code.deinit(gpa); + f.all_buffers.deinit(gpa); } }; @@ -562,9 +575,9 @@ fn flushCTypes( try global_from_decl_map.ensureTotalCapacity(gpa, decl_ctype_pool.items.len); defer global_from_decl_map.clearRetainingCapacity(); - var ctypes_buf = f.ctypes_buf.toManaged(gpa); - defer f.ctypes_buf = ctypes_buf.moveToUnmanaged(); - const writer = ctypes_buf.writer(); + var ctypes_aw: std.io.AllocatingWriter = undefined; + const ctypes_bw = ctypes_aw.fromArrayList(gpa, &f.ctypes); + defer f.ctypes = ctypes_aw.toArrayList(); for (0..decl_ctype_pool.items.len) |decl_ctype_pool_index| { const PoolAdapter = struct { @@ -591,26 +604,25 @@ fn flushCTypes( PoolAdapter{ .global_from_decl_map = global_from_decl_map.items }, ); global_from_decl_map.appendAssumeCapacity(global_ctype); - try codegen.genTypeDecl( + codegen.genTypeDecl( zcu, - writer, + ctypes_bw, global_ctype_pool, global_ctype, pass, decl_ctype_pool, decl_ctype, found_existing, - ); + ) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + }; } } -fn flushErrDecls(self: *C, pt: Zcu.PerThread, ctype_pool: *codegen.CType.Pool) FlushDeclError!void { +fn flushErrDecls(self: *C, pt: Zcu.PerThread, f: *Flush) FlushDeclError!void { const gpa = self.base.comp.gpa; - const fwd_decl = &self.lazy_fwd_decl_buf; - const code = &self.lazy_code_buf; - - var object = codegen.Object{ + var object: codegen.Object = .{ .dg = .{ .gpa = gpa, .pt = pt, @@ -619,27 +631,30 @@ fn flushErrDecls(self: *C, pt: Zcu.PerThread, ctype_pool: *codegen.CType.Pool) F .pass = .flush, .is_naked_fn = false, .expected_block = null, - .fwd_decl = fwd_decl.toManaged(gpa), - .ctype_pool = ctype_pool.*, - .scratch = .{}, + .fwd_decl = undefined, + .ctype_pool = f.lazy_ctype_pool, + .scratch = .initBuffer(self.scratch_buf), .uavs = .empty, }, - .code = code.toManaged(gpa), - .indent_writer = undefined, // set later so we can get a pointer to object.code + .code_header = undefined, + .code = undefined, + .indent_counter = 0, }; - object.indent_writer = .{ .underlying_writer = object.code.writer() }; + _ = object.dg.fwd_decl.fromArrayList(gpa, &f.lazy_fwd_decl); + _ = object.code.fromArrayList(gpa, &f.lazy_code); defer { object.dg.uavs.deinit(gpa); - fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); - ctype_pool.* = object.dg.ctype_pool.move(); - ctype_pool.freeUnusedCapacity(gpa); - object.dg.scratch.deinit(gpa); - code.* = object.code.moveToUnmanaged(); + f.lazy_ctype_pool = object.dg.ctype_pool.move(); + f.lazy_ctype_pool.freeUnusedCapacity(gpa); + + f.lazy_fwd_decl = object.dg.fwd_decl.toArrayList(); + f.lazy_code = object.code.toArrayList(); + self.scratch_buf = object.dg.scratch.allocatedSlice(); } codegen.genErrDecls(&object) catch |err| switch (err) { error.AnalysisFail => unreachable, - else => |e| return e, + error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, }; try self.addUavsFromCodegen(&object.dg.uavs); @@ -649,16 +664,13 @@ fn flushLazyFn( self: *C, pt: Zcu.PerThread, mod: *Module, - ctype_pool: *codegen.CType.Pool, + f: *Flush, lazy_ctype_pool: *const codegen.CType.Pool, lazy_fn: codegen.LazyFnMap.Entry, ) FlushDeclError!void { const gpa = self.base.comp.gpa; - const fwd_decl = &self.lazy_fwd_decl_buf; - const code = &self.lazy_code_buf; - - var object = codegen.Object{ + var object: codegen.Object = .{ .dg = .{ .gpa = gpa, .pt = pt, @@ -667,29 +679,32 @@ fn flushLazyFn( .pass = .flush, .is_naked_fn = false, .expected_block = null, - .fwd_decl = fwd_decl.toManaged(gpa), - .ctype_pool = ctype_pool.*, - .scratch = .{}, + .fwd_decl = undefined, + .ctype_pool = f.lazy_ctype_pool, + .scratch = .initBuffer(self.scratch_buf), .uavs = .empty, }, - .code = code.toManaged(gpa), - .indent_writer = undefined, // set later so we can get a pointer to object.code + .code_header = undefined, + .code = undefined, + .indent_counter = 0, }; - object.indent_writer = .{ .underlying_writer = object.code.writer() }; + _ = object.dg.fwd_decl.fromArrayList(gpa, &f.lazy_fwd_decl); + _ = object.code.fromArrayList(gpa, &f.lazy_code); defer { // If this assert trips just handle the anon_decl_deps the same as // `updateFunc()` does. assert(object.dg.uavs.count() == 0); - fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); - ctype_pool.* = object.dg.ctype_pool.move(); - ctype_pool.freeUnusedCapacity(gpa); - object.dg.scratch.deinit(gpa); - code.* = object.code.moveToUnmanaged(); + f.lazy_ctype_pool = object.dg.ctype_pool.move(); + f.lazy_ctype_pool.freeUnusedCapacity(gpa); + + f.lazy_fwd_decl = object.dg.fwd_decl.toArrayList(); + f.lazy_code = object.code.toArrayList(); + self.scratch_buf = object.dg.scratch.allocatedSlice(); } codegen.genLazyFn(&object, lazy_ctype_pool, lazy_fn) catch |err| switch (err) { error.AnalysisFail => unreachable, - else => |e| return e, + error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, }; } @@ -709,7 +724,7 @@ fn flushLazyFns( const gop = f.lazy_fns.getOrPutAssumeCapacity(entry.key_ptr.*); if (gop.found_existing) continue; gop.value_ptr.* = {}; - try self.flushLazyFn(pt, mod, &f.lazy_ctype_pool, lazy_ctype_pool, entry); + try self.flushLazyFn(pt, mod, f, lazy_ctype_pool, entry); } } @@ -802,8 +817,6 @@ pub fn updateExports( }, }; const ctype_pool = &decl_block.ctype_pool; - const fwd_decl = &self.fwd_decl_buf; - fwd_decl.clearRetainingCapacity(); var dg: codegen.DeclGen = .{ .gpa = gpa, .pt = pt, @@ -812,20 +825,24 @@ pub fn updateExports( .pass = pass, .is_naked_fn = false, .expected_block = null, - .fwd_decl = fwd_decl.toManaged(gpa), + .fwd_decl = undefined, .ctype_pool = decl_block.ctype_pool, - .scratch = .{}, + .scratch = .initBuffer(self.scratch_buf), .uavs = .empty, }; + dg.fwd_decl.initOwnedSlice(gpa, self.fwd_decl_buf); defer { assert(dg.uavs.count() == 0); - fwd_decl.* = dg.fwd_decl.moveToUnmanaged(); ctype_pool.* = dg.ctype_pool.move(); ctype_pool.freeUnusedCapacity(gpa); - dg.scratch.deinit(gpa); + + self.fwd_decl_buf = dg.fwd_decl.toArrayList().allocatedSlice(); + self.scratch_buf = dg.scratch.allocatedSlice(); } - try codegen.genExports(&dg, exported, export_indices); - exported_block.* = .{ .fwd_decl = try self.addString(dg.fwd_decl.items) }; + codegen.genExports(&dg, exported, export_indices) catch |err| switch (err) { + error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, + }; + exported_block.* = .{ .fwd_decl = try self.addString(&.{&dg.fwd_decl}) }; } pub fn deleteExport(