diff --git a/build.zig b/build.zig index daea8c1ca7..5c5c7cc0ae 100644 --- a/build.zig +++ b/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); diff --git a/lib/include/zig.h b/lib/include/zig.h index 32d0c8277e..1db8c06f96 100644 --- a/lib/include/zig.h +++ b/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 diff --git a/lib/std/build.zig b/lib/std/build.zig index 3f903a0b12..8c6af98eea 100644 --- a/lib/std/build.zig +++ b/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)})); } diff --git a/lib/std/crypto/blake3.zig b/lib/std/crypto/blake3.zig index 762ec67f31..4f8d023532 100644 --- a/lib/std/crypto/blake3.zig +++ b/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).*; diff --git a/lib/std/crypto/gimli.zig b/lib/std/crypto/gimli.zig index 96a7d69e6f..4952937697 100644 --- a/lib/std/crypto/gimli.zig +++ b/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; diff --git a/lib/std/multi_array_list.zig b/lib/std/multi_array_list.zig index c3bfa6537b..89c7869b6b 100644 --- a/lib/std/multi_array_list.zig +++ b/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 { diff --git a/lib/std/target.zig b/lib/std/target.zig index 342e535c27..34bae7cda2 100644 --- a/lib/std/target.zig +++ b/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 { diff --git a/lib/std/zig/system/x86.zig b/lib/std/zig/system/x86.zig index 1dbdcabfbc..66468ba6ff 100644 --- a/lib/std/zig/system/x86.zig +++ b/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" ); } diff --git a/src/codegen/c.zig b/src/codegen/c.zig index f8fa2a1003..0f82ed79f1 100644 --- a/src/codegen/c.zig +++ b/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 => struct_ty.unionFields().keys()[index], - .tuple, .anon_struct => |tag| field_name: { + .@"union", .union_safety_tagged, .union_tagged => .{ + .identifier = struct_ty.unionFields().keys()[index], + }, + .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; - - 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); - if (inst_ty.optionalReprIsPayload()) { - return operand; - } + const ty_op = f.air.instructions.items(.data)[inst].ty_op; + const payload = try f.resolveInst(ty_op.operand); + if (inst_ty.optionalReprIsPayload()) return payload; - // .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 payload_ty = f.air.typeOf(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, 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, } }; diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index ddeac2a030..b0b25d3774 100644 --- a/test/behavior/basic.zig +++ b/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 diff --git a/test/behavior/bugs/11139.zig b/test/behavior/bugs/11139.zig index 9ceaaeb0e4..615fd37689 100644 --- a/test/behavior/bugs/11139.zig +++ b/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 diff --git a/test/behavior/bugs/1851.zig b/test/behavior/bugs/1851.zig index cdbd5e26f7..6209a2f2f1 100644 --- a/test/behavior/bugs/1851.zig +++ b/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 diff --git a/test/behavior/bugs/7250.zig b/test/behavior/bugs/7250.zig index dd3d357ad3..011dc87362 100644 --- a/test/behavior/bugs/7250.zig +++ b/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 diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index b1f9e86c05..573f3a7ac2 100644 --- a/test/behavior/cast.zig +++ b/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 diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index ad92cb8962..06127aa855 100644 --- a/test/behavior/fn.zig +++ b/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 diff --git a/test/behavior/if.zig b/test/behavior/if.zig index 8117c1db3f..8a001a65d4 100644 --- a/test/behavior/if.zig +++ b/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; diff --git a/test/behavior/type.zig b/test/behavior/type.zig index 42cb67ba2f..81aeda6171 100644 --- a/test/behavior/type.zig +++ b/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 diff --git a/test/tests.zig b/test/tests.zig index 266d2a37d0..81be2c9d5c 100644 --- a/test/tests.zig +++ b/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;