Commit a0ac10d903

Andrew Kelley <andrew@ziglang.org>
2022-11-02 20:53:59
Merge pull request #13389 from jacobly0/fix-only-c
cbe: enough fixes for `-Donly-c` to be able to produce an executable
1 parent 5f11e34
lib/include/zig.h
@@ -19,7 +19,7 @@
 #endif
 
 #if __STDC_VERSION__ >= 201112L
-#define zig_threadlocal thread_local
+#define zig_threadlocal _Thread_local
 #elif defined(__GNUC__)
 #define zig_threadlocal __thread
 #elif _MSC_VER
lib/std/crypto/blake3.zig
@@ -200,7 +200,10 @@ const CompressGeneric = struct {
     }
 };
 
-const compress = if (builtin.cpu.arch == .x86_64) CompressVectorized.compress else CompressGeneric.compress;
+const compress = if (builtin.cpu.arch == .x86_64 and builtin.zig_backend != .stage2_c)
+    CompressVectorized.compress
+else
+    CompressGeneric.compress;
 
 fn first8Words(words: [16]u32) [8]u32 {
     return @ptrCast(*const [8]u32, &words).*;
lib/std/crypto/gimli.zig
@@ -152,7 +152,7 @@ pub const State = struct {
         self.endianSwap();
     }
 
-    pub const permute = if (builtin.cpu.arch == .x86_64) impl: {
+    pub const permute = if (builtin.cpu.arch == .x86_64 and builtin.zig_backend != .stage2_c) impl: {
         break :impl permute_vectorized;
     } else if (builtin.mode == .ReleaseSmall) impl: {
         break :impl permute_small;
lib/std/zig/system/x86.zig
@@ -528,26 +528,20 @@ const CpuidLeaf = packed struct {
 };
 
 fn cpuid(leaf_id: u32, subid: u32) CpuidLeaf {
-    // Workaround for https://github.com/ziglang/zig/issues/215
-    // Inline assembly in zig only supports one output,
-    // so we pass a pointer to the struct.
-    var cpuid_leaf: CpuidLeaf = undefined;
-
     // valid for both x86 and x86_64
-    asm volatile (
-        \\ cpuid
-        \\ movl %%eax, 0(%[leaf_ptr])
-        \\ movl %%ebx, 4(%[leaf_ptr])
-        \\ movl %%ecx, 8(%[leaf_ptr])
-        \\ movl %%edx, 12(%[leaf_ptr])
-        :
-        : [leaf_id] "{eax}" (leaf_id),
-          [subid] "{ecx}" (subid),
-          [leaf_ptr] "r" (&cpuid_leaf),
-        : "eax", "ebx", "ecx", "edx"
+    var eax: u32 = undefined;
+    var ebx: u32 = undefined;
+    var ecx: u32 = undefined;
+    var edx: u32 = undefined;
+    asm volatile ("cpuid"
+        : [_] "={eax}" (eax),
+          [_] "={ebx}" (ebx),
+          [_] "={ecx}" (ecx),
+          [_] "={edx}" (edx),
+        : [_] "{eax}" (leaf_id),
+          [_] "{ecx}" (subid),
     );
-
-    return cpuid_leaf;
+    return .{ .eax = eax, .ebx = ebx, .ecx = ecx, .edx = edx };
 }
 
 // Read control register 0 (XCR0). Used to detect features such as AVX.
@@ -555,8 +549,8 @@ fn getXCR0() u32 {
     return asm volatile (
         \\ xor %%ecx, %%ecx
         \\ xgetbv
-        : [ret] "={eax}" (-> u32),
+        : [_] "={eax}" (-> u32),
         :
-        : "eax", "edx", "ecx"
+        : "edx", "ecx"
     );
 }
lib/std/build.zig
@@ -1622,7 +1622,6 @@ pub const LibExeObjStep = struct {
     use_stage1: ?bool = null,
     use_llvm: ?bool = null,
     use_lld: ?bool = null,
-    ofmt: ?std.Target.ObjectFormat = null,
 
     output_path_source: GeneratedFile,
     output_lib_path_source: GeneratedFile,
@@ -2490,7 +2489,7 @@ pub const LibExeObjStep = struct {
             }
         }
 
-        if (self.ofmt) |ofmt| {
+        if (self.target.ofmt) |ofmt| {
             try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)}));
         }
 
lib/std/multi_array_list.zig
@@ -436,9 +436,15 @@ pub fn MultiArrayList(comptime S: type) type {
         }
 
         fn capacityInBytes(capacity: usize) usize {
-            const sizes_vector: @Vector(sizes.bytes.len, usize) = sizes.bytes;
-            const capacity_vector = @splat(sizes.bytes.len, capacity);
-            return @reduce(.Add, capacity_vector * sizes_vector);
+            if (builtin.zig_backend == .stage2_c) {
+                var bytes: usize = 0;
+                for (sizes.bytes) |size| bytes += size * capacity;
+                return bytes;
+            } else {
+                const sizes_vector: @Vector(sizes.bytes.len, usize) = sizes.bytes;
+                const capacity_vector = @splat(sizes.bytes.len, capacity);
+                return @reduce(.Add, capacity_vector * sizes_vector);
+            }
         }
 
         fn allocatedBytes(self: Self) []align(@alignOf(S)) u8 {
lib/std/target.zig
@@ -1,4 +1,5 @@
 const std = @import("std.zig");
+const builtin = @import("builtin");
 const mem = std.mem;
 const Version = std.builtin.Version;
 
@@ -719,7 +720,11 @@ pub const Target = struct {
 
                 /// Adds the specified feature set but not its dependencies.
                 pub fn addFeatureSet(set: *Set, other_set: Set) void {
-                    set.ints = @as(@Vector(usize_count, usize), set.ints) | @as(@Vector(usize_count, usize), other_set.ints);
+                    if (builtin.zig_backend == .stage2_c) {
+                        for (set.ints) |*int, i| int.* |= other_set.ints[i];
+                    } else {
+                        set.ints = @as(@Vector(usize_count, usize), set.ints) | @as(@Vector(usize_count, usize), other_set.ints);
+                    }
                 }
 
                 /// Removes the specified feature but not its dependents.
@@ -731,7 +736,11 @@ pub const Target = struct {
 
                 /// Removes the specified feature but not its dependents.
                 pub fn removeFeatureSet(set: *Set, other_set: Set) void {
-                    set.ints = @as(@Vector(usize_count, usize), set.ints) & ~@as(@Vector(usize_count, usize), other_set.ints);
+                    if (builtin.zig_backend == .stage2_c) {
+                        for (set.ints) |*int, i| int.* &= ~other_set.ints[i];
+                    } else {
+                        set.ints = @as(@Vector(usize_count, usize), set.ints) & ~@as(@Vector(usize_count, usize), other_set.ints);
+                    }
                 }
 
                 pub fn populateDependencies(set: *Set, all_features_list: []const Cpu.Feature) void {
src/codegen/c.zig
@@ -32,6 +32,8 @@ pub const CValue = union(enum) {
     constant: Air.Inst.Ref,
     /// Index into the parameters
     arg: usize,
+    /// Index into a tuple's fields
+    field: usize,
     /// By-value
     decl: Decl.Index,
     decl_ref: Decl.Index,
@@ -79,7 +81,6 @@ const BuiltinInfo = enum {
     Bits,
 };
 
-/// TODO make this not cut off at 128 bytes
 fn formatTypeAsCIdentifier(
     data: FormatTypeAsCIdentContext,
     comptime fmt: []const u8,
@@ -1297,7 +1298,8 @@ pub const DeclGen = struct {
         var fqn_buf = std.ArrayList(u8).init(dg.typedefs.allocator);
         defer fqn_buf.deinit();
 
-        const owner_decl = dg.module.declPtr(child_ty.getOwnerDecl());
+        const owner_decl_index = child_ty.getOwnerDecl();
+        const owner_decl = dg.module.declPtr(owner_decl_index);
         try owner_decl.renderFullyQualifiedName(dg.module, fqn_buf.writer());
 
         var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
@@ -1309,7 +1311,11 @@ pub const DeclGen = struct {
             else => unreachable,
         };
         const name_begin = buffer.items.len + "typedef ".len + tag.len;
-        try buffer.writer().print("typedef {s}zig_S_{} ", .{ tag, fmtIdent(fqn_buf.items) });
+        try buffer.writer().print("typedef {s}zig_S_{}__{d} ", .{
+            tag,
+            fmtIdent(fqn_buf.items),
+            @enumToInt(owner_decl_index),
+        });
         const name_end = buffer.items.len - " ".len;
         try buffer.ensureUnusedCapacity((name_end - name_begin) + ";\n".len);
         buffer.appendSliceAssumeCapacity(buffer.items[name_begin..name_end]);
@@ -1378,26 +1384,17 @@ pub const DeclGen = struct {
         try buffer.appendSlice("typedef struct {\n");
         {
             const fields = t.tupleFields();
-            var empty = true;
+            var field_id: usize = 0;
             for (fields.types) |field_ty, i| {
-                if (!field_ty.hasRuntimeBits()) continue;
-                const val = fields.values[i];
-                if (val.tag() != .unreachable_value) continue;
-
-                var field_name_buf: []const u8 = &.{};
-                defer dg.typedefs.allocator.free(field_name_buf);
-                const field_name = if (t.isTuple()) field_name: {
-                    field_name_buf = try std.fmt.allocPrint(dg.typedefs.allocator, "field_{d}", .{i});
-                    break :field_name field_name_buf;
-                } else t.structFieldName(i);
+                if (!field_ty.hasRuntimeBits() or fields.values[i].tag() != .unreachable_value) continue;
 
                 try buffer.append(' ');
-                try dg.renderTypeAndName(buffer.writer(), field_ty, .{ .identifier = field_name }, .Mut, 0, .Complete);
+                try dg.renderTypeAndName(buffer.writer(), field_ty, .{ .field = field_id }, .Mut, 0, .Complete);
                 try buffer.appendSlice(";\n");
 
-                empty = false;
+                field_id += 1;
             }
-            if (empty) try buffer.appendSlice(" char empty_tuple;\n");
+            if (field_id == 0) try buffer.appendSlice(" char empty_tuple;\n");
         }
         const name_begin = buffer.items.len + "} ".len;
         try buffer.writer().print("}} zig_T_{};\n", .{typeToCIdentifier(t, dg.module)});
@@ -1751,12 +1748,38 @@ pub const DeclGen = struct {
             },
             .Struct, .Union => |tag| if (tag == .Struct and t.containerLayout() == .Packed)
                 try dg.renderType(w, t.castTag(.@"struct").?.data.backing_int_ty, kind)
-            else if (kind == .Complete or t.isTupleOrAnonStruct()) {
+            else if (t.isTupleOrAnonStruct()) {
+                const ExpectedContents = struct { types: [8]Type, values: [8]Value };
+                var stack align(@alignOf(ExpectedContents)) =
+                    std.heap.stackFallback(@sizeOf(ExpectedContents), dg.gpa);
+                const allocator = stack.get();
+
+                var tuple_storage = std.MultiArrayList(struct { type: Type, value: Value }){};
+                defer tuple_storage.deinit(allocator);
+                try tuple_storage.ensureTotalCapacity(allocator, t.structFieldCount());
+
+                const fields = t.tupleFields();
+                for (fields.values) |value, index|
+                    if (value.tag() == .unreachable_value)
+                        tuple_storage.appendAssumeCapacity(.{
+                            .type = fields.types[index],
+                            .value = value,
+                        });
+
+                const tuple_slice = tuple_storage.slice();
+                var tuple_pl = Type.Payload.Tuple{ .data = .{
+                    .types = tuple_slice.items(.type),
+                    .values = tuple_slice.items(.value),
+                } };
+                const tuple_ty = Type.initPayload(&tuple_pl.base);
+
+                const name = dg.getTypedefName(tuple_ty) orelse
+                    try dg.renderTupleTypedef(tuple_ty);
+
+                try w.writeAll(name);
+            } else if (kind == .Complete) {
                 const name = dg.getTypedefName(t) orelse switch (tag) {
-                    .Struct => if (t.isTupleOrAnonStruct())
-                        try dg.renderTupleTypedef(t)
-                    else
-                        try dg.renderStructTypedef(t),
+                    .Struct => try dg.renderStructTypedef(t),
                     .Union => try dg.renderUnionTypedef(t),
                     else => unreachable,
                 };
@@ -1976,6 +1999,7 @@ pub const DeclGen = struct {
             .local_ref => |i| return w.print("&t{d}", .{i}),
             .constant => unreachable,
             .arg => |i| return w.print("a{d}", .{i}),
+            .field => |i| return w.print("f{d}", .{i}),
             .decl => |decl| return dg.renderDeclName(w, decl),
             .decl_ref => |decl| {
                 try w.writeByte('&');
@@ -1994,6 +2018,7 @@ pub const DeclGen = struct {
             .local_ref => |i| return w.print("t{d}", .{i}),
             .constant => unreachable,
             .arg => |i| return w.print("(*a{d})", .{i}),
+            .field => |i| return w.print("f{d}", .{i}),
             .decl => |decl| {
                 try w.writeAll("(*");
                 try dg.renderDeclName(w, decl);
@@ -2018,7 +2043,7 @@ pub const DeclGen = struct {
 
     fn writeCValueDerefMember(dg: *DeclGen, writer: anytype, c_value: CValue, member: CValue) !void {
         switch (c_value) {
-            .none, .constant, .undef => unreachable,
+            .none, .constant, .field, .undef => unreachable,
             .local, .arg, .decl, .identifier, .bytes => {
                 try dg.writeCValue(writer, c_value);
                 try writer.writeAll("->");
@@ -2236,12 +2261,6 @@ pub fn genDecl(o: *Object) !void {
         const variable: *Module.Var = var_payload.data;
         const is_global = o.dg.declIsGlobal(tv) or variable.is_extern;
         const fwd_decl_writer = o.dg.fwd_decl.writer();
-        if (is_global) {
-            try fwd_decl_writer.writeAll("zig_extern_c ");
-        }
-        if (variable.is_threadlocal) {
-            try fwd_decl_writer.writeAll("zig_threadlocal ");
-        }
 
         const decl_c_value: CValue = if (is_global) .{
             .bytes = mem.span(o.dg.decl.name),
@@ -2249,6 +2268,8 @@ pub fn genDecl(o: *Object) !void {
             .decl = o.dg.decl_index,
         };
 
+        if (is_global) try fwd_decl_writer.writeAll("zig_extern_c ");
+        if (variable.is_threadlocal) try fwd_decl_writer.writeAll("zig_threadlocal ");
         try o.dg.renderTypeAndName(fwd_decl_writer, o.dg.decl.ty, decl_c_value, .Mut, o.dg.decl.@"align", .Complete);
         try fwd_decl_writer.writeAll(";\n");
 
@@ -2257,6 +2278,7 @@ pub fn genDecl(o: *Object) !void {
         }
 
         const w = o.writer();
+        if (variable.is_threadlocal) try w.writeAll("zig_threadlocal ");
         try o.dg.renderTypeAndName(w, o.dg.decl.ty, decl_c_value, .Mut, o.dg.decl.@"align", .Complete);
         try w.writeAll(" = ");
         if (variable.init.tag() != .unreachable_value) {
@@ -2595,19 +2617,36 @@ fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: [
 }
 
 fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
+    const inst_ty = f.air.typeOfIndex(inst);
     const bin_op = f.air.instructions.items(.data)[inst].bin_op;
     const ptr_ty = f.air.typeOf(bin_op.lhs);
-    if (!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst)) return CValue.none;
+    if ((!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst)) or
+        !inst_ty.hasRuntimeBitsIgnoreComptime()) return CValue.none;
 
     const ptr = try f.resolveInst(bin_op.lhs);
     const index = try f.resolveInst(bin_op.rhs);
+
+    const target = f.object.dg.module.getTarget();
+    const is_array = lowersToArray(inst_ty, target);
+
+    const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const);
     const writer = f.object.writer();
-    const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const);
-    try writer.writeAll(" = ");
+    if (is_array) {
+        try writer.writeAll(";\n");
+        try writer.writeAll("memcpy(");
+        try f.writeCValue(writer, local, .FunctionArgument);
+        try writer.writeAll(", ");
+    } else try writer.writeAll(" = ");
     try f.writeCValue(writer, ptr, .Other);
     try writer.writeByte('[');
     try f.writeCValue(writer, index, .Other);
-    try writer.writeAll("];\n");
+    try writer.writeByte(']');
+    if (is_array) {
+        try writer.writeAll(", sizeof(");
+        try f.renderTypecast(writer, inst_ty);
+        try writer.writeAll("))");
+    }
+    try writer.writeAll(";\n");
     return local;
 }
 
@@ -2637,19 +2676,36 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
 }
 
 fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
+    const inst_ty = f.air.typeOfIndex(inst);
     const bin_op = f.air.instructions.items(.data)[inst].bin_op;
     const slice_ty = f.air.typeOf(bin_op.lhs);
-    if (!slice_ty.isVolatilePtr() and f.liveness.isUnused(inst)) return CValue.none;
+    if ((!slice_ty.isVolatilePtr() and f.liveness.isUnused(inst)) or
+        !inst_ty.hasRuntimeBitsIgnoreComptime()) return CValue.none;
 
     const slice = try f.resolveInst(bin_op.lhs);
     const index = try f.resolveInst(bin_op.rhs);
+
+    const target = f.object.dg.module.getTarget();
+    const is_array = lowersToArray(inst_ty, target);
+
+    const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const);
     const writer = f.object.writer();
-    const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const);
-    try writer.writeAll(" = ");
+    if (is_array) {
+        try writer.writeAll(";\n");
+        try writer.writeAll("memcpy(");
+        try f.writeCValue(writer, local, .FunctionArgument);
+        try writer.writeAll(", ");
+    } else try writer.writeAll(" = ");
     try f.writeCValue(writer, slice, .Other);
     try writer.writeAll(".ptr[");
     try f.writeCValue(writer, index, .Other);
-    try writer.writeAll("];\n");
+    try writer.writeByte(']');
+    if (is_array) {
+        try writer.writeAll(", sizeof(");
+        try f.renderTypecast(writer, inst_ty);
+        try writer.writeAll("))");
+    }
+    try writer.writeAll(";\n");
     return local;
 }
 
@@ -2672,18 +2728,34 @@ fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
 }
 
 fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
-    if (f.liveness.isUnused(inst)) return CValue.none;
+    const inst_ty = f.air.typeOfIndex(inst);
+    if (f.liveness.isUnused(inst) or !inst_ty.hasRuntimeBitsIgnoreComptime()) return CValue.none;
 
     const bin_op = f.air.instructions.items(.data)[inst].bin_op;
     const array = try f.resolveInst(bin_op.lhs);
     const index = try f.resolveInst(bin_op.rhs);
+
+    const target = f.object.dg.module.getTarget();
+    const is_array = lowersToArray(inst_ty, target);
+
+    const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const);
     const writer = f.object.writer();
-    const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const);
-    try writer.writeAll(" = ");
+    if (is_array) {
+        try writer.writeAll(";\n");
+        try writer.writeAll("memcpy(");
+        try f.writeCValue(writer, local, .FunctionArgument);
+        try writer.writeAll(", ");
+    } else try writer.writeAll(" = ");
     try f.writeCValue(writer, array, .Other);
     try writer.writeByte('[');
     try f.writeCValue(writer, index, .Other);
-    try writer.writeAll("];\n");
+    try writer.writeByte(']');
+    if (is_array) {
+        try writer.writeAll(", sizeof(");
+        try f.renderTypecast(writer, inst_ty);
+        try writer.writeAll("))");
+    }
+    try writer.writeAll(";\n");
     return local;
 }
 
@@ -2817,7 +2889,7 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue {
             const array_local = try f.allocLocal(lowered_ret_ty, .Mut);
             try writer.writeAll(";\n");
             try writer.writeAll("memcpy(");
-            try f.writeCValueMember(writer, array_local, .{ .identifier = "array" });
+            try f.writeCValueMember(writer, array_local, .{ .field = 0 });
             try writer.writeAll(", ");
             if (deref)
                 try f.writeCValueDeref(writer, operand)
@@ -3063,13 +3135,13 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info:
     const local = try f.allocLocal(inst_ty, .Mut);
     try w.writeAll(";\n");
 
-    try f.writeCValue(w, local, .Other);
-    try w.writeAll(".field_1 = zig_");
+    try f.writeCValueMember(w, local, .{ .field = 1 });
+    try w.writeAll(" = zig_");
     try w.writeAll(operation);
     try w.writeAll("o_");
     try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty);
     try w.writeAll("(&");
-    try f.writeCValueMember(w, local, .{ .identifier = "field_0" });
+    try f.writeCValueMember(w, local, .{ .field = 0 });
     try w.writeAll(", ");
     try f.writeCValue(w, lhs, .FunctionArgument);
     try w.writeAll(", ");
@@ -3191,7 +3263,7 @@ fn airEquality(
 
     try writer.writeAll(" = ");
 
-    if (operand_ty.tag() == .optional) {
+    if (operand_ty.zigTypeTag() == .Optional and !operand_ty.isPtrLikeOptional()) {
         // (A && B)  || (C && (A == B))
         // A = lhs.is_null  ;  B = rhs.is_null  ;  C = rhs.payload == lhs.payload
 
@@ -3429,7 +3501,7 @@ fn airCall(
     try writer.writeAll("memcpy(");
     try f.writeCValue(writer, array_local, .FunctionArgument);
     try writer.writeAll(", ");
-    try f.writeCValueMember(writer, result_local, .{ .identifier = "array" });
+    try f.writeCValueMember(writer, result_local, .{ .field = 0 });
     try writer.writeAll(", sizeof(");
     try f.renderTypecast(writer, ret_ty);
     try writer.writeAll("));\n");
@@ -3590,25 +3662,39 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !CValue {
     // If result is .none then the value of the block is unused.
     if (result != .none) {
         const operand = try f.resolveInst(branch.operand);
-        try f.writeCValue(writer, result, .Other);
-        try writer.writeAll(" = ");
-        try f.writeCValue(writer, operand, .Other);
+
+        const operand_ty = f.air.typeOf(branch.operand);
+        const target = f.object.dg.module.getTarget();
+        if (lowersToArray(operand_ty, target)) {
+            try writer.writeAll("memcpy(");
+            try f.writeCValue(writer, result, .FunctionArgument);
+            try writer.writeAll(", ");
+            try f.writeCValue(writer, operand, .FunctionArgument);
+            try writer.writeAll(", sizeof(");
+            try f.renderTypecast(writer, operand_ty);
+            try writer.writeAll("))");
+        } else {
+            try f.writeCValue(writer, result, .Other);
+            try writer.writeAll(" = ");
+            try f.writeCValue(writer, operand, .Other);
+        }
         try writer.writeAll(";\n");
     }
 
-    try f.object.writer().print("goto zig_block_{d};\n", .{block.block_id});
+    try writer.print("goto zig_block_{d};\n", .{block.block_id});
     return CValue.none;
 }
 
 fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
-    if (f.liveness.isUnused(inst))
-        return CValue.none;
+    const inst_ty = f.air.typeOfIndex(inst);
+    // No IgnoreComptime until Sema stops giving us garbage Air.
+    // https://github.com/ziglang/zig/issues/13410
+    if (f.liveness.isUnused(inst) or !inst_ty.hasRuntimeBits()) return CValue.none;
 
     const ty_op = f.air.instructions.items(.data)[inst].ty_op;
     const operand = try f.resolveInst(ty_op.operand);
 
     const writer = f.object.writer();
-    const inst_ty = f.air.typeOfIndex(inst);
     if (inst_ty.isPtrAtRuntime() and
         f.air.typeOf(ty_op.operand).isPtrAtRuntime())
     {
@@ -3982,9 +4068,9 @@ fn airIsNull(
 
     const rhs = if (!payload_ty.hasRuntimeBitsIgnoreComptime())
         TypedValue{ .ty = Type.bool, .val = Value.@"true" }
-    else if (operand_ty.isPtrLikeOptional())
+    else if (optional_ty.isPtrLikeOptional())
         // operand is a regular pointer, test `operand !=/== NULL`
-        TypedValue{ .ty = operand_ty, .val = Value.@"null" }
+        TypedValue{ .ty = optional_ty, .val = Value.@"null" }
     else if (payload_ty.zigTypeTag() == .ErrorSet)
         TypedValue{ .ty = payload_ty, .val = Value.zero }
     else if (payload_ty.isSlice() and optional_ty.optionalReprIsPayload()) rhs: {
@@ -4007,26 +4093,34 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue {
     if (f.liveness.isUnused(inst)) return CValue.none;
 
     const ty_op = f.air.instructions.items(.data)[inst].ty_op;
-    const writer = f.object.writer();
     const operand = try f.resolveInst(ty_op.operand);
     const opt_ty = f.air.typeOf(ty_op.operand);
 
     var buf: Type.Payload.ElemType = undefined;
     const payload_ty = opt_ty.optionalChild(&buf);
 
-    if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
-        return CValue.none;
-    }
-
-    if (opt_ty.optionalReprIsPayload()) {
-        return operand;
-    }
+    if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return CValue.none;
+    if (opt_ty.optionalReprIsPayload()) return operand;
 
     const inst_ty = f.air.typeOfIndex(inst);
-    const local = try f.allocLocal(inst_ty, .Const);
-    try writer.writeAll(" = (");
-    try f.writeCValue(writer, operand, .Other);
-    try writer.writeAll(").payload;\n");
+    const target = f.object.dg.module.getTarget();
+    const is_array = lowersToArray(inst_ty, target);
+
+    const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const);
+    const writer = f.object.writer();
+    if (is_array) {
+        try writer.writeAll(";\n");
+        try writer.writeAll("memcpy(");
+        try f.writeCValue(writer, local, .FunctionArgument);
+        try writer.writeAll(", ");
+    } else try writer.writeAll(" = ");
+    try f.writeCValueMember(writer, operand, .{ .identifier = "payload" });
+    if (is_array) {
+        try writer.writeAll(", sizeof(");
+        try f.renderTypecast(writer, inst_ty);
+        try writer.writeAll("))");
+    }
+    try writer.writeAll(";\n");
     return local;
 }
 
@@ -4159,16 +4253,14 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc
     try f.renderTypecast(writer, field_ptr_ty);
     try writer.writeByte(')');
 
-    const extra_name: ?[]const u8 = switch (struct_ty.tag()) {
-        .union_tagged, .union_safety_tagged => "payload",
-        else => null,
+    const extra_name: CValue = switch (struct_ty.tag()) {
+        .union_tagged, .union_safety_tagged => .{ .identifier = "payload" },
+        else => .none,
     };
 
-    var field_name_buf: []const u8 = &.{};
-    defer f.object.dg.gpa.free(field_name_buf);
-    const field_name: ?[]const u8 = switch (struct_ty.tag()) {
+    const field_name: CValue = switch (struct_ty.tag()) {
         .@"struct" => switch (struct_ty.containerLayout()) {
-            .Auto, .Extern => struct_ty.structFieldName(index),
+            .Auto, .Extern => CValue{ .identifier = struct_ty.structFieldName(index) },
             .Packed => if (field_ptr_info.data.host_size == 0) {
                 const target = f.object.dg.module.getTarget();
 
@@ -4189,29 +4281,35 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc
                 try f.writeCValue(writer, struct_ptr, .Other);
                 try writer.print(")[{}];\n", .{try f.fmtIntLiteral(Type.usize, byte_offset_val)});
                 return local;
-            } else null,
+            } else @as(CValue, CValue.none), // this @as is needed because of a stage1 bug
+        },
+        .@"union", .union_safety_tagged, .union_tagged => .{
+            .identifier = struct_ty.unionFields().keys()[index],
         },
-        .@"union", .union_safety_tagged, .union_tagged => struct_ty.unionFields().keys()[index],
-        .tuple, .anon_struct => |tag| field_name: {
+        .tuple, .anon_struct => field_name: {
             const tuple = struct_ty.tupleFields();
             if (tuple.values[index].tag() != .unreachable_value) return CValue.none;
 
-            if (tag == .anon_struct) break :field_name struct_ty.structFieldName(index);
-
-            field_name_buf = try std.fmt.allocPrint(f.object.dg.gpa, "field_{d}", .{index});
-            break :field_name field_name_buf;
+            var id: usize = 0;
+            for (tuple.values[0..index]) |value|
+                id += @boolToInt(value.tag() == .unreachable_value);
+            break :field_name .{ .field = id };
         },
         else => unreachable,
     };
 
     if (field_ty.hasRuntimeBitsIgnoreComptime()) {
         try writer.writeByte('&');
-        if (extra_name orelse field_name) |name|
-            try f.writeCValueDerefMember(writer, struct_ptr, .{ .identifier = name })
+        if (extra_name != .none) {
+            try f.writeCValueDerefMember(writer, struct_ptr, extra_name);
+            if (field_name != .none) {
+                try writer.writeByte('.');
+                try f.writeCValue(writer, field_name, .Other);
+            }
+        } else if (field_name != .none)
+            try f.writeCValueDerefMember(writer, struct_ptr, field_name)
         else
             try f.writeCValueDeref(writer, struct_ptr);
-        if (extra_name) |_| if (field_name) |name|
-            try writer.print(".{ }", .{fmtIdent(name)});
     } else try f.writeCValue(writer, struct_ptr, .Other);
     try writer.writeAll(";\n");
     return local;
@@ -4221,9 +4319,11 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
     if (f.liveness.isUnused(inst))
         return CValue.none;
 
+    const inst_ty = f.air.typeOfIndex(inst);
+    if (!inst_ty.hasRuntimeBitsIgnoreComptime()) return CValue.none;
+
     const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
     const extra = f.air.extraData(Air.StructField, ty_pl.payload).data;
-    const inst_ty = f.air.typeOfIndex(inst);
     const target = f.object.dg.module.getTarget();
     const struct_byval = try f.resolveInst(extra.struct_operand);
     const struct_ty = f.air.typeOf(extra.struct_operand);
@@ -4232,11 +4332,14 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
     // Ensure complete type definition is visible before accessing fields.
     try f.renderType(std.io.null_writer, struct_ty);
 
-    var field_name_buf: []const u8 = "";
-    defer f.object.dg.gpa.free(field_name_buf);
-    const field_name = switch (struct_ty.tag()) {
+    const extra_name: CValue = switch (struct_ty.tag()) {
+        .union_tagged, .union_safety_tagged => .{ .identifier = "payload" },
+        else => .none,
+    };
+
+    const field_name: CValue = switch (struct_ty.tag()) {
         .@"struct" => switch (struct_ty.containerLayout()) {
-            .Auto, .Extern => struct_ty.structFieldName(extra.field_index),
+            .Auto, .Extern => .{ .identifier = struct_ty.structFieldName(extra.field_index) },
             .Packed => {
                 const struct_obj = struct_ty.castTag(.@"struct").?.data;
                 const int_info = struct_ty.intInfo(target);
@@ -4294,19 +4397,20 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
                 return local;
             },
         },
-        .@"union", .union_safety_tagged, .union_tagged => struct_ty.unionFields().keys()[extra.field_index],
-        .tuple, .anon_struct => |tag| blk: {
+        .@"union", .union_safety_tagged, .union_tagged => .{
+            .identifier = struct_ty.unionFields().keys()[extra.field_index],
+        },
+        .tuple, .anon_struct => blk: {
             const tuple = struct_ty.tupleFields();
             if (tuple.values[extra.field_index].tag() != .unreachable_value) return CValue.none;
 
-            if (tag == .anon_struct) break :blk struct_ty.structFieldName(extra.field_index);
-
-            field_name_buf = try std.fmt.allocPrint(f.object.dg.gpa, "field_{d}", .{extra.field_index});
-            break :blk field_name_buf;
+            var id: usize = 0;
+            for (tuple.values[0..extra.field_index]) |value|
+                id += @boolToInt(value.tag() == .unreachable_value);
+            break :blk .{ .field = id };
         },
         else => unreachable,
     };
-    const payload = if (struct_ty.tag() == .union_tagged or struct_ty.tag() == .union_safety_tagged) "payload." else "";
 
     const is_array = lowersToArray(inst_ty, target);
     const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const);
@@ -4315,15 +4419,18 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
         try writer.writeAll("memcpy(");
         try f.writeCValue(writer, local, .FunctionArgument);
         try writer.writeAll(", ");
-        try f.writeCValue(writer, struct_byval, .Other);
-        try writer.print(".{s}{ }, sizeof(", .{ payload, fmtIdent(field_name) });
+    } else try writer.writeAll(" = ");
+    if (extra_name != .none) {
+        try f.writeCValueMember(writer, struct_byval, extra_name);
+        try writer.writeByte('.');
+        try f.writeCValue(writer, field_name, .Other);
+    } else try f.writeCValueMember(writer, struct_byval, field_name);
+    if (is_array) {
+        try writer.writeAll(", sizeof(");
         try f.renderTypecast(writer, inst_ty);
-        try writer.writeAll("));\n");
-    } else {
-        try writer.writeAll(" = ");
-        try f.writeCValue(writer, struct_byval, .Other);
-        try writer.print(".{s}{ };\n", .{ payload, fmtIdent(field_name) });
+        try writer.writeAll("))");
     }
+    try writer.writeAll(";\n");
     return local;
 }
 
@@ -4383,25 +4490,33 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu
 }
 
 fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue {
-    if (f.liveness.isUnused(inst))
-        return CValue.none;
+    if (f.liveness.isUnused(inst)) return CValue.none;
 
+    const inst_ty = f.air.typeOfIndex(inst);
     const ty_op = f.air.instructions.items(.data)[inst].ty_op;
-    const writer = f.object.writer();
-    const operand = try f.resolveInst(ty_op.operand);
+    const payload = try f.resolveInst(ty_op.operand);
+    if (inst_ty.optionalReprIsPayload()) return payload;
 
-    const inst_ty = f.air.typeOfIndex(inst);
-    if (inst_ty.optionalReprIsPayload()) {
-        return operand;
-    }
+    const payload_ty = f.air.typeOf(ty_op.operand);
+    const target = f.object.dg.module.getTarget();
+    const is_array = lowersToArray(payload_ty, target);
 
-    // .wrap_optional is used to convert non-optionals into optionals so it can never be null.
-    const local = try f.allocLocal(inst_ty, .Const);
+    const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const);
+    const writer = f.object.writer();
     try writer.writeAll(" = { .payload = ");
-    try f.writeCValue(writer, operand, .Initializer);
+    try f.writeCValue(writer, if (is_array) CValue{ .undef = payload_ty } else payload, .Initializer);
     try writer.writeAll(", .is_null = ");
     try f.object.dg.renderValue(writer, Type.bool, Value.@"false", .Initializer);
     try writer.writeAll(" };\n");
+    if (is_array) {
+        try writer.writeAll("memcpy(");
+        try f.writeCValueMember(writer, local, .{ .identifier = "payload" });
+        try writer.writeAll(", ");
+        try f.writeCValue(writer, payload, .FunctionArgument);
+        try writer.writeAll(", sizeof(");
+        try f.renderTypecast(writer, payload_ty);
+        try writer.writeAll("));\n");
+    }
     return local;
 }
 
@@ -4473,35 +4588,33 @@ fn airSaveErrReturnTraceIndex(f: *Function, inst: Air.Inst.Index) !CValue {
 }
 
 fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
-    if (f.liveness.isUnused(inst))
-        return CValue.none;
-
-    const ty_op = f.air.instructions.items(.data)[inst].ty_op;
-    const writer = f.object.writer();
-    const operand = try f.resolveInst(ty_op.operand);
+    if (f.liveness.isUnused(inst)) return CValue.none;
 
     const inst_ty = f.air.typeOfIndex(inst);
-    const payload_ty = inst_ty.errorUnionPayload();
     const error_ty = inst_ty.errorUnionSet();
+    const ty_op = f.air.instructions.items(.data)[inst].ty_op;
+    const payload_ty = inst_ty.errorUnionPayload();
+    const payload = try f.resolveInst(ty_op.operand);
+
     const target = f.object.dg.module.getTarget();
     const is_array = lowersToArray(payload_ty, target);
+
     const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const);
+    const writer = f.object.writer();
     try writer.writeAll(" = { .payload = ");
-    try f.writeCValue(writer, if (is_array) CValue{ .undef = payload_ty } else operand, .Initializer);
+    try f.writeCValue(writer, if (is_array) CValue{ .undef = payload_ty } else payload, .Initializer);
     try writer.writeAll(", .error = ");
     try f.object.dg.renderValue(writer, error_ty, Value.zero, .Initializer);
     try writer.writeAll(" };\n");
-
     if (is_array) {
         try writer.writeAll("memcpy(");
-        try f.writeCValue(writer, local, .Other);
-        try writer.writeAll(".payload, ");
-        try f.writeCValue(writer, operand, .FunctionArgument);
+        try f.writeCValueMember(writer, local, .{ .identifier = "payload" });
+        try writer.writeAll(", ");
+        try f.writeCValue(writer, payload, .FunctionArgument);
         try writer.writeAll(", sizeof(");
         try f.renderTypecast(writer, payload_ty);
         try writer.writeAll("));\n");
     }
-
     return local;
 }
 
@@ -4845,10 +4958,41 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa
 fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue {
     const pl_op = f.air.instructions.items(.data)[inst].pl_op;
     const extra = f.air.extraData(Air.Bin, pl_op.payload).data;
+    const dest_ty = f.air.typeOf(pl_op.operand);
     const dest_ptr = try f.resolveInst(pl_op.operand);
     const value = try f.resolveInst(extra.lhs);
     const len = try f.resolveInst(extra.rhs);
+
     const writer = f.object.writer();
+    if (dest_ty.isVolatilePtr()) {
+        var u8_ptr_pl = dest_ty.ptrInfo();
+        u8_ptr_pl.data.pointee_type = Type.u8;
+        const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base);
+
+        try writer.writeAll("for (");
+        const index = try f.allocLocal(Type.usize, .Mut);
+        try writer.writeAll(" = ");
+        try f.object.dg.renderValue(writer, Type.usize, Value.zero, .Initializer);
+        try writer.writeAll("; ");
+        try f.writeCValue(writer, index, .Other);
+        try writer.writeAll(" != ");
+        try f.writeCValue(writer, len, .Other);
+        try writer.writeAll("; ");
+        try f.writeCValue(writer, index, .Other);
+        try writer.writeAll(" += ");
+        try f.object.dg.renderValue(writer, Type.usize, Value.one, .Other);
+        try writer.writeAll(") ((");
+        try f.renderTypecast(writer, u8_ptr_ty);
+        try writer.writeByte(')');
+        try f.writeCValue(writer, dest_ptr, .FunctionArgument);
+        try writer.writeAll(")[");
+        try f.writeCValue(writer, index, .Other);
+        try writer.writeAll("] = ");
+        try f.writeCValue(writer, value, .FunctionArgument);
+        try writer.writeAll(";\n");
+
+        return CValue.none;
+    }
 
     try writer.writeAll("memset(");
     try f.writeCValue(writer, dest_ptr, .FunctionArgument);
@@ -5068,27 +5212,28 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
                 if (empty) try writer.print("{}", .{try f.fmtIntLiteral(Type.u8, Value.zero)});
                 try writer.writeAll("};\n");
 
+                var field_id: usize = 0;
                 for (elements) |element, index| {
                     if (inst_ty.structFieldValueComptime(index)) |_| continue;
 
                     const element_ty = f.air.typeOf(element);
                     if (element_ty.zigTypeTag() != .Array) continue;
 
-                    var field_name_buf: []u8 = &.{};
-                    defer f.object.dg.gpa.free(field_name_buf);
-                    const field_name = if (inst_ty.isTuple()) field_name: {
-                        field_name_buf = try std.fmt.allocPrint(f.object.dg.gpa, "field_{d}", .{index});
-                        break :field_name field_name_buf;
-                    } else inst_ty.structFieldName(index);
+                    const field_name = if (inst_ty.isTupleOrAnonStruct())
+                        CValue{ .field = field_id }
+                    else
+                        CValue{ .identifier = inst_ty.structFieldName(index) };
 
                     try writer.writeAll(";\n");
                     try writer.writeAll("memcpy(");
-                    try f.writeCValue(writer, local, .Other);
-                    try writer.print(".{ }, ", .{fmtIdent(field_name)});
+                    try f.writeCValueMember(writer, local, field_name);
+                    try writer.writeAll(", ");
                     try f.writeCValue(writer, try f.resolveInst(element), .FunctionArgument);
                     try writer.writeAll(", sizeof(");
                     try f.renderTypecast(writer, element_ty);
                     try writer.writeAll("));\n");
+
+                    field_id += 1;
                 }
             },
             .Packed => {
@@ -5634,10 +5779,9 @@ fn isByRef(ty: Type) bool {
 }
 
 const LowerFnRetTyBuffer = struct {
-    const names = [1][]const u8{"array"};
     types: [1]Type,
     values: [1]Value,
-    payload: Type.Payload.AnonStruct,
+    payload: Type.Payload.Tuple,
 };
 fn lowerFnRetTy(ret_ty: Type, buffer: *LowerFnRetTyBuffer, target: std.Target) Type {
     if (ret_ty.zigTypeTag() == .NoReturn) return Type.initTag(.noreturn);
@@ -5646,7 +5790,6 @@ fn lowerFnRetTy(ret_ty: Type, buffer: *LowerFnRetTyBuffer, target: std.Target) T
         buffer.types = [1]Type{ret_ty};
         buffer.values = [1]Value{Value.initTag(.unreachable_value)};
         buffer.payload = .{ .data = .{
-            .names = &LowerFnRetTyBuffer.names,
             .types = &buffer.types,
             .values = &buffer.values,
         } };
test/behavior/bugs/11139.zig
@@ -3,7 +3,6 @@ const builtin = @import("builtin");
 const expect = std.testing.expect;
 
 test "store array of array of structs at comptime" {
-    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
 
test/behavior/bugs/1851.zig
@@ -5,7 +5,6 @@ const expect = std.testing.expect;
 test "allocation and looping over 3-byte integer" {
     if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
-    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
 
test/behavior/bugs/7250.zig
@@ -14,7 +14,6 @@ threadlocal var g_uart0 = nrfx_uart_t{
 };
 
 test "reference a global threadlocal variable" {
-    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
test/behavior/basic.zig
@@ -725,7 +725,6 @@ test "comptime manyptr concatenation" {
 }
 
 test "thread local variable" {
-    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
test/behavior/cast.zig
@@ -1178,7 +1178,6 @@ fn cast128Float(x: u128) f128 {
 
 test "implicit cast from *[N]T to ?[*]T" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
 
test/behavior/fn.zig
@@ -355,7 +355,6 @@ test "function call with anon list literal" {
 }
 
 test "function call with anon list literal - 2D" {
-    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
test/behavior/if.zig
@@ -146,7 +146,6 @@ test "result location with inferred type ends up being pointer to comptime_int"
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
 
     var a: ?u32 = 1234;
     var b: u32 = 2000;
test/behavior/type.zig
@@ -260,7 +260,6 @@ test "Type.ErrorSet" {
 
 test "Type.Struct" {
     if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
-    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
test/tests.zig
@@ -52,6 +52,9 @@ const test_targets = blk: {
         },
 
         .{
+            .target = .{
+                .ofmt = .c,
+            },
             .link_libc = true,
             .backend = .stage2_c,
         },
@@ -720,7 +723,6 @@ pub fn addPkgTests(
             .stage2_c => {
                 these_tests.use_stage1 = false;
                 these_tests.use_llvm = false;
-                these_tests.ofmt = .c;
             },
             else => {
                 these_tests.use_stage1 = false;
build.zig
@@ -17,7 +17,7 @@ pub fn build(b: *Builder) !void {
     b.setPreferredReleaseMode(.ReleaseFast);
     const test_step = b.step("test", "Run all the tests");
     const mode = b.standardReleaseOptions();
-    const target = b.standardTargetOptions(.{});
+    var target = b.standardTargetOptions(.{});
     const single_threaded = b.option(bool, "single-threaded", "Build artifacts that run in single threaded mode");
     const use_zig_libcxx = b.option(bool, "use-zig-libcxx", "If libc++ is needed, use zig's bundled version, don't try to integrate with the system") orelse false;
 
@@ -141,6 +141,10 @@ pub fn build(b: *Builder) !void {
         break :blk 4;
     };
 
+    if (only_c) {
+        target.ofmt = .c;
+    }
+
     const main_file: ?[]const u8 = mf: {
         if (!have_stage1) break :mf "src/main.zig";
         if (use_zig0) break :mf null;
@@ -172,10 +176,6 @@ pub fn build(b: *Builder) !void {
         test_cases.want_lto = false;
     }
 
-    if (only_c) {
-        exe.ofmt = .c;
-    }
-
     const exe_options = b.addOptions();
     exe.addOptions("build_options", exe_options);