From cf28736f0d9f70c801a3cc30293a98067bc96fa0 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 22 Apr 2025 15:46:18 -0400 Subject: [PATCH] cbe: pass behavior tests --- src/codegen/c.zig | 117 ++++++++++++++------------- test/behavior/error.zig | 24 +++++- test/behavior/packed-struct.zig | 5 +- test/behavior/union_with_members.zig | 4 +- test/behavior/var_args.zig | 4 +- 5 files changed, 92 insertions(+), 62 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 639d62bd31..019508aa85 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -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); diff --git a/test/behavior/error.zig b/test/behavior/error.zig index e5afd8255e..6d00f4bba2 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -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" { diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index b7ba14a4b2..5418d82062 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -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" { diff --git a/test/behavior/union_with_members.zig b/test/behavior/union_with_members.zig index 70f1086276..e5e4669608 100644 --- a/test/behavior/union_with_members.zig +++ b/test/behavior/union_with_members.zig @@ -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{}), }; } }; diff --git a/test/behavior/var_args.zig b/test/behavior/var_args.zig index b4c120effd..d836a95813 100644 --- a/test/behavior/var_args.zig +++ b/test/behavior/var_args.zig @@ -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, };