cbe: pass behavior tests

This commit is contained in:
Jacob Young 2025-04-22 15:46:18 -04:00 committed by Andrew Kelley
parent 045c1c1557
commit cf28736f0d
5 changed files with 92 additions and 62 deletions

View File

@ -698,17 +698,27 @@ pub const Object = struct {
indent_counter: usize,
const indent_width = 1;
const indent_char = ' ';
fn newline(o: *Object) !void {
const bw = &o.code.buffered_writer;
try bw.writeByte('\n');
try bw.splatByteAll(' ', o.indent_counter);
try bw.splatByteAll(indent_char, o.indent_counter);
}
fn indent(o: *Object) void {
o.indent_counter += indent_width;
}
fn outdent(o: *Object) void {
fn outdent(o: *Object) !void {
o.indent_counter -= indent_width;
const written = o.code.getWritten();
switch (written[written.len - 1]) {
indent_char => o.code.shrinkRetainingCapacity(written.len - indent_width),
'\n' => try o.code.buffered_writer.splatByteAll(indent_char, o.indent_counter),
else => {
std.debug.print("\"{f}\"\n", .{std.zig.fmtEscapes(written[written.len -| 100..])});
unreachable;
},
}
}
};
@ -1041,7 +1051,7 @@ pub const DeclGen = struct {
.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.writeAll("0"),
.payload => try writer.writeByte('0'),
},
.pointer, .aligned, .array, .vector, .fwd_decl, .function => unreachable,
.aggregate => |aggregate| {
@ -1197,7 +1207,7 @@ pub const DeclGen = struct {
.none => "true",
else => "false",
}) else switch (opt.val) {
.none => try writer.writeAll("0"),
.none => try writer.writeByte('0'),
else => |payload| switch (ip.indexToKey(payload)) {
.undef => |err_ty| try dg.renderUndefValue(
writer,
@ -1528,7 +1538,7 @@ pub const DeclGen = struct {
try writer.writeByte(')');
}
try dg.renderValue(writer, Value.fromInterned(un.val), location);
} else try writer.writeAll("0");
} else try writer.writeByte('0');
return;
}
@ -2786,7 +2796,7 @@ pub fn genErrDecls(o: *Object) Error!void {
try bw.print(" = {d}u,", .{value});
try o.newline();
}
o.outdent();
try o.outdent();
try bw.writeAll("};");
try o.newline();
}
@ -2895,6 +2905,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn
try bw.print("case {f}: {{", .{
try o.dg.fmtIntLiteral(try tag_val.intFromEnum(enum_ty, pt), .Other),
});
o.indent();
try o.newline();
try bw.writeAll("static ");
try o.dg.renderTypeAndName(bw, name_ty, .{ .identifier = "name" }, Const, .none, .complete);
@ -2909,16 +2920,15 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn
try o.dg.fmtIntLiteral(try pt.intValue(.usize, tag_name_len), .Other),
});
try o.newline();
try o.outdent();
try bw.writeByte('}');
try o.newline();
}
try o.outdent();
try bw.writeByte('}');
try o.newline();
try bw.writeAll("while (");
try o.dg.renderValue(bw, Value.true, .Other);
try bw.writeAll(") ");
_ = try airBreakpoint(o, bw);
try airUnreach(o);
try o.outdent();
try bw.writeByte('}');
try o.newline();
},
@ -2940,6 +2950,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn
.fmt_ctype_pool_string = fn_name,
});
try bw.writeAll(" {");
o.indent();
try o.newline();
try bw.writeAll("return ");
try o.dg.renderNavName(bw, fn_nav_index);
@ -2950,6 +2961,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn
}
try bw.writeAll(");");
try o.newline();
try o.outdent();
try bw.writeByte('}');
try o.newline();
},
@ -3066,7 +3078,10 @@ fn genFunc(f: *Function) !void {
f.free_locals_map.clearRetainingCapacity();
const main_body = f.air.getMainBody();
try genBodyResolveState(f, undefined, &.{}, main_body, false);
o.indent();
try genBodyResolveState(f, undefined, &.{}, main_body, true);
try o.outdent();
try o.code.buffered_writer.writeByte('}');
try o.newline();
if (o.dg.expected_block) |_|
return f.fail("runtime code not allowed in naked function", .{});
@ -3285,11 +3300,11 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) Error!void {
if (body.len == 0) {
try bw.writeAll("{}");
} else {
try bw.writeAll("{");
try bw.writeByte('{');
f.object.indent();
try f.object.newline();
try genBodyInner(f, body);
f.object.outdent();
try f.object.outdent();
try bw.writeByte('}');
}
}
@ -3367,7 +3382,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {
.arg => try airArg(f, inst),
.breakpoint => try airBreakpoint(&f.object, &f.object.code.buffered_writer),
.breakpoint => try airBreakpoint(f),
.ret_addr => try airRetAddr(f, inst),
.frame_addr => try airFrameAddress(f, inst),
@ -3621,7 +3636,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {
.ret_safe => return airRet(f, inst, false), // TODO
.ret_load => return airRet(f, inst, true),
.trap => return airTrap(f, &f.object.code.buffered_writer),
.unreach => return airUnreach(f),
.unreach => return airUnreach(&f.object),
// Instructions which may be `noreturn`.
.block => res: {
@ -4017,18 +4032,14 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !void {
try f.writeCValueDeref(bw, ret_val)
else
try f.writeCValue(bw, ret_val, .Other);
try bw.writeByte(';');
try f.object.newline();
try bw.writeAll(";\n");
if (is_array) {
try freeLocal(f, inst, ret_val.new_local, null);
}
} else {
try reap(f, inst, &.{un_op});
// Not even allowed to return void in a naked function.
if (!f.object.dg.is_naked_fn) {
try bw.writeAll("return;");
try f.object.newline();
}
if (!f.object.dg.is_naked_fn) try bw.writeAll("return;\n");
}
}
@ -4817,7 +4828,10 @@ fn airCall(
try f.freeCValue(inst, resolved_arg);
}
try bw.writeAll(");");
try f.object.newline();
switch (modifier) {
.always_tail => try bw.writeByte('\n'),
else => try f.object.newline(),
}
const result = result: {
if (result_local == .none or !lowersToArray(ret_ty, pt))
@ -4925,8 +4939,6 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index)
try die(f, inst, death.toRef());
}
try f.object.newline();
// noreturn blocks have no `br` instructions reaching them, so we don't want a label
if (f.object.dg.is_naked_fn) {
if (f.object.dg.expected_block) |expected_block| {
@ -4936,7 +4948,7 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index)
}
} else if (!f.typeOfIndex(inst).isNoReturn(zcu)) {
// label must be followed by an expression, include an empty one.
try bw.print("zig_block_{d}:;", .{block_id});
try bw.print("\nzig_block_{d}:;", .{block_id});
try f.object.newline();
}
@ -5057,14 +5069,12 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !void {
try a.end(f, bw);
}
try bw.print("goto zig_block_{d};", .{block.block_id});
try f.object.newline();
try bw.print("goto zig_block_{d};\n", .{block.block_id});
}
fn airRepeat(f: *Function, inst: Air.Inst.Index) !void {
const repeat = f.air.instructions.items(.data)[@intFromEnum(inst)].repeat;
try f.object.code.buffered_writer.print("goto zig_loop_{d};", .{@intFromEnum(repeat.loop_inst)});
try f.object.newline();
try f.object.code.buffered_writer.print("goto zig_loop_{d};\n", .{@intFromEnum(repeat.loop_inst)});
}
fn airSwitchDispatch(f: *Function, inst: Air.Inst.Index) !void {
@ -5093,8 +5103,7 @@ fn airSwitchDispatch(f: *Function, inst: Air.Inst.Index) !void {
}
}
} else switch_br.cases_len;
try bw.print("goto zig_switch_{d}_dispatch_{d};", .{ @intFromEnum(br.block_inst), target_case_idx });
try f.object.newline();
try bw.print("goto zig_switch_{d}_dispatch_{d};\n", .{ @intFromEnum(br.block_inst), target_case_idx });
return;
}
@ -5106,7 +5115,7 @@ fn airSwitchDispatch(f: *Function, inst: Air.Inst.Index) !void {
try f.writeCValue(bw, cond, .Other);
try bw.writeByte(';');
try f.object.newline();
try bw.print("goto zig_switch_{d}_loop;", .{@intFromEnum(br.block_inst)});
try bw.print("goto zig_switch_{d}_loop;\n", .{@intFromEnum(br.block_inst)});
}
fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
@ -5241,13 +5250,13 @@ fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !CVal
fn airTrap(f: *Function, bw: *std.io.BufferedWriter) !void {
// Not even allowed to call trap in a naked function.
if (f.object.dg.is_naked_fn) return;
try bw.writeAll("zig_trap();");
try f.object.newline();
try bw.writeAll("zig_trap();\n");
}
fn airBreakpoint(o: *Object, bw: *std.io.BufferedWriter) !CValue {
fn airBreakpoint(f: *Function) !CValue {
const bw = &f.object.code.buffered_writer;
try bw.writeAll("zig_breakpoint();");
try o.newline();
try f.object.newline();
return .none;
}
@ -5273,11 +5282,10 @@ fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue {
return local;
}
fn airUnreach(f: *Function) !void {
fn airUnreach(o: *Object) !void {
// Not even allowed to call unreachable in a naked function.
if (f.object.dg.is_naked_fn) return;
try f.object.code.buffered_writer.writeAll("zig_unreachable();");
try f.object.newline();
if (o.dg.is_naked_fn) return;
try o.code.buffered_writer.writeAll("zig_unreachable();\n");
}
fn airLoop(f: *Function, inst: Air.Inst.Index) !void {
@ -5407,10 +5415,11 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
f.object.indent();
try f.object.newline();
if (is_dispatch_loop) {
try bw.print("zig_switch_{d}_dispatch_{d}: ", .{ @intFromEnum(inst), case.idx });
try bw.print("zig_switch_{d}_dispatch_{d}:;", .{ @intFromEnum(inst), case.idx });
try f.object.newline();
}
try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, true);
f.object.outdent();
try f.object.outdent();
try bw.writeByte('}');
if (f.object.dg.expected_block) |_|
return f.fail("runtime code not allowed in naked function", .{});
@ -5456,7 +5465,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
try bw.print("zig_switch_{d}_dispatch_{d}: ", .{ @intFromEnum(inst), case.idx });
}
try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, true);
f.object.outdent();
try f.object.outdent();
try bw.writeByte('}');
if (f.object.dg.expected_block) |_|
return f.fail("runtime code not allowed in naked function", .{});
@ -5474,14 +5483,10 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
try genBody(f, else_body);
if (f.object.dg.expected_block) |_|
return f.fail("runtime code not allowed in naked function", .{});
} else {
try bw.writeAll("zig_unreachable();");
}
try f.object.newline();
f.object.outdent();
try bw.writeByte('}');
} else try airUnreach(&f.object);
try f.object.newline();
try f.object.outdent();
try bw.writeAll("}\n");
}
fn asmInputNeedsLocal(f: *Function, constraint: []const u8, value: CValue) bool {
@ -6868,7 +6873,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue
try bw.writeAll("NULL");
try a.end(f, bw);
}
f.object.outdent();
try f.object.outdent();
try bw.writeByte('}');
try f.object.newline();
} else {
@ -8119,6 +8124,7 @@ fn compareOperatorC(operator: std.math.CompareOperator) []const u8 {
const StringLiteral = struct {
len: usize,
cur_len: usize,
start_count: usize,
bw: *std.io.BufferedWriter,
// MSVC throws C2078 if an array of size 65536 or greater is initialized with a string literal,
@ -8136,6 +8142,7 @@ const StringLiteral = struct {
return .{
.cur_len = 0,
.len = len,
.start_count = bw.count,
.bw = bw,
};
}
@ -8175,7 +8182,7 @@ const StringLiteral = struct {
pub fn writeChar(sl: *StringLiteral, c: u8) std.io.Writer.Error!void {
if (sl.len <= max_string_initializer_len) {
if (sl.cur_len == 0 and sl.bw.count > 1)
if (sl.cur_len == 0 and sl.bw.count - sl.start_count > 1)
try sl.bw.writeAll("\"\"");
const count = sl.bw.count;
@ -8186,7 +8193,7 @@ const StringLiteral = struct {
if (sl.cur_len >= max_literal_len) sl.cur_len = 0;
} else {
if (sl.bw.count > 1) try sl.bw.writeByte(',');
if (sl.bw.count - sl.start_count > 1) try sl.bw.writeByte(',');
try sl.bw.print("'\\x{x}'", .{c});
}
}
@ -8502,7 +8509,7 @@ const Vectorize = struct {
pub fn end(self: Vectorize, f: *Function, inst: Air.Inst.Index, bw: *std.io.BufferedWriter) !void {
if (self.index != .none) {
f.object.outdent();
try f.object.outdent();
try bw.writeByte('}');
try f.object.newline();
try freeLocal(f, inst, self.index.new_local, null);

View File

@ -1042,12 +1042,32 @@ test "generic type constructed from inferred error set of unresolved function" {
_ = bytes;
return 0;
}
const T = std.io.Writer(void, @typeInfo(@typeInfo(@TypeOf(write)).@"fn".return_type.?).error_union.error_set, write);
fn Writer(
comptime Context: type,
comptime WriteError: type,
comptime writeFn: fn (context: Context, bytes: []const u8) WriteError!usize,
) type {
return struct {
context: Context,
comptime {
_ = writeFn;
}
};
}
const T = Writer(void, @typeInfo(@typeInfo(@TypeOf(write)).@"fn".return_type.?).error_union.error_set, write);
fn writer() T {
return .{ .context = {} };
}
fn multiWriter(streams: anytype) MultiWriter(@TypeOf(streams)) {
return .{ .streams = streams };
}
fn MultiWriter(comptime Writers: type) type {
return struct {
streams: Writers,
};
}
};
_ = std.io.multiWriter(.{S.writer()});
_ = S.multiWriter(.{S.writer()});
}
test "errorCast to adhoc inferred error set" {

View File

@ -1086,7 +1086,10 @@ test "packed struct used as part of anon decl name" {
const S = packed struct { a: u0 = 0 };
var a: u8 = 0;
_ = &a;
try std.io.null_writer.print("\n{} {}\n", .{ a, S{} });
var buffer: [std.atomic.cache_line]u8 = undefined;
var nw: std.io.Writer.Null = undefined;
var bw = nw.writer().buffered(&buffer);
try bw.print("\n{} {}\n", .{ a, S{} });
}
test "packed struct acts as a namespace" {

View File

@ -10,8 +10,8 @@ const ET = union(enum) {
pub fn print(a: *const ET, buf: []u8) anyerror!usize {
return switch (a.*) {
ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, .lower, fmt.FormatOptions{}),
ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, .lower, fmt.FormatOptions{}),
ET.SINT => |x| fmt.printInt(buf, x, 10, .lower, fmt.FormatOptions{}),
ET.UINT => |x| fmt.printInt(buf, x, 10, .lower, fmt.FormatOptions{}),
};
}
};

View File

@ -218,11 +218,11 @@ test "variadic functions" {
for (std.mem.span(format)) |c| switch (c) {
's' => {
const arg = @cVaArg(ap, [*:0]const u8);
list.writer().print("{s}", .{arg}) catch return;
list.print("{s}", .{arg}) catch return;
},
'd' => {
const arg = @cVaArg(ap, c_int);
list.writer().print("{d}", .{arg}) catch return;
list.print("{d}", .{arg}) catch return;
},
else => unreachable,
};