authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2022-10-19 07:56:02-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2022-10-25 05:11:29-04:00
log4765294ca4bd7dd54207892b199f3053702d9a2a
tree678317de9d228b5d4532805fd7457af240cc0110
parent912b84bbad698d18a6b844f9c05c074d2880e35b

cbe: get enough things working to support basic programs

* Enable advanced start support. * Enable advanced test_runner support. * Zig Language Reference's Hello World now works.

11 files changed, 321 insertions(+), 198 deletions(-)

lib/include/zig.h+6-1
......@@ -14,6 +14,12 @@
1414#define zig_threadlocal zig_threadlocal_unavailable
1515#endif
1616
17#if defined(_MSC_VER)
18#define ZIG_NAKED __declspec(naked)
19#else
20#define ZIG_NAKED __attribute__((naked))
21#endif
22
1723#if __GNUC__
1824#define ZIG_COLD __attribute__ ((cold))
1925#else
......@@ -165,7 +171,6 @@
165171#include <stdint.h>
166172#include <stddef.h>
167173#include <limits.h>
168#include <math.h>
169174
170175#define int128_t __int128
171176#define uint128_t unsigned __int128
lib/std/debug.zig+7-1
......@@ -1222,7 +1222,13 @@ pub const DebugInfo = struct {
12221222 }
12231223
12241224 pub fn getModuleForAddress(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
1225 if (comptime builtin.target.isDarwin()) {
1225 if (builtin.zig_backend == .stage2_c) {
1226 return @as(error{
1227 InvalidDebugInfo,
1228 MissingDebugInfo,
1229 UnsupportedBackend,
1230 }, error.UnsupportedBackend);
1231 } else if (comptime builtin.target.isDarwin()) {
12261232 return self.lookupModuleDyld(address);
12271233 } else if (native_os == .windows) {
12281234 return self.lookupModuleWin32(address);
lib/std/os/linux/x86_64.zig+12-5
......@@ -108,11 +108,18 @@ pub extern fn clone(func: CloneFn, stack: usize, flags: usize, arg: usize, ptid:
108108pub const restore = restore_rt;
109109
110110pub fn restore_rt() callconv(.Naked) void {
111 return asm volatile ("syscall"
112 :
113 : [number] "{rax}" (@enumToInt(SYS.rt_sigreturn)),
114 : "rcx", "r11", "memory"
115 );
111 switch (@import("builtin").zig_backend) {
112 .stage2_c => return asm volatile (std.fmt.comptimePrint(
113 \\ movl ${d}, %%eax
114 \\ syscall
115 \\ retq
116 , .{@enumToInt(SYS.rt_sigreturn)}) ::: "rcx", "r11", "memory"),
117 else => return asm volatile ("syscall"
118 :
119 : [number] "{rax}" (@enumToInt(SYS.rt_sigreturn)),
120 : "rcx", "r11", "memory"
121 ),
122 }
116123}
117124
118125pub const mode_t = usize;
lib/std/start.zig+86-72
......@@ -23,7 +23,6 @@ comptime {
2323 // Until then, we have simplified logic here for self-hosted. TODO remove this once
2424 // self-hosted is capable enough to handle all of the real start.zig logic.
2525 if (builtin.zig_backend == .stage2_wasm or
26 builtin.zig_backend == .stage2_c or
2726 builtin.zig_backend == .stage2_x86_64 or
2827 builtin.zig_backend == .stage2_x86 or
2928 builtin.zig_backend == .stage2_aarch64 or
......@@ -265,75 +264,90 @@ fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) callconv
265264}
266265
267266fn _start() callconv(.Naked) noreturn {
268 switch (native_arch) {
269 .x86_64 => {
270 argc_argv_ptr = asm volatile (
271 \\ xor %%rbp, %%rbp
272 : [argc] "={rsp}" (-> [*]usize),
273 );
274 },
275 .i386 => {
276 argc_argv_ptr = asm volatile (
277 \\ xor %%ebp, %%ebp
278 : [argc] "={esp}" (-> [*]usize),
279 );
280 },
281 .aarch64, .aarch64_be, .arm, .armeb, .thumb => {
282 argc_argv_ptr = asm volatile (
283 \\ mov fp, #0
284 \\ mov lr, #0
285 : [argc] "={sp}" (-> [*]usize),
286 );
287 },
288 .riscv64 => {
289 argc_argv_ptr = asm volatile (
290 \\ li s0, 0
291 \\ li ra, 0
292 : [argc] "={sp}" (-> [*]usize),
293 );
294 },
295 .mips, .mipsel => {
296 // The lr is already zeroed on entry, as specified by the ABI.
297 argc_argv_ptr = asm volatile (
298 \\ move $fp, $0
299 : [argc] "={sp}" (-> [*]usize),
300 );
301 },
302 .powerpc => {
303 // Setup the initial stack frame and clear the back chain pointer.
304 argc_argv_ptr = asm volatile (
305 \\ mr 4, 1
306 \\ li 0, 0
307 \\ stwu 1,-16(1)
308 \\ stw 0, 0(1)
309 \\ mtlr 0
310 : [argc] "={r4}" (-> [*]usize),
311 :
312 : "r0"
313 );
314 },
315 .powerpc64le => {
316 // Setup the initial stack frame and clear the back chain pointer.
317 // TODO: Support powerpc64 (big endian) on ELFv2.
318 argc_argv_ptr = asm volatile (
319 \\ mr 4, 1
320 \\ li 0, 0
321 \\ stdu 0, -32(1)
322 \\ mtlr 0
323 : [argc] "={r4}" (-> [*]usize),
324 :
325 : "r0"
326 );
267 switch (builtin.zig_backend) {
268 .stage2_c => switch (native_arch) {
269 .x86_64 => {
270 @export(argc_argv_ptr, .{ .name = "argc_argv_ptr" });
271 @export(posixCallMainAndExit, .{ .name = "_posixCallMainAndExit" });
272 asm volatile (
273 \\ xor %%rbp, %%rbp
274 \\ mov %%rsp, argc_argv_ptr
275 \\ call _posixCallMainAndExit
276 );
277 unreachable;
278 },
279 else => @compileError("unsupported arch"),
327280 },
328 .sparc64 => {
329 // argc is stored after a register window (16 registers) plus stack bias
330 argc_argv_ptr = asm (
331 \\ mov %%g0, %%i6
332 \\ add %%o6, 2175, %[argc]
333 : [argc] "=r" (-> [*]usize),
334 );
281 else => switch (native_arch) {
282 .x86_64 => {
283 argc_argv_ptr = asm volatile (
284 \\ xor %%rbp, %%rbp
285 : [argc] "={rsp}" (-> [*]usize),
286 );
287 },
288 .i386 => {
289 argc_argv_ptr = asm volatile (
290 \\ xor %%ebp, %%ebp
291 : [argc] "={esp}" (-> [*]usize),
292 );
293 },
294 .aarch64, .aarch64_be, .arm, .armeb, .thumb => {
295 argc_argv_ptr = asm volatile (
296 \\ mov fp, #0
297 \\ mov lr, #0
298 : [argc] "={sp}" (-> [*]usize),
299 );
300 },
301 .riscv64 => {
302 argc_argv_ptr = asm volatile (
303 \\ li s0, 0
304 \\ li ra, 0
305 : [argc] "={sp}" (-> [*]usize),
306 );
307 },
308 .mips, .mipsel => {
309 // The lr is already zeroed on entry, as specified by the ABI.
310 argc_argv_ptr = asm volatile (
311 \\ move $fp, $0
312 : [argc] "={sp}" (-> [*]usize),
313 );
314 },
315 .powerpc => {
316 // Setup the initial stack frame and clear the back chain pointer.
317 argc_argv_ptr = asm volatile (
318 \\ mr 4, 1
319 \\ li 0, 0
320 \\ stwu 1,-16(1)
321 \\ stw 0, 0(1)
322 \\ mtlr 0
323 : [argc] "={r4}" (-> [*]usize),
324 :
325 : "r0"
326 );
327 },
328 .powerpc64le => {
329 // Setup the initial stack frame and clear the back chain pointer.
330 // TODO: Support powerpc64 (big endian) on ELFv2.
331 argc_argv_ptr = asm volatile (
332 \\ mr 4, 1
333 \\ li 0, 0
334 \\ stdu 0, -32(1)
335 \\ mtlr 0
336 : [argc] "={r4}" (-> [*]usize),
337 :
338 : "r0"
339 );
340 },
341 .sparc64 => {
342 // argc is stored after a register window (16 registers) plus stack bias
343 argc_argv_ptr = asm (
344 \\ mov %%g0, %%i6
345 \\ add %%o6, 2175, %[argc]
346 : [argc] "=r" (-> [*]usize),
347 );
348 },
349 else => @compileError("unsupported arch"),
335350 },
336 else => @compileError("unsupported arch"),
337351 }
338352 // If LLVM inlines stack variables into _start, they will overwrite
339353 // the command line argument data.
......@@ -363,7 +377,7 @@ fn wWinMainCRTStartup() callconv(std.os.windows.WINAPI) noreturn {
363377 std.os.windows.kernel32.ExitProcess(@bitCast(std.os.windows.UINT, result));
364378}
365379
366fn posixCallMainAndExit() noreturn {
380fn posixCallMainAndExit() callconv(.C) noreturn {
367381 @setAlignStack(16);
368382
369383 const argc = argc_argv_ptr[0];
......@@ -462,7 +476,7 @@ fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 {
462476 return initEventLoopAndCallMain();
463477}
464478
465fn main(c_argc: i32, c_argv: [*][*:0]u8, c_envp: [*:null]?[*:0]u8) callconv(.C) i32 {
479fn main(c_argc: c_int, c_argv: [*c][*c]u8, c_envp: [*c][*c]u8) callconv(.C) c_int {
466480 var env_count: usize = 0;
467481 while (c_envp[env_count] != null) : (env_count += 1) {}
468482 const envp = @ptrCast([*][*:0]u8, c_envp)[0..env_count];
......@@ -474,10 +488,10 @@ fn main(c_argc: i32, c_argv: [*][*:0]u8, c_envp: [*:null]?[*:0]u8) callconv(.C)
474488 expandStackSize(phdrs);
475489 }
476490
477 return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp });
491 return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), @ptrCast([*][*:0]u8, c_argv), envp });
478492}
479493
480fn mainWithoutEnv(c_argc: i32, c_argv: [*][*:0]u8) callconv(.C) usize {
494fn mainWithoutEnv(c_argc: c_int, c_argv: [*c][*c]u8) callconv(.C) c_int {
481495 std.os.argv = c_argv[0..@intCast(usize, c_argc)];
482496 return @call(.{ .modifier = .always_inline }, callMain, .{});
483497}
lib/test_runner.zig+2-1
......@@ -8,7 +8,8 @@ var log_err_count: usize = 0;
88
99pub fn main() void {
1010 if (builtin.zig_backend != .stage1 and
11 (builtin.zig_backend != .stage2_llvm or builtin.cpu.arch == .wasm32))
11 (builtin.zig_backend != .stage2_llvm or builtin.cpu.arch == .wasm32) and
12 builtin.zig_backend != .stage2_c)
1213 {
1314 return main2() catch @panic("test failure");
1415 }
src/codegen/c.zig+204-112
......@@ -34,8 +34,8 @@ pub const CValue = union(enum) {
3434 /// By-value
3535 decl: Decl.Index,
3636 decl_ref: Decl.Index,
37 /// An undefined (void *) pointer (cannot be dereferenced)
38 undefined_ptr: Type,
37 /// An undefined value (cannot be dereferenced)
38 undef: Type,
3939 /// Render the slice as an identifier (using fmtIdent)
4040 identifier: []const u8,
4141 /// Render these bytes literally.
......@@ -342,8 +342,9 @@ pub const Function = struct {
342342 const float_bits = float_ty.floatBits(target);
343343 const is_longdouble = float_bits == CType.longdouble.sizeInBits(target);
344344 const writer = f.object.writer();
345 if (!is_longdouble and float_bits == 80) {
346 try writer.writeAll("__");
345 try writer.writeAll("__");
346 if (is_longdouble or float_bits != 80) {
347 try writer.writeAll("builtin_");
347348 }
348349 try writer.writeAll(fn_name);
349350 if (is_longdouble) {
......@@ -410,10 +411,17 @@ pub const DeclGen = struct {
410411 const decl = dg.module.declPtr(decl_index);
411412 assert(decl.has_tv);
412413
414 // Render an undefined pointer if we have a pointer to a zero-bit or comptime type.
413415 if (ty.isPtrAtRuntime() and !decl.ty.isFnOrHasRuntimeBits()) {
414 return dg.writeCValue(writer, CValue{ .undefined_ptr = ty });
416 return dg.writeCValue(writer, CValue{ .undef = ty });
415417 }
416418
419 // Chase function values in order to be able to reference the original function.
420 inline for (.{ .function, .extern_fn }) |tag|
421 if (decl.val.castTag(tag)) |func|
422 if (func.data.owner_decl != decl_index)
423 return dg.renderDeclValue(writer, ty, val, func.data.owner_decl);
424
417425 if (ty.isSlice()) {
418426 try writer.writeByte('(');
419427 try dg.renderTypecast(writer, ty);
......@@ -588,9 +596,9 @@ pub const DeclGen = struct {
588596 try writer.writeByte('(');
589597 try dg.renderTypecast(writer, ty);
590598 try writer.writeAll("){ .payload = ");
591 try dg.renderValue(writer, Type.bool, val, location);
592 try writer.writeAll(", .is_null = ");
593599 try dg.renderValue(writer, payload_ty, val, location);
600 try writer.writeAll(", .is_null = ");
601 try dg.renderValue(writer, Type.bool, val, location);
594602 return writer.writeAll(" }");
595603 },
596604 .Struct => {
......@@ -635,7 +643,7 @@ pub const DeclGen = struct {
635643 try writer.writeAll("){ .payload = ");
636644 try dg.renderValue(writer, ty.errorUnionPayload(), val, location);
637645 return writer.print(", .error = {x} }}", .{
638 try dg.fmtIntLiteral(ty.errorUnionSet(), Value.undef),
646 try dg.fmtIntLiteral(ty.errorUnionSet(), val),
639647 });
640648 },
641649 .Array => {
......@@ -844,13 +852,13 @@ pub const DeclGen = struct {
844852 }
845853 },
846854 .ErrorUnion => {
847 const error_type = ty.errorUnionSet();
848 const payload_type = ty.errorUnionPayload();
855 const error_ty = ty.errorUnionSet();
856 const payload_ty = ty.errorUnionPayload();
849857
850 if (!payload_type.hasRuntimeBits()) {
858 if (!payload_ty.hasRuntimeBits()) {
851859 // We use the error type directly as the type.
852860 const err_val = if (val.errorUnionIsPayload()) Value.initTag(.zero) else val;
853 return dg.renderValue(writer, error_type, err_val, location);
861 return dg.renderValue(writer, error_ty, err_val, location);
854862 }
855863
856864 try writer.writeByte('(');
......@@ -859,11 +867,15 @@ pub const DeclGen = struct {
859867 if (val.castTag(.eu_payload)) |pl| {
860868 const payload_val = pl.data;
861869 try writer.writeAll(" .payload = ");
862 try dg.renderValue(writer, payload_type, payload_val, location);
863 try writer.writeAll(", .error = 0 }");
870 try dg.renderValue(writer, payload_ty, payload_val, location);
871 try writer.print(", .error = {} }}", .{
872 try dg.fmtIntLiteral(error_ty, Value.zero),
873 });
864874 } else {
865 try writer.writeAll(" .error = ");
866 try dg.renderValue(writer, error_type, val, location);
875 try writer.writeAll(" .payload = ");
876 try dg.renderValue(writer, payload_ty, Value.undef, location);
877 try writer.writeAll(", .error = ");
878 try dg.renderValue(writer, error_ty, val, location);
867879 try writer.writeAll(" }");
868880 }
869881 },
......@@ -987,13 +999,16 @@ pub const DeclGen = struct {
987999 if (!is_global) {
9881000 try w.writeAll("static ");
9891001 }
1002 const fn_info = dg.decl.ty.fnInfo();
1003 if (fn_info.cc == .Naked) {
1004 try w.writeAll("ZIG_NAKED ");
1005 }
9901006 if (dg.decl.val.castTag(.function)) |func_payload| {
9911007 const func: *Module.Fn = func_payload.data;
9921008 if (func.is_cold) {
9931009 try w.writeAll("ZIG_COLD ");
9941010 }
9951011 }
996 const fn_info = dg.decl.ty.fnInfo();
9971012 if (fn_info.return_type.hasRuntimeBits()) {
9981013 try dg.renderType(w, fn_info.return_type);
9991014 } else if (fn_info.return_type.isError()) {
......@@ -1007,21 +1022,21 @@ pub const DeclGen = struct {
10071022 try dg.renderDeclName(w, dg.decl_index);
10081023 try w.writeByte('(');
10091024
1010 var params_written: usize = 0;
1011 for (fn_info.param_types) |param_type, index| {
1025 var index: usize = 0;
1026 for (fn_info.param_types) |param_type| {
10121027 if (!param_type.hasRuntimeBitsIgnoreComptime()) continue;
1013 if (params_written > 0) {
1028 if (index > 0) {
10141029 try w.writeAll(", ");
10151030 }
10161031 const name = CValue{ .arg = index };
10171032 try dg.renderTypeAndName(w, param_type, name, .Mut, 0);
1018 params_written += 1;
1033 index += 1;
10191034 }
10201035
10211036 if (fn_info.is_var_args) {
1022 if (params_written != 0) try w.writeAll(", ");
1037 if (index > 0) try w.writeAll(", ");
10231038 try w.writeAll("...");
1024 } else if (params_written == 0) {
1039 } else if (index == 0) {
10251040 try w.writeAll("void");
10261041 }
10271042 try w.writeByte(')');
......@@ -1307,7 +1322,7 @@ pub const DeclGen = struct {
13071322
13081323 fn renderErrorUnionTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
13091324 const payload_ty = t.errorUnionPayload();
1310 const error_ty = t.errorUnionSet();
1325 assert(t.errorUnionSet().tag() == .anyerror);
13111326
13121327 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
13131328 defer buffer.deinit();
......@@ -1328,15 +1343,7 @@ pub const DeclGen = struct {
13281343 }
13291344
13301345 const name_begin = buffer.items.len;
1331 if (error_ty.castTag(.error_set_inferred)) |inf_err_set_payload| {
1332 const func = inf_err_set_payload.data.func;
1333 try bw.writeAll("zig_E_");
1334 try dg.renderDeclName(bw, func.owner_decl);
1335 } else {
1336 try bw.print("zig_E_{}_{}", .{
1337 typeToCIdentifier(error_ty, dg.module), typeToCIdentifier(payload_ty, dg.module),
1338 });
1339 }
1346 try bw.print("zig_E_{}", .{typeToCIdentifier(payload_ty, dg.module)});
13401347 const name_end = buffer.items.len;
13411348 try bw.writeAll(";\n");
13421349
......@@ -1505,11 +1512,11 @@ pub const DeclGen = struct {
15051512 },
15061513 .Pointer => {
15071514 if (t.isSlice()) {
1508 var slice_ty_pl = Type.Payload.ElemType{
1515 var slice_pl = Type.Payload.ElemType{
15091516 .base = .{ .tag = if (t.ptrIsMutable()) .mut_slice else .const_slice },
15101517 .data = t.childType(),
15111518 };
1512 const slice_ty = Type.initPayload(&slice_ty_pl.base);
1519 const slice_ty = Type.initPayload(&slice_pl.base);
15131520
15141521 const name = dg.getTypedefName(slice_ty) orelse
15151522 try dg.renderSliceTypedef(slice_ty);
......@@ -1525,7 +1532,9 @@ pub const DeclGen = struct {
15251532 }
15261533
15271534 const child_ty = t.childType();
1528 if (t.isCPtr() and child_ty.eql(Type.u8, dg.module) and dg.decl.val.tag() == .extern_fn) {
1535 if (t.isCPtr() and child_ty.eql(Type.u8, dg.module) and
1536 (dg.decl.val.tag() == .extern_fn or std.mem.eql(u8, std.mem.span(dg.decl.name), "main")))
1537 {
15291538 // This is a hack, since the c compiler expects a lot of external
15301539 // library functions to have char pointers in their signatures, but
15311540 // u8 and i8 produce unsigned char and signed char respectively,
......@@ -1543,11 +1552,11 @@ pub const DeclGen = struct {
15431552 return w.writeAll(" *");
15441553 },
15451554 .Array => {
1546 var array_ty_pl = Type.Payload.Array{ .base = .{ .tag = .array }, .data = .{
1555 var array_pl = Type.Payload.Array{ .base = .{ .tag = .array }, .data = .{
15471556 .len = t.arrayLenIncludingSentinel(),
15481557 .elem_type = t.childType(),
15491558 } };
1550 const array_ty = Type.initPayload(&array_ty_pl.base);
1559 const array_ty = Type.initPayload(&array_pl.base);
15511560
15521561 const name = dg.getTypedefName(array_ty) orelse
15531562 try dg.renderArrayTypedef(array_ty);
......@@ -1582,8 +1591,13 @@ pub const DeclGen = struct {
15821591 return dg.renderType(w, Type.anyerror);
15831592 }
15841593
1585 const name = dg.getTypedefName(t) orelse
1586 try dg.renderErrorUnionTypedef(t);
1594 var error_union_pl = Type.Payload.ErrorUnion{
1595 .data = .{ .error_set = Type.anyerror, .payload = payload_ty },
1596 };
1597 const error_union_ty = Type.initPayload(&error_union_pl.base);
1598
1599 const name = dg.getTypedefName(error_union_ty) orelse
1600 try dg.renderErrorUnionTypedef(error_union_ty);
15871601
15881602 return w.writeAll(name);
15891603 },
......@@ -1603,8 +1617,8 @@ pub const DeclGen = struct {
16031617 },
16041618 .Enum => {
16051619 // For enums, we simply use the integer tag type.
1606 var int_tag_ty_buffer: Type.Payload.Bits = undefined;
1607 const int_tag_ty = t.intTagType(&int_tag_ty_buffer);
1620 var int_tag_buf: Type.Payload.Bits = undefined;
1621 const int_tag_ty = t.intTagType(&int_tag_buf);
16081622
16091623 try dg.renderType(w, int_tag_ty);
16101624 },
......@@ -1801,11 +1815,7 @@ pub const DeclGen = struct {
18011815 try w.writeByte('&');
18021816 return dg.renderDeclName(w, decl);
18031817 },
1804 .undefined_ptr => |ty| {
1805 try w.writeAll("((");
1806 try dg.renderTypecast(w, ty);
1807 return w.print("){x})", .{try dg.fmtIntLiteral(Type.usize, Value.undef)});
1808 },
1818 .undef => |ty| return dg.renderValue(w, ty, Value.undef, .Other),
18091819 .identifier => |ident| return w.print("{ }", .{fmtIdent(ident)}),
18101820 .bytes => |bytes| return w.writeAll(bytes),
18111821 }
......@@ -1824,7 +1834,7 @@ pub const DeclGen = struct {
18241834 return w.writeByte(')');
18251835 },
18261836 .decl_ref => |decl| return dg.renderDeclName(w, decl),
1827 .undefined_ptr => unreachable,
1837 .undef => unreachable,
18281838 .identifier => |ident| return w.print("(*{ })", .{fmtIdent(ident)}),
18291839 .bytes => |bytes| {
18301840 try w.writeAll("(*");
......@@ -2151,10 +2161,10 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
21512161
21522162 .mul_add => try airMulAdd(f, inst),
21532163
2154 .add_with_overflow => try airOverflow(f, inst, "addo_"),
2155 .sub_with_overflow => try airOverflow(f, inst, "subo_"),
2156 .mul_with_overflow => try airOverflow(f, inst, "mulo_"),
2157 .shl_with_overflow => try airOverflow(f, inst, "shlo_"),
2164 .add_with_overflow => try airOverflow(f, inst, "addo_", .range),
2165 .sub_with_overflow => try airOverflow(f, inst, "subo_", .range),
2166 .mul_with_overflow => try airOverflow(f, inst, "mulo_", .range),
2167 .shl_with_overflow => try airOverflow(f, inst, "shlo_", .bits),
21582168
21592169 .min => try airMinMax(f, inst, "<"),
21602170 .max => try airMinMax(f, inst, ">"),
......@@ -2284,8 +2294,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
22842294 .slice_ptr => try airSliceField(f, inst, " = ", ".ptr;\n"),
22852295 .slice_len => try airSliceField(f, inst, " = ", ".len;\n"),
22862296
2287 .ptr_slice_len_ptr => try airSliceField(f, inst, " = &", ".len;\n"),
2288 .ptr_slice_ptr_ptr => try airSliceField(f, inst, " = &", ".ptr;\n"),
2297 .ptr_slice_len_ptr => try airSliceField(f, inst, " = &", "->len;\n"),
2298 .ptr_slice_ptr_ptr => try airSliceField(f, inst, " = &", "->ptr;\n"),
22892299
22902300 .ptr_elem_val => try airPtrElemVal(f, inst),
22912301 .ptr_elem_ptr => try airPtrElemPtr(f, inst),
......@@ -2459,7 +2469,7 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
24592469 const elem_type = inst_ty.elemType();
24602470 const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut;
24612471 if (!elem_type.isFnOrHasRuntimeBitsIgnoreComptime()) {
2462 return CValue{ .undefined_ptr = inst_ty };
2472 return CValue{ .undef = inst_ty };
24632473 }
24642474
24652475 const target = f.object.dg.module.getTarget();
......@@ -2474,9 +2484,13 @@ fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue {
24742484 const writer = f.object.writer();
24752485 const inst_ty = f.air.typeOfIndex(inst);
24762486
2487 const elem_ty = inst_ty.elemType();
2488 if (!elem_ty.hasRuntimeBitsIgnoreComptime()) {
2489 return CValue{ .undef = inst_ty };
2490 }
2491
24772492 // First line: the variable used as data storage.
2478 const elem_type = inst_ty.elemType();
2479 const local = try f.allocLocal(elem_type, .Mut);
2493 const local = try f.allocLocal(elem_ty, .Mut);
24802494 try writer.writeAll(";\n");
24812495
24822496 return CValue{ .local_ref = local.local };
......@@ -2507,8 +2521,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
25072521 if (is_array) {
25082522 // Insert a memcpy to initialize this array. The source operand is always a pointer
25092523 // and thus we only need to know size/type information from the local type/dest.
2510 try writer.writeByte(';');
2511 try f.object.indent_writer.insertNewline();
2524 try writer.writeAll(";\n");
25122525 try writer.writeAll("memcpy(");
25132526 try f.writeCValue(writer, local);
25142527 try writer.writeAll(", ");
......@@ -2535,7 +2548,8 @@ fn airRet(f: *Function, inst: Air.Inst.Index) !CValue {
25352548 try writer.writeAll(";\n");
25362549 } else if (ret_ty.isError()) {
25372550 try writer.writeAll("return 0;");
2538 } else {
2551 } else if (f.object.dg.decl.ty.fnCallingConvention() != .Naked) {
2552 // Not even allowed to return void in a naked function.
25392553 try writer.writeAll("return;\n");
25402554 }
25412555 return CValue.none;
......@@ -2847,7 +2861,7 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue {
28472861 return ret;
28482862}
28492863
2850fn airOverflow(f: *Function, inst: Air.Inst.Index, op_abbrev: [*:0]const u8) !CValue {
2864fn airOverflow(f: *Function, inst: Air.Inst.Index, op_abbrev: [*:0]const u8, kind: enum { range, bits }) !CValue {
28512865 if (f.liveness.isUnused(inst))
28522866 return CValue.none;
28532867
......@@ -2879,23 +2893,29 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, op_abbrev: [*:0]const u8) !CV
28792893 try w.writeAll(", &");
28802894 try f.writeCValue(w, ret);
28812895 try w.writeAll(".field_0, ");
2882 {
2883 var arena = std.heap.ArenaAllocator.init(f.object.dg.module.gpa);
2884 defer arena.deinit();
2885
2886 const expected_contents = union { u: Value.Payload.U64, i: Value.Payload.I64 };
2887 var stack align(@alignOf(expected_contents)) =
2888 std.heap.stackFallback(@sizeOf(expected_contents), arena.allocator());
2889
2890 if (int_info.signedness == .signed) {
2891 const min_val = try scalar_ty.minInt(stack.get(), target);
2892 try w.print("{}, ", .{try f.fmtIntLiteral(scalar_ty, min_val)});
2893 }
2896 switch (kind) {
2897 .range => {
2898 var arena = std.heap.ArenaAllocator.init(f.object.dg.module.gpa);
2899 defer arena.deinit();
2900
2901 const expected_contents = union { u: Value.Payload.U64, i: Value.Payload.I64 };
2902 var stack align(@alignOf(expected_contents)) =
2903 std.heap.stackFallback(@sizeOf(expected_contents), arena.allocator());
2904
2905 if (int_info.signedness == .signed) {
2906 const min_val = try scalar_ty.minInt(stack.get(), target);
2907 try w.print("{}, ", .{try f.fmtIntLiteral(scalar_ty, min_val)});
2908 }
28942909
2895 const max_val = try scalar_ty.maxInt(stack.get(), target);
2896 try w.print("{});", .{try f.fmtIntLiteral(scalar_ty, max_val)});
2910 const max_val = try scalar_ty.maxInt(stack.get(), target);
2911 try w.print("{});\n", .{try f.fmtIntLiteral(scalar_ty, max_val)});
2912 },
2913 .bits => {
2914 var bits_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = int_info.bits };
2915 const bits_val = Value.initPayload(&bits_pl.base);
2916 try w.print("{x});\n", .{try f.fmtIntLiteral(Type.u8, bits_val)});
2917 },
28972918 }
2898 try f.object.indent_writer.insertNewline();
28992919 return ret;
29002920}
29012921
......@@ -3075,6 +3095,9 @@ fn airCall(
30753095 inst: Air.Inst.Index,
30763096 modifier: std.builtin.CallOptions.Modifier,
30773097) !CValue {
3098 // Not even allowed to call panic in a naked function.
3099 if (f.object.dg.decl.ty.fnCallingConvention() == .Naked) return .none;
3100
30783101 switch (modifier) {
30793102 .auto => {},
30803103 .always_tail => return f.fail("TODO: C backend: call with always_tail attribute", .{}),
......@@ -3094,10 +3117,10 @@ fn airCall(
30943117 const writer = f.object.writer();
30953118
30963119 const result_local: CValue = r: {
3097 if (f.liveness.isUnused(inst)) {
3098 if (loweredFnRetTyHasBits(fn_ty)) {
3099 try writer.print("(void)", .{});
3100 }
3120 if (!loweredFnRetTyHasBits(fn_ty)) {
3121 break :r .none;
3122 } else if (f.liveness.isUnused(inst)) {
3123 try writer.print("(void)", .{});
31013124 break :r .none;
31023125 } else {
31033126 const local = try f.allocLocal(fn_ty.fnReturnType(), .Const);
......@@ -3107,6 +3130,7 @@ fn airCall(
31073130 };
31083131
31093132 var is_extern = false;
3133 var name: [*:0]const u8 = "";
31103134 callee: {
31113135 known: {
31123136 const fn_decl = fn_decl: {
......@@ -3121,6 +3145,7 @@ fn airCall(
31213145 else => break :known,
31223146 };
31233147 };
3148 name = f.object.dg.module.declPtr(fn_decl).name;
31243149 try f.object.dg.renderDeclName(writer, fn_decl);
31253150 break :callee;
31263151 }
......@@ -3137,7 +3162,9 @@ fn airCall(
31373162 if (args_written != 0) {
31383163 try writer.writeAll(", ");
31393164 }
3140 if (is_extern and ty.isCPtr() and ty.childType().tag() == .u8) {
3165 if ((is_extern or std.mem.eql(u8, std.mem.span(name), "main")) and
3166 ty.isCPtr() and ty.childType().tag() == .u8)
3167 {
31413168 // Corresponds with hack in renderType .Pointer case.
31423169 try writer.writeAll("(char");
31433170 if (ty.isConstPtr()) try writer.writeAll(" const");
......@@ -3286,15 +3313,27 @@ fn lowerTry(
32863313 }
32873314 }
32883315
3289 const local = try f.allocLocal(result_ty, .Const);
3290 if (operand_is_ptr or isByRef(payload_ty)) {
3291 try writer.writeAll(" = &");
3316 const is_array = payload_ty.zigTypeTag() == .Array;
3317 const local = try f.allocLocal(result_ty, if (is_array) .Mut else .Const);
3318 if (is_array) {
3319 try writer.writeAll(";\n");
3320 try writer.writeAll("memcpy(");
3321 try f.writeCValue(writer, local);
3322 try writer.writeAll(", ");
32923323 try f.writeCValue(writer, err_union);
3293 try writer.writeAll("->payload;\n");
3324 try writer.writeAll(".payload, sizeof(");
3325 try f.writeCValue(writer, local);
3326 try writer.writeAll("));\n");
32943327 } else {
3295 try writer.writeAll(" = ");
3296 try f.writeCValue(writer, err_union);
3297 try writer.writeAll(".payload;\n");
3328 if (operand_is_ptr or isByRef(payload_ty)) {
3329 try writer.writeAll(" = &");
3330 try f.writeCValue(writer, err_union);
3331 try writer.writeAll("->payload;\n");
3332 } else {
3333 try writer.writeAll(" = ");
3334 try f.writeCValue(writer, err_union);
3335 try writer.writeAll(".payload;\n");
3336 }
32983337 }
32993338 return local;
33003339}
......@@ -3361,15 +3400,21 @@ fn airBreakpoint(f: *Function) !CValue {
33613400
33623401fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue {
33633402 if (f.liveness.isUnused(inst)) return CValue.none;
3403 const writer = f.object.writer();
33643404 const local = try f.allocLocal(Type.usize, .Const);
3365 try f.object.writer().writeAll(" = zig_return_address();\n");
3405 try writer.writeAll(" = (");
3406 try f.renderTypecast(writer, Type.usize);
3407 try writer.writeAll(")zig_return_address();\n");
33663408 return local;
33673409}
33683410
33693411fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue {
33703412 if (f.liveness.isUnused(inst)) return CValue.none;
3413 const writer = f.object.writer();
33713414 const local = try f.allocLocal(Type.usize, .Const);
3372 try f.object.writer().writeAll(" = zig_frame_address();\n");
3415 try writer.writeAll(" = (");
3416 try f.renderTypecast(writer, Type.usize);
3417 try writer.writeAll(")zig_frame_address();\n");
33733418 return local;
33743419}
33753420
......@@ -3385,6 +3430,9 @@ fn airFence(f: *Function, inst: Air.Inst.Index) !CValue {
33853430}
33863431
33873432fn airUnreach(f: *Function) !CValue {
3433 // Not even allowed to call unreachable in a naked function.
3434 if (f.object.dg.decl.ty.fnCallingConvention() == .Naked) return .none;
3435
33883436 try f.object.writer().writeAll("zig_unreachable();\n");
33893437 return CValue.none;
33903438}
......@@ -3479,15 +3527,12 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
34793527 const local = try f.allocLocal(inst_ty, .Mut);
34803528 if (f.wantSafety()) {
34813529 try writer.writeAll(" = ");
3482 try f.object.dg.renderValue(writer, inst_ty, Value.undef, .Other);
3530 try f.writeCValue(writer, .{ .undef = inst_ty });
34833531 }
34843532 try writer.writeAll(";\n");
34853533 break :local local;
34863534 } else .none;
34873535
3488 try writer.writeAll("{\n");
3489 f.object.indent_writer.pushIndent();
3490
34913536 const output_locals_begin = f.next_local_index;
34923537 f.next_local_index += outputs.len;
34933538 const constraints_extra_begin = extra_i;
......@@ -3510,7 +3555,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
35103555 try writer.writeAll("\")");
35113556 if (f.wantSafety()) {
35123557 try writer.writeAll(" = ");
3513 try f.object.dg.renderValue(writer, output_ty, Value.undef, .Other);
3558 try f.writeCValue(writer, .{ .undef = output_ty });
35143559 }
35153560 try writer.writeAll(";\n");
35163561 } else if (constraint.len < 2 or constraint[0] != '=') {
......@@ -3605,7 +3650,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
36053650 } else {
36063651 const input_val = try f.resolveInst(input);
36073652 try writer.print("{s}(", .{fmtStringLiteral(constraint)});
3608 try f.writeCValue(writer, if (input_val == .constant) .{
3653 try f.writeCValue(writer, if (input_val == .constant) CValue{
36093654 .local = input_locals_begin + index,
36103655 } else input_val);
36113656 }
......@@ -3647,9 +3692,6 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
36473692 }
36483693 }
36493694
3650 f.object.indent_writer.popIndent();
3651 try writer.writeAll("}\n");
3652
36533695 return local;
36543696}
36553697
......@@ -3728,7 +3770,7 @@ fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue {
37283770 const inst_ty = f.air.typeOfIndex(inst);
37293771
37303772 if (!inst_ty.childType().hasRuntimeBitsIgnoreComptime()) {
3731 return CValue{ .undefined_ptr = inst_ty };
3773 return CValue{ .undef = inst_ty };
37323774 }
37333775
37343776 if (opt_ty.optionalReprIsPayload()) {
......@@ -3996,7 +4038,9 @@ fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
39964038 }
39974039
39984040 const local = try f.allocLocal(err_un_ty, .Const);
3999 try writer.writeAll(" = { .error = ");
4041 try writer.writeAll(" = { .payload = ");
4042 try f.writeCValue(writer, .{ .undef = payload_ty });
4043 try writer.writeAll(", .error = ");
40004044 try f.writeCValue(writer, operand);
40014045 try writer.writeAll(" };\n");
40024046 return local;
......@@ -4059,10 +4103,25 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
40594103 const operand = try f.resolveInst(ty_op.operand);
40604104
40614105 const inst_ty = f.air.typeOfIndex(inst);
4062 const local = try f.allocLocal(inst_ty, .Const);
4063 try writer.writeAll(" = { .error = 0, .payload = ");
4064 try f.writeCValue(writer, operand);
4065 try writer.writeAll(" };\n");
4106 const payload_ty = inst_ty.errorUnionPayload();
4107 const is_array = payload_ty.zigTypeTag() == .Array;
4108 const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const);
4109 try writer.writeAll(" = { .payload = ");
4110 try f.writeCValue(writer, if (is_array) CValue{ .undef = payload_ty } else operand);
4111 try writer.print(", .error = {} }};\n", .{
4112 try f.fmtIntLiteral(inst_ty.errorUnionSet(), Value.zero),
4113 });
4114
4115 if (is_array) {
4116 try writer.writeAll("memcpy(");
4117 try f.writeCValue(writer, local);
4118 try writer.writeAll(".payload, ");
4119 try f.writeCValue(writer, operand);
4120 try writer.writeAll(", sizeof(");
4121 try f.writeCValue(writer, local);
4122 try writer.writeAll(".payload));\n");
4123 }
4124
40664125 return local;
40674126}
40684127
......@@ -4114,11 +4173,11 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
41144173 const array_len = f.air.typeOf(ty_op.operand).elemType().arrayLen();
41154174
41164175 try writer.writeAll(" = { .ptr = ");
4117 if (operand == .undefined_ptr) {
4176 if (operand == .undef) {
41184177 // Unfortunately, C does not support any equivalent to
41194178 // &(*(void *)p)[0], although LLVM does via GetElementPtr
41204179 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
4121 try f.writeCValue(writer, CValue{ .undefined_ptr = inst_ty.slicePtrFieldType(&buf) });
4180 try f.writeCValue(writer, CValue{ .undef = inst_ty.slicePtrFieldType(&buf) });
41224181 } else {
41234182 try writer.writeAll("&(");
41244183 try f.writeCValueDeref(writer, operand);
......@@ -4528,9 +4587,12 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
45284587 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
45294588 const len = @intCast(usize, inst_ty.arrayLen());
45304589 const elements = @ptrCast([]const Air.Inst.Ref, f.air.extra[ty_pl.payload..][0..len]);
4590 const mutability: Mutability = for (elements) |element| {
4591 if (f.air.typeOf(element).zigTypeTag() == .Array) break .Mut;
4592 } else .Const;
45314593
45324594 const writer = f.object.writer();
4533 const local = try f.allocLocal(inst_ty, .Const);
4595 const local = try f.allocLocal(inst_ty, mutability);
45344596 try writer.writeAll(" = {");
45354597 switch (inst_ty.zigTypeTag()) {
45364598 .Array => {
......@@ -4547,6 +4609,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
45474609 empty = false;
45484610 }
45494611 if (empty) try writer.print("{}", .{try f.fmtIntLiteral(Type.u8, Value.zero)});
4612 try writer.writeAll("};\n");
45504613 },
45514614 .Struct => {
45524615 var empty = true;
......@@ -4557,15 +4620,44 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
45574620 if (!inst_ty.isTupleOrAnonStruct()) {
45584621 try writer.print(".{ } = ", .{fmtIdent(inst_ty.structFieldName(index))});
45594622 }
4560 try f.writeCValue(writer, try f.resolveInst(element));
4623
4624 const element_ty = f.air.typeOf(element);
4625 try f.writeCValue(writer, switch (element_ty.zigTypeTag()) {
4626 .Array => CValue{ .undef = element_ty },
4627 else => try f.resolveInst(element),
4628 });
45614629 empty = false;
45624630 }
45634631 if (empty) try writer.print("{}", .{try f.fmtIntLiteral(Type.u8, Value.zero)});
4632 try writer.writeAll("};\n");
4633
4634 for (elements) |element, index| {
4635 if (inst_ty.structFieldValueComptime(index)) |_| continue;
4636
4637 const element_ty = f.air.typeOf(element);
4638 if (element_ty.zigTypeTag() != .Array) continue;
4639
4640 var field_name_storage: ?[]u8 = null;
4641 defer if (field_name_storage) |storage| f.object.dg.gpa.free(storage);
4642 const field_name = if (inst_ty.isTupleOrAnonStruct()) field_name: {
4643 const name = try std.fmt.allocPrint(f.object.dg.gpa, "field_{d}", .{index});
4644 field_name_storage = name;
4645 break :field_name name;
4646 } else inst_ty.structFieldName(index);
4647
4648 try writer.writeAll(";\n");
4649 try writer.writeAll("memcpy(");
4650 try f.writeCValue(writer, local);
4651 try writer.print(".{ }, ", .{fmtIdent(field_name)});
4652 try f.writeCValue(writer, try f.resolveInst(element));
4653 try writer.writeAll(", sizeof(");
4654 try f.writeCValue(writer, local);
4655 try writer.print(".{ }));\n", .{fmtIdent(field_name)});
4656 }
45644657 },
45654658 .Vector => return f.fail("TODO: C backend: implement airAggregateInit for vectors", .{}),
45664659 else => unreachable,
45674660 }
4568 try writer.writeAll("};\n");
45694661
45704662 return local;
45714663}
......@@ -4732,8 +4824,8 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {
47324824
47334825fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 {
47344826 return switch (order) {
4735 .Unordered => "memory_order_relaxed",
4736 .Monotonic => "memory_order_consume",
4827 // Note: unordered is actually even less atomic than relaxed
4828 .Unordered, .Monotonic => "memory_order_relaxed",
47374829 .Acquire => "memory_order_acquire",
47384830 .Release => "memory_order_release",
47394831 .AcqRel => "memory_order_acq_rel",
src/main.zig+4-1
......@@ -3018,7 +3018,10 @@ fn buildOutputType(
30183018 const c_code_path = try fs.path.join(arena, &[_][]const u8{
30193019 c_code_directory.path orelse ".", c_code_loc.basename,
30203020 });
3021 try test_exec_args.appendSlice(&.{ self_exe_path, "run", "-lc", c_code_path });
3021 try test_exec_args.append(self_exe_path);
3022 try test_exec_args.append("run");
3023 if (link_libc) try test_exec_args.append("-lc");
3024 try test_exec_args.append(c_code_path);
30223025 }
30233026
30243027 const run_or_test = switch (arg_mode) {
test/behavior/bugs/3742.zig-1
......@@ -38,6 +38,5 @@ test "fixed" {
3838 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
3939 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
4040 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
41 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
4241 ArgSerializer.serializeCommand(GET.init("banana"));
4342}
test/behavior/fn.zig-1
......@@ -272,7 +272,6 @@ test "void parameters" {
272272 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
273273 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
274274
275 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
276275 try voidFun(1, void{}, 2, {});
277276}
278277fn voidFun(a: i32, b: void, c: i32, d: void) !void {
test/behavior/struct.zig-2
......@@ -1163,7 +1163,6 @@ test "for loop over pointers to struct, getting field from struct pointer" {
11631163
11641164test "anon init through error unions and optionals" {
11651165 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
1166 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
11671166 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
11681167 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
11691168 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
......@@ -1234,7 +1233,6 @@ test "anon init through error union" {
12341233
12351234test "typed init through error unions and optionals" {
12361235 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
1237 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
12381236 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
12391237 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
12401238 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/behavior/translate_c_macros.zig-1
......@@ -100,7 +100,6 @@ test "nested comma operator" {
100100test "cast functions" {
101101 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
102102 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
103 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
104103 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
105104 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
106105