cbe: reapply writer changes

This commit is contained in:
Jacob Young 2025-06-24 12:23:12 -04:00 committed by Andrew Kelley
parent 69fd07bd50
commit 725dbf295e
2 changed files with 401 additions and 373 deletions

View File

@ -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();

View File

@ -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(