From 458233a68478fae94c340b7f1814efde493ac4d7 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 25 Oct 2022 05:06:54 -0400 Subject: [PATCH 01/47] fmt: fix f80 hex formatting These ifs were missing a case for f80 which should have shifted by one, but we can just compute the correct value instead. Also, we want the fractional bits to be a multiple of four, not the mantissa bits, since the mantissa could have a leading one which we want to be separated. --- lib/std/fmt.zig | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 0c9ecbc08c..0fcb2ae5b1 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -1207,10 +1207,9 @@ pub fn formatFloatHexadecimal( mantissa |= 1 << fractional_bits; // Add the implicit integer bit. } - // Fill in zeroes to round the mantissa width to a multiple of 4. - if (T == f16) mantissa <<= 2 else if (T == f32) mantissa <<= 1; - const mantissa_digits = (fractional_bits + 3) / 4; + // Fill in zeroes to round the fraction width to a multiple of 4. + mantissa <<= mantissa_digits * 4 - fractional_bits; if (options.precision) |precision| { // Round if needed. @@ -2317,21 +2316,25 @@ test "float.hexadecimal" { try expectFmt("f16: 0x1.554p-2", "f16: {x}", .{@as(f16, 1.0 / 3.0)}); try expectFmt("f32: 0x1.555556p-2", "f32: {x}", .{@as(f32, 1.0 / 3.0)}); try expectFmt("f64: 0x1.5555555555555p-2", "f64: {x}", .{@as(f64, 1.0 / 3.0)}); + try expectFmt("f80: 0x1.5555555555555556p-2", "f80: {x}", .{@as(f80, 1.0 / 3.0)}); try expectFmt("f128: 0x1.5555555555555555555555555555p-2", "f128: {x}", .{@as(f128, 1.0 / 3.0)}); try expectFmt("f16: 0x1p-14", "f16: {x}", .{math.floatMin(f16)}); try expectFmt("f32: 0x1p-126", "f32: {x}", .{math.floatMin(f32)}); try expectFmt("f64: 0x1p-1022", "f64: {x}", .{math.floatMin(f64)}); + try expectFmt("f80: 0x1p-16382", "f80: {x}", .{math.floatMin(f80)}); try expectFmt("f128: 0x1p-16382", "f128: {x}", .{math.floatMin(f128)}); try expectFmt("f16: 0x0.004p-14", "f16: {x}", .{math.floatTrueMin(f16)}); try expectFmt("f32: 0x0.000002p-126", "f32: {x}", .{math.floatTrueMin(f32)}); try expectFmt("f64: 0x0.0000000000001p-1022", "f64: {x}", .{math.floatTrueMin(f64)}); + try expectFmt("f80: 0x0.0000000000000002p-16382", "f80: {x}", .{math.floatTrueMin(f80)}); try expectFmt("f128: 0x0.0000000000000000000000000001p-16382", "f128: {x}", .{math.floatTrueMin(f128)}); try expectFmt("f16: 0x1.ffcp15", "f16: {x}", .{math.floatMax(f16)}); try expectFmt("f32: 0x1.fffffep127", "f32: {x}", .{math.floatMax(f32)}); try expectFmt("f64: 0x1.fffffffffffffp1023", "f64: {x}", .{math.floatMax(f64)}); + try expectFmt("f80: 0x1.fffffffffffffffep16383", "f80: {x}", .{math.floatMax(f80)}); try expectFmt("f128: 0x1.ffffffffffffffffffffffffffffp16383", "f128: {x}", .{math.floatMax(f128)}); } @@ -2339,11 +2342,13 @@ test "float.hexadecimal.precision" { try expectFmt("f16: 0x1.5p-2", "f16: {x:.1}", .{@as(f16, 1.0 / 3.0)}); try expectFmt("f32: 0x1.555p-2", "f32: {x:.3}", .{@as(f32, 1.0 / 3.0)}); try expectFmt("f64: 0x1.55555p-2", "f64: {x:.5}", .{@as(f64, 1.0 / 3.0)}); - try expectFmt("f128: 0x1.5555555p-2", "f128: {x:.7}", .{@as(f128, 1.0 / 3.0)}); + try expectFmt("f80: 0x1.5555555p-2", "f80: {x:.7}", .{@as(f80, 1.0 / 3.0)}); + try expectFmt("f128: 0x1.555555555p-2", "f128: {x:.9}", .{@as(f128, 1.0 / 3.0)}); try expectFmt("f16: 0x1.00000p0", "f16: {x:.5}", .{@as(f16, 1.0)}); try expectFmt("f32: 0x1.00000p0", "f32: {x:.5}", .{@as(f32, 1.0)}); try expectFmt("f64: 0x1.00000p0", "f64: {x:.5}", .{@as(f64, 1.0)}); + try expectFmt("f80: 0x1.00000p0", "f80: {x:.5}", .{@as(f80, 1.0)}); try expectFmt("f128: 0x1.00000p0", "f128: {x:.5}", .{@as(f128, 1.0)}); } From ab024d3524f60c691221f6c838d6fe340c7c7cd7 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 4 Oct 2022 18:32:03 -0400 Subject: [PATCH 02/47] c: fix undefined tests in ReleaseSafe --- src/codegen/c.zig | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index d6584d75ae..704e8a1391 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -2279,16 +2279,17 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue { } fn airStoreUndefined(f: *Function, dest_ptr: CValue) !CValue { - const is_debug_build = f.object.dg.module.optimizeMode() == .Debug; - if (!is_debug_build) - return CValue.none; - - const writer = f.object.writer(); - try writer.writeAll("memset("); - try f.writeCValue(writer, dest_ptr); - try writer.writeAll(", 0xaa, sizeof("); - try f.writeCValueDeref(writer, dest_ptr); - try writer.writeAll("));\n"); + switch (f.object.dg.module.optimizeMode()) { + .Debug, .ReleaseSafe => { + const writer = f.object.writer(); + try writer.writeAll("memset("); + try f.writeCValue(writer, dest_ptr); + try writer.writeAll(", 0xaa, sizeof("); + try f.writeCValueDeref(writer, dest_ptr); + try writer.writeAll("));\n"); + }, + .ReleaseFast, .ReleaseSmall => {}, + } return CValue.none; } From c8d0e71de6c0e59b8f103526204b990fafe41f54 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 5 Oct 2022 01:21:11 -0400 Subject: [PATCH 03/47] c: fix mangling of error names Closes #12751 --- src/codegen/c.zig | 2 +- src/link/C.zig | 13 +++++++------ test/behavior/error.zig | 17 +++++++++++++++++ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 704e8a1391..4bc573ba6c 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -746,7 +746,7 @@ pub const DeclGen = struct { .@"error" => { const payload = val.castTag(.@"error").?; // error values will be #defined at the top of the file - return writer.print("zig_error_{s}", .{payload.data.name}); + return writer.print("zig_error_{s}", .{fmtIdent(payload.data.name)}); }, else => { // In this case we are rendering an error union which has a diff --git a/src/link/C.zig b/src/link/C.zig index 955044f90d..ebe37a8192 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -275,13 +275,14 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) const err_typedef_index = f.all_buffers.items.len; f.all_buffers.items.len += 1; - render_errors: { - if (module.global_error_set.size == 0) break :render_errors; + if (module.global_error_set.size > 0) { + try err_typedef_writer.writeAll("enum {\n"); var it = module.global_error_set.iterator(); - while (it.next()) |entry| { - try err_typedef_writer.print("#define zig_error_{s} {d}\n", .{ entry.key_ptr.*, entry.value_ptr.* }); - } - try err_typedef_writer.writeByte('\n'); + while (it.next()) |entry| try err_typedef_writer.print(" zig_error_{s} = {d},\n", .{ + codegen.fmtIdent(entry.key_ptr.*), + entry.value_ptr.*, + }); + try err_typedef_writer.writeAll("};\n"); } // Typedefs, forward decls, and non-functions first. diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 6b64a0dc01..7fb125ab2a 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -843,3 +843,20 @@ fn non_errorable() void { test "catch within a function that calls no errorable functions" { non_errorable(); } + +test "error from comptime string" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) 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 + + const name = "Weird error name!"; + const S = struct { + fn foo() !void { + return @field(anyerror, name); + } + }; + if (S.foo()) unreachable else |err| { + try expect(mem.eql(u8, name, @errorName(err))); + } +} From 6f3654ad694946876e3aac971fd284230424c915 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 5 Oct 2022 01:29:15 -0400 Subject: [PATCH 04/47] c: implement @errorName --- src/codegen/c.zig | 69 +++++++++++++++-- src/link/C.zig | 166 ++++++++++++++++++++-------------------- test/behavior/error.zig | 2 - 3 files changed, 145 insertions(+), 92 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 4bc573ba6c..183baca48d 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1580,6 +1580,66 @@ pub const DeclGen = struct { } }; +pub fn genErrDecls(o: *Object) !void { + if (o.dg.module.global_error_set.size == 0) return; + const writer = o.writer(); + + try writer.writeAll("enum {\n"); + o.indent_writer.pushIndent(); + var max_name_len: usize = 0; + for (o.dg.module.error_name_list.items) |name, value| { + max_name_len = std.math.max(name.len, max_name_len); + var err_val_payload = Value.Payload.Error{ .data = .{ .name = name } }; + try o.dg.renderValue(writer, Type.anyerror, Value.initPayload(&err_val_payload.base), .Other); + try writer.print(" = {d}u,\n", .{value}); + } + o.indent_writer.popIndent(); + try writer.writeAll("};\n"); + + const name_prefix = "zig_errorName_"; + const name_buf = try o.dg.gpa.alloc(u8, name_prefix.len + max_name_len + 1); + defer o.dg.gpa.free(name_buf); + + std.mem.copy(u8, name_buf, name_prefix); + for (o.dg.module.error_name_list.items) |name| { + std.mem.copy(u8, name_buf[name_prefix.len..], name); + name_buf[name_prefix.len + name.len] = 0; + + const identifier = name_buf[0 .. name_prefix.len + name.len :0]; + const nameZ = identifier[name_prefix.len..]; + + var name_ty_payload = Type.Payload.Len{ + .base = .{ .tag = .array_u8_sentinel_0 }, + .data = name.len, + }; + const name_ty = Type.initPayload(&name_ty_payload.base); + + var name_val_payload = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = nameZ }; + const name_val = Value.initPayload(&name_val_payload.base); + + try writer.writeAll("static "); + try o.dg.renderTypeAndName(writer, name_ty, .{ .identifier = identifier }, .Const, 0); + try writer.writeAll(" = "); + try o.dg.renderValue(writer, name_ty, name_val, .Other); + try writer.writeAll(";\n"); + } + + var name_array_ty_payload = Type.Payload.Array{ .base = .{ .tag = .array }, .data = .{ + .len = o.dg.module.error_name_list.items.len, + .elem_type = Type.initTag(.const_slice_u8_sentinel_0), + } }; + const name_array_ty = Type.initPayload(&name_array_ty_payload.base); + + try writer.writeAll("static "); + try o.dg.renderTypeAndName(writer, name_array_ty, .{ .identifier = "zig_errorName" }, .Const, 0); + try writer.writeAll(" = {"); + for (o.dg.module.error_name_list.items) |name, value| { + if (value != 0) try writer.writeByte(','); + try writer.print("{{zig_errorName_{}, {d}u}}", .{ fmtIdent(name), name.len }); + } + try writer.writeAll("};\n"); +} + pub fn genFunc(f: *Function) !void { const tracy = trace(@src()); defer tracy.end(); @@ -3982,11 +4042,10 @@ fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(un_op); const local = try f.allocLocal(inst_ty, .Const); - try writer.writeAll(" = "); - - _ = operand; - _ = local; - return f.fail("TODO: C backend: implement airErrorName", .{}); + try writer.writeAll(" = zig_errorName["); + try f.writeCValue(writer, operand); + try writer.writeAll("];\n"); + return local; } fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue { diff --git a/src/link/C.zig b/src/link/C.zig index ebe37a8192..ad64f76466 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -260,30 +260,15 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) var f: Flush = .{}; defer f.deinit(gpa); - // Covers zig.h and err_typedef_item. + // Covers zig.h and typedef. try f.all_buffers.ensureUnusedCapacity(gpa, 2); - if (zig_h.len != 0) { - f.all_buffers.appendAssumeCapacity(.{ - .iov_base = zig_h, - .iov_len = zig_h.len, - }); - f.file_size += zig_h.len; - } + f.appendBufAssumeCapacity(zig_h); - const err_typedef_writer = f.err_typedef_buf.writer(gpa); - const err_typedef_index = f.all_buffers.items.len; + const typedef_index = f.all_buffers.items.len; f.all_buffers.items.len += 1; - if (module.global_error_set.size > 0) { - try err_typedef_writer.writeAll("enum {\n"); - var it = module.global_error_set.iterator(); - while (it.next()) |entry| try err_typedef_writer.print(" zig_error_{s} = {d},\n", .{ - codegen.fmtIdent(entry.key_ptr.*), - entry.value_ptr.*, - }); - try err_typedef_writer.writeAll("};\n"); - } + try self.flushErrDecls(&f); // Typedefs, forward decls, and non-functions first. // Unlike other backends, the .c code we are emitting is order-dependent. Therefore @@ -301,38 +286,20 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) while (f.remaining_decls.popOrNull()) |kv| { const decl_index = kv.key; - try flushDecl(self, &f, decl_index); + try self.flushDecl(&f, decl_index); } - if (f.err_typedef_buf.items.len == 0) { - f.all_buffers.items[err_typedef_index] = .{ - .iov_base = "", - .iov_len = 0, - }; - } else { - f.all_buffers.items[err_typedef_index] = .{ - .iov_base = f.err_typedef_buf.items.ptr, - .iov_len = f.err_typedef_buf.items.len, - }; - f.file_size += f.err_typedef_buf.items.len; - } + f.all_buffers.items[typedef_index] = .{ + .iov_base = if (f.typedef_buf.items.len > 0) f.typedef_buf.items.ptr else "", + .iov_len = f.typedef_buf.items.len, + }; + f.file_size += f.typedef_buf.items.len; // Now the function bodies. try f.all_buffers.ensureUnusedCapacity(gpa, f.fn_count); - for (decl_keys) |decl_index, i| { - const decl = module.declPtr(decl_index); - if (decl.getFunction() != null) { - const decl_block = &decl_values[i]; - const buf = decl_block.code.items; - if (buf.len != 0) { - f.all_buffers.appendAssumeCapacity(.{ - .iov_base = buf.ptr, - .iov_len = buf.len, - }); - f.file_size += buf.len; - } - } - } + for (decl_keys) |decl_index, i| + if (module.declPtr(decl_index).getFunction() != null) + f.appendBufAssumeCapacity(decl_values[i].code.items); const file = self.base.file.?; try file.setEndPos(f.file_size); @@ -342,7 +309,8 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) const Flush = struct { remaining_decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, void) = .{}, typedefs: Typedefs = .{}, - err_typedef_buf: std.ArrayListUnmanaged(u8) = .{}, + typedef_buf: std.ArrayListUnmanaged(u8) = .{}, + err_buf: std.ArrayListUnmanaged(u8) = .{}, /// We collect a list of buffers to write, and write them all at once with pwritev 😎 all_buffers: std.ArrayListUnmanaged(std.os.iovec_const) = .{}, /// Keeps track of the total bytes of `all_buffers`. @@ -356,9 +324,16 @@ const Flush = struct { std.hash_map.default_max_load_percentage, ); + fn appendBufAssumeCapacity(f: *Flush, buf: []const u8) void { + if (buf.len == 0) return; + f.all_buffers.appendAssumeCapacity(.{ .iov_base = buf.ptr, .iov_len = buf.len }); + f.file_size += buf.len; + } + fn deinit(f: *Flush, gpa: Allocator) void { f.all_buffers.deinit(gpa); - f.err_typedef_buf.deinit(gpa); + f.err_buf.deinit(gpa); + f.typedef_buf.deinit(gpa); f.typedefs.deinit(gpa); f.remaining_decls.deinit(gpa); } @@ -368,6 +343,57 @@ const FlushDeclError = error{ OutOfMemory, }; +fn flushTypedefs(self: *C, f: *Flush, typedefs: codegen.TypedefMap.Unmanaged) FlushDeclError!void { + if (typedefs.count() == 0) return; + const gpa = self.base.allocator; + const module = self.base.options.module.?; + + try f.typedefs.ensureUnusedCapacityContext(gpa, @intCast(u32, typedefs.count()), .{ + .mod = module, + }); + var it = typedefs.iterator(); + while (it.next()) |new| { + const gop = f.typedefs.getOrPutAssumeCapacityContext(new.key_ptr.*, .{ + .mod = module, + }); + if (!gop.found_existing) { + try f.typedef_buf.appendSlice(gpa, new.value_ptr.rendered); + } + } +} + +fn flushErrDecls(self: *C, f: *Flush) FlushDeclError!void { + const gpa = self.base.allocator; + const module = self.base.options.module.?; + + var object = codegen.Object{ + .dg = .{ + .gpa = gpa, + .module = module, + .error_msg = null, + .decl_index = undefined, + .decl = undefined, + .fwd_decl = undefined, + .typedefs = codegen.TypedefMap.initContext(gpa, .{ .mod = module }), + .typedefs_arena = gpa, + }, + .code = f.err_buf.toManaged(gpa), + .indent_writer = undefined, // set later so we can get a pointer to object.code + }; + object.indent_writer = .{ .underlying_writer = object.code.writer() }; + defer object.dg.typedefs.deinit(); + defer f.err_buf = object.code.moveToUnmanaged(); + + codegen.genErrDecls(&object) catch |err| switch (err) { + error.AnalysisFail => unreachable, + else => |e| return e, + }; + + try self.flushTypedefs(f, object.dg.typedefs.unmanaged); + try f.all_buffers.ensureUnusedCapacity(gpa, 1); + f.appendBufAssumeCapacity(object.code.items); +} + /// Assumes `decl` was in the `remaining_decls` set, and has already been removed. fn flushDecl(self: *C, f: *Flush, decl_index: Module.Decl.Index) FlushDeclError!void { const module = self.base.options.module.?; @@ -384,43 +410,13 @@ fn flushDecl(self: *C, f: *Flush, decl_index: Module.Decl.Index) FlushDeclError! const decl_block = self.decl_table.getPtr(decl_index).?; const gpa = self.base.allocator; - if (decl_block.typedefs.count() != 0) { - try f.typedefs.ensureUnusedCapacityContext(gpa, @intCast(u32, decl_block.typedefs.count()), .{ - .mod = module, - }); - var it = decl_block.typedefs.iterator(); - while (it.next()) |new| { - const gop = f.typedefs.getOrPutAssumeCapacityContext(new.key_ptr.*, .{ - .mod = module, - }); - if (!gop.found_existing) { - try f.err_typedef_buf.appendSlice(gpa, new.value_ptr.rendered); - } - } - } - - if (decl_block.fwd_decl.items.len != 0) { - const buf = decl_block.fwd_decl.items; - if (buf.len != 0) { - try f.all_buffers.append(gpa, .{ - .iov_base = buf.ptr, - .iov_len = buf.len, - }); - f.file_size += buf.len; - } - } - if (decl.getFunction() != null) { - f.fn_count += 1; - } else if (decl_block.code.items.len != 0) { - const buf = decl_block.code.items; - if (buf.len != 0) { - try f.all_buffers.append(gpa, .{ - .iov_base = buf.ptr, - .iov_len = buf.len, - }); - f.file_size += buf.len; - } - } + try self.flushTypedefs(f, decl_block.typedefs); + try f.all_buffers.ensureUnusedCapacity(gpa, 2); + f.appendBufAssumeCapacity(decl_block.fwd_decl.items); + if (decl.getFunction()) |_| + f.fn_count += 1 + else + f.appendBufAssumeCapacity(decl_block.code.items); } pub fn flushEmitH(module: *Module) !void { diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 7fb125ab2a..8a78e6eaa0 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -556,7 +556,6 @@ test "error union comptime caching" { } test "@errorName" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO 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; @@ -570,7 +569,6 @@ fn gimmeItBroke() anyerror { } test "@errorName sentinel length matches slice length" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO 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; From da939b032e90795c25f23efa68a9a1c50397fb48 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 5 Oct 2022 06:25:05 -0400 Subject: [PATCH 05/47] c: fix empty container warnings --- src/codegen/c.zig | 96 +++++++++++++++++++++++++++++------------------ 1 file changed, 59 insertions(+), 37 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 183baca48d..33339f589d 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -389,7 +389,7 @@ pub const DeclGen = struct { try dg.renderValue(writer, ty.slicePtrFieldType(&buf), val.slicePtr(), .Other); try writer.writeAll(", "); try writer.print("{d}", .{val.sliceLen(dg.module)}); - try writer.writeAll("}"); + try writer.writeByte('}'); return; } @@ -563,6 +563,7 @@ pub const DeclGen = struct { switch (ty.zigTypeTag()) { // Using '{}' for integer and floats seemed to error C compilers (both GCC and Clang) // with 'error: expected expression' (including when built with 'zig cc') + .Bool => return writer.writeAll("false"), .Int => { const c_bits = toCIntBits(ty.intInfo(dg.module.getTarget()).bits) orelse return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); @@ -583,14 +584,19 @@ pub const DeclGen = struct { } }, .Pointer => switch (dg.module.getTarget().cpu.arch.ptrBitWidth()) { - 32 => return writer.writeAll("(void *)0xaaaaaaaa"), - 64 => return writer.writeAll("(void *)0xaaaaaaaaaaaaaaaa"), + 32 => return writer.writeAll("(void *)0xaaaaaaaau"), + 64 => return writer.writeAll("(void *)0xaaaaaaaaaaaaaaaau"), else => unreachable, }, .Struct, .ErrorUnion => { try writer.writeByte('('); try dg.renderTypecast(writer, ty); - return writer.writeAll("){0xaa}"); + return writer.writeAll("){0xaau}"); + }, + .Array => { + try writer.writeByte('{'); + try dg.renderValue(writer, ty.childType(), val, location); + return writer.writeByte('}'); }, else => { // This should lower to 0xaa bytes in safe modes, and for unsafe modes should @@ -652,7 +658,7 @@ pub const DeclGen = struct { try dg.renderValue(writer, ty.slicePtrFieldType(&buf), slice.ptr, location); try writer.writeAll(", "); try dg.renderValue(writer, Type.usize, slice.len, location); - try writer.writeAll("}"); + try writer.writeByte('}'); }, .function => { const func = val.castTag(.function).?.data; @@ -684,6 +690,8 @@ pub const DeclGen = struct { const ai = ty.arrayInfo(); if (ai.sentinel) |s| { try dg.renderValue(writer, ai.elem_type, s, location); + } else { + try writer.writeByte('0'); } try writer.writeByte('}'); }, @@ -703,12 +711,12 @@ pub const DeclGen = struct { const ai = ty.arrayInfo(); var index: usize = 0; while (index < ai.len) : (index += 1) { - if (index != 0) try writer.writeAll(","); + if (index != 0) try writer.writeByte(','); const elem_val = try val.elemValue(dg.module, arena_allocator, index); try dg.renderValue(writer, ai.elem_type, elem_val, .Other); } if (ai.sentinel) |s| { - if (index != 0) try writer.writeAll(","); + if (index != 0) try writer.writeByte(','); try dg.renderValue(writer, ai.elem_type, s, .Other); } try writer.writeByte('}'); @@ -751,7 +759,7 @@ pub const DeclGen = struct { else => { // In this case we are rendering an error union which has a // 0 bits payload. - return writer.writeAll("0"); + return writer.writeByte('0'); }, } }, @@ -827,27 +835,29 @@ pub const DeclGen = struct { .Struct => { const field_vals = val.castTag(.aggregate).?.data; - try writer.writeAll("("); + try writer.writeByte('('); try dg.renderTypecast(writer, ty); try writer.writeAll("){"); - var i: usize = 0; + var empty = true; for (field_vals) |field_val, field_index| { const field_ty = ty.structFieldType(field_index); if (!field_ty.hasRuntimeBits()) continue; - if (i != 0) try writer.writeAll(","); + if (!empty) try writer.writeByte(','); try dg.renderValue(writer, field_ty, field_val, location); - i += 1; - } - try writer.writeAll("}"); + empty = false; + } + if (empty) try writer.writeByte('0'); + + try writer.writeByte('}'); }, .Union => { const union_obj = val.castTag(.@"union").?.data; const layout = ty.unionGetLayout(target); - try writer.writeAll("("); + try writer.writeByte('('); try dg.renderTypecast(writer, ty); try writer.writeAll("){"); @@ -866,11 +876,11 @@ pub const DeclGen = struct { if (field_ty.hasRuntimeBits()) { try writer.print(".{ } = ", .{fmtIdent(field_name)}); try dg.renderValue(writer, field_ty, union_obj.val, location); - } + } else try writer.writeByte('0'); if (ty.unionTagTypeSafety()) |_| { - try writer.writeAll("}"); + try writer.writeByte('}'); } - try writer.writeAll("}"); + try writer.writeByte('}'); }, .ComptimeInt => unreachable, @@ -913,9 +923,9 @@ pub const DeclGen = struct { } else { try w.writeAll("void"); } - try w.writeAll(" "); + try w.writeByte(' '); try dg.renderDeclName(w, dg.decl_index); - try w.writeAll("("); + try w.writeByte('('); var params_written: usize = 0; for (fn_info.param_types) |param_type, index| { @@ -1038,6 +1048,7 @@ pub const DeclGen = struct { try buffer.appendSlice("typedef struct {\n"); { var it = struct_obj.fields.iterator(); + var empty = true; while (it.next()) |entry| { const field_ty = entry.value_ptr.ty; if (!field_ty.hasRuntimeBits()) continue; @@ -1047,7 +1058,10 @@ pub const DeclGen = struct { try buffer.append(' '); try dg.renderTypeAndName(buffer.writer(), field_ty, name, .Mut, alignment); try buffer.appendSlice(";\n"); + + empty = false; } + if (empty) try buffer.appendSlice(" char empty_struct;\n"); } try buffer.appendSlice("} "); @@ -1076,7 +1090,9 @@ pub const DeclGen = struct { try buffer.appendSlice("typedef struct {\n"); { + var empty = true; for (tuple.types) |field_ty, i| { + if (!field_ty.hasRuntimeBits()) continue; const val = tuple.values[i]; if (val.tag() != .unreachable_value) continue; @@ -1087,7 +1103,10 @@ pub const DeclGen = struct { try buffer.append(' '); try dg.renderTypeAndName(writer, field_ty, .{ .bytes = name.items }, .Mut, 0); try buffer.appendSlice(";\n"); + + empty = false; } + if (empty) try buffer.appendSlice(" char empty_tuple;\n"); } try buffer.appendSlice("} "); @@ -1129,17 +1148,23 @@ pub const DeclGen = struct { } try buffer.appendSlice("union {\n"); + const fields = t.unionFields(); { - var it = t.unionFields().iterator(); + var it = fields.iterator(); + var empty = true; while (it.next()) |entry| { const field_ty = entry.value_ptr.ty; if (!field_ty.hasRuntimeBits()) continue; + const alignment = entry.value_ptr.abi_align; const name: CValue = .{ .identifier = entry.key_ptr.* }; try buffer.append(' '); try dg.renderTypeAndName(buffer.writer(), field_ty, name, .Mut, alignment); try buffer.appendSlice(";\n"); + + empty = false; } + if (empty) try buffer.appendSlice(" char empty_union;\n"); } try buffer.appendSlice("} "); @@ -1725,7 +1750,7 @@ pub fn genDecl(o: *Object) !void { if (variable.init.tag() != .unreachable_value) { try o.dg.renderValue(w, tv.ty, variable.init, .Other); } - try w.writeAll(";"); + try w.writeByte(';'); try o.indent_writer.insertNewline(); } else { const writer = o.writer(); @@ -2035,7 +2060,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO } f.object.indent_writer.popIndent(); - try writer.writeAll("}"); + try writer.writeByte('}'); } fn airSliceField(f: *Function, inst: Air.Inst.Index, suffix: []const u8) !CValue { @@ -2154,7 +2179,7 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue { const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const); try writer.writeAll(" = "); try f.writeCValue(writer, array); - try writer.writeAll("["); + try writer.writeByte('['); try f.writeCValue(writer, index); try writer.writeAll("];\n"); return local; @@ -2214,7 +2239,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { if (is_array) { // Insert a memcpy to initialize this array. The source operand is always a pointer // and thus we only need to know size/type information from the local type/dest. - try writer.writeAll(";"); + try writer.writeByte(';'); try f.object.indent_writer.insertNewline(); try writer.writeAll("memcpy("); try f.writeCValue(writer, local); @@ -2278,7 +2303,7 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = ("); try f.renderTypecast(writer, inst_ty); - try writer.writeAll(")"); + try writer.writeByte(')'); try f.writeCValue(writer, operand); try writer.writeAll(";\n"); return local; @@ -2381,7 +2406,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { const new_local = try f.allocLocal(rhs_type, .Const); try writer.writeAll(" = "); try f.writeCValue(writer, src_val); - try writer.writeAll(";"); + try writer.writeByte(';'); try f.object.indent_writer.insertNewline(); break :blk new_local; @@ -2605,7 +2630,7 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, op_abbrev: [*:0]const u8) !CV const max = intMax(scalar_ty, target, &max_buf); const ret = try f.allocLocal(inst_ty, .Mut); - try w.writeAll(";"); + try w.writeByte(';'); try f.object.indent_writer.insertNewline(); try f.writeCValue(w, ret); @@ -2654,10 +2679,7 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = "); - if (inst_ty.zigTypeTag() == .Bool) - try writer.writeAll("!") - else - try writer.writeAll("~"); + try writer.writeByte(if (inst_ty.tag() == .bool) '!' else '~'); try f.writeCValue(writer, op); try writer.writeAll(";\n"); @@ -2868,7 +2890,7 @@ fn airCall( try f.writeCValue(writer, callee); } - try writer.writeAll("("); + try writer.writeByte('('); var args_written: usize = 0; for (args) |arg| { const ty = f.air.typeOf(arg); @@ -2992,7 +3014,7 @@ fn lowerTry( try writer.writeAll("if("); } try f.writeCValue(writer, err_union); - try writer.writeAll(")"); + try writer.writeByte(')'); break :err; } if (operand_is_ptr or isByRef(err_union_ty)) { @@ -3066,7 +3088,7 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(" = ("); try f.renderTypecast(writer, inst_ty); - try writer.writeAll(")"); + try writer.writeByte(')'); try f.writeCValue(writer, operand); try writer.writeAll(";\n"); return local; @@ -3797,7 +3819,7 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(" = ("); try f.renderTypecast(writer, inst_ty); - try writer.writeAll(")"); + try writer.writeByte(')'); try f.writeCValue(writer, operand); try writer.writeAll(";\n"); return local; @@ -4212,7 +4234,7 @@ fn airNeg(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.air.typeOfIndex(inst); const operand = try f.resolveInst(un_op); const local = try f.allocLocal(inst_ty, .Const); - try writer.writeAll("-"); + try writer.writeByte('-'); try f.writeCValue(writer, operand); try writer.writeAll(";\n"); return local; From 61bf43d790d0f56f9c030afd8ec761a0f043a8a5 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 5 Oct 2022 04:57:27 -0400 Subject: [PATCH 06/47] c: fix switch on bool warning --- src/codegen/c.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 33339f589d..2ae0421008 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -3180,6 +3180,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); try writer.writeAll("switch ("); + if (condition_ty.tag() == .bool) try writer.writeAll("(int)"); try f.writeCValue(writer, condition); try writer.writeAll(") {"); f.object.indent_writer.pushIndent(); From 0092f6ed88f02a7930d9c27732a888c2e6c5e2dc Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 5 Oct 2022 15:03:24 -0400 Subject: [PATCH 07/47] c: fix int literal warnings --- src/codegen/c.zig | 403 ++++++++++++++++++++++++++-------------------- 1 file changed, 225 insertions(+), 178 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 2ae0421008..2684ebf341 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -62,6 +62,7 @@ const FormatTypeAsCIdentContext = struct { }; const ValueRenderLocation = enum { + Identifier, FunctionArgument, Other, }; @@ -330,6 +331,10 @@ pub const Function = struct { fn renderTypecast(f: *Function, w: anytype, t: Type) !void { return f.object.dg.renderTypecast(w, t); } + + fn fmtIntLiteral(f: *Function, ty: Type, int_val: anytype) !IntLiteralFormatter(@TypeOf(int_val)) { + return f.object.dg.fmtIntLiteral(ty, int_val, .Other); + } }; /// This data is available when outputting .c code for a `Module`. @@ -564,30 +569,19 @@ pub const DeclGen = struct { // Using '{}' for integer and floats seemed to error C compilers (both GCC and Clang) // with 'error: expected expression' (including when built with 'zig cc') .Bool => return writer.writeAll("false"), - .Int => { - const c_bits = toCIntBits(ty.intInfo(dg.module.getTarget()).bits) orelse - return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); - switch (c_bits) { - 8 => return writer.writeAll("0xaau"), - 16 => return writer.writeAll("0xaaaau"), - 32 => return writer.writeAll("0xaaaaaaaau"), - 64 => return writer.writeAll("0xaaaaaaaaaaaaaaaau"), - 128 => return renderInt128(writer, @as(u128, 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)), - else => unreachable, - } - }, - .Float => { - switch (ty.floatBits(dg.module.getTarget())) { - 32 => return writer.writeAll("zig_bitcast_f32_u32(0xaaaaaaaau)"), - 64 => return writer.writeAll("zig_bitcast_f64_u64(0xaaaaaaaaaaaaaaaau)"), - else => return dg.fail("TODO float types > 64 bits are not support in renderValue() as of now", .{}), - } - }, - .Pointer => switch (dg.module.getTarget().cpu.arch.ptrBitWidth()) { - 32 => return writer.writeAll("(void *)0xaaaaaaaau"), - 64 => return writer.writeAll("(void *)0xaaaaaaaaaaaaaaaau"), - else => unreachable, + .Int => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, UndefInt{}, location)}), + .Float => switch (ty.tag()) { + .f32 => return writer.print("zig_bitcast_f32_u32({x})", .{ + try dg.fmtIntLiteral(Type.u32, UndefInt{}, location), + }), + .f64 => return writer.print("zig_bitcast_f64_u64({x})", .{ + try dg.fmtIntLiteral(Type.u64, UndefInt{}, location), + }), + else => return dg.fail("TODO float types > 64 bits are not support in renderValue() as of now", .{}), }, + .Pointer => return writer.print("((void *){x})", .{ + try dg.fmtIntLiteral(Type.usize, UndefInt{}, location), + }), .Struct, .ErrorUnion => { try writer.writeByte('('); try dg.renderTypecast(writer, ty); @@ -608,8 +602,12 @@ pub const DeclGen = struct { } switch (ty.zigTypeTag()) { .Int => switch (val.tag()) { - .int_big_positive => try dg.renderBigIntConst(writer, val.castTag(.int_big_positive).?.asBigInt(), ty.isSignedInt()), - .int_big_negative => try dg.renderBigIntConst(writer, val.castTag(.int_big_negative).?.asBigInt(), true), + .int_big_positive => return writer.print("{x}", .{ + try dg.fmtIntLiteral(ty, val.castTag(.int_big_positive).?.asBigInt(), location), + }), + .int_big_negative => return writer.print("{x}", .{ + try dg.fmtIntLiteral(ty, val.castTag(.int_big_negative).?.asBigInt(), location), + }), .field_ptr, .elem_ptr, .opt_payload_ptr, @@ -617,19 +615,32 @@ pub const DeclGen = struct { .decl_ref_mut, .decl_ref, => try dg.renderParentPtr(writer, val, ty), - else => { - if (ty.isSignedInt()) - return writer.print("{d}", .{val.toSignedInt()}); - return writer.print("{d}u", .{val.toUnsignedInt(target)}); - }, + else => if (ty.isSignedInt()) + return writer.print("{d}", .{try dg.fmtIntLiteral(ty, val.toSignedInt(), location)}) + else + return writer.print("{d}", .{ + try dg.fmtIntLiteral(ty, val.toUnsignedInt(target), location), + }), }, .Float => { - if (ty.floatBits(dg.module.getTarget()) <= 64) { + if (ty.floatBits(target) <= 64) { if (std.math.isNan(val.toFloat(f64)) or std.math.isInf(val.toFloat(f64))) { // just generate a bit cast (exactly like we do in airBitcast) switch (ty.tag()) { - .f32 => return writer.print("zig_bitcast_f32_u32(0x{x})", .{@bitCast(u32, val.toFloat(f32))}), - .f64 => return writer.print("zig_bitcast_f64_u64(0x{x})", .{@bitCast(u64, val.toFloat(f64))}), + .f32 => return writer.print("zig_bitcast_f32_u32({x})", .{ + try dg.fmtIntLiteral( + Type.u32, + @bitCast(u32, val.toFloat(f32)), + location, + ), + }), + .f64 => return writer.print("zig_bitcast_f64_u64({x})", .{ + try dg.fmtIntLiteral( + Type.u64, + @bitCast(u64, val.toFloat(f64)), + location, + ), + }), else => return dg.fail("TODO float types > 64 bits are not support in renderValue() as of now", .{}), } } else { @@ -671,7 +682,9 @@ pub const DeclGen = struct { .int_u64, .one => { try writer.writeAll("(("); try dg.renderTypecast(writer, ty); - try writer.print(")0x{x}u)", .{val.toUnsignedInt(target)}); + return writer.print("){x})", .{ + try dg.fmtIntLiteral(Type.usize, val.toUnsignedInt(target), location), + }); }, .field_ptr, .elem_ptr, @@ -1020,7 +1033,7 @@ pub const DeclGen = struct { } if (ptr_sentinel) |s| { try bw.writeAll("_s_"); - try dg.renderValue(bw, child_type, s, .Other); + try dg.renderValue(bw, child_type, s, .Identifier); } try bw.writeAll(";\n"); @@ -1540,7 +1553,7 @@ pub const DeclGen = struct { } } - fn writeCValue(dg: DeclGen, w: anytype, c_value: CValue) !void { + fn writeCValue(dg: *DeclGen, w: anytype, c_value: CValue) !void { switch (c_value) { .none => unreachable, .local => |i| return w.print("t{d}", .{i}), @@ -1552,20 +1565,15 @@ pub const DeclGen = struct { try w.writeByte('&'); return dg.renderDeclName(w, decl); }, - .undefined_ptr => { - const target = dg.module.getTarget(); - switch (target.cpu.arch.ptrBitWidth()) { - 32 => try w.writeAll("(void *)0xaaaaaaaa"), - 64 => try w.writeAll("(void *)0xaaaaaaaaaaaaaaaa"), - else => unreachable, - } - }, + .undefined_ptr => return w.print("((void *){x})", .{ + try dg.fmtIntLiteral(Type.usize, UndefInt{}, .Other), + }), .identifier => |ident| return w.print("{ }", .{fmtIdent(ident)}), .bytes => |bytes| return w.writeAll(bytes), } } - fn writeCValueDeref(dg: DeclGen, w: anytype, c_value: CValue) !void { + fn writeCValueDeref(dg: *DeclGen, w: anytype, c_value: CValue) !void { switch (c_value) { .none => unreachable, .local => |i| return w.print("(*t{d})", .{i}), @@ -1588,7 +1596,7 @@ pub const DeclGen = struct { } } - fn renderDeclName(dg: DeclGen, writer: anytype, decl_index: Decl.Index) !void { + fn renderDeclName(dg: *DeclGen, writer: anytype, decl_index: Decl.Index) !void { const decl = dg.module.declPtr(decl_index); dg.module.markDeclAlive(decl); @@ -1603,6 +1611,19 @@ pub const DeclGen = struct { return writer.print("{ }", .{fmtIdent(name)}); } } + + fn fmtIntLiteral( + dg: *DeclGen, + ty: Type, + int_val: anytype, + location: ValueRenderLocation, + ) !IntLiteralFormatter(@TypeOf(int_val)) { + const target = dg.module.getTarget(); + const int_info = ty.intInfo(target); + _ = toCIntBits(int_info.bits) orelse + return dg.fail("TODO implement integer constants larger than 128 bits", .{}); + return .{ .ty = ty, .target = target, .int_val = int_val, .location = location }; + } }; pub fn genErrDecls(o: *Object) !void { @@ -2369,7 +2390,7 @@ fn airStoreUndefined(f: *Function, dest_ptr: CValue) !CValue { const writer = f.object.writer(); try writer.writeAll("memset("); try f.writeCValue(writer, dest_ptr); - try writer.writeAll(", 0xaa, sizeof("); + try writer.print(", {x}, sizeof(", .{try f.fmtIntLiteral(Type.u8, UndefInt{})}); try f.writeCValueDeref(writer, dest_ptr); try writer.writeAll("));\n"); }, @@ -2458,9 +2479,6 @@ fn airWrapOp( return f.fail("TODO: C backend: airWrapOp for large integers", .{}); } - var max_buf: [80]u8 = undefined; - const max = intMax(inst_ty, target, &max_buf); - const lhs = try f.resolveInst(bin_op.lhs); const rhs = try f.resolveInst(bin_op.rhs); const w = f.object.writer(); @@ -2491,15 +2509,9 @@ fn airWrapOp( try f.writeCValue(w, lhs); try w.writeAll(", "); try f.writeCValue(w, rhs); - - if (int_info.signedness == .signed) { - var min_buf: [80]u8 = undefined; - const min = intMin(inst_ty, target, &min_buf); - - try w.print(", {s}", .{min}); - } - - try w.print(", {s});", .{max}); + if (int_info.signedness == .signed) + try w.print(", {}", .{try f.fmtIntLiteral(inst_ty, MinInt{})}); + try w.print(", {});", .{try f.fmtIntLiteral(inst_ty, MaxInt{})}); try f.object.indent_writer.insertNewline(); return ret; @@ -2524,49 +2536,6 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue { return f.object.dg.fail("TODO: C backend: airSatOp for large integers", .{}); } - var min_buf: [80]u8 = undefined; - const min = switch (int_info.signedness) { - .unsigned => "0", - else => switch (inst_ty.tag()) { - .c_short => "SHRT_MIN", - .c_int => "INT_MIN", - .c_long => "LONG_MIN", - .c_longlong => "LLONG_MIN", - .isize => "INTPTR_MIN", - else => blk: { - // compute the type minimum based on the bitcount (bits) - const val = -1 * std.math.pow(i65, 2, @intCast(i65, bits - 1)); - break :blk std.fmt.bufPrint(&min_buf, "{d}", .{val}) catch |err| switch (err) { - error.NoSpaceLeft => unreachable, - }; - }, - }, - }; - - var max_buf: [80]u8 = undefined; - const max = switch (inst_ty.tag()) { - .c_short => "SHRT_MAX", - .c_ushort => "USHRT_MAX", - .c_int => "INT_MAX", - .c_uint => "UINT_MAX", - .c_long => "LONG_MAX", - .c_ulong => "ULONG_MAX", - .c_longlong => "LLONG_MAX", - .c_ulonglong => "ULLONG_MAX", - .isize => "INTPTR_MAX", - .usize => "UINTPTR_MAX", - else => blk: { - const pow_bits = switch (int_info.signedness) { - .signed => bits - 1, - .unsigned => bits, - }; - const val = std.math.pow(u65, 2, pow_bits) - 1; - break :blk std.fmt.bufPrint(&max_buf, "{}", .{val}) catch |err| switch (err) { - error.NoSpaceLeft => unreachable, - }; - }, - }; - const lhs = try f.resolveInst(bin_op.lhs); const rhs = try f.resolveInst(bin_op.rhs); const w = f.object.writer(); @@ -2597,12 +2566,9 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue { try f.writeCValue(w, lhs); try w.writeAll(", "); try f.writeCValue(w, rhs); - - if (int_info.signedness == .signed) { - try w.print(", {s}", .{min}); - } - - try w.print(", {s});", .{max}); + if (int_info.signedness == .signed) + try w.print(", {}", .{try f.fmtIntLiteral(inst_ty, MinInt{})}); + try w.print(", {});", .{try f.fmtIntLiteral(inst_ty, MaxInt{})}); try f.object.indent_writer.insertNewline(); return ret; @@ -2626,43 +2592,23 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, op_abbrev: [*:0]const u8) !CV const c_bits = toCIntBits(int_info.bits) orelse return f.fail("TODO: C backend: implement integer arithmetic larger than 128 bits", .{}); - var max_buf: [80]u8 = undefined; - const max = intMax(scalar_ty, target, &max_buf); - const ret = try f.allocLocal(inst_ty, .Mut); try w.writeByte(';'); try f.object.indent_writer.insertNewline(); try f.writeCValue(w, ret); - switch (int_info.signedness) { - .unsigned => { - try w.print(".field_1 = zig_{s}u{d}(", .{ - op_abbrev, c_bits, - }); - try f.writeCValue(w, lhs); - try w.writeAll(", "); - try f.writeCValue(w, rhs); - try w.writeAll(", &"); - try f.writeCValue(w, ret); - try w.print(".field_0, {s}", .{max}); - }, - .signed => { - var min_buf: [80]u8 = undefined; - const min = intMin(scalar_ty, target, &min_buf); - - try w.print(".field_1 = zig_{s}i{d}(", .{ - op_abbrev, c_bits, - }); - try f.writeCValue(w, lhs); - try w.writeAll(", "); - try f.writeCValue(w, rhs); - try w.writeAll(", &"); - try f.writeCValue(w, ret); - try w.print(".field_0, {s}, {s}", .{ min, max }); - }, - } - - try w.writeAll(");"); + try w.print(".field_1 = zig_{s}{c}{d}(", .{ + op_abbrev, signAbbrev(int_info.signedness), c_bits, + }); + try f.writeCValue(w, lhs); + try w.writeAll(", "); + try f.writeCValue(w, rhs); + try w.writeAll(", &"); + try f.writeCValue(w, ret); + try w.writeAll(".field_0, "); + if (int_info.signedness == .signed) + try w.print("{}, ", .{try f.fmtIntLiteral(scalar_ty, MinInt{})}); + try w.print("{});", .{try f.fmtIntLiteral(scalar_ty, MaxInt{})}); try f.object.indent_writer.insertNewline(); return ret; } @@ -4368,47 +4314,148 @@ fn signAbbrev(signedness: std.builtin.Signedness) u8 { }; } -fn intMax(ty: Type, target: std.Target, buf: []u8) []const u8 { - switch (ty.tag()) { - .c_short => return "SHRT_MAX", - .c_ushort => return "USHRT_MAX", - .c_int => return "INT_MAX", - .c_uint => return "UINT_MAX", - .c_long => return "LONG_MAX", - .c_ulong => return "ULONG_MAX", - .c_longlong => return "LLONG_MAX", - .c_ulonglong => return "ULLONG_MAX", - else => { - const int_info = ty.intInfo(target); - const rhs = @intCast(u7, int_info.bits - @boolToInt(int_info.signedness == .signed)); - const val = (@as(u128, 1) << rhs) - 1; - // TODO make this integer literal have a suffix if necessary (such as "ull") - return std.fmt.bufPrint(buf, "{}", .{val}) catch |err| switch (err) { - error.NoSpaceLeft => unreachable, - }; - }, +const UndefInt = struct { + pub fn to(_: UndefInt, comptime T: type) error{}!T { + comptime { + if (@bitSizeOf(T) < 2) return 0; + var value: T = 2; + var shift = 2; + while (shift < @bitSizeOf(T)) : (shift <<= 1) + value |= value << shift; + return value; + } } -} +}; +const MaxInt = struct { + pub fn to(_: MaxInt, comptime T: type) error{}!T { + return std.math.maxInt(T); + } +}; +const MinInt = struct { + pub fn to(_: MinInt, comptime T: type) error{}!T { + return std.math.minInt(T); + } +}; -fn intMin(ty: Type, target: std.Target, buf: []u8) []const u8 { - switch (ty.tag()) { - .c_short => return "SHRT_MIN", - .c_int => return "INT_MIN", - .c_long => return "LONG_MIN", - .c_longlong => return "LLONG_MIN", - else => { - const int_info = ty.intInfo(target); - assert(int_info.signedness == .signed); - const val = v: { - if (int_info.bits == 0) break :v 0; - const rhs = @intCast(u7, (int_info.bits - 1)); - break :v -(@as(i128, 1) << rhs); +fn IntLiteralFormatter(comptime IntType: type) type { + return struct { + ty: Type, + target: std.Target, + int_val: IntType, + location: ValueRenderLocation, + + fn formatHelper( + self: @This(), + comptime CIntType: type, + comptime fmt: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + const c_int_info = @typeInfo(CIntType).Int; + const c_int_val = switch (@typeInfo(IntType)) { + .Int => @intCast(CIntType, self.int_val), + .Struct, .Pointer => self.int_val.to(CIntType) catch unreachable, + else => unreachable, }; - return std.fmt.bufPrint(buf, "{d}", .{val}) catch |err| switch (err) { - error.NoSpaceLeft => unreachable, - }; - }, - } + const c_abs_val = std.math.absCast(c_int_val); + if (self.location == .Identifier) { + if (c_int_val < 0) try writer.writeAll("_N"); + try writer.print("{d}", .{c_abs_val}); + } else if (c_int_info.bits == 128) { + // Clang and GCC don't support 128-bit integer constants but + // will hopefully unfold them if we construct one manually. + //std.debug.todo("128-bit is unimplemented"); + try writer.writeByte('('); + if (c_int_info.signedness == .signed) { + try writer.writeAll("(int128_t)"); + if (c_int_val < 0) try writer.writeByte('-'); + } + + const upper = @intCast(u64, c_abs_val >> 64); + if (upper != 0) try writer.writeByte('('); + if (upper != 0 or c_int_val < 0) try writer.writeAll("(uint128_t)"); + if (upper != 0) { + try (IntLiteralFormatter(u64){ + .ty = Type.u64, + .target = self.target, + .int_val = upper, + .location = self.location, + }).formatHelper(u64, fmt, options, writer); + try writer.writeAll("<<64|"); + } + + const lower = @truncate(u64, c_abs_val); + try (IntLiteralFormatter(u64){ + .ty = Type.u64, + .target = self.target, + .int_val = lower, + .location = self.location, + }).formatHelper(u64, fmt, options, writer); + + if (upper != 0) try writer.writeByte(')'); + try writer.writeByte(')'); + } else if (c_int_val == std.math.maxInt(CIntType) or + c_int_info.signedness == .signed and c_int_val == std.math.minInt(CIntType)) + { + if (c_int_info.signedness == .unsigned) try writer.writeByte('U'); + try writer.writeAll(switch (self.ty.tag()) { + .c_short, .c_ushort => "SHRT", + .c_int, .c_uint => "INT", + .c_long, .c_ulong => "LONG", + .c_longlong, .c_ulonglong => "LLONG", + .isize, .usize => "INTPTR", + else => std.fmt.comptimePrint("INT{d}", .{c_int_info.bits}), + }); + try writer.writeAll(if (c_int_val < 0) "_MIN" else "_MAX"); + } else { + if (c_int_val < 0) try writer.writeByte('-'); + if (c_int_info.signedness == .unsigned) try writer.writeByte('U'); + try writer.print("INT{d}_C(" ++ switch (fmt.len) { + 0 => "{d}", + 1 => switch (fmt[0]) { + 'o' => "0{o}", + 'd' => "{d}", + 'x' => "0x{x}", + 'X' => "0x{X}", + else => @compileError("Invalid fmt: " ++ fmt), + }, + else => @compileError("Invalid fmt: " ++ fmt), + } ++ ")", .{ c_int_info.bits, c_abs_val }); + } + } + + pub fn format( + self: @This(), + comptime fmt: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + const int_info = self.ty.intInfo(self.target); + switch (toCIntBits(int_info.bits).?) { + 8 => switch (int_info.signedness) { + .signed => try self.formatHelper(i8, fmt, options, writer), + .unsigned => try self.formatHelper(u8, fmt, options, writer), + }, + 16 => switch (int_info.signedness) { + .signed => try self.formatHelper(i16, fmt, options, writer), + .unsigned => try self.formatHelper(u16, fmt, options, writer), + }, + 32 => switch (int_info.signedness) { + .signed => try self.formatHelper(i32, fmt, options, writer), + .unsigned => try self.formatHelper(u32, fmt, options, writer), + }, + 64 => switch (int_info.signedness) { + .signed => try self.formatHelper(i64, fmt, options, writer), + .unsigned => try self.formatHelper(u64, fmt, options, writer), + }, + 128 => switch (int_info.signedness) { + .signed => try self.formatHelper(i128, fmt, options, writer), + .unsigned => try self.formatHelper(u128, fmt, options, writer), + }, + else => unreachable, + } + } + }; } fn loweredFnRetTyHasBits(fn_ty: Type) bool { From 72e2fc4c73b095eebe790047890c6283db501732 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 5 Oct 2022 21:51:37 -0400 Subject: [PATCH 08/47] c: fix stage1 compatibility --- src/codegen/c.zig | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 2684ebf341..645bc7074f 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -492,11 +492,11 @@ pub const DeclGen = struct { const index = field_ptr.field_index; const FieldInfo = struct { name: []const u8, ty: Type }; const field_info: FieldInfo = switch (container_ty.zigTypeTag()) { - .Struct => .{ + .Struct => FieldInfo{ .name = container_ty.structFields().keys()[index], .ty = container_ty.structFields().values()[index].ty, }, - .Union => .{ + .Union => FieldInfo{ .name = container_ty.unionFields().keys()[index], .ty = container_ty.unionFields().values()[index].ty, }, @@ -1622,7 +1622,12 @@ pub const DeclGen = struct { const int_info = ty.intInfo(target); _ = toCIntBits(int_info.bits) orelse return dg.fail("TODO implement integer constants larger than 128 bits", .{}); - return .{ .ty = ty, .target = target, .int_val = int_val, .location = location }; + return IntLiteralFormatter(@TypeOf(int_val)){ + .ty = ty, + .target = target, + .int_val = int_val, + .location = location, + }; } }; From 497329622ae49908da6bb61faee1b39f3ade0b96 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 5 Oct 2022 22:45:43 -0400 Subject: [PATCH 09/47] c: fix nonscalar type cast warning --- src/codegen/c.zig | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 645bc7074f..be69321ec6 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -473,9 +473,14 @@ pub const DeclGen = struct { // // Used for .elem_ptr, .field_ptr, .opt_payload_ptr, .eu_payload_ptr fn renderParentPtr(dg: *DeclGen, writer: anytype, ptr_val: Value, ptr_ty: Type) error{ OutOfMemory, AnalysisFail }!void { - try writer.writeByte('('); - try dg.renderTypecast(writer, ptr_ty); - try writer.writeByte(')'); + switch (ptr_ty.ptrSize()) { + .Slice => {}, + .Many, .C, .One => { + try writer.writeByte('('); + try dg.renderTypecast(writer, ptr_ty); + try writer.writeByte(')'); + }, + } switch (ptr_val.tag()) { .decl_ref_mut, .decl_ref, .variable => { const decl_index = switch (ptr_val.tag()) { From 3b501b2d81e26fcadd15495eac62545fab1c4b49 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 6 Oct 2022 00:54:45 -0400 Subject: [PATCH 10/47] c: cast NULL in advance to avoid comparison warnings --- src/codegen/c.zig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index be69321ec6..f52d2e83cb 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -655,11 +655,11 @@ pub const DeclGen = struct { return dg.fail("TODO: C backend: implement lowering large float values", .{}); }, .Pointer => switch (val.tag()) { - .null_value => try writer.writeAll("NULL"), - // Technically this should produce NULL but the integer literal 0 will always coerce - // to the assigned pointer type. Note this is just a hack to fix warnings from ordered comparisons (<, >, etc) - // between pointers and 0, which is an extension to begin with. - .zero => try writer.writeByte('0'), + .null_value, .zero => { + try writer.writeAll("(("); + try dg.renderTypecast(writer, ty); + try writer.writeAll(")NULL)"); + }, .variable => { const decl = val.castTag(.variable).?.data.owner_decl; return dg.renderDeclValue(writer, ty, val, decl); From f8a8197caae9306b3f69a7af3445c76eea172f0f Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 6 Oct 2022 01:10:29 -0400 Subject: [PATCH 11/47] c: fix incompatible pointer types warning --- src/codegen/c.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index f52d2e83cb..c11187e6da 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -2782,7 +2782,10 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.air.typeOfIndex(inst); const local = try f.allocLocal(inst_ty, .Const); - try writer.writeAll(" = {"); + try writer.writeAll(" = {("); + var buf: Type.SlicePtrFieldTypeBuffer = undefined; + try f.renderTypecast(writer, inst_ty.slicePtrFieldType(&buf)); + try writer.writeByte(')'); try f.writeCValue(writer, ptr); try writer.writeAll(", "); try f.writeCValue(writer, len); From f81651932af18938fc88dc536806f3adbe5c11c4 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 6 Oct 2022 03:59:07 -0400 Subject: [PATCH 12/47] c: hacks to fix incompatible redeclaration of library function warnings --- src/codegen/c.zig | 62 +++++++++++++++++++++++++++++++++++-- test/behavior/bugs/4328.zig | 16 +++++----- 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index c11187e6da..b884e2fb0f 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1309,6 +1309,33 @@ pub const DeclGen = struct { return name; } + fn renderOpaqueTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { + const opaque_ty = t.cast(Type.Payload.Opaque).?.data; + const unqualified_name = dg.module.declPtr(opaque_ty.owner_decl).name; + const fqn = try opaque_ty.getFullyQualifiedName(dg.module); + defer dg.typedefs.allocator.free(fqn); + + var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); + defer buffer.deinit(); + + try buffer.writer().print("typedef struct {} ", .{fmtIdent(std.mem.span(unqualified_name))}); + + const name_start = buffer.items.len; + try buffer.writer().print("zig_O_{};\n", .{fmtIdent(fqn)}); + + const rendered = buffer.toOwnedSlice(); + errdefer dg.typedefs.allocator.free(rendered); + const name = rendered[name_start .. rendered.len - 2]; + + try dg.typedefs.ensureUnusedCapacity(1); + dg.typedefs.putAssumeCapacityNoClobber( + try t.copy(dg.typedefs_arena), + .{ .name = name, .rendered = rendered }, + ); + + return name; + } + /// Renders a type as a single identifier, generating intermediate typedefs /// if necessary. /// @@ -1387,7 +1414,16 @@ pub const DeclGen = struct { return w.writeAll(name); } - try dg.renderType(w, t.elemType()); + const child_ty = t.childType(); + if (t.isCPtr() and child_ty.eql(Type.u8, dg.module) and dg.decl.val.tag() == .extern_fn) { + // This is a hack, since the c compiler expects a lot of external + // library functions to have char pointers in their signatures, but + // u8 and i8 produce unsigned char and signed char respectively, + // which in C are not very usefully different than char. + try w.writeAll("char"); + } else { + try dg.renderType(w, child_ty); + } if (t.isConstPtr()) { try w.writeAll(" const"); } @@ -1456,7 +1492,16 @@ pub const DeclGen = struct { try dg.renderType(w, int_tag_ty); }, - .Opaque => return w.writeAll("void"), + .Opaque => switch (t.tag()) { + .anyopaque => try w.writeAll("void"), + .@"opaque" => { + const name = dg.getTypedefName(t) orelse + try dg.renderOpaqueTypedef(t); + + try w.writeAll(name); + }, + else => unreachable, + }, .Frame, .AnyFrame, @@ -2830,12 +2875,16 @@ fn airCall( } }; + var is_extern = false; callee: { known: { const fn_decl = fn_decl: { const callee_val = f.air.value(pl_op.operand) orelse break :known; break :fn_decl switch (callee_val.tag()) { - .extern_fn => callee_val.castTag(.extern_fn).?.data.owner_decl, + .extern_fn => blk: { + is_extern = true; + break :blk callee_val.castTag(.extern_fn).?.data.owner_decl; + }, .function => callee_val.castTag(.function).?.data.owner_decl, .decl_ref => callee_val.castTag(.decl_ref).?.data, else => break :known, @@ -2857,6 +2906,13 @@ fn airCall( if (args_written != 0) { try writer.writeAll(", "); } + if (is_extern and ty.isCPtr() and ty.childType().tag() == .u8) { + // Corresponds with hack in renderType .Pointer case. + try writer.writeAll("(char"); + if (ty.isConstPtr()) try writer.writeAll(" const"); + if (ty.isVolatilePtr()) try writer.writeAll(" volatile"); + try writer.writeAll(" *)"); + } if (f.air.value(arg)) |val| { try f.object.dg.renderValue(writer, f.air.typeOf(arg), val, .FunctionArgument); } else { diff --git a/test/behavior/bugs/4328.zig b/test/behavior/bugs/4328.zig index 5cacd29044..704dd829fb 100644 --- a/test/behavior/bugs/4328.zig +++ b/test/behavior/bugs/4328.zig @@ -5,10 +5,10 @@ const FILE = extern struct { dummy_field: u8, }; -extern fn printf([*c]const u8, ...) c_int; -extern fn fputs([*c]const u8, noalias [*c]FILE) c_int; -extern fn ftell([*c]FILE) c_long; -extern fn fopen([*c]const u8, [*c]const u8) [*c]FILE; +extern fn c_printf([*c]const u8, ...) c_int; +extern fn c_fputs([*c]const u8, noalias [*c]FILE) c_int; +extern fn c_ftell([*c]FILE) c_long; +extern fn c_fopen([*c]const u8, [*c]const u8) [*c]FILE; const S = extern struct { state: c_short, @@ -18,7 +18,7 @@ const S = extern struct { test "Extern function calls in @TypeOf" { const Test = struct { - fn test_fn_1(a: anytype, b: anytype) @TypeOf(printf("%d %s\n", a, b)) { + fn test_fn_1(a: anytype, b: anytype) @TypeOf(c_printf("%d %s\n", a, b)) { return 0; } @@ -38,7 +38,7 @@ test "Extern function calls in @TypeOf" { test "Peer resolution of extern function calls in @TypeOf" { const Test = struct { - fn test_fn() @TypeOf(ftell(null), fputs(null, null)) { + fn test_fn() @TypeOf(c_ftell(null), c_fputs(null, null)) { return 0; } @@ -55,12 +55,12 @@ test "Extern function calls, dereferences and field access in @TypeOf" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const Test = struct { - fn test_fn_1(a: c_long) @TypeOf(fopen("test", "r").*) { + fn test_fn_1(a: c_long) @TypeOf(c_fopen("test", "r").*) { _ = a; return .{ .dummy_field = 0 }; } - fn test_fn_2(a: anytype) @TypeOf(fopen("test", "r").*.dummy_field) { + fn test_fn_2(a: anytype) @TypeOf(c_fopen("test", "r").*.dummy_field) { _ = a; return 255; } From 5ae3ac9c431de65c2c075793e98a4a3257ac039e Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 6 Oct 2022 04:31:18 -0400 Subject: [PATCH 13/47] c: fix redefinition of typedef warnings Closes #11651 --- src/codegen/c.zig | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index b884e2fb0f..fddf33defd 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1259,17 +1259,19 @@ pub const DeclGen = struct { const bw = buffer.writer(); const elem_type = t.elemType(); - const sentinel_bit = @boolToInt(t.sentinel() != null); - const c_len = t.arrayLen() + sentinel_bit; try bw.writeAll("typedef "); try dg.renderType(bw, elem_type); const name_start = buffer.items.len + 1; - try bw.print(" zig_A_{s}_{d}", .{ typeToCIdentifier(elem_type, dg.module), c_len }); + try bw.print(" zig_A_{s}_{d}", .{ typeToCIdentifier(elem_type, dg.module), t.arrayLen() }); + if (t.sentinel()) |s| { + try bw.writeAll("_s_"); + try dg.renderValue(bw, elem_type, s, .Identifier); + } const name_end = buffer.items.len; - try bw.print("[{d}];\n", .{c_len}); + try bw.print("[{d}];\n", .{t.arrayLenIncludingSentinel()}); const rendered = buffer.toOwnedSlice(); errdefer dg.typedefs.allocator.free(rendered); From e875530f8f886cbf8272ab18d063252b94a835c9 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 6 Oct 2022 05:17:57 -0400 Subject: [PATCH 14/47] c: improve lowering of undef values All remaining behavior test -Wall -Wextra warnings are unused (but set) warnings. --- src/codegen/c.zig | 118 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 105 insertions(+), 13 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index fddf33defd..3e8398bb83 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -574,7 +574,12 @@ pub const DeclGen = struct { // Using '{}' for integer and floats seemed to error C compilers (both GCC and Clang) // with 'error: expected expression' (including when built with 'zig cc') .Bool => return writer.writeAll("false"), - .Int => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, UndefInt{}, location)}), + .Int, + .Enum, + .ErrorSet, + => return writer.print("{x}", .{ + try dg.fmtIntLiteral(ty, UndefInt{}, location), + }), .Float => switch (ty.tag()) { .f32 => return writer.print("zig_bitcast_f32_u32({x})", .{ try dg.fmtIntLiteral(Type.u32, UndefInt{}, location), @@ -584,25 +589,112 @@ pub const DeclGen = struct { }), else => return dg.fail("TODO float types > 64 bits are not support in renderValue() as of now", .{}), }, - .Pointer => return writer.print("((void *){x})", .{ - try dg.fmtIntLiteral(Type.usize, UndefInt{}, location), - }), - .Struct, .ErrorUnion => { + .Pointer => switch (ty.ptrSize()) { + .Slice => { + try writer.writeByte('('); + try dg.renderTypecast(writer, ty); + return writer.print("){{(void *){x}, {0x}}}", .{ + try dg.fmtIntLiteral(Type.usize, UndefInt{}, location), + }); + }, + .Many, .C, .One => return writer.print("((void *){x})", .{ + try dg.fmtIntLiteral(Type.usize, UndefInt{}, location), + }), + }, + .Optional => { + var opt_buf: Type.Payload.ElemType = undefined; + const payload_ty = ty.optionalChild(&opt_buf); + + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + return dg.renderValue(writer, Type.bool, val, location); + } + + if (ty.optionalReprIsPayload()) { + return dg.renderValue(writer, payload_ty, val, location); + } + try writer.writeByte('('); try dg.renderTypecast(writer, ty); - return writer.writeAll("){0xaau}"); + try writer.writeAll("){ .is_null = "); + try dg.renderValue(writer, Type.bool, val, location); + try writer.writeAll(", .payload = "); + try dg.renderValue(writer, payload_ty, val, location); + return writer.writeAll(" }"); + }, + .Struct => { + try writer.writeByte('('); + try dg.renderTypecast(writer, ty); + try writer.writeAll("){"); + + var empty = true; + for (ty.structFields().values()) |field| { + if (!field.ty.hasRuntimeBits()) continue; + + if (!empty) try writer.writeByte(','); + try dg.renderValue(writer, field.ty, val, location); + + empty = false; + } + if (empty) try writer.print("{x}", .{ + try dg.fmtIntLiteral(Type.u8, UndefInt{}, location), + }); + + return writer.writeByte('}'); + }, + .Union => { + try writer.writeByte('('); + try dg.renderTypecast(writer, ty); + try writer.writeAll("){"); + + for (ty.unionFields().values()) |field| { + if (!field.ty.hasRuntimeBits()) continue; + try dg.renderValue(writer, field.ty, val, location); + break; + } else try writer.print("{x}", .{ + try dg.fmtIntLiteral(Type.u8, UndefInt{}, location), + }); + + return writer.writeByte('}'); + }, + .ErrorUnion => { + try writer.writeByte('('); + try dg.renderTypecast(writer, ty); + try writer.writeAll("){ .payload = "); + try dg.renderValue(writer, ty.errorUnionPayload(), val, location); + return writer.print(", .error = {x} }}", .{ + try dg.fmtIntLiteral(ty.errorUnionSet(), UndefInt{}, location), + }); }, .Array => { try writer.writeByte('{'); - try dg.renderValue(writer, ty.childType(), val, location); + + const c_len = ty.arrayLenIncludingSentinel(); + var index: usize = 0; + while (index < c_len) : (index += 1) { + if (index > 0) try writer.writeAll(", "); + try dg.renderValue(writer, ty.childType(), val, location); + } + return writer.writeByte('}'); }, - else => { - // This should lower to 0xaa bytes in safe modes, and for unsafe modes should - // lower to leaving variables uninitialized (that might need to be implemented - // outside of this function). - return writer.writeAll("{}"); - }, + .ComptimeInt, + .ComptimeFloat, + .Type, + .EnumLiteral, + .Void, + .NoReturn, + .Undefined, + .Null, + .BoundFn, + .Opaque, + => unreachable, + .Fn, + .Frame, + .AnyFrame, + .Vector, + => |tag| return dg.fail("TODO: C backend: implement value of type {s}", .{ + @tagName(tag), + }), } } switch (ty.zigTypeTag()) { From 45c667eb21b1edde991435871523ece82793b449 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 6 Oct 2022 06:33:21 -0400 Subject: [PATCH 15/47] behavior: fix redefined exports --- test/behavior.zig | 8 +------- .../export_self_referential_type_info.zig | 2 +- test/behavior/generics.zig | 9 +++++---- test/behavior/type_info.zig | 15 ++++----------- 4 files changed, 11 insertions(+), 23 deletions(-) diff --git a/test/behavior.zig b/test/behavior.zig index 2f5d752087..3e4a45c992 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -211,13 +211,7 @@ test { _ = @import("behavior/export.zig"); } - if (builtin.zig_backend != .stage2_arm and - builtin.zig_backend != .stage2_x86_64 and - builtin.zig_backend != .stage2_aarch64 and - builtin.zig_backend != .stage2_wasm and - builtin.zig_backend != .stage2_c and - builtin.zig_backend != .stage1) - { + if (builtin.zig_backend != .stage2_wasm) { _ = @import("behavior/export_self_referential_type_info.zig"); } } diff --git a/test/behavior/export_self_referential_type_info.zig b/test/behavior/export_self_referential_type_info.zig index f822a81a13..0982985cee 100644 --- a/test/behavior/export_self_referential_type_info.zig +++ b/test/behavior/export_self_referential_type_info.zig @@ -1 +1 @@ -export const foo: c_int = @boolToInt(@typeInfo(@This()).Struct.is_tuple); +export const self_referential_type_info: c_int = @boolToInt(@typeInfo(@This()).Struct.is_tuple); diff --git a/test/behavior/generics.zig b/test/behavior/generics.zig index 4a483ec0d5..a0c21a75d8 100644 --- a/test/behavior/generics.zig +++ b/test/behavior/generics.zig @@ -361,13 +361,14 @@ test "nested generic function" { test "extern function used as generic parameter" { const S = struct { - extern fn foo() void; - extern fn bar() void; - inline fn baz(comptime _: anytype) type { + extern fn usedAsGenericParameterFoo() void; + extern fn usedAsGenericParameterBar() void; + inline fn usedAsGenericParameterBaz(comptime _: anytype) type { return struct {}; } }; - try expect(S.baz(S.foo) != S.baz(S.bar)); + try expect(S.usedAsGenericParameterBaz(S.usedAsGenericParameterFoo) != + S.usedAsGenericParameterBaz(S.usedAsGenericParameterBar)); } test "generic struct as parameter type" { diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig index b85d39d7bb..48f2e3e22d 100644 --- a/test/behavior/type_info.zig +++ b/test/behavior/type_info.zig @@ -355,19 +355,12 @@ fn testOpaque() !void { } test "type info: function type info" { - 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; - - // wasm doesn't support align attributes on functions - if (builtin.target.cpu.arch == .wasm32 or builtin.target.cpu.arch == .wasm64) return error.SkipZigTest; - try testFunction(); comptime try testFunction(); } fn testFunction() !void { - const fn_info = @typeInfo(@TypeOf(foo)); + const fn_info = @typeInfo(@TypeOf(typeInfoFoo)); try expect(fn_info == .Fn); try expect(fn_info.Fn.alignment > 0); try expect(fn_info.Fn.calling_convention == .C); @@ -375,12 +368,12 @@ fn testFunction() !void { try expect(fn_info.Fn.args.len == 2); try expect(fn_info.Fn.is_var_args); try expect(fn_info.Fn.return_type.? == usize); - const fn_aligned_info = @typeInfo(@TypeOf(fooAligned)); + const fn_aligned_info = @typeInfo(@TypeOf(typeInfoFooAligned)); try expect(fn_aligned_info.Fn.alignment == 4); } -extern fn foo(a: usize, b: bool, ...) callconv(.C) usize; -extern fn fooAligned(a: usize, b: bool, ...) align(4) callconv(.C) usize; +extern fn typeInfoFoo(a: usize, b: bool, ...) callconv(.C) usize; +extern fn typeInfoFooAligned(a: usize, b: bool, ...) align(4) callconv(.C) usize; test "type info: generic function types" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; From 525dcaecba43f9931aff69fd7dd0cd5b443c2859 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 6 Oct 2022 22:27:32 -0400 Subject: [PATCH 16/47] behavior: enable stage2_c tests that are currently passing Also fix C warnings triggered by these tests. --- src/codegen/c.zig | 5 +++-- test/behavior/align.zig | 7 ------- test/behavior/alignof.zig | 2 -- test/behavior/array.zig | 5 ----- test/behavior/basic.zig | 4 ---- test/behavior/bitcast.zig | 6 ------ test/behavior/bugs/10970.zig | 1 - test/behavior/bugs/11159.zig | 1 - test/behavior/bugs/11181.zig | 4 ---- test/behavior/bugs/11213.zig | 2 -- test/behavior/bugs/12776.zig | 1 - test/behavior/bugs/1421.zig | 1 - test/behavior/bugs/1442.zig | 1 - test/behavior/bugs/1607.zig | 1 - test/behavior/bugs/2692.zig | 1 - test/behavior/bugs/3007.zig | 1 - test/behavior/bugs/3046.zig | 1 - test/behavior/bugs/3367.zig | 1 - test/behavior/bugs/4954.zig | 1 - test/behavior/bugs/5398.zig | 1 - test/behavior/bugs/5487.zig | 1 - test/behavior/bugs/726.zig | 2 -- test/behavior/bugs/9584.zig | 1 - test/behavior/call.zig | 1 - test/behavior/cast.zig | 20 -------------------- test/behavior/cast_int.zig | 1 - test/behavior/empty_union.zig | 2 -- test/behavior/error.zig | 2 -- test/behavior/eval.zig | 3 --- test/behavior/fn.zig | 5 ----- test/behavior/math.zig | 8 -------- test/behavior/optional.zig | 3 --- test/behavior/pointers.zig | 1 - test/behavior/slice.zig | 5 ----- test/behavior/struct.zig | 19 ------------------- test/behavior/switch_prong_err_enum.zig | 1 - test/behavior/translate_c_macros.zig | 1 - test/behavior/type.zig | 4 ---- test/behavior/type_info.zig | 2 -- test/behavior/union.zig | 3 --- test/behavior/union_with_members.zig | 1 - test/behavior/widening.zig | 2 -- 42 files changed, 3 insertions(+), 132 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 3e8398bb83..a973dd1fc5 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1363,7 +1363,8 @@ pub const DeclGen = struct { } const name_end = buffer.items.len; - try bw.print("[{d}];\n", .{t.arrayLenIncludingSentinel()}); + const c_len = t.arrayLenIncludingSentinel(); + try bw.print("[{d}];\n", .{if (c_len > 0) c_len else 1}); const rendered = buffer.toOwnedSlice(); errdefer dg.typedefs.allocator.free(rendered); @@ -3514,7 +3515,7 @@ fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue { const payload_ty = opt_ty.optionalChild(&buf); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { - return operand; + return CValue.undefined_ptr; } if (opt_ty.optionalReprIsPayload()) { diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 2ebdda341a..3f7b7184c2 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -7,8 +7,6 @@ const assert = std.debug.assert; var foo: u8 align(4) = 100; test "global variable alignment" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - comptime try expect(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4); comptime try expect(@TypeOf(&foo) == *align(4) u8); { @@ -235,7 +233,6 @@ fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 { } test "specifying alignment allows pointer cast" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; try testBytesAlign(0x33); @@ -301,7 +298,6 @@ fn noop4() align(4) void {} test "function alignment" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // function alignment is a compile error on wasm32/wasm64 @@ -316,7 +312,6 @@ test "function alignment" { test "implicitly decreasing fn alignment" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; @@ -341,7 +336,6 @@ fn alignedBig() align(16) i32 { test "@alignCast functions" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // function alignment is a compile error on wasm32/wasm64 @@ -546,7 +540,6 @@ test "comptime alloc alignment" { 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 - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO comptime var bytes1 = [_]u8{0}; diff --git a/test/behavior/alignof.zig b/test/behavior/alignof.zig index d6491ff22e..4dd62906c4 100644 --- a/test/behavior/alignof.zig +++ b/test/behavior/alignof.zig @@ -12,7 +12,6 @@ const Foo = struct { test "@alignOf(T) before referencing T" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; comptime try expect(@alignOf(Foo) != maxInt(usize)); if (native_arch == .x86_64) { comptime try expect(@alignOf(Foo) == 4); @@ -20,7 +19,6 @@ test "@alignOf(T) before referencing T" { } test "comparison of @alignOf(T) against zero" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; { const T = struct { x: u32 }; try expect(!(@alignOf(T) == 0)); diff --git a/test/behavior/array.zig b/test/behavior/array.zig index ab084ff30a..911fe28cd8 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -6,8 +6,6 @@ const expect = testing.expect; const expectEqual = testing.expectEqual; test "array to slice" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - const a: u32 align(4) = 3; const b: u32 align(8) = 4; const a_slice: []align(1) const u32 = @as(*const [1]u32, &a)[0..]; @@ -160,7 +158,6 @@ test "nested arrays of strings" { test "nested arrays of integers" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const array_of_numbers = [_][2]u8{ [2]u8{ 1, 2 }, @@ -479,7 +476,6 @@ test "sentinel element count towards the ABI size calculation" { test "zero-sized array with recursive type definition" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const U = struct { fn foo(comptime T: type, comptime n: usize) type { @@ -501,7 +497,6 @@ test "zero-sized array with recursive type definition" { test "type coercion of anon struct literal to array" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index f82e6eef4b..e2df3d6aed 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -333,7 +333,6 @@ test "call result of if else expression" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO try expect(mem.eql(u8, f2(true), "a")); try expect(mem.eql(u8, f2(false), "b")); @@ -364,8 +363,6 @@ fn testMemcpyMemset() !void { } test "variable is allowed to be a pointer to an opaque type" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - var x: i32 = 1234; _ = hereIsAnOpaqueType(@ptrCast(*OpaqueA, &x)); } @@ -881,7 +878,6 @@ test "labeled block implicitly ends in a break" { } test "catch in block has correct result location" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const S = struct { diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig index c629a1a34b..e35b037749 100644 --- a/test/behavior/bitcast.zig +++ b/test/behavior/bitcast.zig @@ -136,7 +136,6 @@ test "@bitCast packed structs at runtime and comptime" { test "@bitCast extern structs at runtime and comptime" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const Full = extern struct { @@ -208,7 +207,6 @@ test "implicit cast to error union by returning" { test "bitcast packed struct literal to byte" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; const Foo = packed struct { value: u8, @@ -218,8 +216,6 @@ test "bitcast packed struct literal to byte" { } test "comptime bitcast used in expression has the correct type" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - const Foo = packed struct { value: u8, }; @@ -237,8 +233,6 @@ test "bitcast passed as tuple element" { } test "triple level result location with bitcast sandwich passed as tuple element" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - const S = struct { fn foo(args: anytype) !void { comptime try expect(@TypeOf(args[0]) == f64); diff --git a/test/behavior/bugs/10970.zig b/test/behavior/bugs/10970.zig index 72084188d7..e781b51fe9 100644 --- a/test/behavior/bugs/10970.zig +++ b/test/behavior/bugs/10970.zig @@ -7,7 +7,6 @@ test "breaking from a loop in an if statement" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; var cond = true; const opt = while (cond) { diff --git a/test/behavior/bugs/11159.zig b/test/behavior/bugs/11159.zig index 02cccf3e0a..035ea103d9 100644 --- a/test/behavior/bugs/11159.zig +++ b/test/behavior/bugs/11159.zig @@ -9,7 +9,6 @@ test { test { if (builtin.zig_backend == .stage1) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { comptime x: i32 = 0, diff --git a/test/behavior/bugs/11181.zig b/test/behavior/bugs/11181.zig index 67d26d6b4e..8e45c6612e 100644 --- a/test/behavior/bugs/11181.zig +++ b/test/behavior/bugs/11181.zig @@ -1,8 +1,6 @@ const builtin = @import("builtin"); test "const inferred array of slices" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - const T = struct { v: bool }; const decls = [_][]const T{ @@ -14,8 +12,6 @@ test "const inferred array of slices" { } test "var inferred array of slices" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - const T = struct { v: bool }; var decls = [_][]const T{ diff --git a/test/behavior/bugs/11213.zig b/test/behavior/bugs/11213.zig index 4699bda333..7f13b6efd8 100644 --- a/test/behavior/bugs/11213.zig +++ b/test/behavior/bugs/11213.zig @@ -3,8 +3,6 @@ const builtin = @import("builtin"); const testing = std.testing; test { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - const g: error{Test}!void = error.Test; var v: u32 = 0; diff --git a/test/behavior/bugs/12776.zig b/test/behavior/bugs/12776.zig index e8fe106ac7..3c89163f27 100644 --- a/test/behavior/bugs/12776.zig +++ b/test/behavior/bugs/12776.zig @@ -31,7 +31,6 @@ const CPU = packed struct { test { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; diff --git a/test/behavior/bugs/1421.zig b/test/behavior/bugs/1421.zig index d6ea8a2bad..fbd05fb73c 100644 --- a/test/behavior/bugs/1421.zig +++ b/test/behavior/bugs/1421.zig @@ -10,7 +10,6 @@ const S = struct { test "functions with return type required to be comptime are generic" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; const ti = S.method(); try expect(@as(std.builtin.TypeId, ti) == std.builtin.TypeId.Struct); } diff --git a/test/behavior/bugs/1442.zig b/test/behavior/bugs/1442.zig index 1933c8022e..95eafbeeaa 100644 --- a/test/behavior/bugs/1442.zig +++ b/test/behavior/bugs/1442.zig @@ -10,7 +10,6 @@ test "const error union field alignment" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; var union_or_err: anyerror!Union = Union{ .Color = 1234 }; try std.testing.expect((union_or_err catch unreachable).Color == 1234); } diff --git a/test/behavior/bugs/1607.zig b/test/behavior/bugs/1607.zig index dfcea537b5..5eff7f81b9 100644 --- a/test/behavior/bugs/1607.zig +++ b/test/behavior/bugs/1607.zig @@ -14,7 +14,6 @@ test "slices pointing at the same address as global array." { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; try checkAddress(&a); comptime try checkAddress(&a); } diff --git a/test/behavior/bugs/2692.zig b/test/behavior/bugs/2692.zig index 36fd763ebd..63fc43b85b 100644 --- a/test/behavior/bugs/2692.zig +++ b/test/behavior/bugs/2692.zig @@ -5,7 +5,6 @@ fn foo(a: []u8) void { } test "address of 0 length array" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; var pt: [0]u8 = undefined; diff --git a/test/behavior/bugs/3007.zig b/test/behavior/bugs/3007.zig index c93bbf8d20..20c9a8c913 100644 --- a/test/behavior/bugs/3007.zig +++ b/test/behavior/bugs/3007.zig @@ -22,7 +22,6 @@ test "fixed" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) 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 default_foo = get_foo() catch null; // This Line try std.testing.expect(!default_foo.?.free); diff --git a/test/behavior/bugs/3046.zig b/test/behavior/bugs/3046.zig index 1122ab4fe9..3cf42a2ce7 100644 --- a/test/behavior/bugs/3046.zig +++ b/test/behavior/bugs/3046.zig @@ -15,7 +15,6 @@ var some_struct: SomeStruct = undefined; test "fixed" { 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; some_struct = SomeStruct{ .field = couldFail() catch @as(i32, 0), diff --git a/test/behavior/bugs/3367.zig b/test/behavior/bugs/3367.zig index bd289af2b4..68b7d90ae8 100644 --- a/test/behavior/bugs/3367.zig +++ b/test/behavior/bugs/3367.zig @@ -10,7 +10,6 @@ const Mixin = struct { }; test "container member access usingnamespace decls" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; var foo = Foo{}; foo.two(); } diff --git a/test/behavior/bugs/4954.zig b/test/behavior/bugs/4954.zig index aa339de326..737333a3d7 100644 --- a/test/behavior/bugs/4954.zig +++ b/test/behavior/bugs/4954.zig @@ -5,7 +5,6 @@ fn f(buf: []u8) void { } test "crash" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; diff --git a/test/behavior/bugs/5398.zig b/test/behavior/bugs/5398.zig index caf36390ec..d7d01a46c3 100644 --- a/test/behavior/bugs/5398.zig +++ b/test/behavior/bugs/5398.zig @@ -22,7 +22,6 @@ test "assignment of field with padding" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; renderable = Renderable{ .mesh = Mesh{ .id = 0 }, diff --git a/test/behavior/bugs/5487.zig b/test/behavior/bugs/5487.zig index 9955d1f2f5..04d5f0e5b4 100644 --- a/test/behavior/bugs/5487.zig +++ b/test/behavior/bugs/5487.zig @@ -13,6 +13,5 @@ test "crash" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; _ = io.multiWriter(.{writer()}); } diff --git a/test/behavior/bugs/726.zig b/test/behavior/bugs/726.zig index 03e010e4f1..165bf899ca 100644 --- a/test/behavior/bugs/726.zig +++ b/test/behavior/bugs/726.zig @@ -5,7 +5,6 @@ test "@ptrCast from const to nullable" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; const c: u8 = 4; var x: ?*const u8 = @ptrCast(?*const u8, &c); @@ -16,7 +15,6 @@ test "@ptrCast from var in empty struct to nullable" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; const container = struct { var c: u8 = 4; diff --git a/test/behavior/bugs/9584.zig b/test/behavior/bugs/9584.zig index 503a402fed..48475cd96b 100644 --- a/test/behavior/bugs/9584.zig +++ b/test/behavior/bugs/9584.zig @@ -49,7 +49,6 @@ test { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var flags = A{ .a = false, diff --git a/test/behavior/call.zig b/test/behavior/call.zig index 37c6ce3691..a16c7af5a5 100644 --- a/test/behavior/call.zig +++ b/test/behavior/call.zig @@ -58,7 +58,6 @@ test "basic invocations" { test "tuple parameters" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) 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 const add = struct { diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 9a02e74853..cf086328a4 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -839,8 +839,6 @@ test "peer cast *[0]T to []const T" { } test "peer cast *[N]T to [*]T" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - var array = [4:99]i32{ 1, 2, 3, 4 }; var dest: [*]i32 = undefined; try expect(@TypeOf(&array, dest) == [*]i32); @@ -849,7 +847,6 @@ test "peer cast *[N]T to [*]T" { test "peer resolution of string literals" { 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 @@ -872,7 +869,6 @@ test "peer resolution of string literals" { test "peer cast [:x]T to []T" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -888,7 +884,6 @@ test "peer cast [:x]T to []T" { test "peer cast [N:x]T to [N]T" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -903,7 +898,6 @@ test "peer cast [N:x]T to [N]T" { test "peer cast *[N:x]T to *[N]T" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -937,7 +931,6 @@ test "peer cast [*:x]T to [*]T" { test "peer cast [:x]T to [*:x]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 @@ -1018,7 +1011,6 @@ test "cast between C pointer with different but compatible types" { test "peer type resolve string lit with sentinel-terminated mutable slice" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var array: [4:0]u8 = undefined; array[4] = 0; // TODO remove this when #4372 is solved @@ -1035,8 +1027,6 @@ test "peer type resolve array pointers, one of them const" { } test "peer type resolve array pointer and unknown pointer" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - const const_array: [4]u8 = undefined; var array: [4]u8 = undefined; var const_ptr: [*]const u8 = undefined; @@ -1069,7 +1059,6 @@ test "comptime float casts" { test "pointer reinterpret const float to int" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO // The hex representation is 0x3fe3333333333303. const float: f64 = 5.99999999999994648725e-01; @@ -1084,7 +1073,6 @@ test "pointer reinterpret const float to int" { test "implicit cast from [*]T to ?*anyopaque" { 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 var a = [_]u8{ 3, 2, 1 }; @@ -1102,8 +1090,6 @@ fn incrementVoidPtrArray(array: ?*anyopaque, len: usize) void { test "compile time int to ptr of function" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO try foobar(FUNCTION_CONSTANT); @@ -1120,7 +1106,6 @@ fn foobar(func: PFN_void) !void { test "implicit ptr to *anyopaque" { 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 var a: u32 = 1; @@ -1168,7 +1153,6 @@ fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { test "implicitly cast from [N]T to ?[]const 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 try expect(mem.eql(u8, castToOptionalSlice().?, "hi")); @@ -1220,7 +1204,6 @@ test "implicit cast from *[N]T to ?[*]T" { test "implicit cast from *T to ?*anyopaque" { 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 var a: u8 = 1; @@ -1246,8 +1229,6 @@ test "cast from array reference to fn: comptime fn ptr" { try expect(@ptrToInt(f) == @ptrToInt(&global_array)); } test "cast from array reference to fn: runtime fn ptr" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - var f = @ptrCast(*align(1) const fn () callconv(.C) void, &global_array); try expect(@ptrToInt(f) == @ptrToInt(&global_array)); } @@ -1354,7 +1335,6 @@ test "cast f128 to narrower types" { test "peer type resolution: unreachable, null, slice" { 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/cast_int.zig b/test/behavior/cast_int.zig index c30039eb52..c6df9d3257 100644 --- a/test/behavior/cast_int.zig +++ b/test/behavior/cast_int.zig @@ -8,7 +8,6 @@ test "@intCast i32 to u7" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var x: u128 = maxInt(u128); var y: i32 = 120; diff --git a/test/behavior/empty_union.zig b/test/behavior/empty_union.zig index 051e464b72..3f79df0446 100644 --- a/test/behavior/empty_union.zig +++ b/test/behavior/empty_union.zig @@ -12,7 +12,6 @@ test "switch on empty enum" { } test "switch on empty enum with a specified tag type" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const E = enum(u8) {}; @@ -21,7 +20,6 @@ test "switch on empty enum with a specified tag type" { } test "switch on empty auto numbered tagged union" { - 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_wasm) return error.SkipZigTest; // TODO diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 8a78e6eaa0..25ac894c6c 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -258,7 +258,6 @@ fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) !void { } test "comptime err to int of error set with only 1 possible value" { - 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_aarch64) return error.SkipZigTest; // TODO @@ -845,7 +844,6 @@ test "catch within a function that calls no errorable functions" { test "error from comptime string" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) 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 const name = "Weird error name!"; diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 2fa07d0de7..8ae10fa1ad 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -763,7 +763,6 @@ test "array concatenation peer resolves element types - value" { test "array concatenation peer resolves element types - pointer" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; @@ -801,7 +800,6 @@ test "array concatenation sets the sentinel - value" { test "array concatenation sets the sentinel - pointer" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; var a = [2]u3{ 1, 7 }; @@ -838,7 +836,6 @@ test "array multiplication sets the sentinel - value" { test "array multiplication sets the sentinel - pointer" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index 47ba63c429..e46a910227 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -146,7 +146,6 @@ fn fnWithUnreachable() noreturn { } test "extern struct with stdcallcc fn pointer" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; @@ -286,7 +285,6 @@ fn voidFun(a: i32, b: void, c: i32, d: void) !void { } test "call function with empty string" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; acceptsString(""); @@ -305,7 +303,6 @@ test "function pointers" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const fns = [_]*const @TypeOf(fn1){ &fn1, @@ -399,8 +396,6 @@ test "ability to give comptime types and non comptime types to same parameter" { } test "function with inferred error set but returning no error" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - const S = struct { fn foo() !void {} }; diff --git a/test/behavior/math.zig b/test/behavior/math.zig index d70b7a8f7e..744f4adb9e 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -616,8 +616,6 @@ test "128-bit multiplication" { } test "@addWithOverflow" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - { var result: u8 = undefined; try expect(@addWithOverflow(u8, 250, 100, &result)); @@ -673,8 +671,6 @@ test "small int addition" { } test "basic @mulWithOverflow" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - var result: u8 = undefined; try expect(@mulWithOverflow(u8, 86, 3, &result)); try expect(result == 2); @@ -897,8 +893,6 @@ test "@mulWithOverflow bitsize > 32" { } test "@subWithOverflow" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - { var result: u8 = undefined; try expect(@subWithOverflow(u8, 1, 2, &result)); @@ -977,8 +971,6 @@ test "@shlWithOverflow" { } test "overflow arithmetic with u0 values" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - var result: u0 = undefined; try expect(!@addWithOverflow(u0, 0, 0, &result)); try expect(result == 0); diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index eb693147e6..67b29c6885 100644 --- a/test/behavior/optional.zig +++ b/test/behavior/optional.zig @@ -65,7 +65,6 @@ test "optional with void type" { } test "address of unwrap optional" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -269,7 +268,6 @@ test "0-bit child type coerced to optional return ptr result location" { } test "0-bit child type coerced to optional" { - 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_aarch64) return error.SkipZigTest; // TODO @@ -332,7 +330,6 @@ test "array of optional unaligned types" { } test "optional pointer to zero bit optional payload" { - 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/pointers.zig b/test/behavior/pointers.zig index b0fb0f3a42..d3fb839baa 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -205,7 +205,6 @@ test "allowzero pointer and slice" { } test "assign null directly to C pointer and test null equality" { - 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/slice.zig b/test/behavior/slice.zig index 25527ed742..1b6fc3634f 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -229,7 +229,6 @@ fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 { test "C pointer" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; var buf: [*c]const u8 = "kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf"; var len: u32 = 10; @@ -477,7 +476,6 @@ test "slice syntax resulting in pointer-to-array" { } test "slice pointer-to-array null terminated" { - 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_aarch64) return error.SkipZigTest; // TODO @@ -503,7 +501,6 @@ test "slice pointer-to-array null terminated" { } test "slice pointer-to-array zero length" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO comptime { @@ -637,7 +634,6 @@ test "slice sentinel access at comptime" { test "slicing array with sentinel as end index" { // Doesn't work in stage1 if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const S = struct { @@ -657,7 +653,6 @@ test "slicing array with sentinel as end index" { test "slicing slice with sentinel as end index" { // Doesn't work in stage1 if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const S = struct { diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 2b9df06527..acd28426ab 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -319,7 +319,6 @@ const VoidStructFieldsFoo = struct { test "return empty struct from fn" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO _ = testReturnEmptyStructFromFn(); @@ -421,7 +420,6 @@ const Foo96Bits = packed struct { test "packed struct 24bits" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; 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_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.cpu.arch == .wasm32) return error.SkipZigTest; // TODO @@ -470,7 +468,6 @@ test "packed struct 24bits" { test "runtime struct initialization of bitfield" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; 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_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -537,7 +534,6 @@ test "packed struct fields are ordered from LSB to MSB" { test "implicit cast packed struct field to const ptr" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; 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_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -559,7 +555,6 @@ test "implicit cast packed struct field to const ptr" { test "zero-bit field in packed 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_sparc64) return error.SkipZigTest; // TODO @@ -605,7 +600,6 @@ const bit_field_1 = BitField1{ test "bit field access" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; 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_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -638,7 +632,6 @@ fn getC(data: *const BitField1) u2 { test "default struct initialization fields" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { @@ -771,7 +764,6 @@ test "pointer to packed struct member in a stack variable" { test "packed struct with u0 field access" { 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_sparc64) return error.SkipZigTest; // TODO const S = packed struct { @@ -783,7 +775,6 @@ test "packed struct with u0 field access" { test "access to global struct fields" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO g_foo.bar.value = 42; @@ -810,7 +801,6 @@ test "packed struct with fp fields" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = packed struct { data0: f32, @@ -886,7 +876,6 @@ test "packed struct field passed to generic function" { 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 - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { @@ -910,7 +899,6 @@ test "packed struct field passed to generic function" { test "anonymous struct literal syntax" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { const Point = struct { @@ -932,8 +920,6 @@ test "anonymous struct literal syntax" { } test "fully anonymous struct" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - const S = struct { fn doTheTest() !void { try dump(.{ @@ -956,8 +942,6 @@ test "fully anonymous struct" { } test "fully anonymous list literal" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - const S = struct { fn doTheTest() !void { try dump(.{ @as(u32, 1234), @as(f64, 12.34), true, "hi" }); @@ -1118,7 +1102,6 @@ test "type coercion of pointer to anon struct literal to pointer to struct" { test "packed struct with undefined initializers" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; 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_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1219,7 +1202,6 @@ test "anon init through error unions and optionals" { test "anon init through optional" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - 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_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -1241,7 +1223,6 @@ test "anon init through optional" { test "anon init through error union" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - 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_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/switch_prong_err_enum.zig b/test/behavior/switch_prong_err_enum.zig index 6abae1a7b6..36076df9cd 100644 --- a/test/behavior/switch_prong_err_enum.zig +++ b/test/behavior/switch_prong_err_enum.zig @@ -24,7 +24,6 @@ test "switch prong returns error enum" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; switch (doThing(17) catch unreachable) { FormValue.Address => |payload| { try expect(payload == 1); diff --git a/test/behavior/translate_c_macros.zig b/test/behavior/translate_c_macros.zig index 04d217f488..3a4de7b6f6 100644 --- a/test/behavior/translate_c_macros.zig +++ b/test/behavior/translate_c_macros.zig @@ -75,7 +75,6 @@ test "casting to union with a macro" { test "casting or calling a value with a paren-surrounded macro" { 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/type.zig b/test/behavior/type.zig index 4b70f502c7..1b71d67df3 100644 --- a/test/behavior/type.zig +++ b/test/behavior/type.zig @@ -248,7 +248,6 @@ fn add(a: i32, b: i32) i32 { test "Type.ErrorSet" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO try testing.expect(@Type(.{ .ErrorSet = null }) == anyerror); @@ -462,7 +461,6 @@ test "Type.Union" { } test "Type.Union from Type.Enum" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO const Tag = @Type(.{ @@ -490,7 +488,6 @@ test "Type.Union from Type.Enum" { } test "Type.Union from regular enum" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO const E = enum { working_as_expected }; @@ -509,7 +506,6 @@ test "Type.Union from regular enum" { test "Type.Fn" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (true) { // https://github.com/ziglang/zig/issues/12360 diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig index 48f2e3e22d..80a3c7210a 100644 --- a/test/behavior/type_info.zig +++ b/test/behavior/type_info.zig @@ -376,8 +376,6 @@ extern fn typeInfoFoo(a: usize, b: bool, ...) callconv(.C) usize; extern fn typeInfoFooAligned(a: usize, b: bool, ...) align(4) callconv(.C) usize; test "type info: generic function types" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend != .stage1) { // stage1 marks all args/return types as null if the function // is generic at all. stage2 is more specific. diff --git a/test/behavior/union.zig b/test/behavior/union.zig index ddad27e150..cfb522b74e 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -431,7 +431,6 @@ const Foo1 = union(enum) { var glbl: Foo1 = undefined; test "global union with single field is correctly initialized" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; 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; @@ -476,7 +475,6 @@ test "update the tag value for zero-sized unions" { } test "union initializer generates padding only if needed" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; @@ -753,7 +751,6 @@ fn Setter(comptime attr: Attribute) type { } test "return union init with void payload" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; 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; diff --git a/test/behavior/union_with_members.zig b/test/behavior/union_with_members.zig index 72e86ca7ac..0309ed801e 100644 --- a/test/behavior/union_with_members.zig +++ b/test/behavior/union_with_members.zig @@ -19,7 +19,6 @@ const ET = union(enum) { test "enum with members" { 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/widening.zig b/test/behavior/widening.zig index 691755ac2a..801c7d6a9b 100644 --- a/test/behavior/widening.zig +++ b/test/behavior/widening.zig @@ -8,7 +8,6 @@ test "integer widening" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var a: u8 = 250; var b: u16 = a; @@ -30,7 +29,6 @@ test "integer widening u0 to u8" { test "implicit unsigned integer to signed integer" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var a: u8 = 250; var b: i16 = a; From 7c9a9a0fd4560d8ce5d61ab96b9403d27dd872b3 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 7 Oct 2022 18:55:15 -0400 Subject: [PATCH 17/47] cbe: cleanup code and fix cases test breakage --- lib/include/zig.h | 2 +- src/codegen/c.zig | 608 +++++++++++++++++++++++++--------------------- src/link/C.zig | 20 +- src/type.zig | 26 +- 4 files changed, 360 insertions(+), 296 deletions(-) diff --git a/lib/include/zig.h b/lib/include/zig.h index 43d9913039..9568753345 100644 --- a/lib/include/zig.h +++ b/lib/include/zig.h @@ -165,7 +165,7 @@ #define int128_t __int128 #define uint128_t unsigned __int128 -#define UINT128_MAX ((uint128_t)(0xffffffffffffffffull) | 0xffffffffffffffffull) +#define UINT128_MAX (((uint128_t)UINT64_MAX<<64|UINT64_MAX)) ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t); ZIG_EXTERN_C void *memset (void *, int, size_t); ZIG_EXTERN_C int64_t __addodi4(int64_t lhs, int64_t rhs, int *overflow); diff --git a/src/codegen/c.zig b/src/codegen/c.zig index a973dd1fc5..e006c9c795 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -19,7 +19,7 @@ const Liveness = @import("../Liveness.zig"); const CType = @import("../type.zig").CType; const Mutability = enum { Const, Mut }; -const BigIntConst = std.math.big.int.Const; +const BigInt = std.math.big.int; pub const CValue = union(enum) { none: void, @@ -35,7 +35,7 @@ pub const CValue = union(enum) { decl: Decl.Index, decl_ref: Decl.Index, /// An undefined (void *) pointer (cannot be dereferenced) - undefined_ptr: void, + undefined_ptr: Type, /// Render the slice as an identifier (using fmtIdent) identifier: []const u8, /// Render these bytes literally. @@ -74,11 +74,11 @@ fn formatTypeAsCIdentifier( options: std.fmt.FormatOptions, writer: anytype, ) !void { - _ = fmt; - _ = options; - var buffer = [1]u8{0} ** 128; - var buf = std.fmt.bufPrint(&buffer, "{}", .{data.ty.fmt(data.mod)}) catch &buffer; - return formatIdent(buf, "", .{}, writer); + var stack = std.heap.stackFallback(128, data.mod.gpa); + const allocator = stack.get(); + const str = std.fmt.allocPrint(allocator, "{}", .{data.ty.fmt(data.mod)}) catch ""; + defer allocator.free(str); + return formatIdent(str, fmt, options, writer); } pub fn typeToCIdentifier(ty: Type, mod: *Module) std.fmt.Formatter(formatTypeAsCIdentifier) { @@ -332,8 +332,8 @@ pub const Function = struct { return f.object.dg.renderTypecast(w, t); } - fn fmtIntLiteral(f: *Function, ty: Type, int_val: anytype) !IntLiteralFormatter(@TypeOf(int_val)) { - return f.object.dg.fmtIntLiteral(ty, int_val, .Other); + fn fmtIntLiteral(f: *Function, ty: Type, val: Value) !std.fmt.Formatter(formatIntLiteral) { + return f.object.dg.fmtIntLiteral(ty, val, .Other); } }; @@ -423,51 +423,6 @@ pub const DeclGen = struct { try dg.renderDeclName(writer, decl_index); } - fn renderInt128( - writer: anytype, - int_val: anytype, - ) error{ OutOfMemory, AnalysisFail }!void { - const int_info = @typeInfo(@TypeOf(int_val)).Int; - const is_signed = int_info.signedness == .signed; - const is_neg = int_val < 0; - comptime assert(int_info.bits > 64 and int_info.bits <= 128); - - // Clang and GCC don't support 128-bit integer constants but will hopefully unfold them - // if we construct one manually. - const magnitude = std.math.absCast(int_val); - - const high = @truncate(u64, magnitude >> 64); - const low = @truncate(u64, magnitude); - - // (int128_t)/<->( ( (uint128_t)( val_high << 64 )u ) + (uint128_t)val_low/u ) - if (is_signed) try writer.writeAll("(int128_t)"); - if (is_neg) try writer.writeByte('-'); - - try writer.print("(((uint128_t)0x{x}u<<64)", .{high}); - - if (low > 0) - try writer.print("+(uint128_t)0x{x}u", .{low}); - - return writer.writeByte(')'); - } - - fn renderBigIntConst( - dg: *DeclGen, - writer: anytype, - val: BigIntConst, - signed: bool, - ) error{ OutOfMemory, AnalysisFail }!void { - if (signed) { - try renderInt128(writer, val.to(i128) catch { - return dg.fail("TODO implement integer constants larger than 128 bits", .{}); - }); - } else { - try renderInt128(writer, val.to(u128) catch { - return dg.fail("TODO implement integer constants larger than 128 bits", .{}); - }); - } - } - // Renders a "parent" pointer by recursing to the root decl/variable // that its contents are defined with respect to. // @@ -578,14 +533,14 @@ pub const DeclGen = struct { .Enum, .ErrorSet, => return writer.print("{x}", .{ - try dg.fmtIntLiteral(ty, UndefInt{}, location), + try dg.fmtIntLiteral(ty, val, location), }), .Float => switch (ty.tag()) { .f32 => return writer.print("zig_bitcast_f32_u32({x})", .{ - try dg.fmtIntLiteral(Type.u32, UndefInt{}, location), + try dg.fmtIntLiteral(Type.u32, val, location), }), .f64 => return writer.print("zig_bitcast_f64_u64({x})", .{ - try dg.fmtIntLiteral(Type.u64, UndefInt{}, location), + try dg.fmtIntLiteral(Type.u64, val, location), }), else => return dg.fail("TODO float types > 64 bits are not support in renderValue() as of now", .{}), }, @@ -593,13 +548,19 @@ pub const DeclGen = struct { .Slice => { try writer.writeByte('('); try dg.renderTypecast(writer, ty); - return writer.print("){{(void *){x}, {0x}}}", .{ - try dg.fmtIntLiteral(Type.usize, UndefInt{}, location), + try writer.writeAll("){("); + var buf: Type.SlicePtrFieldTypeBuffer = undefined; + const ptr_ty = ty.slicePtrFieldType(&buf); + try dg.renderTypecast(writer, ptr_ty); + return writer.print("){x}, {0x}}}", .{ + try dg.fmtIntLiteral(Type.usize, val, location), }); }, - .Many, .C, .One => return writer.print("((void *){x})", .{ - try dg.fmtIntLiteral(Type.usize, UndefInt{}, location), - }), + .Many, .C, .One => { + try writer.writeAll("(("); + try dg.renderTypecast(writer, ty); + return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val, location)}); + }, }, .Optional => { var opt_buf: Type.Payload.ElemType = undefined; @@ -636,7 +597,7 @@ pub const DeclGen = struct { empty = false; } if (empty) try writer.print("{x}", .{ - try dg.fmtIntLiteral(Type.u8, UndefInt{}, location), + try dg.fmtIntLiteral(Type.u8, Value.undef, location), }); return writer.writeByte('}'); @@ -651,7 +612,7 @@ pub const DeclGen = struct { try dg.renderValue(writer, field.ty, val, location); break; } else try writer.print("{x}", .{ - try dg.fmtIntLiteral(Type.u8, UndefInt{}, location), + try dg.fmtIntLiteral(Type.u8, Value.undef, location), }); return writer.writeByte('}'); @@ -662,7 +623,7 @@ pub const DeclGen = struct { try writer.writeAll("){ .payload = "); try dg.renderValue(writer, ty.errorUnionPayload(), val, location); return writer.print(", .error = {x} }}", .{ - try dg.fmtIntLiteral(ty.errorUnionSet(), UndefInt{}, location), + try dg.fmtIntLiteral(ty.errorUnionSet(), Value.undef, location), }); }, .Array => { @@ -696,15 +657,10 @@ pub const DeclGen = struct { @tagName(tag), }), } + unreachable; } switch (ty.zigTypeTag()) { .Int => switch (val.tag()) { - .int_big_positive => return writer.print("{x}", .{ - try dg.fmtIntLiteral(ty, val.castTag(.int_big_positive).?.asBigInt(), location), - }), - .int_big_negative => return writer.print("{x}", .{ - try dg.fmtIntLiteral(ty, val.castTag(.int_big_negative).?.asBigInt(), location), - }), .field_ptr, .elem_ptr, .opt_payload_ptr, @@ -712,32 +668,35 @@ pub const DeclGen = struct { .decl_ref_mut, .decl_ref, => try dg.renderParentPtr(writer, val, ty), - else => if (ty.isSignedInt()) - return writer.print("{d}", .{try dg.fmtIntLiteral(ty, val.toSignedInt(), location)}) - else - return writer.print("{d}", .{ - try dg.fmtIntLiteral(ty, val.toUnsignedInt(target), location), - }), + else => try writer.print("{}", .{try dg.fmtIntLiteral(ty, val, location)}), }, .Float => { if (ty.floatBits(target) <= 64) { if (std.math.isNan(val.toFloat(f64)) or std.math.isInf(val.toFloat(f64))) { // just generate a bit cast (exactly like we do in airBitcast) switch (ty.tag()) { - .f32 => return writer.print("zig_bitcast_f32_u32({x})", .{ - try dg.fmtIntLiteral( + .f32 => { + var bitcast_val_pl = Value.Payload.U64{ + .base = .{ .tag = .int_u64 }, + .data = @bitCast(u32, val.toFloat(f32)), + }; + return writer.print("zig_bitcast_f32_u32({x})", .{try dg.fmtIntLiteral( Type.u32, - @bitCast(u32, val.toFloat(f32)), + Value.initPayload(&bitcast_val_pl.base), location, - ), - }), - .f64 => return writer.print("zig_bitcast_f64_u64({x})", .{ - try dg.fmtIntLiteral( + )}); + }, + .f64 => { + var bitcast_val_pl = Value.Payload.U64{ + .base = .{ .tag = .int_u64 }, + .data = @bitCast(u64, val.toFloat(f64)), + }; + return writer.print("zig_bitcast_f32_u32({x})", .{try dg.fmtIntLiteral( Type.u64, - @bitCast(u64, val.toFloat(f64)), + Value.initPayload(&bitcast_val_pl.base), location, - ), - }), + )}); + }, else => return dg.fail("TODO float types > 64 bits are not support in renderValue() as of now", .{}), } } else { @@ -779,9 +738,7 @@ pub const DeclGen = struct { .int_u64, .one => { try writer.writeAll("(("); try dg.renderTypecast(writer, ty); - return writer.print("){x})", .{ - try dg.fmtIntLiteral(Type.usize, val.toUnsignedInt(target), location), - }); + return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val, location)}); }, .field_ptr, .elem_ptr, @@ -1069,7 +1026,7 @@ pub const DeclGen = struct { try bw.writeAll(" (*"); const name_start = buffer.items.len; - try bw.print("zig_F_{s})(", .{typeToCIdentifier(t, dg.module)}); + try bw.print("zig_F_{})(", .{typeToCIdentifier(t, dg.module)}); const name_end = buffer.items.len - 2; const param_len = fn_info.param_types.len; @@ -1124,13 +1081,16 @@ pub const DeclGen = struct { try bw.writeAll("; size_t len; } "); const name_index = buffer.items.len; if (t.isConstPtr()) { - try bw.print("zig_L_{s}", .{typeToCIdentifier(child_type, dg.module)}); + try bw.print("zig_L_{}", .{typeToCIdentifier(child_type, dg.module)}); } else { - try bw.print("zig_M_{s}", .{typeToCIdentifier(child_type, dg.module)}); + try bw.print("zig_M_{}", .{typeToCIdentifier(child_type, dg.module)}); } if (ptr_sentinel) |s| { - try bw.writeAll("_s_"); - try dg.renderValue(bw, child_type, s, .Identifier); + var sentinel_buffer = std.ArrayList(u8).init(dg.typedefs.allocator); + defer sentinel_buffer.deinit(); + + try dg.renderValue(sentinel_buffer.writer(), child_type, s, .Identifier); + try bw.print("_s_{}", .{fmtIdent(sentinel_buffer.items)}); } try bw.writeAll(";\n"); @@ -1327,7 +1287,7 @@ pub const DeclGen = struct { try dg.renderDeclName(bw, func.owner_decl); try bw.writeAll(";\n"); } else { - try bw.print("zig_E_{s}_{s};\n", .{ + try bw.print("zig_E_{}_{};\n", .{ typeToCIdentifier(error_ty, dg.module), typeToCIdentifier(payload_ty, dg.module), }); } @@ -1356,10 +1316,13 @@ pub const DeclGen = struct { try dg.renderType(bw, elem_type); const name_start = buffer.items.len + 1; - try bw.print(" zig_A_{s}_{d}", .{ typeToCIdentifier(elem_type, dg.module), t.arrayLen() }); + try bw.print(" zig_A_{}_{d}", .{ typeToCIdentifier(elem_type, dg.module), t.arrayLen() }); if (t.sentinel()) |s| { - try bw.writeAll("_s_"); - try dg.renderValue(bw, elem_type, s, .Identifier); + var sentinel_buffer = std.ArrayList(u8).init(dg.typedefs.allocator); + defer sentinel_buffer.deinit(); + + try dg.renderValue(sentinel_buffer.writer(), elem_type, s, .Identifier); + try bw.print("_s_{}", .{fmtIdent(sentinel_buffer.items)}); } const name_end = buffer.items.len; @@ -1389,7 +1352,7 @@ pub const DeclGen = struct { try dg.renderTypeAndName(bw, child_type, payload_name, .Mut, 0); try bw.writeAll("; bool is_null; } "); const name_index = buffer.items.len; - try bw.print("zig_Q_{s};\n", .{typeToCIdentifier(child_type, dg.module)}); + try bw.print("zig_Q_{};\n", .{typeToCIdentifier(child_type, dg.module)}); const rendered = buffer.toOwnedSlice(); errdefer dg.typedefs.allocator.free(rendered); @@ -1413,7 +1376,7 @@ pub const DeclGen = struct { var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); defer buffer.deinit(); - try buffer.writer().print("typedef struct {} ", .{fmtIdent(std.mem.span(unqualified_name))}); + try buffer.writer().print("typedef struct { } ", .{fmtIdent(std.mem.span(unqualified_name))}); const name_start = buffer.items.len; try buffer.writer().print("zig_O_{};\n", .{fmtIdent(fqn)}); @@ -1710,9 +1673,11 @@ pub const DeclGen = struct { try w.writeByte('&'); return dg.renderDeclName(w, decl); }, - .undefined_ptr => return w.print("((void *){x})", .{ - try dg.fmtIntLiteral(Type.usize, UndefInt{}, .Other), - }), + .undefined_ptr => |ty| { + try w.writeAll("(("); + try dg.renderTypecast(w, ty); + return w.print("){x})", .{try dg.fmtIntLiteral(Type.usize, Value.undef, .Other)}); + }, .identifier => |ident| return w.print("{ }", .{fmtIdent(ident)}), .bytes => |bytes| return w.writeAll(bytes), } @@ -1760,19 +1725,19 @@ pub const DeclGen = struct { fn fmtIntLiteral( dg: *DeclGen, ty: Type, - int_val: anytype, + val: Value, location: ValueRenderLocation, - ) !IntLiteralFormatter(@TypeOf(int_val)) { - const target = dg.module.getTarget(); - const int_info = ty.intInfo(target); - _ = toCIntBits(int_info.bits) orelse + ) !std.fmt.Formatter(formatIntLiteral) { + const int_info = ty.intInfo(dg.module.getTarget()); + const c_bits = toCIntBits(int_info.bits); + if (c_bits == null or c_bits.? > 128) return dg.fail("TODO implement integer constants larger than 128 bits", .{}); - return IntLiteralFormatter(@TypeOf(int_val)){ + return std.fmt.Formatter(formatIntLiteral){ .data = .{ .ty = ty, - .target = target, - .int_val = int_val, + .val = val, + .mod = dg.module, .location = location, - }; + } }; } }; @@ -1785,8 +1750,8 @@ pub fn genErrDecls(o: *Object) !void { var max_name_len: usize = 0; for (o.dg.module.error_name_list.items) |name, value| { max_name_len = std.math.max(name.len, max_name_len); - var err_val_payload = Value.Payload.Error{ .data = .{ .name = name } }; - try o.dg.renderValue(writer, Type.anyerror, Value.initPayload(&err_val_payload.base), .Other); + var err_val_pl = Value.Payload.Error{ .data = .{ .name = name } }; + try o.dg.renderValue(writer, Type.anyerror, Value.initPayload(&err_val_pl.base), .Other); try writer.print(" = {d}u,\n", .{value}); } o.indent_writer.popIndent(); @@ -1804,14 +1769,14 @@ pub fn genErrDecls(o: *Object) !void { const identifier = name_buf[0 .. name_prefix.len + name.len :0]; const nameZ = identifier[name_prefix.len..]; - var name_ty_payload = Type.Payload.Len{ + var name_ty_pl = Type.Payload.Len{ .base = .{ .tag = .array_u8_sentinel_0 }, .data = name.len, }; - const name_ty = Type.initPayload(&name_ty_payload.base); + const name_ty = Type.initPayload(&name_ty_pl.base); - var name_val_payload = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = nameZ }; - const name_val = Value.initPayload(&name_val_payload.base); + var name_val_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = nameZ }; + const name_val = Value.initPayload(&name_val_pl.base); try writer.writeAll("static "); try o.dg.renderTypeAndName(writer, name_ty, .{ .identifier = identifier }, .Const, 0); @@ -1820,11 +1785,11 @@ pub fn genErrDecls(o: *Object) !void { try writer.writeAll(";\n"); } - var name_array_ty_payload = Type.Payload.Array{ .base = .{ .tag = .array }, .data = .{ + var name_array_ty_pl = Type.Payload.Array{ .base = .{ .tag = .array }, .data = .{ .len = o.dg.module.error_name_list.items.len, .elem_type = Type.initTag(.const_slice_u8_sentinel_0), } }; - const name_array_ty = Type.initPayload(&name_array_ty_payload.base); + const name_array_ty = Type.initPayload(&name_array_ty_pl.base); try writer.writeAll("static "); try o.dg.renderTypeAndName(writer, name_array_ty, .{ .identifier = "zig_errorName" }, .Const, 0); @@ -2363,7 +2328,7 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { const elem_type = inst_ty.elemType(); const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut; if (!elem_type.isFnOrHasRuntimeBitsIgnoreComptime()) { - return CValue.undefined_ptr; + return CValue{ .undefined_ptr = inst_ty }; } const target = f.object.dg.module.getTarget(); @@ -2540,7 +2505,7 @@ fn airStoreUndefined(f: *Function, dest_ptr: CValue) !CValue { const writer = f.object.writer(); try writer.writeAll("memset("); try f.writeCValue(writer, dest_ptr); - try writer.print(", {x}, sizeof(", .{try f.fmtIntLiteral(Type.u8, UndefInt{})}); + try writer.print(", {x}, sizeof(", .{try f.fmtIntLiteral(Type.u8, Value.undef)}); try f.writeCValueDeref(writer, dest_ptr); try writer.writeAll("));\n"); }, @@ -2659,9 +2624,22 @@ fn airWrapOp( try f.writeCValue(w, lhs); try w.writeAll(", "); try f.writeCValue(w, rhs); - if (int_info.signedness == .signed) - try w.print(", {}", .{try f.fmtIntLiteral(inst_ty, MinInt{})}); - try w.print(", {});", .{try f.fmtIntLiteral(inst_ty, MaxInt{})}); + { + var arena = std.heap.ArenaAllocator.init(f.object.dg.module.gpa); + defer arena.deinit(); + + const expected_contents = union { u: Value.Payload.U64, i: Value.Payload.I64 }; + var stack align(@alignOf(expected_contents)) = + std.heap.stackFallback(@sizeOf(expected_contents), arena.allocator()); + + if (int_info.signedness == .signed) { + const min_val = try inst_ty.minInt(stack.get(), target); + try w.print(", {}", .{try f.fmtIntLiteral(inst_ty, min_val)}); + } + + const max_val = try inst_ty.maxInt(stack.get(), target); + try w.print(", {});", .{try f.fmtIntLiteral(inst_ty, max_val)}); + } try f.object.indent_writer.insertNewline(); return ret; @@ -2673,7 +2651,8 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue { const bin_op = f.air.instructions.items(.data)[inst].bin_op; const inst_ty = f.air.typeOfIndex(inst); - const int_info = inst_ty.intInfo(f.object.dg.module.getTarget()); + const target = f.object.dg.module.getTarget(); + const int_info = inst_ty.intInfo(target); const bits = int_info.bits; switch (bits) { @@ -2716,9 +2695,22 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue { try f.writeCValue(w, lhs); try w.writeAll(", "); try f.writeCValue(w, rhs); - if (int_info.signedness == .signed) - try w.print(", {}", .{try f.fmtIntLiteral(inst_ty, MinInt{})}); - try w.print(", {});", .{try f.fmtIntLiteral(inst_ty, MaxInt{})}); + { + var arena = std.heap.ArenaAllocator.init(f.object.dg.module.gpa); + defer arena.deinit(); + + const expected_contents = union { u: Value.Payload.U64, i: Value.Payload.I64 }; + var stack align(@alignOf(expected_contents)) = + std.heap.stackFallback(@sizeOf(expected_contents), arena.allocator()); + + if (int_info.signedness == .signed) { + const min_val = try inst_ty.minInt(stack.get(), target); + try w.print(", {}", .{try f.fmtIntLiteral(inst_ty, min_val)}); + } + + const max_val = try inst_ty.maxInt(stack.get(), target); + try w.print(", {});", .{try f.fmtIntLiteral(inst_ty, max_val)}); + } try f.object.indent_writer.insertNewline(); return ret; @@ -2756,9 +2748,22 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, op_abbrev: [*:0]const u8) !CV try w.writeAll(", &"); try f.writeCValue(w, ret); try w.writeAll(".field_0, "); - if (int_info.signedness == .signed) - try w.print("{}, ", .{try f.fmtIntLiteral(scalar_ty, MinInt{})}); - try w.print("{});", .{try f.fmtIntLiteral(scalar_ty, MaxInt{})}); + { + var arena = std.heap.ArenaAllocator.init(f.object.dg.module.gpa); + defer arena.deinit(); + + const expected_contents = union { u: Value.Payload.U64, i: Value.Payload.I64 }; + var stack align(@alignOf(expected_contents)) = + std.heap.stackFallback(@sizeOf(expected_contents), arena.allocator()); + + if (int_info.signedness == .signed) { + const min_val = try scalar_ty.minInt(stack.get(), target); + try w.print("{}, ", .{try f.fmtIntLiteral(scalar_ty, min_val)}); + } + + const max_val = try scalar_ty.maxInt(stack.get(), target); + try w.print("{});", .{try f.fmtIntLiteral(scalar_ty, max_val)}); + } try f.object.indent_writer.insertNewline(); return ret; } @@ -3360,9 +3365,9 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const inputs_extra_begin = extra_i; for (inputs) |input, i| { - const input_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); - const constraint = std.mem.sliceTo(input_bytes, 0); - const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); + const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); + const constraint = std.mem.sliceTo(extra_bytes, 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. extra_i += (constraint.len + name.len + (2 + 3)) / 4; @@ -3411,10 +3416,12 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(": "); extra_i = inputs_extra_begin; for (inputs) |_, index| { - const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); + const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); + const constraint = std.mem.sliceTo(extra_bytes, 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. - extra_i += constraint.len / 4 + 1; + extra_i += (constraint.len + name.len + (2 + 3)) / 4; if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') { const reg = constraint[1 .. constraint.len - 1]; @@ -3511,11 +3518,10 @@ fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(ty_op.operand); const ptr_ty = f.air.typeOf(ty_op.operand); const opt_ty = ptr_ty.childType(); - var buf: Type.Payload.ElemType = undefined; - const payload_ty = opt_ty.optionalChild(&buf); + const inst_ty = f.air.typeOfIndex(inst); - if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { - return CValue.undefined_ptr; + if (!inst_ty.childType().hasRuntimeBitsIgnoreComptime()) { + return CValue{ .undefined_ptr = inst_ty }; } if (opt_ty.optionalReprIsPayload()) { @@ -3524,7 +3530,6 @@ fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue { 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); @@ -3892,7 +3897,8 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { if (operand == .undefined_ptr) { // Unfortunately, C does not support any equivalent to // &(*(void *)p)[0], although LLVM does via GetElementPtr - try f.writeCValue(writer, CValue.undefined_ptr); + var buf: Type.SlicePtrFieldTypeBuffer = undefined; + try f.writeCValue(writer, CValue{ .undefined_ptr = inst_ty.slicePtrFieldType(&buf) }); } else { try writer.writeAll("&("); try f.writeCValueDeref(writer, operand); @@ -4478,148 +4484,186 @@ fn signAbbrev(signedness: std.builtin.Signedness) u8 { }; } -const UndefInt = struct { - pub fn to(_: UndefInt, comptime T: type) error{}!T { - comptime { - if (@bitSizeOf(T) < 2) return 0; - var value: T = 2; - var shift = 2; - while (shift < @bitSizeOf(T)) : (shift <<= 1) - value |= value << shift; - return value; - } - } -}; -const MaxInt = struct { - pub fn to(_: MaxInt, comptime T: type) error{}!T { - return std.math.maxInt(T); - } -}; -const MinInt = struct { - pub fn to(_: MinInt, comptime T: type) error{}!T { - return std.math.minInt(T); - } +const FormatIntLiteralContext = struct { + ty: Type, + val: Value, + mod: *Module, + location: ValueRenderLocation, }; +fn formatIntLiteral( + data: FormatIntLiteralContext, + comptime fmt: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) @TypeOf(writer).Error!void { + const target = data.mod.getTarget(); + const int_info = data.ty.intInfo(target); -fn IntLiteralFormatter(comptime IntType: type) type { - return struct { - ty: Type, - target: std.Target, - int_val: IntType, - location: ValueRenderLocation, + const Limb = std.math.big.Limb; + const expected_contents = struct { + const base = 10; + const limbs_count_128 = BigInt.calcTwosCompLimbCount(128); + const expected_needed_limbs_count = BigInt.calcToStringLimbsBufferLen(limbs_count_128, base); + const worst_case_int = BigInt.Const{ + .limbs = &([1]Limb{std.math.maxInt(Limb)} ** expected_needed_limbs_count), + .positive = false, + }; - fn formatHelper( - self: @This(), - comptime CIntType: type, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - const c_int_info = @typeInfo(CIntType).Int; - const c_int_val = switch (@typeInfo(IntType)) { - .Int => @intCast(CIntType, self.int_val), - .Struct, .Pointer => self.int_val.to(CIntType) catch unreachable, - else => unreachable, - }; - const c_abs_val = std.math.absCast(c_int_val); - if (self.location == .Identifier) { - if (c_int_val < 0) try writer.writeAll("_N"); - try writer.print("{d}", .{c_abs_val}); - } else if (c_int_info.bits == 128) { - // Clang and GCC don't support 128-bit integer constants but - // will hopefully unfold them if we construct one manually. - //std.debug.todo("128-bit is unimplemented"); - try writer.writeByte('('); - if (c_int_info.signedness == .signed) { - try writer.writeAll("(int128_t)"); - if (c_int_val < 0) try writer.writeByte('-'); - } - - const upper = @intCast(u64, c_abs_val >> 64); - if (upper != 0) try writer.writeByte('('); - if (upper != 0 or c_int_val < 0) try writer.writeAll("(uint128_t)"); - if (upper != 0) { - try (IntLiteralFormatter(u64){ - .ty = Type.u64, - .target = self.target, - .int_val = upper, - .location = self.location, - }).formatHelper(u64, fmt, options, writer); - try writer.writeAll("<<64|"); - } - - const lower = @truncate(u64, c_abs_val); - try (IntLiteralFormatter(u64){ - .ty = Type.u64, - .target = self.target, - .int_val = lower, - .location = self.location, - }).formatHelper(u64, fmt, options, writer); - - if (upper != 0) try writer.writeByte(')'); - try writer.writeByte(')'); - } else if (c_int_val == std.math.maxInt(CIntType) or - c_int_info.signedness == .signed and c_int_val == std.math.minInt(CIntType)) - { - if (c_int_info.signedness == .unsigned) try writer.writeByte('U'); - try writer.writeAll(switch (self.ty.tag()) { - .c_short, .c_ushort => "SHRT", - .c_int, .c_uint => "INT", - .c_long, .c_ulong => "LONG", - .c_longlong, .c_ulonglong => "LLONG", - .isize, .usize => "INTPTR", - else => std.fmt.comptimePrint("INT{d}", .{c_int_info.bits}), - }); - try writer.writeAll(if (c_int_val < 0) "_MIN" else "_MAX"); - } else { - if (c_int_val < 0) try writer.writeByte('-'); - if (c_int_info.signedness == .unsigned) try writer.writeByte('U'); - try writer.print("INT{d}_C(" ++ switch (fmt.len) { - 0 => "{d}", - 1 => switch (fmt[0]) { - 'o' => "0{o}", - 'd' => "{d}", - 'x' => "0x{x}", - 'X' => "0x{X}", - else => @compileError("Invalid fmt: " ++ fmt), - }, - else => @compileError("Invalid fmt: " ++ fmt), - } ++ ")", .{ c_int_info.bits, c_abs_val }); - } - } - - pub fn format( - self: @This(), - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - const int_info = self.ty.intInfo(self.target); - switch (toCIntBits(int_info.bits).?) { - 8 => switch (int_info.signedness) { - .signed => try self.formatHelper(i8, fmt, options, writer), - .unsigned => try self.formatHelper(u8, fmt, options, writer), - }, - 16 => switch (int_info.signedness) { - .signed => try self.formatHelper(i16, fmt, options, writer), - .unsigned => try self.formatHelper(u16, fmt, options, writer), - }, - 32 => switch (int_info.signedness) { - .signed => try self.formatHelper(i32, fmt, options, writer), - .unsigned => try self.formatHelper(u32, fmt, options, writer), - }, - 64 => switch (int_info.signedness) { - .signed => try self.formatHelper(i64, fmt, options, writer), - .unsigned => try self.formatHelper(u64, fmt, options, writer), - }, - 128 => switch (int_info.signedness) { - .signed => try self.formatHelper(i128, fmt, options, writer), - .unsigned => try self.formatHelper(u128, fmt, options, writer), - }, - else => unreachable, - } - } + undef_limbs: [limbs_count_128]Limb, + str: [worst_case_int.sizeInBaseUpperBound(base)]u8, + limbs_limbs: [expected_needed_limbs_count]Limb, }; + var stack align(@alignOf(expected_contents)) = + std.heap.stackFallback(@sizeOf(expected_contents), data.mod.gpa); + const allocator = stack.get(); + + var undef_limbs: []Limb = &.{}; + defer allocator.free(undef_limbs); + + var int_buf: Value.BigIntSpace = undefined; + const int = if (data.val.isUndefDeep()) blk: { + undef_limbs = try allocator.alloc(Limb, BigInt.calcTwosCompLimbCount(int_info.bits)); + + const undef_pattern: Limb = (1 << (@bitSizeOf(Limb) | 1)) / 3; + std.mem.set(Limb, undef_limbs, undef_pattern); + + var undef_int = BigInt.Mutable{ + .limbs = undef_limbs, + .len = undef_limbs.len, + .positive = true, + }; + undef_int.truncate(undef_int.toConst(), int_info.signedness, int_info.bits); + break :blk undef_int.toConst(); + } else data.val.toBigInt(&int_buf, target); + assert(int.fitsInTwosComp(int_info.signedness, int_info.bits)); + + if (data.location == .Identifier) { + const str = try int.toStringAlloc(allocator, 10, undefined); + defer allocator.free(str); + + return writer.writeAll(str); + } + + const limbs_count_64 = @divExact(64, @bitSizeOf(Limb)); + const c_bits = toCIntBits(int_info.bits) orelse unreachable; + if (c_bits == 128) { + // Clang and GCC don't support 128-bit integer constants but + // will hopefully unfold them if we construct one manually. + //std.debug.todo("128-bit is unimplemented"); + try writer.writeByte('('); + if (int_info.signedness == .signed) { + try writer.writeAll("(int128_t)"); + if (!int.positive) try writer.writeByte('-'); + } + + const split = std.math.min(int.limbs.len, limbs_count_64); + var upper_val_pl = Value.Payload.BigInt{ + .base = .{ .tag = .int_big_positive }, + .data = int.limbs[split..], + }; + const have_upper = !upper_val_pl.asBigInt().eqZero(); + if (have_upper) try writer.writeByte('('); + if (have_upper or !int.positive) try writer.writeAll("(uint128_t)"); + if (have_upper) { + const upper_val = Value.initPayload(&upper_val_pl.base); + try formatIntLiteral(.{ + .ty = Type.u64, + .val = upper_val, + .mod = data.mod, + .location = data.location, + }, fmt, options, writer); + try writer.writeAll("<<64|"); + } + + var lower_val_pl = Value.Payload.BigInt{ + .base = .{ .tag = .int_big_positive }, + .data = int.limbs[0..split], + }; + const lower_val = Value.initPayload(&lower_val_pl.base); + try formatIntLiteral(.{ + .ty = Type.u64, + .val = lower_val, + .mod = data.mod, + .location = data.location, + }, fmt, options, writer); + + if (have_upper) try writer.writeByte(')'); + return writer.writeByte(')'); + } + + assert(c_bits <= 64); + var one_limbs: [BigInt.calcLimbLen(1)]Limb = undefined; + const one = BigInt.Mutable.init(&one_limbs, 1).toConst(); + + var wrap_limbs: [BigInt.calcTwosCompLimbCount(64)]Limb = undefined; + var wrap = BigInt.Mutable{ .limbs = &wrap_limbs, .len = undefined, .positive = undefined }; + if (wrap.addWrap(int, one, int_info.signedness, c_bits) or + int_info.signedness == .signed and wrap.subWrap(int, one, int_info.signedness, c_bits)) + { + if (int_info.signedness == .unsigned) try writer.writeByte('U'); + switch (data.ty.tag()) { + .c_short, .c_ushort => try writer.writeAll("SHRT"), + .c_int, .c_uint => try writer.writeAll("INT"), + .c_long, .c_ulong => try writer.writeAll("LONG"), + .c_longlong, .c_ulonglong => try writer.writeAll("LLONG"), + .isize, .usize => try writer.writeAll("INTPTR"), + else => try writer.print("INT{d}", .{c_bits}), + } + try writer.writeAll(if (int.positive) "_MAX" else "_MIN"); + return; + } + + if (!int.positive) try writer.writeByte('-'); + switch (data.ty.tag()) { + .c_short, .c_ushort, .c_int, .c_uint, .c_long, .c_ulong, .c_longlong, .c_ulonglong => {}, + else => { + if (int_info.signedness == .unsigned) try writer.writeByte('U'); + try writer.print("INT{d}_C(", .{c_bits}); + }, + } + + var base: u8 = undefined; + var case: std.fmt.Case = undefined; + switch (fmt.len) { + 0 => base = 10, + 1 => switch (fmt[0]) { + 'b' => { + base = 2; + try writer.writeAll("0b"); + }, + 'o' => { + base = 8; + try writer.writeByte('0'); + }, + 'd' => base = 10, + 'x' => { + base = 16; + case = .lower; + try writer.writeAll("0x"); + }, + 'X' => { + base = 16; + case = .upper; + try writer.writeAll("0x"); + }, + else => @compileError("Invalid fmt: " ++ fmt), + }, + else => @compileError("Invalid fmt: " ++ fmt), + } + + var str: [64]u8 = undefined; + var limbs_buf: [BigInt.calcToStringLimbsBufferLen(limbs_count_64, 10)]Limb = undefined; + try writer.writeAll(str[0..int.abs().toString(&str, base, case, &limbs_buf)]); + + switch (data.ty.tag()) { + .c_short, .c_ushort, .c_int => {}, + .c_uint => try writer.writeAll("u"), + .c_long => try writer.writeAll("l"), + .c_ulong => try writer.writeAll("ul"), + .c_longlong => try writer.writeAll("ll"), + .c_ulonglong => try writer.writeAll("ull"), + else => try writer.writeByte(')'), + } } fn loweredFnRetTyHasBits(fn_ty: Type) bool { diff --git a/src/link/C.zig b/src/link/C.zig index ad64f76466..bb321c1e83 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -363,32 +363,38 @@ fn flushTypedefs(self: *C, f: *Flush, typedefs: codegen.TypedefMap.Unmanaged) Fl } fn flushErrDecls(self: *C, f: *Flush) FlushDeclError!void { - const gpa = self.base.allocator; const module = self.base.options.module.?; var object = codegen.Object{ .dg = .{ - .gpa = gpa, + .gpa = module.gpa, .module = module, .error_msg = null, .decl_index = undefined, .decl = undefined, .fwd_decl = undefined, - .typedefs = codegen.TypedefMap.initContext(gpa, .{ .mod = module }), - .typedefs_arena = gpa, + .typedefs = codegen.TypedefMap.initContext(module.gpa, .{ .mod = module }), + .typedefs_arena = self.arena.allocator(), }, - .code = f.err_buf.toManaged(gpa), + .code = f.err_buf.toManaged(module.gpa), .indent_writer = undefined, // set later so we can get a pointer to object.code }; object.indent_writer = .{ .underlying_writer = object.code.writer() }; - defer object.dg.typedefs.deinit(); - defer f.err_buf = object.code.moveToUnmanaged(); + defer { + f.err_buf = object.code.moveToUnmanaged(); + for (object.dg.typedefs.values()) |value| { + module.gpa.free(value.rendered); + } + object.dg.typedefs.deinit(); + } codegen.genErrDecls(&object) catch |err| switch (err) { error.AnalysisFail => unreachable, else => |e| return e, }; + const gpa = self.base.allocator; + try self.flushTypedefs(f, object.dg.typedefs.unmanaged); try f.all_buffers.ensureUnusedCapacity(gpa, 1); f.appendBufAssumeCapacity(object.code.items); diff --git a/src/type.zig b/src/type.zig index a2f0bb9e8f..5c74f39290 100644 --- a/src/type.zig +++ b/src/type.zig @@ -5347,7 +5347,7 @@ pub const Type = extern union { // Works for vectors and vectors of integers. pub fn minInt(ty: Type, arena: Allocator, target: Target) !Value { const scalar = try minIntScalar(ty.scalarType(), arena, target); - if (ty.zigTypeTag() == .Vector) { + if (ty.zigTypeTag() == .Vector and scalar.tag() != .the_only_possible_value) { return Value.Tag.repeated.create(arena, scalar); } else { return scalar; @@ -5359,12 +5359,16 @@ pub const Type = extern union { assert(ty.zigTypeTag() == .Int); const info = ty.intInfo(target); + if (info.bits == 0) { + return Value.initTag(.the_only_possible_value); + } + if (info.signedness == .unsigned) { return Value.zero; } - if (info.bits <= 6) { - const n: i64 = -(@as(i64, 1) << @truncate(u6, info.bits - 1)); + if (std.math.cast(u6, info.bits - 1)) |shift| { + const n = @as(i64, std.math.minInt(i64)) >> (63 - shift); return Value.Tag.int_i64.create(arena, n); } @@ -5384,13 +5388,23 @@ pub const Type = extern union { assert(self.zigTypeTag() == .Int); const info = self.intInfo(target); - if (info.bits <= 6) switch (info.signedness) { + if (info.bits == 0) { + return Value.initTag(.the_only_possible_value); + } + + switch (info.bits - @boolToInt(info.signedness == .signed)) { + 0 => return Value.zero, + 1 => return Value.one, + else => {}, + } + + if (std.math.cast(u6, info.bits - 1)) |shift| switch (info.signedness) { .signed => { - const n: i64 = (@as(i64, 1) << @truncate(u6, info.bits - 1)) - 1; + const n = @as(i64, std.math.maxInt(i64)) >> (63 - shift); return Value.Tag.int_i64.create(arena, n); }, .unsigned => { - const n: u64 = (@as(u64, 1) << @truncate(u6, info.bits)) - 1; + const n = @as(u64, std.math.maxInt(u64)) >> (63 - shift); return Value.Tag.int_u64.create(arena, n); }, }; From 6a4266d62aacf887a81549d81ae6f312992d2b2c Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 8 Oct 2022 08:54:49 -0400 Subject: [PATCH 18/47] cbe: fix infinite recursion on recursive types --- lib/include/zig.h | 1 + src/Compilation.zig | 13 +- src/codegen/c.zig | 231 +++++++++++------- src/link/C.zig | 59 +++-- test/behavior/align.zig | 1 - test/behavior/atomics.zig | 1 - test/behavior/basic.zig | 1 - test/behavior/bugs/1310.zig | 1 - test/behavior/bugs/1500.zig | 1 - test/behavior/bugs/2006.zig | 1 - test/behavior/cast.zig | 4 - test/behavior/floatop.zig | 2 - test/behavior/generics.zig | 1 - test/behavior/math.zig | 3 - test/behavior/maximum_minimum.zig | 2 - test/behavior/muladd.zig | 1 - test/behavior/packed-struct.zig | 1 - test/behavior/saturating_arithmetic.zig | 1 - test/behavior/struct.zig | 2 - .../struct_contains_null_ptr_itself.zig | 1 - test/behavior/switch_prong_implicit_cast.zig | 1 - test/behavior/union.zig | 3 - test/behavior/vector.zig | 1 - 23 files changed, 181 insertions(+), 152 deletions(-) diff --git a/lib/include/zig.h b/lib/include/zig.h index 9568753345..e4a0c4cc59 100644 --- a/lib/include/zig.h +++ b/lib/include/zig.h @@ -162,6 +162,7 @@ #include #include #include +#include #define int128_t __int128 #define uint128_t unsigned __int128 diff --git a/src/Compilation.zig b/src/Compilation.zig index fc71da56f3..99332e5d2b 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3099,13 +3099,16 @@ fn processOneJob(comp: *Compilation, job: Job) !void { .decl_index = decl_index, .decl = decl, .fwd_decl = fwd_decl.toManaged(gpa), - .typedefs = c_codegen.TypedefMap.initContext(gpa, .{ - .mod = module, - }), + .typedefs = c_codegen.TypedefMap.initContext(gpa, .{ .mod = module }), .typedefs_arena = typedefs_arena.allocator(), }; - defer dg.fwd_decl.deinit(); - defer dg.typedefs.deinit(); + defer { + for (dg.typedefs.values()) |typedef| { + module.gpa.free(typedef.rendered); + } + dg.typedefs.deinit(); + dg.fwd_decl.deinit(); + } c_codegen.genHeader(&dg) catch |err| switch (err) { error.AnalysisFail => { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index e006c9c795..beafafc94f 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -372,8 +372,8 @@ pub const DeclGen = struct { } fn getTypedefName(dg: *DeclGen, t: Type) ?[]const u8 { - if (dg.typedefs.get(t)) |some| { - return some.name; + if (dg.typedefs.get(t)) |typedef| { + return typedef.name; } else { return null; } @@ -1025,9 +1025,10 @@ pub const DeclGen = struct { try dg.renderType(bw, fn_info.return_type); try bw.writeAll(" (*"); - const name_start = buffer.items.len; - try bw.print("zig_F_{})(", .{typeToCIdentifier(t, dg.module)}); - const name_end = buffer.items.len - 2; + const name_begin = buffer.items.len; + try bw.print("zig_F_{}", .{typeToCIdentifier(t, dg.module)}); + const name_end = buffer.items.len; + try bw.writeAll(")("); const param_len = fn_info.param_types.len; @@ -1052,7 +1053,7 @@ pub const DeclGen = struct { const rendered = buffer.toOwnedSlice(); errdefer dg.typedefs.allocator.free(rendered); - const name = rendered[name_start..name_end]; + const name = rendered[name_begin..name_end]; try dg.typedefs.ensureUnusedCapacity(1); dg.typedefs.putAssumeCapacityNoClobber( @@ -1079,12 +1080,11 @@ pub const DeclGen = struct { const child_type = t.childType(); try bw.writeAll("; size_t len; } "); - const name_index = buffer.items.len; - if (t.isConstPtr()) { - try bw.print("zig_L_{}", .{typeToCIdentifier(child_type, dg.module)}); - } else { - try bw.print("zig_M_{}", .{typeToCIdentifier(child_type, dg.module)}); - } + const name_begin = buffer.items.len; + try bw.print("zig_{c}_{}", .{ + @as(u8, if (t.isConstPtr()) 'L' else 'M'), + typeToCIdentifier(child_type, dg.module), + }); if (ptr_sentinel) |s| { var sentinel_buffer = std.ArrayList(u8).init(dg.typedefs.allocator); defer sentinel_buffer.deinit(); @@ -1092,11 +1092,12 @@ pub const DeclGen = struct { try dg.renderValue(sentinel_buffer.writer(), child_type, s, .Identifier); try bw.print("_s_{}", .{fmtIdent(sentinel_buffer.items)}); } + const name_end = buffer.items.len; try bw.writeAll(";\n"); const rendered = buffer.toOwnedSlice(); errdefer dg.typedefs.allocator.free(rendered); - const name = rendered[name_index .. rendered.len - 2]; + const name = rendered[name_begin..name_end]; try dg.typedefs.ensureUnusedCapacity(1); dg.typedefs.putAssumeCapacityNoClobber( @@ -1115,7 +1116,30 @@ pub const DeclGen = struct { var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); defer buffer.deinit(); - try buffer.appendSlice("typedef struct {\n"); + const tag = "struct"; + const tagged_name_begin = buffer.items.len + "typedef ".len + tag.len + " ".len; + try buffer.writer().print("typedef " ++ tag ++ " zig_S_{} ", .{fmtIdent(fqn)}); + const tagged_name_end = buffer.items.len - " ".len; + try buffer.ensureUnusedCapacity(tagged_name_end - tagged_name_begin + ";\n".len); + const name_begin = buffer.items.len; + buffer.appendSliceAssumeCapacity(buffer.items[tagged_name_begin..tagged_name_end]); + const name_end = buffer.items.len; + buffer.appendSliceAssumeCapacity(";\n"); + + const rendered = buffer.toOwnedSlice(); + errdefer dg.typedefs.allocator.free(rendered); + const tagged_name = rendered[tagged_name_begin..tagged_name_end]; + const name = rendered[name_begin..name_end]; + + try dg.typedefs.ensureUnusedCapacity(1); + dg.typedefs.putAssumeCapacityNoClobber( + try t.copy(dg.typedefs_arena), + .{ .name = name, .rendered = rendered }, + ); + + try buffer.appendSlice(tag ++ " "); + try buffer.appendSlice(tagged_name); + try buffer.appendSlice(" {\n"); { var it = struct_obj.fields.iterator(); var empty = true; @@ -1124,68 +1148,63 @@ pub const DeclGen = struct { if (!field_ty.hasRuntimeBits()) continue; const alignment = entry.value_ptr.abi_align; - const name: CValue = .{ .identifier = entry.key_ptr.* }; + const field_name: CValue = .{ .identifier = entry.key_ptr.* }; try buffer.append(' '); - try dg.renderTypeAndName(buffer.writer(), field_ty, name, .Mut, alignment); + try dg.renderTypeAndName(buffer.writer(), field_ty, field_name, .Mut, alignment); try buffer.appendSlice(";\n"); empty = false; } if (empty) try buffer.appendSlice(" char empty_struct;\n"); } - try buffer.appendSlice("} "); + try buffer.appendSlice("};\n"); - const name_start = buffer.items.len; - try buffer.writer().print("zig_S_{};\n", .{fmtIdent(fqn)}); - - const rendered = buffer.toOwnedSlice(); - errdefer dg.typedefs.allocator.free(rendered); - const name = rendered[name_start .. rendered.len - 2]; + const rendered_body = buffer.toOwnedSlice(); + errdefer dg.typedefs.allocator.free(rendered_body); + // We need to add another item to the TypedefMap, so we need a distinct + // type that is not used anywhere, but is still uniquely associated with + // this type, so use an empty struct which references our unique decls. try dg.typedefs.ensureUnusedCapacity(1); dg.typedefs.putAssumeCapacityNoClobber( - try t.copy(dg.typedefs_arena), - .{ .name = name, .rendered = rendered }, + try Type.Tag.empty_struct.create(dg.typedefs_arena, &struct_obj.namespace), + .{ .name = undefined, .rendered = rendered_body }, ); return name; } fn renderTupleTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { - const tuple = t.tupleFields(); - var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); defer buffer.deinit(); - const writer = buffer.writer(); try buffer.appendSlice("typedef struct {\n"); { + const fields = t.tupleFields(); var empty = true; - for (tuple.types) |field_ty, i| { + for (fields.types) |field_ty, i| { if (!field_ty.hasRuntimeBits()) continue; - const val = tuple.values[i]; + const val = fields.values[i]; if (val.tag() != .unreachable_value) continue; - var name = std.ArrayList(u8).init(dg.gpa); - defer name.deinit(); - try name.writer().print("field_{d}", .{i}); + const field_name = try std.fmt.allocPrint(dg.typedefs.allocator, "field_{d}", .{i}); + defer dg.typedefs.allocator.free(field_name); try buffer.append(' '); - try dg.renderTypeAndName(writer, field_ty, .{ .bytes = name.items }, .Mut, 0); + try dg.renderTypeAndName(buffer.writer(), field_ty, .{ .identifier = field_name }, .Mut, 0); try buffer.appendSlice(";\n"); empty = false; } if (empty) try buffer.appendSlice(" char empty_tuple;\n"); } - try buffer.appendSlice("} "); - - const name_start = buffer.items.len; - try writer.print("zig_T_{};\n", .{typeToCIdentifier(t, dg.module)}); + const name_begin = buffer.items.len + "} ".len; + try buffer.writer().print("}} zig_T_{};\n", .{typeToCIdentifier(t, dg.module)}); + const name_end = buffer.items.len - ";\n".len; const rendered = buffer.toOwnedSlice(); errdefer dg.typedefs.allocator.free(rendered); - const name = rendered[name_start .. rendered.len - 2]; + const name = rendered[name_begin..name_end]; try dg.typedefs.ensureUnusedCapacity(1); dg.typedefs.putAssumeCapacityNoClobber( @@ -1197,62 +1216,85 @@ pub const DeclGen = struct { } fn renderUnionTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { - const union_ty = t.cast(Type.Payload.Union).?.data; - const fqn = try union_ty.getFullyQualifiedName(dg.module); + const union_obj = t.cast(Type.Payload.Union).?.data; + const fqn = try union_obj.getFullyQualifiedName(dg.module); defer dg.typedefs.allocator.free(fqn); - const target = dg.module.getTarget(); - const layout = t.unionGetLayout(target); - var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); defer buffer.deinit(); - try buffer.appendSlice("typedef "); - if (t.unionTagTypeSafety()) |tag_ty| { - const name: CValue = .{ .bytes = "tag" }; - try buffer.appendSlice("struct {\n "); + const tag: []const u8 = if (t.unionTagTypeSafety()) |_| "struct" else "union"; + const tagged_name_begin = buffer.items.len + "typedef ".len + tag.len + " ".len; + try buffer.writer().print("typedef {s} zig_S_{} ", .{ tag, fmtIdent(fqn) }); + const tagged_name_end = buffer.items.len - " ".len; + try buffer.ensureUnusedCapacity(tagged_name_end - tagged_name_begin + ";\n".len); + const name_begin = buffer.items.len; + buffer.appendSliceAssumeCapacity(buffer.items[tagged_name_begin..tagged_name_end]); + const name_end = buffer.items.len; + buffer.appendSliceAssumeCapacity(";\n"); + + const rendered = buffer.toOwnedSlice(); + errdefer dg.typedefs.allocator.free(rendered); + const tagged_name = rendered[tagged_name_begin..tagged_name_end]; + const name = rendered[name_begin..name_end]; + + try dg.typedefs.ensureUnusedCapacity(1); + dg.typedefs.putAssumeCapacityNoClobber( + try t.copy(dg.typedefs_arena), + .{ .name = name, .rendered = rendered }, + ); + + try buffer.appendSlice(tag); + try buffer.append(' '); + try buffer.appendSlice(tagged_name); + try buffer.appendSlice(" {\n"); + + const indent = if (t.unionTagTypeSafety()) |tag_ty| indent: { + const target = dg.module.getTarget(); + const layout = t.unionGetLayout(target); if (layout.tag_size != 0) { - try dg.renderTypeAndName(buffer.writer(), tag_ty, name, .Mut, 0); + try buffer.append(' '); + try dg.renderTypeAndName(buffer.writer(), tag_ty, .{ .identifier = "tag" }, .Mut, 0); try buffer.appendSlice(";\n"); } - } + try buffer.appendSlice(" union {\n"); + break :indent " "; + } else " "; - try buffer.appendSlice("union {\n"); - const fields = t.unionFields(); { - var it = fields.iterator(); + var it = t.unionFields().iterator(); var empty = true; while (it.next()) |entry| { const field_ty = entry.value_ptr.ty; if (!field_ty.hasRuntimeBits()) continue; const alignment = entry.value_ptr.abi_align; - const name: CValue = .{ .identifier = entry.key_ptr.* }; - try buffer.append(' '); - try dg.renderTypeAndName(buffer.writer(), field_ty, name, .Mut, alignment); + const field_name: CValue = .{ .identifier = entry.key_ptr.* }; + try buffer.appendSlice(indent); + try dg.renderTypeAndName(buffer.writer(), field_ty, field_name, .Mut, alignment); try buffer.appendSlice(";\n"); empty = false; } - if (empty) try buffer.appendSlice(" char empty_union;\n"); - } - try buffer.appendSlice("} "); - - if (t.unionTagTypeSafety()) |_| { - try buffer.appendSlice("payload;\n} "); + if (empty) { + try buffer.appendSlice(indent); + try buffer.appendSlice("char empty_union;\n"); + } } - const name_start = buffer.items.len; - try buffer.writer().print("zig_U_{};\n", .{fmtIdent(fqn)}); + if (t.unionTagTypeSafety()) |_| try buffer.appendSlice(" } payload;\n"); + try buffer.appendSlice("};\n"); - const rendered = buffer.toOwnedSlice(); - errdefer dg.typedefs.allocator.free(rendered); - const name = rendered[name_start .. rendered.len - 2]; + const rendered_body = buffer.toOwnedSlice(); + errdefer dg.typedefs.allocator.free(rendered_body); + // We need to add another item to the TypedefMap, so we need a distinct + // type that is not used anywhere, but is still uniquely associated with + // this type, so use an empty struct which references our unique decls. try dg.typedefs.ensureUnusedCapacity(1); dg.typedefs.putAssumeCapacityNoClobber( - try t.copy(dg.typedefs_arena), - .{ .name = name, .rendered = rendered }, + try Type.Tag.empty_struct.create(dg.typedefs_arena, &union_obj.namespace), + .{ .name = undefined, .rendered = rendered_body }, ); return name; @@ -1280,21 +1322,22 @@ pub const DeclGen = struct { try bw.writeAll("; } "); } - const name_index = buffer.items.len; + const name_begin = buffer.items.len; if (error_ty.castTag(.error_set_inferred)) |inf_err_set_payload| { const func = inf_err_set_payload.data.func; try bw.writeAll("zig_E_"); try dg.renderDeclName(bw, func.owner_decl); - try bw.writeAll(";\n"); } else { - try bw.print("zig_E_{}_{};\n", .{ + try bw.print("zig_E_{}_{}", .{ typeToCIdentifier(error_ty, dg.module), typeToCIdentifier(payload_ty, dg.module), }); } + const name_end = buffer.items.len; + try bw.writeAll(";\n"); const rendered = buffer.toOwnedSlice(); errdefer dg.typedefs.allocator.free(rendered); - const name = rendered[name_index .. rendered.len - 2]; + const name = rendered[name_begin..name_end]; try dg.typedefs.ensureUnusedCapacity(1); dg.typedefs.putAssumeCapacityNoClobber( @@ -1315,7 +1358,7 @@ pub const DeclGen = struct { try bw.writeAll("typedef "); try dg.renderType(bw, elem_type); - const name_start = buffer.items.len + 1; + const name_begin = buffer.items.len + " ".len; try bw.print(" zig_A_{}_{d}", .{ typeToCIdentifier(elem_type, dg.module), t.arrayLen() }); if (t.sentinel()) |s| { var sentinel_buffer = std.ArrayList(u8).init(dg.typedefs.allocator); @@ -1331,7 +1374,7 @@ pub const DeclGen = struct { const rendered = buffer.toOwnedSlice(); errdefer dg.typedefs.allocator.free(rendered); - const name = rendered[name_start..name_end]; + const name = rendered[name_begin..name_end]; try dg.typedefs.ensureUnusedCapacity(1); dg.typedefs.putAssumeCapacityNoClobber( @@ -1351,12 +1394,15 @@ pub const DeclGen = struct { const payload_name = CValue{ .bytes = "payload" }; try dg.renderTypeAndName(bw, child_type, payload_name, .Mut, 0); try bw.writeAll("; bool is_null; } "); - const name_index = buffer.items.len; - try bw.print("zig_Q_{};\n", .{typeToCIdentifier(child_type, dg.module)}); + + const name_begin = buffer.items.len; + try bw.print("zig_Q_{}", .{typeToCIdentifier(child_type, dg.module)}); + const name_end = buffer.items.len; + try bw.writeAll(";\n"); const rendered = buffer.toOwnedSlice(); errdefer dg.typedefs.allocator.free(rendered); - const name = rendered[name_index .. rendered.len - 2]; + const name = rendered[name_begin..name_end]; try dg.typedefs.ensureUnusedCapacity(1); dg.typedefs.putAssumeCapacityNoClobber( @@ -1378,12 +1424,14 @@ pub const DeclGen = struct { try buffer.writer().print("typedef struct { } ", .{fmtIdent(std.mem.span(unqualified_name))}); - const name_start = buffer.items.len; - try buffer.writer().print("zig_O_{};\n", .{fmtIdent(fqn)}); + const name_begin = buffer.items.len; + try buffer.writer().print("zig_O_{}", .{fmtIdent(fqn)}); + const name_end = buffer.items.len; + try buffer.appendSlice(";\n"); const rendered = buffer.toOwnedSlice(); errdefer dg.typedefs.allocator.free(rendered); - const name = rendered[name_start .. rendered.len - 2]; + const name = rendered[name_begin..name_end]; try dg.typedefs.ensureUnusedCapacity(1); dg.typedefs.putAssumeCapacityNoClobber( @@ -1530,7 +1578,7 @@ pub const DeclGen = struct { return w.writeAll(name); }, .Struct => { - const name = dg.getTypedefName(t) orelse if (t.isTuple() or t.tag() == .anon_struct) + const name = dg.getTypedefName(t) orelse if (t.isTupleOrAnonStruct()) try dg.renderTupleTypedef(t) else try dg.renderStructTypedef(t); @@ -3597,8 +3645,8 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc var field_name: []const u8 = undefined; var field_val_ty: Type = undefined; - var buf = std.ArrayList(u8).init(f.object.dg.gpa); - defer buf.deinit(); + var field_name_buf: []const u8 = ""; + defer f.object.dg.gpa.free(field_name_buf); switch (struct_ty.tag()) { .@"struct" => { const fields = struct_ty.structFields(); @@ -3614,8 +3662,8 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc const tuple = struct_ty.tupleFields(); if (tuple.values[index].tag() != .unreachable_value) return CValue.none; - try buf.writer().print("field_{d}", .{index}); - field_name = buf.items; + field_name_buf = try std.fmt.allocPrint(f.object.dg.gpa, "field_{d}", .{index}); + field_name = field_name_buf; field_val_ty = tuple.types[index]; }, else => unreachable, @@ -3648,8 +3696,8 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const struct_byval = try f.resolveInst(extra.struct_operand); const struct_ty = f.air.typeOf(extra.struct_operand); - var buf = std.ArrayList(u8).init(f.object.dg.gpa); - defer buf.deinit(); + var field_name_buf: []const u8 = ""; + defer f.object.dg.gpa.free(field_name_buf); const field_name = switch (struct_ty.tag()) { .@"struct" => struct_ty.structFields().keys()[extra.field_index], .@"union", .union_safety_tagged, .union_tagged => struct_ty.unionFields().keys()[extra.field_index], @@ -3657,8 +3705,8 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { const tuple = struct_ty.tupleFields(); if (tuple.values[extra.field_index].tag() != .unreachable_value) return CValue.none; - try buf.writer().print("field_{d}", .{extra.field_index}); - break :blk buf.items; + field_name_buf = try std.fmt.allocPrint(f.object.dg.gpa, "field_{d}", .{extra.field_index}); + break :blk field_name_buf; }, else => unreachable, }; @@ -4125,8 +4173,9 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { const layout = union_ty.unionGetLayout(target); if (layout.tag_size == 0) return CValue.none; + try writer.writeByte('('); try f.writeCValue(writer, union_ptr); - try writer.writeAll("->tag = "); + try writer.writeAll(")->tag = "); try f.writeCValue(writer, new_tag); try writer.writeAll(";\n"); diff --git a/src/link/C.zig b/src/link/C.zig index bb321c1e83..d7432eb015 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -108,10 +108,8 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes const typedefs = &gop.value_ptr.typedefs; const code = &gop.value_ptr.code; fwd_decl.shrinkRetainingCapacity(0); - { - for (typedefs.values()) |value| { - module.gpa.free(value.rendered); - } + for (typedefs.values()) |typedef| { + module.gpa.free(typedef.rendered); } typedefs.clearRetainingCapacity(); code.shrinkRetainingCapacity(0); @@ -139,14 +137,14 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() }; defer { - function.value_map.deinit(); function.blocks.deinit(module.gpa); + function.value_map.deinit(); function.object.code.deinit(); - function.object.dg.fwd_decl.deinit(); - for (function.object.dg.typedefs.values()) |value| { - module.gpa.free(value.rendered); + for (function.object.dg.typedefs.values()) |typedef| { + module.gpa.free(typedef.rendered); } function.object.dg.typedefs.deinit(); + function.object.dg.fwd_decl.deinit(); } codegen.genFunc(&function) catch |err| switch (err) { @@ -179,10 +177,8 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi const typedefs = &gop.value_ptr.typedefs; const code = &gop.value_ptr.code; fwd_decl.shrinkRetainingCapacity(0); - { - for (typedefs.values()) |value| { - module.gpa.free(value.rendered); - } + for (typedefs.values()) |value| { + module.gpa.free(value.rendered); } typedefs.clearRetainingCapacity(); code.shrinkRetainingCapacity(0); @@ -206,11 +202,11 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi object.indent_writer = .{ .underlying_writer = object.code.writer() }; defer { object.code.deinit(); - object.dg.fwd_decl.deinit(); - for (object.dg.typedefs.values()) |value| { - module.gpa.free(value.rendered); + for (object.dg.typedefs.values()) |typedef| { + module.gpa.free(typedef.rendered); } object.dg.typedefs.deinit(); + object.dg.fwd_decl.deinit(); } codegen.genDecl(&object) catch |err| switch (err) { @@ -307,10 +303,10 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) } const Flush = struct { + err_decls: DeclBlock = .{}, remaining_decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, void) = .{}, typedefs: Typedefs = .{}, typedef_buf: std.ArrayListUnmanaged(u8) = .{}, - err_buf: std.ArrayListUnmanaged(u8) = .{}, /// We collect a list of buffers to write, and write them all at once with pwritev 😎 all_buffers: std.ArrayListUnmanaged(std.os.iovec_const) = .{}, /// Keeps track of the total bytes of `all_buffers`. @@ -332,10 +328,10 @@ const Flush = struct { fn deinit(f: *Flush, gpa: Allocator) void { f.all_buffers.deinit(gpa); - f.err_buf.deinit(gpa); f.typedef_buf.deinit(gpa); f.typedefs.deinit(gpa); f.remaining_decls.deinit(gpa); + f.err_decls.deinit(gpa); } }; @@ -365,6 +361,10 @@ fn flushTypedefs(self: *C, f: *Flush, typedefs: codegen.TypedefMap.Unmanaged) Fl fn flushErrDecls(self: *C, f: *Flush) FlushDeclError!void { const module = self.base.options.module.?; + const fwd_decl = &f.err_decls.fwd_decl; + const typedefs = &f.err_decls.typedefs; + const code = &f.err_decls.code; + var object = codegen.Object{ .dg = .{ .gpa = module.gpa, @@ -372,20 +372,21 @@ fn flushErrDecls(self: *C, f: *Flush) FlushDeclError!void { .error_msg = null, .decl_index = undefined, .decl = undefined, - .fwd_decl = undefined, - .typedefs = codegen.TypedefMap.initContext(module.gpa, .{ .mod = module }), + .fwd_decl = fwd_decl.toManaged(module.gpa), + .typedefs = typedefs.promoteContext(module.gpa, .{ .mod = module }), .typedefs_arena = self.arena.allocator(), }, - .code = f.err_buf.toManaged(module.gpa), + .code = code.toManaged(module.gpa), .indent_writer = undefined, // set later so we can get a pointer to object.code }; object.indent_writer = .{ .underlying_writer = object.code.writer() }; defer { - f.err_buf = object.code.moveToUnmanaged(); - for (object.dg.typedefs.values()) |value| { - module.gpa.free(value.rendered); + object.code.deinit(); + for (object.dg.typedefs.values()) |typedef| { + module.gpa.free(typedef.rendered); } object.dg.typedefs.deinit(); + object.dg.fwd_decl.deinit(); } codegen.genErrDecls(&object) catch |err| switch (err) { @@ -393,11 +394,15 @@ fn flushErrDecls(self: *C, f: *Flush) FlushDeclError!void { else => |e| return e, }; - const gpa = self.base.allocator; + fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); + typedefs.* = object.dg.typedefs.unmanaged; + object.dg.typedefs.unmanaged = .{}; + code.* = object.code.moveToUnmanaged(); - try self.flushTypedefs(f, object.dg.typedefs.unmanaged); - try f.all_buffers.ensureUnusedCapacity(gpa, 1); - f.appendBufAssumeCapacity(object.code.items); + try self.flushTypedefs(f, typedefs.*); + try f.all_buffers.ensureUnusedCapacity(self.base.allocator, 1); + f.appendBufAssumeCapacity(fwd_decl.items); + f.appendBufAssumeCapacity(code.items); } /// Assumes `decl` was in the `remaining_decls` set, and has already been removed. diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 3f7b7184c2..3bed9cbbb6 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -476,7 +476,6 @@ test "read 128-bit field from default aligned struct in global memory" { } test "struct field explicit alignment" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; 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; diff --git a/test/behavior/atomics.zig b/test/behavior/atomics.zig index 1495ac75a4..50bf65a8a4 100644 --- a/test/behavior/atomics.zig +++ b/test/behavior/atomics.zig @@ -33,7 +33,6 @@ fn testCmpxchg() !void { } test "fence" { - 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/basic.zig b/test/behavior/basic.zig index e2df3d6aed..78cc683310 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -590,7 +590,6 @@ test "equality compare fn ptrs" { test "self reference through fn ptr field" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; const S = struct { const A = struct { diff --git a/test/behavior/bugs/1310.zig b/test/behavior/bugs/1310.zig index 0f574942d3..9d93dacca2 100644 --- a/test/behavior/bugs/1310.zig +++ b/test/behavior/bugs/1310.zig @@ -24,6 +24,5 @@ fn agent_callback(_vm: [*]VM, options: [*]u8) callconv(.C) i32 { test "fixed" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; try expect(agent_callback(undefined, undefined) == 11); } diff --git a/test/behavior/bugs/1500.zig b/test/behavior/bugs/1500.zig index 6a41617d2b..cc12c5d0be 100644 --- a/test/behavior/bugs/1500.zig +++ b/test/behavior/bugs/1500.zig @@ -6,7 +6,6 @@ const A = struct { const B = *const fn (A) void; test "allow these dependencies" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; var a: A = undefined; var b: B = undefined; if (false) { diff --git a/test/behavior/bugs/2006.zig b/test/behavior/bugs/2006.zig index 4d76230c88..ff759f2846 100644 --- a/test/behavior/bugs/2006.zig +++ b/test/behavior/bugs/2006.zig @@ -6,7 +6,6 @@ const S = struct { p: *S, }; test "bug 2006" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; var a: S = undefined; a = S{ .p = undefined }; try expect(@sizeOf(S) != 0); diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index cf086328a4..6445bbf3e3 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -410,7 +410,6 @@ fn testCastIntToErr(err: anyerror) !void { test "peer resolve array and const slice" { 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 @@ -546,7 +545,6 @@ fn testPeerErrorAndArray2(x: u8) anyerror![]const u8 { test "single-item pointer of array to slice to unknown length pointer" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO try testCastPtrOfArrayToSliceAndPtr(); comptime try testCastPtrOfArrayToSliceAndPtr(); @@ -575,7 +573,6 @@ fn testCastPtrOfArrayToSliceAndPtr() !void { test "cast *[1][*]const u8 to [*]const ?[*]const u8" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO const window_name = [1][*]const u8{"window name"}; @@ -1235,7 +1232,6 @@ test "cast from array reference to fn: runtime fn ptr" { test "*const [N]null u8 to ?[]const u8" { 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 const S = struct { diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index a5eb25d4f5..3bb4ba9d0a 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -21,7 +21,6 @@ fn epsForType(comptime T: type) T { test "floating point comparisons" { 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 try testFloatComparisons(); @@ -91,7 +90,6 @@ fn testDifferentSizedFloatComparisons() !void { test "negative f128 floatToInt at compile-time" { 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 diff --git a/test/behavior/generics.zig b/test/behavior/generics.zig index a0c21a75d8..b7904fb7f9 100644 --- a/test/behavior/generics.zig +++ b/test/behavior/generics.zig @@ -204,7 +204,6 @@ fn foo2(arg: anytype) bool { } test "generic struct" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; var a1 = GenNode(i32){ .value = 13, .next = null, diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 744f4adb9e..193efd3e7a 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -603,7 +603,6 @@ fn should_not_be_zero(x: f128) !void { } test "128-bit multiplication" { - 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 @@ -650,8 +649,6 @@ test "@addWithOverflow" { } test "small int addition" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - var x: u2 = 0; try expect(x == 0); diff --git a/test/behavior/maximum_minimum.zig b/test/behavior/maximum_minimum.zig index 685f65b2b9..977d06204e 100644 --- a/test/behavior/maximum_minimum.zig +++ b/test/behavior/maximum_minimum.zig @@ -6,7 +6,6 @@ const expectEqual = std.testing.expectEqual; test "@max" { 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 @@ -55,7 +54,6 @@ test "@min" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { diff --git a/test/behavior/muladd.zig b/test/behavior/muladd.zig index 1ce5ffb1e7..814e007950 100644 --- a/test/behavior/muladd.zig +++ b/test/behavior/muladd.zig @@ -2,7 +2,6 @@ const builtin = @import("builtin"); const expect = @import("std").testing.expect; test "@mulAdd" { - 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/packed-struct.zig b/test/behavior/packed-struct.zig index 5a878112b5..f9571e9590 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -293,7 +293,6 @@ test "regular in irregular packed struct" { test "byte-aligned field pointer offsets" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; diff --git a/test/behavior/saturating_arithmetic.zig b/test/behavior/saturating_arithmetic.zig index 1790fe4505..16bc32a5f1 100644 --- a/test/behavior/saturating_arithmetic.zig +++ b/test/behavior/saturating_arithmetic.zig @@ -239,7 +239,6 @@ test "saturating shl uses the LHS type" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const lhs_const: u8 = 1; var lhs_var: u8 = 1; diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index acd28426ab..cd9572ae3a 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -284,7 +284,6 @@ const Val = struct { test "struct point to self" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO var root: Node = undefined; @@ -393,7 +392,6 @@ const APackedStruct = packed struct { test "packed struct" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; 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_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/struct_contains_null_ptr_itself.zig b/test/behavior/struct_contains_null_ptr_itself.zig index 7a2e32b2c2..0e015aaf58 100644 --- a/test/behavior/struct_contains_null_ptr_itself.zig +++ b/test/behavior/struct_contains_null_ptr_itself.zig @@ -5,7 +5,6 @@ const builtin = @import("builtin"); test "struct contains null pointer which contains original struct" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; var x: ?*NodeLineComment = null; try expect(x == null); } diff --git a/test/behavior/switch_prong_implicit_cast.zig b/test/behavior/switch_prong_implicit_cast.zig index 716afc3ace..f58853adb1 100644 --- a/test/behavior/switch_prong_implicit_cast.zig +++ b/test/behavior/switch_prong_implicit_cast.zig @@ -18,7 +18,6 @@ test "switch prong implicit cast" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; const result = switch (foo(2) catch unreachable) { FormValue.One => false, FormValue.Two => |x| x, diff --git a/test/behavior/union.zig b/test/behavior/union.zig index cfb522b74e..0160c21761 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1186,7 +1186,6 @@ test "comptime equality of extern unions with same tag" { } test "union tag is set when initiated as a temporary value at runtime" { - 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 @@ -1350,7 +1349,6 @@ test "@unionInit uses tag value instead of field index" { } test "union field ptr - zero sized payload" { - 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 @@ -1364,7 +1362,6 @@ test "union field ptr - zero sized payload" { } test "union field ptr - zero sized field" { - 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/vector.zig b/test/behavior/vector.zig index 80fa2021d8..581d68dcb6 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -811,7 +811,6 @@ test "vector reduce operation" { test "vector @reduce comptime" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; 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_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO From 962f33ee11676934eeece4595cdc372662986f6d Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 8 Oct 2022 11:29:06 -0400 Subject: [PATCH 19/47] cbe: implement airUnionInit --- src/codegen/c.zig | 39 +++++++++++++++++++++++----- test/behavior/translate_c_macros.zig | 1 - test/behavior/union.zig | 6 ----- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index beafafc94f..39ed7cf477 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -4331,16 +4331,43 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; - const inst_ty = f.air.typeOfIndex(inst); const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; + const extra = f.air.extraData(Air.UnionInit, ty_pl.payload).data; + const union_ty = f.air.typeOfIndex(inst); + const target = f.object.dg.module.getTarget(); + const layout = union_ty.unionGetLayout(target); + const union_obj = union_ty.cast(Type.Payload.Union).?.data; + const field_name = union_obj.fields.keys()[extra.field_index]; + const payload = try f.resolveInst(extra.init); const writer = f.object.writer(); - const local = try f.allocLocal(inst_ty, .Const); - try writer.writeAll(" = "); + const local = try f.allocLocal(union_ty, .Const); + try writer.writeAll(" = {"); + if (union_ty.unionTagTypeSafety()) |tag_ty| { + if (layout.tag_size != 0) { + const field_index = tag_ty.enumFieldIndex(field_name).?; - _ = local; - _ = ty_pl; - return f.fail("TODO: C backend: implement airUnionInit", .{}); + var tag_val_pl: Value.Payload.U32 = .{ + .base = .{ .tag = .enum_field_index }, + .data = @intCast(u32, field_index), + }; + const tag_val = Value.initPayload(&tag_val_pl.base); + + var int_val_pl: Value.Payload.U64 = undefined; + const int_val = tag_val.enumToInt(tag_ty, &int_val_pl); + + try writer.print(".tag = {}, ", .{try f.fmtIntLiteral(tag_ty, int_val)}); + } + try writer.writeAll(".payload = {"); + } + + try writer.print(".{ } = ", .{fmtIdent(field_name)}); + try f.writeCValue(writer, payload); + + if (union_ty.unionTagTypeSafety()) |_| try writer.writeByte('}'); + try writer.writeAll("};\n"); + + return local; } fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { diff --git a/test/behavior/translate_c_macros.zig b/test/behavior/translate_c_macros.zig index 3a4de7b6f6..94590d3159 100644 --- a/test/behavior/translate_c_macros.zig +++ b/test/behavior/translate_c_macros.zig @@ -58,7 +58,6 @@ test "cast negative integer to pointer" { test "casting to union with a macro" { 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/union.zig b/test/behavior/union.zig index 0160c21761..91dea304c7 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -776,7 +776,6 @@ test "return union init with void payload" { } test "@unionInit stored to a const" { - 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_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -939,7 +938,6 @@ test "function call result coerces from tagged union to the tag" { } test "cast from anonymous struct to union" { - 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_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -972,7 +970,6 @@ test "cast from anonymous struct to union" { } test "cast from pointer to anonymous struct to pointer to union" { - 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_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -1135,7 +1132,6 @@ test "global variable struct contains union initialized to non-most-aligned fiel } test "union with no result loc initiated with a runtime value" { - 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 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -1152,7 +1148,6 @@ test "union with no result loc initiated with a runtime value" { } test "union with a large struct field" { - 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 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -1321,7 +1316,6 @@ test "union and enum field order doesn't match" { } test "@unionInit uses tag value instead of field index" { - 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_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO From 40b5bb71618825dbdba512b43ebf8c4cb5caf153 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 8 Oct 2022 12:37:05 -0400 Subject: [PATCH 20/47] cbe: fix loads and stores of 0-bit types --- src/codegen/c.zig | 9 ++++++--- test/behavior/empty_union.zig | 2 -- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 39ed7cf477..f00bd28773 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -2409,10 +2409,11 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; const is_volatile = f.air.typeOf(ty_op.operand).isVolatilePtr(); - if (!is_volatile and f.liveness.isUnused(inst)) + const inst_ty = f.air.typeOfIndex(inst); + if (!inst_ty.hasRuntimeBitsIgnoreComptime() or + !is_volatile and f.liveness.isUnused(inst)) return CValue.none; - const inst_ty = f.air.typeOfIndex(inst); const is_array = inst_ty.zigTypeTag() == .Array; const operand = try f.resolveInst(ty_op.operand); const writer = f.object.writer(); @@ -2565,9 +2566,11 @@ fn airStoreUndefined(f: *Function, dest_ptr: CValue) !CValue { fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { // *a = b; const bin_op = f.air.instructions.items(.data)[inst].bin_op; + const lhs_child_type = f.air.typeOf(bin_op.lhs).childType(); + if (!lhs_child_type.hasRuntimeBitsIgnoreComptime()) return CValue.none; + const dest_ptr = try f.resolveInst(bin_op.lhs); const src_val = try f.resolveInst(bin_op.rhs); - const lhs_child_type = f.air.typeOf(bin_op.lhs).childType(); // TODO Sema should emit a different instruction when the store should // possibly do the safety 0xaa bytes for undefined. diff --git a/test/behavior/empty_union.zig b/test/behavior/empty_union.zig index 3f79df0446..55dac727e8 100644 --- a/test/behavior/empty_union.zig +++ b/test/behavior/empty_union.zig @@ -3,7 +3,6 @@ const std = @import("std"); const expect = std.testing.expect; test "switch on empty enum" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const E = enum {}; @@ -29,7 +28,6 @@ test "switch on empty auto numbered tagged union" { } test "switch on empty tagged union" { - 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_wasm) return error.SkipZigTest; // TODO From f399dd107a2b0387ca54688741d6c3fa0109ed39 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 8 Oct 2022 15:04:58 -0400 Subject: [PATCH 21/47] cbe: implement tag name --- src/codegen/c.zig | 117 +++++++++++++++++++++++++++++++++-------- test/behavior/enum.zig | 4 -- 2 files changed, 96 insertions(+), 25 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index f00bd28773..6b25cf8360 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1694,6 +1694,76 @@ pub const DeclGen = struct { try w.writeAll(suffix.items); } + fn renderTagNameFn(dg: *DeclGen, enum_ty: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { + var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); + defer buffer.deinit(); + const bw = buffer.writer(); + + const name_slice_ty = Type.initTag(.const_slice_u8_sentinel_0); + + try buffer.appendSlice("static "); + try dg.renderType(bw, name_slice_ty); + const name_begin = buffer.items.len + " ".len; + try bw.print(" zig_tagName_{}(", .{typeToCIdentifier(enum_ty, dg.module)}); + const name_end = buffer.items.len - "(".len; + try dg.renderTypeAndName(bw, enum_ty, .{ .identifier = "tag" }, .Const, 0); + try buffer.appendSlice(") {\n switch (tag) {\n"); + for (enum_ty.enumFields().keys()) |name, index| { + const name_z = try dg.typedefs.allocator.dupeZ(u8, name); + defer dg.typedefs.allocator.free(name_z); + const name_bytes = name_z[0 .. name_z.len + 1]; + + var tag_val_pl: Value.Payload.U32 = .{ + .base = .{ .tag = .enum_field_index }, + .data = @intCast(u32, index), + }; + const tag_val = Value.initPayload(&tag_val_pl.base); + + var int_val_pl: Value.Payload.U64 = undefined; + const int_val = tag_val.enumToInt(enum_ty, &int_val_pl); + + var name_ty_pl = Type.Payload.Len{ .base = .{ .tag = .array_u8_sentinel_0 }, .data = name.len }; + const name_ty = Type.initPayload(&name_ty_pl.base); + + var name_val_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name_bytes }; + const name_val = Value.initPayload(&name_val_pl.base); + + var len_val_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len }; + const len_val = Value.initPayload(&len_val_pl.base); + + try bw.print(" case {}: {{\n static ", .{try dg.fmtIntLiteral(enum_ty, int_val, .Other)}); + try dg.renderTypeAndName(bw, name_ty, .{ .identifier = "name" }, .Const, 0); + try buffer.appendSlice(" = "); + try dg.renderValue(bw, name_ty, name_val, .Other); + try buffer.appendSlice(";\n return ("); + try dg.renderTypecast(bw, name_slice_ty); + try bw.print("){{{}, {}}};\n", .{ + fmtIdent("name"), + try dg.fmtIntLiteral(Type.usize, len_val, .Other), + }); + + try buffer.appendSlice(" }\n"); + } + try buffer.appendSlice(" }\n while (true) zig_breakpoint();\n}\n"); + + const rendered = buffer.toOwnedSlice(); + errdefer dg.typedefs.allocator.free(rendered); + const name = rendered[name_begin..name_end]; + + try dg.typedefs.ensureUnusedCapacity(1); + dg.typedefs.putAssumeCapacityNoClobber( + try enum_ty.copy(dg.typedefs_arena), + .{ .name = name, .rendered = rendered }, + ); + + return name; + } + + fn getTagNameFn(dg: *DeclGen, enum_ty: Type) ![]const u8 { + return dg.getTypedefName(enum_ty) orelse + try dg.renderTagNameFn(enum_ty); + } + fn declIsGlobal(dg: *DeclGen, tv: TypedValue) bool { switch (tv.val.tag()) { .extern_fn => return true, @@ -1805,25 +1875,22 @@ pub fn genErrDecls(o: *Object) !void { o.indent_writer.popIndent(); try writer.writeAll("};\n"); - const name_prefix = "zig_errorName_"; - const name_buf = try o.dg.gpa.alloc(u8, name_prefix.len + max_name_len + 1); + const name_prefix = "zig_errorName"; + const name_buf = try o.dg.gpa.alloc(u8, name_prefix.len + "_".len + max_name_len + 1); defer o.dg.gpa.free(name_buf); - std.mem.copy(u8, name_buf, name_prefix); + std.mem.copy(u8, name_buf, name_prefix ++ "_"); for (o.dg.module.error_name_list.items) |name| { - std.mem.copy(u8, name_buf[name_prefix.len..], name); - name_buf[name_prefix.len + name.len] = 0; + std.mem.copy(u8, name_buf[name_prefix.len + "_".len ..], name); + name_buf[name_prefix.len + "_".len + name.len] = 0; - const identifier = name_buf[0 .. name_prefix.len + name.len :0]; - const nameZ = identifier[name_prefix.len..]; + const identifier = name_buf[0 .. name_prefix.len + "_".len + name.len :0]; + const name_z = identifier[name_prefix.len + "_".len ..]; - var name_ty_pl = Type.Payload.Len{ - .base = .{ .tag = .array_u8_sentinel_0 }, - .data = name.len, - }; + var name_ty_pl = Type.Payload.Len{ .base = .{ .tag = .array_u8_sentinel_0 }, .data = name.len }; const name_ty = Type.initPayload(&name_ty_pl.base); - var name_val_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = nameZ }; + var name_val_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name_z }; const name_val = Value.initPayload(&name_val_pl.base); try writer.writeAll("static "); @@ -1840,11 +1907,18 @@ pub fn genErrDecls(o: *Object) !void { const name_array_ty = Type.initPayload(&name_array_ty_pl.base); try writer.writeAll("static "); - try o.dg.renderTypeAndName(writer, name_array_ty, .{ .identifier = "zig_errorName" }, .Const, 0); + try o.dg.renderTypeAndName(writer, name_array_ty, .{ .identifier = name_prefix }, .Const, 0); try writer.writeAll(" = {"); for (o.dg.module.error_name_list.items) |name, value| { if (value != 0) try writer.writeByte(','); - try writer.print("{{zig_errorName_{}, {d}u}}", .{ fmtIdent(name), name.len }); + + var len_val_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len }; + const len_val = Value.initPayload(&len_val_pl.base); + + try writer.print("{{" ++ name_prefix ++ "_{}, {}}}", .{ + fmtIdent(name), + try o.dg.fmtIntLiteral(Type.usize, len_val, .Other), + }); } try writer.writeAll("};\n"); } @@ -4210,18 +4284,19 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; const un_op = f.air.instructions.items(.data)[inst].un_op; - const writer = f.object.writer(); const inst_ty = f.air.typeOfIndex(inst); + const enum_ty = f.air.typeOf(un_op); const operand = try f.resolveInst(un_op); + + const writer = f.object.writer(); const local = try f.allocLocal(inst_ty, .Const); + try writer.print(" = {s}(", .{try f.object.dg.getTagNameFn(enum_ty)}); + try f.writeCValue(writer, operand); + try writer.writeAll(");\n"); - try writer.writeAll(" = "); + try f.object.dg.fwd_decl.writer().writeAll("// This is where the fwd decl for tagName ended up\n"); - _ = operand; - _ = local; - return f.fail("TODO: C backend: implement airTagName", .{}); - //try writer.writeAll(";\n"); - //return local; + return local; } fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue { diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index acd8d17302..b00c7dbca2 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -972,7 +972,6 @@ fn test3_2(f: Test3Foo) !void { } test "@tagName" { - 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; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; @@ -989,7 +988,6 @@ fn testEnumTagNameBare(n: anytype) []const u8 { const BareNumber = enum { One, Two, Three }; test "@tagName non-exhaustive enum" { - 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; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; @@ -1001,7 +999,6 @@ test "@tagName non-exhaustive enum" { const NonExhaustive = enum(u8) { A, B, _ }; test "@tagName is null-terminated" { - 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; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; @@ -1017,7 +1014,6 @@ test "@tagName is null-terminated" { } test "tag name with assigned enum values" { - 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; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; From a12535f5014cb2d4878581871287f17a31f28961 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 9 Oct 2022 01:48:26 -0400 Subject: [PATCH 22/47] cbe: fix global access --- src/codegen/c.zig | 24 ++++++++++-------------- test/behavior/basic.zig | 2 -- test/behavior/struct.zig | 3 --- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 6b25cf8360..a60e6986a4 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -386,6 +386,10 @@ pub const DeclGen = struct { val: Value, decl_index: Decl.Index, ) error{ OutOfMemory, AnalysisFail }!void { + if (ty.isPtrAtRuntime() and !ty.elemType2().isFnOrHasRuntimeBits()) { + return dg.writeCValue(writer, CValue{ .undefined_ptr = ty }); + } + if (ty.isSlice()) { try writer.writeByte('('); try dg.renderTypecast(writer, ty); @@ -404,23 +408,15 @@ pub const DeclGen = struct { // them). The analysis until now should ensure that the C function // pointers are compatible. If they are not, then there is a bug // somewhere and we should let the C compiler tell us about it. - if (ty.castPtrToFn() == null) { - // Determine if we must pointer cast. - if (ty.eql(decl.ty, dg.module)) { - try writer.writeByte('&'); - try dg.renderDeclName(writer, decl_index); - return; - } - + const need_typecast = if (ty.castPtrToFn()) |_| false else !ty.eql(decl.ty, dg.module); + if (need_typecast) { try writer.writeAll("(("); try dg.renderTypecast(writer, ty); - try writer.writeAll(")&"); - try dg.renderDeclName(writer, decl_index); try writer.writeByte(')'); - return; } - + try writer.writeByte('&'); try dg.renderDeclName(writer, decl_index); + if (need_typecast) try writer.writeByte(')'); } // Renders a "parent" pointer by recursing to the root decl/variable @@ -1830,7 +1826,7 @@ pub const DeclGen = struct { if (dg.module.decl_exports.get(decl_index)) |exports| { return writer.writeAll(exports[0].options.name); - } else if (decl.val.tag() == .extern_fn) { + } else if (decl.isExtern()) { return writer.writeAll(mem.sliceTo(decl.name, 0)); } else { const gpa = dg.module.gpa; @@ -1997,7 +1993,7 @@ pub fn genDecl(o: *Object) !void { try o.dg.renderTypeAndName(fwd_decl_writer, o.dg.decl.ty, decl_c_value, .Mut, o.dg.decl.@"align"); try fwd_decl_writer.writeAll(";\n"); - if (variable.init.isUndefDeep()) { + if (variable.is_extern or variable.init.isUndefDeep()) { return; } diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 78cc683310..d47138010d 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -383,8 +383,6 @@ fn testTakeAddressOfParameter(f: f32) !void { } test "pointer to void return type" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - try testPointerToVoidReturnType(); } fn testPointerToVoidReturnType() anyerror!void { diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index cd9572ae3a..6f5a4d93f4 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -329,7 +329,6 @@ fn testReturnEmptyStructFromFn() EmptyStruct2 { test "pass slice of empty struct to fn" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO try expect(testPassSliceOfEmptyStructToFn(&[_]EmptyStruct2{EmptyStruct2{}}) == 1); @@ -354,8 +353,6 @@ test "self-referencing struct via array member" { } test "empty struct method call" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - const es = EmptyStruct{}; try expect(es.method() == 1234); } From c126a1018eec54c568aaa642013e3c86bdd6d3e4 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 10 Oct 2022 00:09:18 -0400 Subject: [PATCH 23/47] cbe: implement more asm features --- src/codegen/c.zig | 246 +++++++++++++++++++++++++++--------------- test/behavior/asm.zig | 1 - 2 files changed, 158 insertions(+), 89 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index a60e6986a4..d479f30e60 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -274,6 +274,13 @@ pub const Function = struct { } } + fn wantSafety(f: *Function) bool { + return switch (f.object.dg.module.optimizeMode()) { + .Debug, .ReleaseSafe => true, + .ReleaseFast, .ReleaseSmall => false, + }; + } + fn allocLocalValue(f: *Function) CValue { const result = f.next_local_index; f.next_local_index += 1; @@ -528,9 +535,7 @@ pub const DeclGen = struct { .Int, .Enum, .ErrorSet, - => return writer.print("{x}", .{ - try dg.fmtIntLiteral(ty, val, location), - }), + => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, val, location)}), .Float => switch (ty.tag()) { .f32 => return writer.print("zig_bitcast_f32_u32({x})", .{ try dg.fmtIntLiteral(Type.u32, val, location), @@ -2619,16 +2624,13 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue { } fn airStoreUndefined(f: *Function, dest_ptr: CValue) !CValue { - switch (f.object.dg.module.optimizeMode()) { - .Debug, .ReleaseSafe => { - const writer = f.object.writer(); - try writer.writeAll("memset("); - try f.writeCValue(writer, dest_ptr); - try writer.print(", {x}, sizeof(", .{try f.fmtIntLiteral(Type.u8, Value.undef)}); - try f.writeCValueDeref(writer, dest_ptr); - try writer.writeAll("));\n"); - }, - .ReleaseFast, .ReleaseSmall => {}, + if (f.wantSafety()) { + const writer = f.object.writer(); + try writer.writeAll("memset("); + try f.writeCValue(writer, dest_ptr); + try writer.print(", {x}, sizeof(", .{try f.fmtIntLiteral(Type.u8, Value.undef)}); + try f.writeCValueDeref(writer, dest_ptr); + try writer.writeAll("));\n"); } return CValue.none; } @@ -3463,29 +3465,23 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { if (!is_volatile and f.liveness.isUnused(inst)) return CValue.none; - if (outputs.len > 1) { - return f.fail("TODO implement codegen for asm with more than 1 output", .{}); - } - - const output_constraint: ?[]const u8 = for (outputs) |output| { - if (output != .none) { - return f.fail("TODO implement codegen for non-expr asm", .{}); - } - const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); - const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += (constraint.len + name.len + (2 + 3)) / 4; - - break constraint; - } else null; - const writer = f.object.writer(); - try writer.writeAll("{\n"); + const inst_ty = f.air.typeOfIndex(inst); + const local = if (inst_ty.hasRuntimeBitsIgnoreComptime()) local: { + const local = try f.allocLocal(inst_ty, .Mut); + if (f.wantSafety()) { + try writer.writeAll(" = "); + try f.object.dg.renderValue(writer, inst_ty, Value.undef, .Other); + } + try writer.writeAll(";\n"); + break :local local; + } else .none; - const inputs_extra_begin = extra_i; - for (inputs) |input, i| { + try writer.writeAll("{\n"); + f.object.indent_writer.pushIndent(); + + const constraints_extra_begin = extra_i; + for (outputs) |output| { const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); const constraint = std.mem.sliceTo(extra_bytes, 0); const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); @@ -3493,24 +3489,89 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { // for the string, we still use the next u32 for the null terminator. extra_i += (constraint.len + name.len + (2 + 3)) / 4; - if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') { - const reg = constraint[1 .. constraint.len - 1]; - const arg_c_value = try f.resolveInst(input); - try writer.writeAll("register "); - try f.renderType(writer, f.air.typeOf(input)); + const output_ty = if (output == .none) inst_ty else f.air.typeOf(output).childType(); + try writer.writeAll("register "); + try f.object.dg.renderTypeAndName(writer, output_ty, .{ .identifier = name }, .Mut, 0); + if (std.mem.startsWith(u8, constraint, "={") and std.mem.endsWith(u8, constraint, "}")) { + try writer.writeAll(" __asm(\""); + try writer.writeAll(constraint["={".len .. constraint.len - "}".len]); + try writer.writeAll("\")"); + } else if (constraint.len < 2 or constraint[0] != '=') { + return f.fail("CBE: constraint not supported: '{s}'", .{constraint}); + } + if (f.wantSafety()) { + try writer.writeAll(" = "); + try f.object.dg.renderValue(writer, output_ty, Value.undef, .Other); + } + try writer.writeAll(";\n"); + } + for (inputs) |input| { + const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); + const constraint = std.mem.sliceTo(extra_bytes, 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += (constraint.len + name.len + (2 + 3)) / 4; - try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg }); - try f.writeCValue(writer, arg_c_value); - try writer.writeAll(";\n"); - } else { - try writer.writeAll("register "); - try f.renderType(writer, f.air.typeOf(input)); - try writer.print(" input_{d} = ", .{i}); - try f.writeCValue(writer, try f.resolveInst(input)); - try writer.writeAll(";\n"); + const input_ty = f.air.typeOf(input); + try writer.writeAll("register "); + try f.object.dg.renderTypeAndName(writer, input_ty, .{ .identifier = name }, .Const, 0); + if (std.mem.startsWith(u8, constraint, "{") and std.mem.endsWith(u8, constraint, "}")) { + try writer.writeAll(" __asm(\""); + try writer.writeAll(constraint["{".len .. constraint.len - "}".len]); + try writer.writeAll("\")"); + } else if (constraint.len < 1 or std.mem.indexOfScalar(u8, "=+&%", constraint[0]) != null) { + return f.fail("CBE: constraint not supported: '{s}'", .{constraint}); + } + try writer.writeAll(" = "); + try f.writeCValue(writer, try f.resolveInst(input)); + try writer.writeAll(";\n"); + } + { + var clobber_i: u32 = 0; + while (clobber_i < clobbers_len) : (clobber_i += 1) { + const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += clobber.len / 4 + 1; } } + const asm_source = std.mem.sliceAsBytes(f.air.extra[extra_i..])[0..extra.data.source_len]; + try writer.writeAll("__asm"); + if (is_volatile) try writer.writeAll(" volatile"); + try writer.print("({s}", .{fmtStringLiteral(asm_source)}); + + extra_i = constraints_extra_begin; + try writer.writeByte(':'); + for (outputs) |_, index| { + const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); + const constraint = std.mem.sliceTo(extra_bytes, 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += (constraint.len + name.len + (2 + 3)) / 4; + + if (index > 0) try writer.writeByte(','); + try writer.print(" {s}(", .{fmtStringLiteral(if (constraint[1] == '{') "=r" else constraint)}); + try f.writeCValue(writer, .{ .identifier = name }); + try writer.writeByte(')'); + } + try writer.writeByte(':'); + for (inputs) |_, index| { + const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); + const constraint = std.mem.sliceTo(extra_bytes, 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += (constraint.len + name.len + (2 + 3)) / 4; + + if (index > 0) try writer.writeByte(','); + try writer.print(" {s}(", .{fmtStringLiteral(if (constraint[0] == '{') "r" else constraint)}); + try f.writeCValue(writer, .{ .identifier = name }); + try writer.writeByte(')'); + } + try writer.writeByte(':'); { var clobber_i: u32 = 0; while (clobber_i < clobbers_len) : (clobber_i += 1) { @@ -3519,52 +3580,33 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { // for the string, we still use the next u32 for the null terminator. extra_i += clobber.len / 4 + 1; - // TODO honor these - } - } + if (clobber.len == 0) continue; - const asm_source = std.mem.sliceAsBytes(f.air.extra[extra_i..])[0..extra.data.source_len]; - - const volatile_string: []const u8 = if (is_volatile) "volatile " else ""; - try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, asm_source }); - if (output_constraint) |_| { - return f.fail("TODO: CBE inline asm output", .{}); - } - if (inputs.len > 0) { - if (output_constraint == null) { - try writer.writeAll(" :"); - } - try writer.writeAll(": "); - extra_i = inputs_extra_begin; - for (inputs) |_, index| { - const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); - const constraint = std.mem.sliceTo(extra_bytes, 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += (constraint.len + name.len + (2 + 3)) / 4; - - if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') { - const reg = constraint[1 .. constraint.len - 1]; - if (index > 0) { - try writer.writeAll(", "); - } - try writer.print("\"r\"({s}_constant)", .{reg}); - } else { - if (index > 0) { - try writer.writeAll(", "); - } - try writer.print("\"r\"(input_{d})", .{index}); - } + if (clobber_i > 0) try writer.writeByte(','); + try writer.print(" {s}", .{fmtStringLiteral(clobber)}); } } try writer.writeAll(");\n"); + + extra_i = constraints_extra_begin; + for (outputs) |output| { + const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); + const constraint = std.mem.sliceTo(extra_bytes, 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += (constraint.len + name.len + (2 + 3)) / 4; + + try f.writeCValueDeref(writer, if (output == .none) .{ .local_ref = local.local } else try f.resolveInst(output)); + try writer.writeAll(" = "); + try f.writeCValue(writer, .{ .identifier = name }); + try writer.writeAll(";\n"); + } + + f.object.indent_writer.popIndent(); try writer.writeAll("}\n"); - if (f.liveness.isUnused(inst)) - return CValue.none; - - return f.fail("TODO: C backend: inline asm expression result used", .{}); + return local; } fn airIsNull( @@ -4634,6 +4676,34 @@ fn signAbbrev(signedness: std.builtin.Signedness) u8 { }; } +fn formatStringLiteral( + str: []const u8, + comptime fmt: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, +) @TypeOf(writer).Error!void { + if (fmt.len != 1 or fmt[0] != 's') @compileError("Invalid fmt: " ++ fmt); + try writer.writeByte('\"'); + for (str) |c| switch (c) { + 7 => try writer.writeAll("\\a"), + 8 => try writer.writeAll("\\b"), + '\t' => try writer.writeAll("\\t"), + '\n' => try writer.writeAll("\\n"), + 11 => try writer.writeAll("\\v"), + 12 => try writer.writeAll("\\f"), + '\r' => try writer.writeAll("\\r"), + '"', '\'', '?', '\\' => try writer.print("\\{c}", .{c}), + else => switch (c) { + ' '...'~' => try writer.writeByte(c), + else => try writer.print("\\{o:0>3}", .{c}), + }, + }; + try writer.writeByte('\"'); +} +fn fmtStringLiteral(str: []const u8) std.fmt.Formatter(formatStringLiteral) { + return .{ .data = str }; +} + const FormatIntLiteralContext = struct { ty: Type, val: Value, diff --git a/test/behavior/asm.zig b/test/behavior/asm.zig index 8f235a384b..c154271964 100644 --- a/test/behavior/asm.zig +++ b/test/behavior/asm.zig @@ -30,7 +30,6 @@ test "module level assembly" { } test "output constraint modifiers" { - 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_arm) return error.SkipZigTest; // TODO From 5eafc10bf41451fb694f324dcaefe5c458e1ca3a Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 10 Oct 2022 00:47:45 -0400 Subject: [PATCH 24/47] cbe: fix global access fix --- src/codegen/c.zig | 7 ++++--- test/behavior/bugs/12984.zig | 1 - test/behavior/bugs/1914.zig | 1 - test/behavior/cast.zig | 7 ------- test/behavior/fn.zig | 1 - test/behavior/slice.zig | 3 --- test/behavior/struct.zig | 1 - test/behavior/struct_contains_slice_of_itself.zig | 2 -- test/behavior/tuple.zig | 3 --- 9 files changed, 4 insertions(+), 22 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index d479f30e60..e3391c450a 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -393,7 +393,10 @@ pub const DeclGen = struct { val: Value, decl_index: Decl.Index, ) error{ OutOfMemory, AnalysisFail }!void { - if (ty.isPtrAtRuntime() and !ty.elemType2().isFnOrHasRuntimeBits()) { + const decl = dg.module.declPtr(decl_index); + assert(decl.has_tv); + + if (ty.isPtrAtRuntime() and !decl.ty.isFnOrHasRuntimeBits()) { return dg.writeCValue(writer, CValue{ .undefined_ptr = ty }); } @@ -409,8 +412,6 @@ pub const DeclGen = struct { return; } - const decl = dg.module.declPtr(decl_index); - assert(decl.has_tv); // We shouldn't cast C function pointers as this is UB (when you call // them). The analysis until now should ensure that the C function // pointers are compatible. If they are not, then there is a bug diff --git a/test/behavior/bugs/12984.zig b/test/behavior/bugs/12984.zig index a538b62e8d..fec32947c9 100644 --- a/test/behavior/bugs/12984.zig +++ b/test/behavior/bugs/12984.zig @@ -14,7 +14,6 @@ pub const CustomDraw = DeleagateWithContext(fn (?OnConfirm) void); test "simple test" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) 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 var c: CustomDraw = undefined; diff --git a/test/behavior/bugs/1914.zig b/test/behavior/bugs/1914.zig index 4ac2b929a2..67b2e90930 100644 --- a/test/behavior/bugs/1914.zig +++ b/test/behavior/bugs/1914.zig @@ -29,6 +29,5 @@ pub const B2 = struct { var b_value = B2{ .pointer_array = &[_]*A2{} }; test "basic stuff" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO std.debug.assert(&b_value == &b_value); } diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 6445bbf3e3..b9317d96ad 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -450,7 +450,6 @@ fn castToOptionalTypeError(z: i32) !void { test "implicitly cast from [0]T to anyerror![]T" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO try testCastZeroArrayToErrSliceMut(); comptime try testCastZeroArrayToErrSliceMut(); @@ -466,7 +465,6 @@ fn gimmeErrOrSlice() anyerror![]u8 { test "peer type resolution: [0]u8, []const u8, and anyerror![]u8" { 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 const S = struct { @@ -810,7 +808,6 @@ test "peer type resolution: error union after non-error" { test "peer cast *[0]T to E![]const T" { 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; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO var buffer: [5]u8 = "abcde".*; @@ -825,7 +822,6 @@ test "peer cast *[0]T to E![]const T" { test "peer cast *[0]T to []const T" { 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; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO var buffer: [5]u8 = "abcde".*; @@ -1131,7 +1127,6 @@ fn returnNullLitFromOptionalTypeErrorRef() anyerror!?*A { test "peer type resolution: [0]u8 and []const u8" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO try expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); try expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); @@ -1214,7 +1209,6 @@ fn incrementVoidPtrValue(value: ?*anyopaque) void { test "implicit cast *[0]T to E![]const u8" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var x = @as(anyerror![]const u8, &[0]u8{}); try expect((x catch unreachable).len == 0); @@ -1386,7 +1380,6 @@ test "coerce undefined single-item pointer of array to error union of slice" { } test "pointer to empty struct literal to mutable slice" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var x: []i32 = &.{}; try expect(x.len == 0); } diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index e46a910227..64112d0d08 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -405,7 +405,6 @@ test "function with inferred error set but returning no error" { } test "import passed byref to function in return type" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const S = struct { diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 1b6fc3634f..3b79f83d8f 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -169,7 +169,6 @@ test "comptime pointer cast array and then slice" { test "slicing zero length array" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const s1 = ""[0..]; @@ -206,8 +205,6 @@ test "slice string literal has correct type" { } test "result location zero sized array inside struct field implicit cast to slice" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - const E = struct { entries: []u32, }; diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 6f5a4d93f4..a6b9a76ba6 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -365,7 +365,6 @@ const EmptyStruct = struct { test "align 1 field before self referential align 8 field as slice return type" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const result = alloc(Expr); diff --git a/test/behavior/struct_contains_slice_of_itself.zig b/test/behavior/struct_contains_slice_of_itself.zig index feb382ed3e..22e5327b38 100644 --- a/test/behavior/struct_contains_slice_of_itself.zig +++ b/test/behavior/struct_contains_slice_of_itself.zig @@ -12,7 +12,6 @@ const NodeAligned = struct { }; test "struct contains slice of itself" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO var other_nodes = [_]Node{ @@ -52,7 +51,6 @@ test "struct contains slice of itself" { } test "struct contains aligned slice of itself" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO var other_nodes = [_]NodeAligned{ diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig index 2b715c3b23..4b36ded087 100644 --- a/test/behavior/tuple.zig +++ b/test/behavior/tuple.zig @@ -257,7 +257,6 @@ test "initializing anon struct with mixed comptime-runtime fields" { } test "tuple in tuple passed to generic function" { - 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 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; @@ -277,7 +276,6 @@ test "tuple in tuple passed to generic function" { } test "coerce tuple to tuple" { - 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 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; @@ -292,7 +290,6 @@ test "coerce tuple to tuple" { } test "tuple type with void field" { - 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 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO From feb8f81cd9d8df282d57441422dc70cee079a993 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 10 Oct 2022 02:04:37 -0400 Subject: [PATCH 25/47] cbe: canonicalize types that have the same C type when emitting typedefs --- src/codegen/c.zig | 53 +++++++++++++++++++---------------------- test/behavior/align.zig | 2 -- test/behavior/for.zig | 1 - test/behavior/slice.zig | 2 -- 4 files changed, 25 insertions(+), 33 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index e3391c450a..cecc2f8dbe 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1067,6 +1067,8 @@ pub const DeclGen = struct { } fn renderSliceTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { + std.debug.assert(t.sentinel() == null); // expected canonical type + var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); defer buffer.deinit(); const bw = buffer.writer(); @@ -1078,22 +1080,12 @@ pub const DeclGen = struct { const ptr_name = CValue{ .bytes = "ptr" }; try dg.renderTypeAndName(bw, ptr_type, ptr_name, .Mut, 0); - const ptr_sentinel = ptr_type.ptrInfo().data.sentinel; - const child_type = t.childType(); - try bw.writeAll("; size_t len; } "); const name_begin = buffer.items.len; try bw.print("zig_{c}_{}", .{ @as(u8, if (t.isConstPtr()) 'L' else 'M'), - typeToCIdentifier(child_type, dg.module), + typeToCIdentifier(t.childType(), dg.module), }); - if (ptr_sentinel) |s| { - var sentinel_buffer = std.ArrayList(u8).init(dg.typedefs.allocator); - defer sentinel_buffer.deinit(); - - try dg.renderValue(sentinel_buffer.writer(), child_type, s, .Identifier); - try bw.print("_s_{}", .{fmtIdent(sentinel_buffer.items)}); - } const name_end = buffer.items.len; try bw.writeAll(";\n"); @@ -1351,28 +1343,21 @@ pub const DeclGen = struct { } fn renderArrayTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { + const info = t.arrayInfo(); + std.debug.assert(info.sentinel == null); // expected canonical type + var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); defer buffer.deinit(); const bw = buffer.writer(); - const elem_type = t.elemType(); - try bw.writeAll("typedef "); - try dg.renderType(bw, elem_type); + try dg.renderType(bw, info.elem_type); const name_begin = buffer.items.len + " ".len; - try bw.print(" zig_A_{}_{d}", .{ typeToCIdentifier(elem_type, dg.module), t.arrayLen() }); - if (t.sentinel()) |s| { - var sentinel_buffer = std.ArrayList(u8).init(dg.typedefs.allocator); - defer sentinel_buffer.deinit(); - - try dg.renderValue(sentinel_buffer.writer(), elem_type, s, .Identifier); - try bw.print("_s_{}", .{fmtIdent(sentinel_buffer.items)}); - } + try bw.print(" zig_A_{}_{d}", .{ typeToCIdentifier(info.elem_type, dg.module), info.len }); const name_end = buffer.items.len; - const c_len = t.arrayLenIncludingSentinel(); - try bw.print("[{d}];\n", .{if (c_len > 0) c_len else 1}); + try bw.print("[{d}];\n", .{if (info.len > 0) info.len else 1}); const rendered = buffer.toOwnedSlice(); errdefer dg.typedefs.allocator.free(rendered); @@ -1509,8 +1494,14 @@ pub const DeclGen = struct { }, .Pointer => { if (t.isSlice()) { - const name = dg.getTypedefName(t) orelse - try dg.renderSliceTypedef(t); + var slice_ty_pl = Type.Payload.ElemType{ + .base = .{ .tag = if (t.ptrIsMutable()) .mut_slice else .const_slice }, + .data = t.childType(), + }; + const slice_ty = Type.initPayload(&slice_ty_pl.base); + + const name = dg.getTypedefName(slice_ty) orelse + try dg.renderSliceTypedef(slice_ty); return w.writeAll(name); } @@ -1541,8 +1532,14 @@ pub const DeclGen = struct { return w.writeAll(" *"); }, .Array => { - const name = dg.getTypedefName(t) orelse - try dg.renderArrayTypedef(t); + var array_ty_pl = Type.Payload.Array{ .base = .{ .tag = .array }, .data = .{ + .len = t.arrayLenIncludingSentinel(), + .elem_type = t.childType(), + } }; + const array_ty = Type.initPayload(&array_ty_pl.base); + + const name = dg.getTypedefName(array_ty) orelse + try dg.renderArrayTypedef(array_ty); return w.writeAll(name); }, diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 3bed9cbbb6..4188104459 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -221,7 +221,6 @@ fn fnWithAlignedStack() i32 { } test "implicitly decreasing slice alignment" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const a: u32 align(4) = 3; @@ -244,7 +243,6 @@ fn testBytesAlign(b: u8) !void { } test "@alignCast slices" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; diff --git a/test/behavior/for.zig b/test/behavior/for.zig index 7f2cd2ab8d..b88df909cd 100644 --- a/test/behavior/for.zig +++ b/test/behavior/for.zig @@ -195,7 +195,6 @@ test "for on slice with allowzero ptr" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest(slice: []const u8) !void { diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 3b79f83d8f..8965c667c1 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -319,7 +319,6 @@ test "empty array to slice" { test "@ptrCast slice to pointer" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -335,7 +334,6 @@ test "@ptrCast slice to pointer" { } test "slice syntax resulting in pointer-to-array" { - 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_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO From 87d432328e564ac138a1b773ab025324b5c191ed Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 10 Oct 2022 04:08:52 -0400 Subject: [PATCH 26/47] cbe: implement aggregate_init of struct --- src/codegen/c.zig | 117 ++++++++++++++++++--------------------- test/behavior/call.zig | 1 - test/behavior/struct.zig | 2 - 3 files changed, 55 insertions(+), 65 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index cecc2f8dbe..1ccb03a554 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -62,7 +62,6 @@ const FormatTypeAsCIdentContext = struct { }; const ValueRenderLocation = enum { - Identifier, FunctionArgument, Other, }; @@ -340,7 +339,7 @@ pub const Function = struct { } fn fmtIntLiteral(f: *Function, ty: Type, val: Value) !std.fmt.Formatter(formatIntLiteral) { - return f.object.dg.fmtIntLiteral(ty, val, .Other); + return f.object.dg.fmtIntLiteral(ty, val); } }; @@ -533,16 +532,13 @@ pub const DeclGen = struct { // Using '{}' for integer and floats seemed to error C compilers (both GCC and Clang) // with 'error: expected expression' (including when built with 'zig cc') .Bool => return writer.writeAll("false"), - .Int, - .Enum, - .ErrorSet, - => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, val, location)}), + .Int, .Enum, .ErrorSet => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, val)}), .Float => switch (ty.tag()) { .f32 => return writer.print("zig_bitcast_f32_u32({x})", .{ - try dg.fmtIntLiteral(Type.u32, val, location), + try dg.fmtIntLiteral(Type.u32, val), }), .f64 => return writer.print("zig_bitcast_f64_u64({x})", .{ - try dg.fmtIntLiteral(Type.u64, val, location), + try dg.fmtIntLiteral(Type.u64, val), }), else => return dg.fail("TODO float types > 64 bits are not support in renderValue() as of now", .{}), }, @@ -554,14 +550,12 @@ pub const DeclGen = struct { var buf: Type.SlicePtrFieldTypeBuffer = undefined; const ptr_ty = ty.slicePtrFieldType(&buf); try dg.renderTypecast(writer, ptr_ty); - return writer.print("){x}, {0x}}}", .{ - try dg.fmtIntLiteral(Type.usize, val, location), - }); + return writer.print("){x}, {0x}}}", .{try dg.fmtIntLiteral(Type.usize, val)}); }, .Many, .C, .One => { try writer.writeAll("(("); try dg.renderTypecast(writer, ty); - return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val, location)}); + return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val)}); }, }, .Optional => { @@ -598,9 +592,7 @@ pub const DeclGen = struct { empty = false; } - if (empty) try writer.print("{x}", .{ - try dg.fmtIntLiteral(Type.u8, Value.undef, location), - }); + if (empty) try writer.print("{x}", .{try dg.fmtIntLiteral(Type.u8, Value.undef)}); return writer.writeByte('}'); }, @@ -613,9 +605,7 @@ pub const DeclGen = struct { if (!field.ty.hasRuntimeBits()) continue; try dg.renderValue(writer, field.ty, val, location); break; - } else try writer.print("{x}", .{ - try dg.fmtIntLiteral(Type.u8, Value.undef, location), - }); + } else try writer.print("{x}", .{try dg.fmtIntLiteral(Type.u8, Value.undef)}); return writer.writeByte('}'); }, @@ -625,7 +615,7 @@ pub const DeclGen = struct { try writer.writeAll("){ .payload = "); try dg.renderValue(writer, ty.errorUnionPayload(), val, location); return writer.print(", .error = {x} }}", .{ - try dg.fmtIntLiteral(ty.errorUnionSet(), Value.undef, location), + try dg.fmtIntLiteral(ty.errorUnionSet(), Value.undef), }); }, .Array => { @@ -670,7 +660,7 @@ pub const DeclGen = struct { .decl_ref_mut, .decl_ref, => try dg.renderParentPtr(writer, val, ty), - else => try writer.print("{}", .{try dg.fmtIntLiteral(ty, val, location)}), + else => try writer.print("{}", .{try dg.fmtIntLiteral(ty, val)}), }, .Float => { if (ty.floatBits(target) <= 64) { @@ -682,22 +672,20 @@ pub const DeclGen = struct { .base = .{ .tag = .int_u64 }, .data = @bitCast(u32, val.toFloat(f32)), }; - return writer.print("zig_bitcast_f32_u32({x})", .{try dg.fmtIntLiteral( - Type.u32, - Value.initPayload(&bitcast_val_pl.base), - location, - )}); + const bitcast_val = Value.initPayload(&bitcast_val_pl.base); + return writer.print("zig_bitcast_f32_u32({x})", .{ + try dg.fmtIntLiteral(Type.u32, bitcast_val), + }); }, .f64 => { var bitcast_val_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = @bitCast(u64, val.toFloat(f64)), }; - return writer.print("zig_bitcast_f32_u32({x})", .{try dg.fmtIntLiteral( - Type.u64, - Value.initPayload(&bitcast_val_pl.base), - location, - )}); + const bitcast_val = Value.initPayload(&bitcast_val_pl.base); + return writer.print("zig_bitcast_f64_u64({x})", .{ + try dg.fmtIntLiteral(Type.u64, bitcast_val), + }); }, else => return dg.fail("TODO float types > 64 bits are not support in renderValue() as of now", .{}), } @@ -740,7 +728,7 @@ pub const DeclGen = struct { .int_u64, .one => { try writer.writeAll("(("); try dg.renderTypecast(writer, ty); - return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val, location)}); + return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val)}); }, .field_ptr, .elem_ptr, @@ -918,7 +906,7 @@ pub const DeclGen = struct { empty = false; } - if (empty) try writer.writeByte('0'); + if (empty) try writer.print("{}", .{try dg.fmtIntLiteral(Type.u8, Value.zero)}); try writer.writeByte('}'); }, @@ -1730,7 +1718,7 @@ pub const DeclGen = struct { var len_val_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len }; const len_val = Value.initPayload(&len_val_pl.base); - try bw.print(" case {}: {{\n static ", .{try dg.fmtIntLiteral(enum_ty, int_val, .Other)}); + try bw.print(" case {}: {{\n static ", .{try dg.fmtIntLiteral(enum_ty, int_val)}); try dg.renderTypeAndName(bw, name_ty, .{ .identifier = "name" }, .Const, 0); try buffer.appendSlice(" = "); try dg.renderValue(bw, name_ty, name_val, .Other); @@ -1738,7 +1726,7 @@ pub const DeclGen = struct { try dg.renderTypecast(bw, name_slice_ty); try bw.print("){{{}, {}}};\n", .{ fmtIdent("name"), - try dg.fmtIntLiteral(Type.usize, len_val, .Other), + try dg.fmtIntLiteral(Type.usize, len_val), }); try buffer.appendSlice(" }\n"); @@ -1793,7 +1781,7 @@ pub const DeclGen = struct { .undefined_ptr => |ty| { try w.writeAll("(("); try dg.renderTypecast(w, ty); - return w.print("){x})", .{try dg.fmtIntLiteral(Type.usize, Value.undef, .Other)}); + return w.print("){x})", .{try dg.fmtIntLiteral(Type.usize, Value.undef)}); }, .identifier => |ident| return w.print("{ }", .{fmtIdent(ident)}), .bytes => |bytes| return w.writeAll(bytes), @@ -1843,7 +1831,6 @@ pub const DeclGen = struct { dg: *DeclGen, ty: Type, val: Value, - location: ValueRenderLocation, ) !std.fmt.Formatter(formatIntLiteral) { const int_info = ty.intInfo(dg.module.getTarget()); const c_bits = toCIntBits(int_info.bits); @@ -1853,7 +1840,6 @@ pub const DeclGen = struct { .ty = ty, .val = val, .mod = dg.module, - .location = location, } }; } }; @@ -1916,7 +1902,7 @@ pub fn genErrDecls(o: *Object) !void { try writer.print("{{" ++ name_prefix ++ "_{}, {}}}", .{ fmtIdent(name), - try o.dg.fmtIntLiteral(Type.usize, len_val, .Other), + try o.dg.fmtIntLiteral(Type.usize, len_val), }); } try writer.writeAll("};\n"); @@ -4415,27 +4401,44 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.air.typeOfIndex(inst); const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; - const vector_ty = f.air.getRefType(ty_pl.ty); - const len = vector_ty.vectorLen(); + const len = @intCast(usize, inst_ty.arrayLen()); const elements = @ptrCast([]const Air.Inst.Ref, f.air.extra[ty_pl.payload..][0..len]); const writer = f.object.writer(); const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = {"); - switch (vector_ty.zigTypeTag()) { - .Struct => { - const tuple = vector_ty.tupleFields(); - var i: usize = 0; - for (elements) |elem, elem_index| { - if (tuple.values[elem_index].tag() != .unreachable_value) continue; - - const value = try f.resolveInst(elem); - if (i != 0) try writer.writeAll(", "); - try f.writeCValue(writer, value); - i += 1; + switch (inst_ty.zigTypeTag()) { + .Array => { + const elem_ty = inst_ty.childType(); + var empty = true; + for (elements) |element| { + if (empty) try writer.writeAll(", "); + try f.writeCValue(writer, try f.resolveInst(element)); + empty = false; } + if (inst_ty.sentinel()) |sentinel| { + if (empty) try writer.writeAll(", "); + try f.object.dg.renderValue(writer, elem_ty, sentinel, .Other); + empty = false; + } + if (empty) try writer.print("{}", .{try f.fmtIntLiteral(Type.u8, Value.zero)}); }, - else => |tag| return f.fail("TODO: C backend: implement airAggregateInit for type {s}", .{@tagName(tag)}), + .Struct => { + var empty = true; + for (elements) |element, index| { + if (inst_ty.structFieldValueComptime(index)) |_| continue; + + if (!empty) try writer.writeAll(", "); + if (!inst_ty.isTupleOrAnonStruct()) { + try writer.print(".{ } = ", .{fmtIdent(inst_ty.structFieldName(index))}); + } + try f.writeCValue(writer, try f.resolveInst(element)); + empty = false; + } + if (empty) try writer.print("{}", .{try f.fmtIntLiteral(Type.u8, Value.zero)}); + }, + .Vector => return f.fail("TODO: C backend: implement airAggregateInit for vectors", .{}), + else => unreachable, } try writer.writeAll("};\n"); @@ -4706,7 +4709,6 @@ const FormatIntLiteralContext = struct { ty: Type, val: Value, mod: *Module, - location: ValueRenderLocation, }; fn formatIntLiteral( data: FormatIntLiteralContext, @@ -4755,13 +4757,6 @@ fn formatIntLiteral( } else data.val.toBigInt(&int_buf, target); assert(int.fitsInTwosComp(int_info.signedness, int_info.bits)); - if (data.location == .Identifier) { - const str = try int.toStringAlloc(allocator, 10, undefined); - defer allocator.free(str); - - return writer.writeAll(str); - } - const limbs_count_64 = @divExact(64, @bitSizeOf(Limb)); const c_bits = toCIntBits(int_info.bits) orelse unreachable; if (c_bits == 128) { @@ -4788,7 +4783,6 @@ fn formatIntLiteral( .ty = Type.u64, .val = upper_val, .mod = data.mod, - .location = data.location, }, fmt, options, writer); try writer.writeAll("<<64|"); } @@ -4802,7 +4796,6 @@ fn formatIntLiteral( .ty = Type.u64, .val = lower_val, .mod = data.mod, - .location = data.location, }, fmt, options, writer); if (have_upper) try writer.writeByte(')'); diff --git a/test/behavior/call.zig b/test/behavior/call.zig index a16c7af5a5..a8d59bddff 100644 --- a/test/behavior/call.zig +++ b/test/behavior/call.zig @@ -86,7 +86,6 @@ test "tuple parameters" { } test "result location of function call argument through runtime condition and struct init" { - 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/struct.zig b/test/behavior/struct.zig index a6b9a76ba6..f29df6becf 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -1017,7 +1017,6 @@ test "struct with union field" { } test "type coercion of anon struct literal to struct" { - 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 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1056,7 +1055,6 @@ test "type coercion of anon struct literal to struct" { test "type coercion of pointer to anon struct literal to pointer to struct" { 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 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO From e8bda9eb3ae9634a7b434e59df883ada3fdf8d9d Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 10 Oct 2022 05:43:51 -0400 Subject: [PATCH 27/47] cbe: implement ptr slice ptr --- src/codegen/c.zig | 36 ++++++++++++------------------------ test/behavior/bugs/2578.zig | 1 - test/behavior/cast.zig | 1 - test/behavior/fn.zig | 1 - 4 files changed, 12 insertions(+), 27 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 1ccb03a554..37baad3e95 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -791,7 +791,8 @@ pub const DeclGen = struct { } if (ty.optionalReprIsPayload()) { - return dg.renderValue(writer, payload_ty, val, location); + const payload_val = if (val.castTag(.opt_payload)) |pl| pl.data else val; + return dg.renderValue(writer, payload_ty, payload_val, location); } try writer.writeByte('('); @@ -1457,7 +1458,7 @@ pub const DeclGen = struct { .c_ulong => try w.writeAll("unsigned long"), .c_longlong => try w.writeAll("long long"), .c_ulonglong => try w.writeAll("unsigned long long"), - .int_signed, .int_unsigned => { + .u29, .int_signed, .int_unsigned => { const info = t.intInfo(target); const sign_prefix = switch (info.signedness) { .signed => "", @@ -2242,11 +2243,11 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .field_parent_ptr => try airFieldParentPtr(f, inst), .struct_field_val => try airStructFieldVal(f, inst), - .slice_ptr => try airSliceField(f, inst, ".ptr;\n"), - .slice_len => try airSliceField(f, inst, ".len;\n"), + .slice_ptr => try airSliceField(f, inst, " = ", ".ptr;\n"), + .slice_len => try airSliceField(f, inst, " = ", ".len;\n"), - .ptr_slice_len_ptr => try airPtrSliceFieldPtr(f, inst, ".len;\n"), - .ptr_slice_ptr_ptr => try airPtrSliceFieldPtr(f, inst, ".ptr;\n"), + .ptr_slice_len_ptr => try airSliceField(f, inst, " = &", ".len;\n"), + .ptr_slice_ptr_ptr => try airSliceField(f, inst, " = &", ".ptr;\n"), .ptr_elem_val => try airPtrElemVal(f, inst), .ptr_elem_ptr => try airPtrElemPtr(f, inst), @@ -2306,7 +2307,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO try writer.writeByte('}'); } -fn airSliceField(f: *Function, inst: Air.Inst.Index, suffix: []const u8) !CValue { +fn airSliceField(f: *Function, inst: Air.Inst.Index, prefix: []const u8, suffix: []const u8) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; const inst_ty = f.air.typeOfIndex(inst); @@ -2314,27 +2315,12 @@ fn airSliceField(f: *Function, inst: Air.Inst.Index, suffix: []const u8) !CValue const operand = try f.resolveInst(ty_op.operand); const writer = f.object.writer(); const local = try f.allocLocal(inst_ty, .Const); - try writer.writeAll(" = "); + try writer.writeAll(prefix); try f.writeCValue(writer, operand); try writer.writeAll(suffix); return local; } -fn airPtrSliceFieldPtr(f: *Function, inst: Air.Inst.Index, suffix: []const u8) !CValue { - if (f.liveness.isUnused(inst)) - 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(); - - _ = writer; - _ = operand; - _ = suffix; - - return f.fail("TODO: C backend: airPtrSliceFieldPtr", .{}); -} - fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue { const bin_op = f.air.instructions.items(.data)[inst].bin_op; const ptr_ty = f.air.typeOf(bin_op.lhs); @@ -3581,7 +3567,9 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { // for the string, we still use the next u32 for the null terminator. extra_i += (constraint.len + name.len + (2 + 3)) / 4; - try f.writeCValueDeref(writer, if (output == .none) .{ .local_ref = local.local } else try f.resolveInst(output)); + try f.writeCValueDeref(writer, if (output == .none) CValue{ + .local_ref = local.local, + } else try f.resolveInst(output)); try writer.writeAll(" = "); try f.writeCValue(writer, .{ .identifier = name }); try writer.writeAll(";\n"); diff --git a/test/behavior/bugs/2578.zig b/test/behavior/bugs/2578.zig index 90db296158..91bf28386a 100644 --- a/test/behavior/bugs/2578.zig +++ b/test/behavior/bugs/2578.zig @@ -15,7 +15,6 @@ test "fixed" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) 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 bar(t); } diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index b9317d96ad..3a7dda642c 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -1260,7 +1260,6 @@ test "cast between [*c]T and ?[*:0]T on fn parameter" { var global_struct: struct { f0: usize } = undefined; test "assignment to optional pointer result loc" { 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 var foo: struct { ptr: ?*anyopaque } = .{ .ptr = &global_struct }; diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index 64112d0d08..22935feaa1 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -97,7 +97,6 @@ test "discard the result of a function that returns a struct" { } test "inline function call that calls optional function pointer, return pointer at callsite interacts correctly with callsite return type" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; From 1dd4a6102ff983c828cdaa3dbadef37272ffce7c Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 11 Oct 2022 04:51:52 -0400 Subject: [PATCH 28/47] cbe: implement global assembly --- src/codegen/c.zig | 31 +++++++++++++++++++++++-------- src/link/C.zig | 13 ++++++++++++- test/behavior/array.zig | 1 - test/behavior/asm.zig | 2 -- test/behavior/eval.zig | 3 --- test/behavior/slice.zig | 1 - 6 files changed, 35 insertions(+), 16 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 37baad3e95..1e3669849b 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1845,6 +1845,13 @@ pub const DeclGen = struct { } }; +pub fn genGlobalAsm(mod: *Module, code: *std.ArrayList(u8)) !void { + var it = mod.global_assembly.valueIterator(); + while (it.next()) |asm_source| { + try code.writer().print("__asm({s});\n", .{fmtStringLiteral(asm_source.*)}); + } +} + pub fn genErrDecls(o: *Object) !void { if (o.dg.module.global_error_set.size == 0) return; const writer = o.writer(); @@ -3450,8 +3457,10 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll("{\n"); f.object.indent_writer.pushIndent(); + const output_locals_begin = f.next_local_index; + f.next_local_index += outputs.len; const constraints_extra_begin = extra_i; - for (outputs) |output| { + for (outputs) |output, index| { const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); const constraint = std.mem.sliceTo(extra_bytes, 0); const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); @@ -3461,7 +3470,9 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const output_ty = if (output == .none) inst_ty else f.air.typeOf(output).childType(); try writer.writeAll("register "); - try f.object.dg.renderTypeAndName(writer, output_ty, .{ .identifier = name }, .Mut, 0); + try f.object.dg.renderTypeAndName(writer, output_ty, .{ + .local = output_locals_begin + index, + }, .Mut, 0); if (std.mem.startsWith(u8, constraint, "={") and std.mem.endsWith(u8, constraint, "}")) { try writer.writeAll(" __asm(\""); try writer.writeAll(constraint["={".len .. constraint.len - "}".len]); @@ -3475,7 +3486,9 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { } try writer.writeAll(";\n"); } - for (inputs) |input| { + const input_locals_begin = f.next_local_index; + f.next_local_index += inputs.len; + for (inputs) |input, index| { const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); const constraint = std.mem.sliceTo(extra_bytes, 0); const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); @@ -3485,7 +3498,9 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const input_ty = f.air.typeOf(input); try writer.writeAll("register "); - try f.object.dg.renderTypeAndName(writer, input_ty, .{ .identifier = name }, .Const, 0); + try f.object.dg.renderTypeAndName(writer, input_ty, .{ + .local = input_locals_begin + index, + }, .Const, 0); if (std.mem.startsWith(u8, constraint, "{") and std.mem.endsWith(u8, constraint, "}")) { try writer.writeAll(" __asm(\""); try writer.writeAll(constraint["{".len .. constraint.len - "}".len]); @@ -3524,7 +3539,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { if (index > 0) try writer.writeByte(','); try writer.print(" {s}(", .{fmtStringLiteral(if (constraint[1] == '{') "=r" else constraint)}); - try f.writeCValue(writer, .{ .identifier = name }); + try f.writeCValue(writer, .{ .local = output_locals_begin + index }); try writer.writeByte(')'); } try writer.writeByte(':'); @@ -3538,7 +3553,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { if (index > 0) try writer.writeByte(','); try writer.print(" {s}(", .{fmtStringLiteral(if (constraint[0] == '{') "r" else constraint)}); - try f.writeCValue(writer, .{ .identifier = name }); + try f.writeCValue(writer, .{ .local = input_locals_begin + index }); try writer.writeByte(')'); } try writer.writeByte(':'); @@ -3559,7 +3574,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(");\n"); extra_i = constraints_extra_begin; - for (outputs) |output| { + for (outputs) |output, index| { const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); const constraint = std.mem.sliceTo(extra_bytes, 0); const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); @@ -3571,7 +3586,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { .local_ref = local.local, } else try f.resolveInst(output)); try writer.writeAll(" = "); - try f.writeCValue(writer, .{ .identifier = name }); + try f.writeCValue(writer, .{ .local = output_locals_begin + index }); try writer.writeAll(";\n"); } diff --git a/src/link/C.zig b/src/link/C.zig index d7432eb015..8509e4ae8a 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -256,7 +256,7 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) var f: Flush = .{}; defer f.deinit(gpa); - // Covers zig.h and typedef. + // Covers zig.h, typedef, and asm. try f.all_buffers.ensureUnusedCapacity(gpa, 2); f.appendBufAssumeCapacity(zig_h); @@ -264,6 +264,16 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) const typedef_index = f.all_buffers.items.len; f.all_buffers.items.len += 1; + { + var asm_buf = f.asm_buf.toManaged(module.gpa); + defer asm_buf.deinit(); + + try codegen.genGlobalAsm(module, &asm_buf); + + f.asm_buf = asm_buf.moveToUnmanaged(); + f.appendBufAssumeCapacity(f.asm_buf.items); + } + try self.flushErrDecls(&f); // Typedefs, forward decls, and non-functions first. @@ -307,6 +317,7 @@ const Flush = struct { remaining_decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, void) = .{}, typedefs: Typedefs = .{}, typedef_buf: std.ArrayListUnmanaged(u8) = .{}, + asm_buf: std.ArrayListUnmanaged(u8) = .{}, /// We collect a list of buffers to write, and write them all at once with pwritev 😎 all_buffers: std.ArrayListUnmanaged(std.os.iovec_const) = .{}, /// Keeps track of the total bytes of `all_buffers`. diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 911fe28cd8..6311c4cd30 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -527,7 +527,6 @@ test "type coercion of anon struct literal to array" { } test "type coercion of pointer to anon struct literal to pointer to array" { - 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 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO diff --git a/test/behavior/asm.zig b/test/behavior/asm.zig index c154271964..6366479479 100644 --- a/test/behavior/asm.zig +++ b/test/behavior/asm.zig @@ -18,7 +18,6 @@ comptime { } test "module level assembly" { - 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_arm) return error.SkipZigTest; // TODO @@ -50,7 +49,6 @@ test "output constraint modifiers" { } test "alternative constraints" { - 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_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 8ae10fa1ad..438f05b563 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -746,7 +746,6 @@ fn scalar(x: u32) u32 { test "array concatenation peer resolves element types - value" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; @@ -781,7 +780,6 @@ test "array concatenation sets the sentinel - value" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; @@ -819,7 +817,6 @@ test "array multiplication sets the sentinel - value" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 8965c667c1..4fe61f521d 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -533,7 +533,6 @@ test "slice pointer-to-array zero length" { } test "type coercion of pointer to anon struct literal to pointer to slice" { - 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_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO From b48417aed25db27aa3b200c2f118b88b8f91c48c Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 18 Oct 2022 21:41:00 -0400 Subject: [PATCH 29/47] cbe: misc fixes --- src/codegen/c.zig | 124 +++++++++++++++++++++-------------- test/behavior/asm.zig | 1 - test/behavior/bugs/12972.zig | 1 - test/behavior/bugs/13128.zig | 1 - 4 files changed, 75 insertions(+), 52 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 1e3669849b..942c2e28ac 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -88,23 +88,9 @@ pub fn typeToCIdentifier(ty: Type, mod: *Module) std.fmt.Formatter(formatTypeAsC } const reserved_idents = std.ComptimeStringMap(void, .{ - .{ "_Alignas", { + .{ "alignas", { @setEvalBranchQuota(4000); } }, - .{ "_Alignof", {} }, - .{ "_Atomic", {} }, - .{ "_Bool", {} }, - .{ "_Complex", {} }, - .{ "_Decimal128", {} }, - .{ "_Decimal32", {} }, - .{ "_Decimal64", {} }, - .{ "_Generic", {} }, - .{ "_Imaginary", {} }, - .{ "_Noreturn", {} }, - .{ "_Pragma", {} }, - .{ "_Static_assert", {} }, - .{ "_Thread_local", {} }, - .{ "alignas", {} }, .{ "alignof", {} }, .{ "asm", {} }, .{ "atomic_bool", {} }, @@ -199,6 +185,15 @@ const reserved_idents = std.ComptimeStringMap(void, .{ .{ "while ", {} }, }); +fn isReservedIdent(ident: []const u8) bool { + if (ident.len >= 2 and ident[0] == '_') { + switch (ident[1]) { + 'A'...'Z', '_' => return true, + else => return false, + } + } else return reserved_idents.has(ident); +} + fn formatIdent( ident: []const u8, comptime fmt: []const u8, @@ -207,7 +202,7 @@ fn formatIdent( ) !void { _ = options; const solo = fmt.len != 0 and fmt[0] == ' '; // space means solo; not part of a bigger ident. - if (solo and reserved_idents.has(ident)) { + if (solo and isReservedIdent(ident)) { try writer.writeAll("zig_e_"); } for (ident) |c, i| { @@ -601,12 +596,17 @@ pub const DeclGen = struct { try dg.renderTypecast(writer, ty); try writer.writeAll("){"); + if (ty.unionTagTypeSafety()) |tag_ty| { + try writer.writeAll(".tag = "); + try dg.renderValue(writer, tag_ty, val, location); + try writer.writeAll(", .payload = {"); + } for (ty.unionFields().values()) |field| { if (!field.ty.hasRuntimeBits()) continue; try dg.renderValue(writer, field.ty, val, location); break; } else try writer.print("{x}", .{try dg.fmtIntLiteral(Type.u8, Value.undef)}); - + if (ty.unionTagTypeSafety()) |_| try writer.writeByte('}'); return writer.writeByte('}'); }, .ErrorUnion => { @@ -3469,22 +3469,22 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { extra_i += (constraint.len + name.len + (2 + 3)) / 4; const output_ty = if (output == .none) inst_ty else f.air.typeOf(output).childType(); - try writer.writeAll("register "); - try f.object.dg.renderTypeAndName(writer, output_ty, .{ - .local = output_locals_begin + index, - }, .Mut, 0); if (std.mem.startsWith(u8, constraint, "={") and std.mem.endsWith(u8, constraint, "}")) { + try writer.writeAll("register "); + try f.object.dg.renderTypeAndName(writer, output_ty, .{ + .local = output_locals_begin + index, + }, .Mut, 0); try writer.writeAll(" __asm(\""); try writer.writeAll(constraint["={".len .. constraint.len - "}".len]); try writer.writeAll("\")"); + if (f.wantSafety()) { + try writer.writeAll(" = "); + try f.object.dg.renderValue(writer, output_ty, Value.undef, .Other); + } + try writer.writeAll(";\n"); } else if (constraint.len < 2 or constraint[0] != '=') { return f.fail("CBE: constraint not supported: '{s}'", .{constraint}); } - if (f.wantSafety()) { - try writer.writeAll(" = "); - try f.object.dg.renderValue(writer, output_ty, Value.undef, .Other); - } - try writer.writeAll(";\n"); } const input_locals_begin = f.next_local_index; f.next_local_index += inputs.len; @@ -3497,20 +3497,29 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { extra_i += (constraint.len + name.len + (2 + 3)) / 4; const input_ty = f.air.typeOf(input); - try writer.writeAll("register "); - try f.object.dg.renderTypeAndName(writer, input_ty, .{ - .local = input_locals_begin + index, - }, .Const, 0); if (std.mem.startsWith(u8, constraint, "{") and std.mem.endsWith(u8, constraint, "}")) { + try writer.writeAll("register "); + try f.object.dg.renderTypeAndName(writer, input_ty, .{ + .local = input_locals_begin + index, + }, .Const, 0); try writer.writeAll(" __asm(\""); try writer.writeAll(constraint["{".len .. constraint.len - "}".len]); - try writer.writeAll("\")"); - } else if (constraint.len < 1 or std.mem.indexOfScalar(u8, "=+&%", constraint[0]) != null) { + try writer.writeAll("\") = "); + try f.writeCValue(writer, try f.resolveInst(input)); + try writer.writeAll(";\n"); + } else if (constraint.len >= 1 and std.mem.indexOfScalar(u8, "=+&%", constraint[0]) == null) { + const input_val = try f.resolveInst(input); + if (input_val == .constant) { + try f.object.dg.renderTypeAndName(writer, input_ty, .{ + .local = input_locals_begin + index, + }, .Const, 0); + try writer.writeAll(" = "); + try f.writeCValue(writer, input_val); + try writer.writeAll(";\n"); + } + } else { return f.fail("CBE: constraint not supported: '{s}'", .{constraint}); } - try writer.writeAll(" = "); - try f.writeCValue(writer, try f.resolveInst(input)); - try writer.writeAll(";\n"); } { var clobber_i: u32 = 0; @@ -3529,7 +3538,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { extra_i = constraints_extra_begin; try writer.writeByte(':'); - for (outputs) |_, index| { + for (outputs) |output, index| { const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); const constraint = std.mem.sliceTo(extra_bytes, 0); const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); @@ -3538,12 +3547,18 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { extra_i += (constraint.len + name.len + (2 + 3)) / 4; if (index > 0) try writer.writeByte(','); - try writer.print(" {s}(", .{fmtStringLiteral(if (constraint[1] == '{') "=r" else constraint)}); - try f.writeCValue(writer, .{ .local = output_locals_begin + index }); + try writer.writeByte(' '); + if (constraint[1] == '{') { + try writer.print("{s}(", .{fmtStringLiteral("=r")}); + try f.writeCValue(writer, .{ .local = output_locals_begin + index }); + } else { + try writer.print("{s}(", .{fmtStringLiteral(constraint)}); + try f.writeCValueDeref(writer, try f.resolveInst(output)); + } try writer.writeByte(')'); } try writer.writeByte(':'); - for (inputs) |_, index| { + for (inputs) |input, index| { const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); const constraint = std.mem.sliceTo(extra_bytes, 0); const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); @@ -3552,8 +3567,17 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { extra_i += (constraint.len + name.len + (2 + 3)) / 4; if (index > 0) try writer.writeByte(','); - try writer.print(" {s}(", .{fmtStringLiteral(if (constraint[0] == '{') "r" else constraint)}); - try f.writeCValue(writer, .{ .local = input_locals_begin + index }); + try writer.writeByte(' '); + if (constraint[0] == '{') { + try writer.print("{s}(", .{fmtStringLiteral("r")}); + try f.writeCValue(writer, .{ .local = input_locals_begin + index }); + } else { + const input_val = try f.resolveInst(input); + try writer.print("{s}(", .{fmtStringLiteral(constraint)}); + try f.writeCValue(writer, if (input_val == .constant) .{ + .local = input_locals_begin + index, + } else input_val); + } try writer.writeByte(')'); } try writer.writeByte(':'); @@ -3582,12 +3606,14 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { // for the string, we still use the next u32 for the null terminator. extra_i += (constraint.len + name.len + (2 + 3)) / 4; - try f.writeCValueDeref(writer, if (output == .none) CValue{ - .local_ref = local.local, - } else try f.resolveInst(output)); - try writer.writeAll(" = "); - try f.writeCValue(writer, .{ .local = output_locals_begin + index }); - try writer.writeAll(";\n"); + if (constraint[1] == '{') { + try f.writeCValueDeref(writer, if (output == .none) CValue{ + .local_ref = local.local, + } else try f.resolveInst(output)); + try writer.writeAll(" = "); + try f.writeCValue(writer, .{ .local = output_locals_begin + index }); + try writer.writeAll(";\n"); + } } f.object.indent_writer.popIndent(); @@ -4415,12 +4441,12 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { const elem_ty = inst_ty.childType(); var empty = true; for (elements) |element| { - if (empty) try writer.writeAll(", "); + if (!empty) try writer.writeAll(", "); try f.writeCValue(writer, try f.resolveInst(element)); empty = false; } if (inst_ty.sentinel()) |sentinel| { - if (empty) try writer.writeAll(", "); + if (!empty) try writer.writeAll(", "); try f.object.dg.renderValue(writer, elem_ty, sentinel, .Other); empty = false; } diff --git a/test/behavior/asm.zig b/test/behavior/asm.zig index 6366479479..fd1601bf06 100644 --- a/test/behavior/asm.zig +++ b/test/behavior/asm.zig @@ -112,7 +112,6 @@ test "sized integer/float in asm input" { } test "struct/array/union types as input values" { - 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_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/bugs/12972.zig b/test/behavior/bugs/12972.zig index 0e01782705..8ec2b42692 100644 --- a/test/behavior/bugs/12972.zig +++ b/test/behavior/bugs/12972.zig @@ -5,7 +5,6 @@ pub fn f(_: [:null]const ?u8) void {} test { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const c: u8 = 42; f(&[_:null]?u8{c}); diff --git a/test/behavior/bugs/13128.zig b/test/behavior/bugs/13128.zig index 057270a96f..9e92e66daf 100644 --- a/test/behavior/bugs/13128.zig +++ b/test/behavior/bugs/13128.zig @@ -12,7 +12,6 @@ fn foo(val: U) !void { } test "runtime union init, most-aligned field != largest" { - 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 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO From 6921b0a850771fa7729930a9ec64b27b4e57e79b Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 18 Oct 2022 22:43:34 -0400 Subject: [PATCH 30/47] cbe: implement some float ops --- src/codegen/c.zig | 81 ++++++++++++++++++++++++++++++++------- test/behavior/floatop.zig | 2 - test/behavior/math.zig | 2 - 3 files changed, 68 insertions(+), 17 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 942c2e28ac..26b177c2b7 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -336,6 +336,26 @@ pub const Function = struct { fn fmtIntLiteral(f: *Function, ty: Type, val: Value) !std.fmt.Formatter(formatIntLiteral) { return f.object.dg.fmtIntLiteral(ty, val); } + + fn renderFloatFnName(f: *Function, fn_name: []const u8, float_ty: Type) !void { + const target = f.object.dg.module.getTarget(); + const float_bits = float_ty.floatBits(target); + const is_longdouble = float_bits == CType.longdouble.sizeInBits(target); + const writer = f.object.writer(); + if (!is_longdouble and float_bits == 80) { + try writer.writeAll("__"); + } + try writer.writeAll(fn_name); + if (is_longdouble) { + try writer.writeByte('l'); + } else switch (float_bits) { + 16, 32 => try writer.writeByte('f'), + 64 => {}, + 80 => try writer.writeByte('x'), + 128 => try writer.writeByte('q'), + else => unreachable, + } + } }; /// This data is available when outputting .c code for a `Module`. @@ -2076,8 +2096,17 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .sub => try airBinOp (f, inst, " - "), .mul => try airBinOp (f, inst, " * "), .div_float, .div_exact => try airBinOp( f, inst, " / "), - .rem => try airBinOp( f, inst, " % "), + .rem => blk: { + const bin_op = f.air.instructions.items(.data)[inst].bin_op; + const lhs_ty = f.air.typeOf(bin_op.lhs); + // For binary operations @TypeOf(lhs)==@TypeOf(rhs), + // so we only check one. + break :blk if (lhs_ty.isInt()) + try airBinOp(f, inst, " % ") + else + try airBinFloatOp(f, inst, "fmod"); // yes, @rem() => fmod() + }, .div_trunc => blk: { const bin_op = f.air.instructions.items(.data)[inst].bin_op; const lhs_ty = f.air.typeOf(bin_op.lhs); @@ -2115,8 +2144,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .floor, .ceil, .round, - .trunc_float, - => |tag| return f.fail("TODO: C backend: implement unary op for tag '{s}'", .{@tagName(tag)}), + => |tag| try airUnFloatOp(f, inst, @tagName(tag)), + .trunc_float => try airUnFloatOp(f, inst, "trunc"), .mul_add => try airMulAdd(f, inst), @@ -4573,12 +4602,45 @@ fn airNeg(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.air.typeOfIndex(inst); const operand = try f.resolveInst(un_op); const local = try f.allocLocal(inst_ty, .Const); - try writer.writeByte('-'); + try writer.writeAll(" = -"); try f.writeCValue(writer, operand); try writer.writeAll(";\n"); return local; } +fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, fn_name: []const u8) !CValue { + if (f.liveness.isUnused(inst)) return CValue.none; + const un_op = f.air.instructions.items(.data)[inst].un_op; + const writer = f.object.writer(); + const inst_ty = f.air.typeOfIndex(inst); + const operand = try f.resolveInst(un_op); + const local = try f.allocLocal(inst_ty, .Const); + try writer.writeAll(" = "); + try f.renderFloatFnName(fn_name, inst_ty); + try writer.writeByte('('); + try f.writeCValue(writer, operand); + try writer.writeAll(");\n"); + return local; +} + +fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, fn_name: []const u8) !CValue { + if (f.liveness.isUnused(inst)) return CValue.none; + const bin_op = f.air.instructions.items(.data)[inst].bin_op; + const writer = f.object.writer(); + const inst_ty = f.air.typeOfIndex(inst); + const lhs = try f.resolveInst(bin_op.lhs); + const rhs = try f.resolveInst(bin_op.rhs); + const local = try f.allocLocal(inst_ty, .Const); + try writer.writeAll(" = "); + try f.renderFloatFnName(fn_name, inst_ty); + try writer.writeByte('('); + try f.writeCValue(writer, lhs); + try writer.writeAll(", "); + try f.writeCValue(writer, rhs); + try writer.writeAll(");\n"); + return local; +} + fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; const pl_op = f.air.instructions.items(.data)[inst].pl_op; @@ -4588,17 +4650,10 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { const mulend2 = try f.resolveInst(extra.rhs); const addend = try f.resolveInst(pl_op.operand); const writer = f.object.writer(); - const target = f.object.dg.module.getTarget(); - const fn_name = switch (inst_ty.floatBits(target)) { - 16, 32 => "fmaf", - 64 => "fma", - 80 => if (CType.longdouble.sizeInBits(target) == 80) "fmal" else "__fmax", - 128 => if (CType.longdouble.sizeInBits(target) == 128) "fmal" else "fmaq", - else => unreachable, - }; const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = "); - try writer.print("{s}(", .{fn_name}); + try f.renderFloatFnName("fma", inst_ty); + try writer.writeByte('('); try f.writeCValue(writer, mulend1); try writer.writeAll(", "); try f.writeCValue(writer, mulend2); diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index 3bb4ba9d0a..4abd09b9c6 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -571,7 +571,6 @@ test "negation f32" { 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 - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const S = struct { @@ -593,7 +592,6 @@ test "negation f64" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 193efd3e7a..e8cecf803a 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -376,7 +376,6 @@ fn testBinaryNot(x: u16) !void { } test "division" { - 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 @@ -1706,7 +1705,6 @@ test "absFloat" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) 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 try testAbsFloat(); comptime try testAbsFloat(); From 912b84bbad698d18a6b844f9c05c074d2880e35b Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 19 Oct 2022 02:10:30 -0400 Subject: [PATCH 31/47] cbe: fix atomics --- lib/include/zig.h | 65 ++++++----- src/codegen/c.zig | 109 ++++++++++++++---- test/behavior/atomics.zig | 13 --- test/behavior/bugs/13068.zig | 1 - test/behavior/defer.zig | 1 - test/behavior/eval.zig | 1 - test/behavior/packed-struct.zig | 3 - .../packed_struct_explicit_backing_int.zig | 1 - test/behavior/pointers.zig | 2 - test/behavior/struct.zig | 2 - test/behavior/switch.zig | 3 - test/behavior/translate_c_macros.zig | 4 - test/behavior/type.zig | 3 - test/behavior/union.zig | 1 - 14 files changed, 122 insertions(+), 87 deletions(-) diff --git a/lib/include/zig.h b/lib/include/zig.h index e4a0c4cc59..ee9e31496d 100644 --- a/lib/include/zig.h +++ b/lib/include/zig.h @@ -101,19 +101,20 @@ #if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__) #include +#define zig_atomic(type) _Atomic(type) #define zig_cmpxchg_strong(obj, expected, desired, succ, fail) atomic_compare_exchange_strong_explicit(obj, &(expected), desired, succ, fail) -#define zig_cmpxchg_weak (obj, expected, desired, succ, fail) atomic_compare_exchange_weak_explicit (obj, &(expected), desired, succ, fail) +#define zig_cmpxchg_weak(obj, expected, desired, succ, fail) atomic_compare_exchange_weak_explicit (obj, &(expected), desired, succ, fail) #define zig_atomicrmw_xchg(obj, arg, order) atomic_exchange_explicit (obj, arg, order) -#define zig_atomicrmw_add (obj, arg, order) atomic_fetch_add_explicit (obj, arg, order) -#define zig_atomicrmw_sub (obj, arg, order) atomic_fetch_sub_explicit (obj, arg, order) -#define zig_atomicrmw_or (obj, arg, order) atomic_fetch_or_explicit (obj, arg, order) -#define zig_atomicrmw_xor (obj, arg, order) atomic_fetch_xor_explicit (obj, arg, order) -#define zig_atomicrmw_and (obj, arg, order) atomic_fetch_and_explicit (obj, arg, order) -#define zig_atomicrmw_nand(obj, arg, order) atomic_fetch_nand_explicit(obj, arg, order) -#define zig_atomicrmw_min (obj, arg, order) atomic_fetch_min_explicit (obj, arg, order) -#define zig_atomicrmw_max (obj, arg, order) atomic_fetch_max_explicit (obj, arg, order) -#define zig_atomic_store (obj, arg, order) atomic_store_explicit (obj, arg, order) -#define zig_atomic_load (obj, order) atomic_load_explicit (obj, order) +#define zig_atomicrmw_add(obj, arg, order) atomic_fetch_add_explicit (obj, arg, order) +#define zig_atomicrmw_sub(obj, arg, order) atomic_fetch_sub_explicit (obj, arg, order) +#define zig_atomicrmw_or(obj, arg, order) atomic_fetch_or_explicit (obj, arg, order) +#define zig_atomicrmw_xor(obj, arg, order) atomic_fetch_xor_explicit (obj, arg, order) +#define zig_atomicrmw_and(obj, arg, order) atomic_fetch_and_explicit (obj, arg, order) +#define zig_atomicrmw_nand(obj, arg, order) __atomic_fetch_nand (obj, arg, order) +#define zig_atomicrmw_min(obj, arg, order) __atomic_fetch_min (obj, arg, order) +#define zig_atomicrmw_max(obj, arg, order) __atomic_fetch_max (obj, arg, order) +#define zig_atomic_store(obj, arg, order) atomic_store_explicit (obj, arg, order) +#define zig_atomic_load(obj, order) atomic_load_explicit (obj, order) #define zig_fence(order) atomic_thread_fence(order) #elif __GNUC__ #define memory_order_relaxed __ATOMIC_RELAXED @@ -122,19 +123,20 @@ #define memory_order_release __ATOMIC_RELEASE #define memory_order_acq_rel __ATOMIC_ACQ_REL #define memory_order_seq_cst __ATOMIC_SEQ_CST +#define zig_atomic(type) type #define zig_cmpxchg_strong(obj, expected, desired, succ, fail) __atomic_compare_exchange_n(obj, &(expected), desired, false, succ, fail) -#define zig_cmpxchg_weak (obj, expected, desired, succ, fail) __atomic_compare_exchange_n(obj, &(expected), desired, true , succ, fail) +#define zig_cmpxchg_weak(obj, expected, desired, succ, fail) __atomic_compare_exchange_n(obj, &(expected), desired, true , succ, fail) #define zig_atomicrmw_xchg(obj, arg, order) __atomic_exchange_n(obj, arg, order) -#define zig_atomicrmw_add (obj, arg, order) __atomic_fetch_add (obj, arg, order) -#define zig_atomicrmw_sub (obj, arg, order) __atomic_fetch_sub (obj, arg, order) -#define zig_atomicrmw_or (obj, arg, order) __atomic_fetch_or (obj, arg, order) -#define zig_atomicrmw_xor (obj, arg, order) __atomic_fetch_xor (obj, arg, order) -#define zig_atomicrmw_and (obj, arg, order) __atomic_fetch_and (obj, arg, order) +#define zig_atomicrmw_add(obj, arg, order) __atomic_fetch_add (obj, arg, order) +#define zig_atomicrmw_sub(obj, arg, order) __atomic_fetch_sub (obj, arg, order) +#define zig_atomicrmw_or(obj, arg, order) __atomic_fetch_or (obj, arg, order) +#define zig_atomicrmw_xor(obj, arg, order) __atomic_fetch_xor (obj, arg, order) +#define zig_atomicrmw_and(obj, arg, order) __atomic_fetch_and (obj, arg, order) #define zig_atomicrmw_nand(obj, arg, order) __atomic_fetch_nand(obj, arg, order) -#define zig_atomicrmw_min (obj, arg, order) __atomic_fetch_min (obj, arg, order) -#define zig_atomicrmw_max (obj, arg, order) __atomic_fetch_max (obj, arg, order) -#define zig_atomic_store (obj, arg, order) __atomic_store (obj, arg, order) -#define zig_atomic_load (obj, order) __atomic_load (obj, order) +#define zig_atomicrmw_min(obj, arg, order) __atomic_fetch_min (obj, arg, order) +#define zig_atomicrmw_max(obj, arg, order) __atomic_fetch_max (obj, arg, order) +#define zig_atomic_store(obj, arg, order) __atomic_store_n (obj, arg, order) +#define zig_atomic_load(obj, order) __atomic_load_n (obj, order) #define zig_fence(order) __atomic_thread_fence(order) #else #define memory_order_relaxed 0 @@ -143,19 +145,20 @@ #define memory_order_release 3 #define memory_order_acq_rel 4 #define memory_order_seq_cst 5 +#define zig_atomic(type) type #define zig_cmpxchg_strong(obj, expected, desired, succ, fail) zig_unimplemented() -#define zig_cmpxchg_weak (obj, expected, desired, succ, fail) zig_unimplemented() +#define zig_cmpxchg_weak(obj, expected, desired, succ, fail) zig_unimplemented() #define zig_atomicrmw_xchg(obj, arg, order) zig_unimplemented() -#define zig_atomicrmw_add (obj, arg, order) zig_unimplemented() -#define zig_atomicrmw_sub (obj, arg, order) zig_unimplemented() -#define zig_atomicrmw_or (obj, arg, order) zig_unimplemented() -#define zig_atomicrmw_xor (obj, arg, order) zig_unimplemented() -#define zig_atomicrmw_and (obj, arg, order) zig_unimplemented() +#define zig_atomicrmw_add(obj, arg, order) zig_unimplemented() +#define zig_atomicrmw_sub(obj, arg, order) zig_unimplemented() +#define zig_atomicrmw_or(obj, arg, order) zig_unimplemented() +#define zig_atomicrmw_xor(obj, arg, order) zig_unimplemented() +#define zig_atomicrmw_and(obj, arg, order) zig_unimplemented() #define zig_atomicrmw_nand(obj, arg, order) zig_unimplemented() -#define zig_atomicrmw_min (obj, arg, order) zig_unimplemented() -#define zig_atomicrmw_max (obj, arg, order) zig_unimplemented() -#define zig_atomic_store (obj, arg, order) zig_unimplemented() -#define zig_atomic_load (obj, order) zig_unimplemented() +#define zig_atomicrmw_min(obj, arg, order) zig_unimplemented() +#define zig_atomicrmw_max(obj, arg, order) zig_unimplemented() +#define zig_atomic_store(obj, arg, order) zig_unimplemented() +#define zig_atomic_load(obj, order) zig_unimplemented() #define zig_fence(order) zig_unimplemented() #endif diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 26b177c2b7..c9e77d0df4 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -587,9 +587,9 @@ pub const DeclGen = struct { try writer.writeByte('('); try dg.renderTypecast(writer, ty); - try writer.writeAll("){ .is_null = "); + try writer.writeAll("){ .payload = "); try dg.renderValue(writer, Type.bool, val, location); - try writer.writeAll(", .payload = "); + try writer.writeAll(", .is_null = "); try dg.renderValue(writer, payload_ty, val, location); return writer.writeAll(" }"); }, @@ -820,11 +820,13 @@ pub const DeclGen = struct { try writer.writeAll("){"); if (val.castTag(.opt_payload)) |pl| { const payload_val = pl.data; - try writer.writeAll(" .is_null = false, .payload = "); + try writer.writeAll(" .payload = "); try dg.renderValue(writer, payload_ty, payload_val, location); - try writer.writeAll(" }"); + try writer.writeAll(", .is_null = false }"); } else { - try writer.writeAll(" .is_null = true }"); + try writer.writeAll(" .payload = "); + try dg.renderValue(writer, payload_ty, Value.undef, location); + try writer.writeAll(", .is_null = true }"); } }, .ErrorSet => { @@ -3870,10 +3872,23 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { const payload = if (struct_ty.tag() == .union_tagged or struct_ty.tag() == .union_safety_tagged) "payload." else ""; const inst_ty = f.air.typeOfIndex(inst); - const local = try f.allocLocal(inst_ty, .Const); - try writer.writeAll(" = "); - try f.writeCValue(writer, struct_byval); - try writer.print(".{s}{ };\n", .{ payload, fmtIdent(field_name) }); + const is_array = inst_ty.zigTypeTag() == .Array; + const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const); + + if (is_array) { + try writer.writeAll(";\n"); + try writer.writeAll("memcpy("); + try f.writeCValue(writer, local); + try writer.writeAll(", "); + try f.writeCValue(writer, struct_byval); + try writer.print(".{s}{ }, sizeof(", .{ payload, fmtIdent(field_name) }); + try f.writeCValue(writer, local); + try writer.writeAll("));\n"); + } else { + try writer.writeAll(" = "); + try f.writeCValue(writer, struct_byval); + try writer.print(".{s}{ };\n", .{ payload, fmtIdent(field_name) }); + } return local; } @@ -3962,9 +3977,9 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { // .wrap_optional is used to convert non-optionals into optionals so it can never be null. const local = try f.allocLocal(inst_ty, .Const); - try writer.writeAll(" = { .is_null = false, .payload ="); + try writer.writeAll(" = { .payload = "); try f.writeCValue(writer, operand); - try writer.writeAll("};\n"); + try writer.writeAll(", .is_null = false };\n"); return local; } @@ -4203,23 +4218,54 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const extra = f.air.extraData(Air.Cmpxchg, ty_pl.payload).data; const inst_ty = f.air.typeOfIndex(inst); + const is_struct = !inst_ty.isPtrLikeOptional(); + const ptr_ty = f.air.typeOf(extra.ptr); const ptr = try f.resolveInst(extra.ptr); const expected_value = try f.resolveInst(extra.expected_value); const new_value = try f.resolveInst(extra.new_value); - const local = try f.allocLocal(inst_ty, .Const); const writer = f.object.writer(); - try writer.print(" = zig_cmpxchg_{s}(", .{flavor}); + const local = try f.allocLocal(inst_ty, .Mut); + try writer.writeAll(" = "); + if (is_struct) try writer.writeAll("{ .payload = "); + try f.writeCValue(writer, expected_value); + if (is_struct) try writer.writeAll(", .is_null = false }"); + try writer.writeAll(";\n"); + + if (is_struct) { + try f.writeCValue(writer, local); + try writer.writeAll(".is_null = "); + } else { + try writer.writeAll("if ("); + } + try writer.print("zig_cmpxchg_{s}((zig_atomic(", .{flavor}); + try f.object.dg.renderTypecast(writer, ptr_ty.elemType()); + try writer.writeByte(')'); + if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); + try writer.writeAll(" *)"); try f.writeCValue(writer, ptr); try writer.writeAll(", "); - try f.writeCValue(writer, expected_value); + try f.writeCValue(writer, local); + if (is_struct) { + try writer.writeAll(".payload"); + } try writer.writeAll(", "); try f.writeCValue(writer, new_value); try writer.writeAll(", "); try writeMemoryOrder(writer, extra.successOrder()); try writer.writeAll(", "); try writeMemoryOrder(writer, extra.failureOrder()); - try writer.writeAll(");\n"); + try writer.writeByte(')'); + if (is_struct) { + try writer.writeAll(";\n"); + } else { + try writer.writeAll(") {\n"); + f.object.indent_writer.pushIndent(); + try f.writeCValue(writer, local); + try writer.writeAll(" = NULL;\n"); + f.object.indent_writer.popIndent(); + try writer.writeAll("}\n"); + } return local; } @@ -4228,12 +4274,26 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { const pl_op = f.air.instructions.items(.data)[inst].pl_op; const extra = f.air.extraData(Air.AtomicRmw, pl_op.payload).data; const inst_ty = f.air.typeOfIndex(inst); + const ptr_ty = f.air.typeOf(pl_op.operand); const ptr = try f.resolveInst(pl_op.operand); const operand = try f.resolveInst(extra.operand); const local = try f.allocLocal(inst_ty, .Const); const writer = f.object.writer(); - try writer.print(" = zig_atomicrmw_{s}(", .{toAtomicRmwSuffix(extra.op())}); + try writer.print(" = zig_atomicrmw_{s}((", .{toAtomicRmwSuffix(extra.op())}); + switch (extra.op()) { + else => { + try writer.writeAll("zig_atomic("); + try f.object.dg.renderTypecast(writer, ptr_ty.elemType()); + try writer.writeByte(')'); + }, + .Nand, .Min, .Max => { + // These are missing from stdatomic.h, so no atomic types for now. + try f.object.dg.renderTypecast(writer, ptr_ty.elemType()); + }, + } + if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); + try writer.writeAll(" *)"); try f.writeCValue(writer, ptr); try writer.writeAll(", "); try f.writeCValue(writer, operand); @@ -4255,7 +4315,11 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue { const local = try f.allocLocal(inst_ty, .Const); const writer = f.object.writer(); - try writer.writeAll(" = zig_atomic_load("); + try writer.writeAll(" = zig_atomic_load((zig_atomic("); + try f.object.dg.renderTypecast(writer, ptr_ty.elemType()); + try writer.writeByte(')'); + if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); + try writer.writeAll(" *)"); try f.writeCValue(writer, ptr); try writer.writeAll(", "); try writeMemoryOrder(writer, atomic_load.order); @@ -4266,19 +4330,22 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue { fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CValue { const bin_op = f.air.instructions.items(.data)[inst].bin_op; + const ptr_ty = f.air.typeOf(bin_op.lhs); const ptr = try f.resolveInst(bin_op.lhs); const element = try f.resolveInst(bin_op.rhs); - const inst_ty = f.air.typeOfIndex(inst); - const local = try f.allocLocal(inst_ty, .Const); const writer = f.object.writer(); - try writer.writeAll(" = zig_atomic_store("); + try writer.writeAll("zig_atomic_store((zig_atomic("); + try f.object.dg.renderTypecast(writer, ptr_ty.elemType()); + try writer.writeByte(')'); + if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); + try writer.writeAll(" *)"); try f.writeCValue(writer, ptr); try writer.writeAll(", "); try f.writeCValue(writer, element); try writer.print(", {s});\n", .{order}); - return local; + return CValue.none; } fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { diff --git a/test/behavior/atomics.zig b/test/behavior/atomics.zig index 50bf65a8a4..365df09928 100644 --- a/test/behavior/atomics.zig +++ b/test/behavior/atomics.zig @@ -4,7 +4,6 @@ const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; test "cmpxchg" { - 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 @@ -44,7 +43,6 @@ test "fence" { } test "atomicrmw and atomicload" { - 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 @@ -74,7 +72,6 @@ fn testAtomicLoad(ptr: *u8) !void { } test "cmpxchg with ptr" { - 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 @@ -101,7 +98,6 @@ test "cmpxchg with ptr" { } test "cmpxchg with ignored result" { - 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 @@ -116,7 +112,6 @@ test "cmpxchg with ignored result" { } test "128-bit cmpxchg" { - 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 @@ -150,7 +145,6 @@ fn test_u128_cmpxchg() !void { var a_global_variable = @as(u32, 1234); test "cmpxchg on a global 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 @@ -169,7 +163,6 @@ test "cmpxchg on a global variable" { } test "atomic load and rmw with enum" { - 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 @@ -188,7 +181,6 @@ test "atomic load and rmw with enum" { } test "atomic store" { - 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 @@ -203,7 +195,6 @@ test "atomic store" { } test "atomic store comptime" { - 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 @@ -223,7 +214,6 @@ fn testAtomicStore() !void { } test "atomicrmw with floats" { - 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 @@ -252,7 +242,6 @@ fn testAtomicRmwFloat() !void { } test "atomicrmw with ints" { - 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 @@ -287,7 +276,6 @@ fn testAtomicRmwInt() !void { } test "atomics with different types" { - 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 @@ -318,7 +306,6 @@ fn testAtomicsWithType(comptime T: type, a: T, b: T) !void { } test "return @atomicStore, using it as a void value" { - 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/13068.zig b/test/behavior/bugs/13068.zig index bfc05452a4..414217b7ea 100644 --- a/test/behavior/bugs/13068.zig +++ b/test/behavior/bugs/13068.zig @@ -7,7 +7,6 @@ var list = std.ArrayList(u32).init(allocator); test { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - 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 diff --git a/test/behavior/defer.zig b/test/behavior/defer.zig index 7bf66496a4..fdf43750be 100644 --- a/test/behavior/defer.zig +++ b/test/behavior/defer.zig @@ -108,7 +108,6 @@ test "mixing normal and error defers" { } test "errdefer with payload" { - 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_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 438f05b563..82bfee7e1c 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -531,7 +531,6 @@ test "@tagName of @typeInfo" { } test "static eval list init" { - 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_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index f9571e9590..1b5ee24936 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -150,7 +150,6 @@ test "consistent size of packed structs" { test "correct sizeOf and offsets in packed structs" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - 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 @@ -221,7 +220,6 @@ test "correct sizeOf and offsets in packed structs" { test "nested packed structs" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - 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 @@ -270,7 +268,6 @@ test "nested packed structs" { test "regular in irregular packed struct" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; diff --git a/test/behavior/packed_struct_explicit_backing_int.zig b/test/behavior/packed_struct_explicit_backing_int.zig index 165e94fd4e..daed41a626 100644 --- a/test/behavior/packed_struct_explicit_backing_int.zig +++ b/test/behavior/packed_struct_explicit_backing_int.zig @@ -6,7 +6,6 @@ const native_endian = builtin.cpu.arch.endian(); test "packed struct explicit backing integer" { assert(builtin.zig_backend != .stage1); - 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/pointers.zig b/test/behavior/pointers.zig index d3fb839baa..05d2c7784c 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -157,7 +157,6 @@ test "implicit casting between C pointer and optional non-C pointer" { } test "implicit cast error unions with non-optional to optional pointer" { - 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 @@ -403,7 +402,6 @@ test "@ptrToInt on null optional at comptime" { } test "indexing array with sentinel returns correct type" { - 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 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index f29df6becf..f9c1aec1e4 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -339,7 +339,6 @@ fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { test "self-referencing struct via array member" { 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 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -978,7 +977,6 @@ test "comptime struct field" { } test "tuple element initialized with fn call" { - 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/switch.zig b/test/behavior/switch.zig index 9552ea5008..b852974240 100644 --- a/test/behavior/switch.zig +++ b/test/behavior/switch.zig @@ -420,7 +420,6 @@ test "else prong of switch on error set excludes other cases" { 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 - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -455,7 +454,6 @@ test "switch prongs with error set cases make a new error set type for capture v 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 - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -621,7 +619,6 @@ test "switch capture copies its payload" { 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 - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { diff --git a/test/behavior/translate_c_macros.zig b/test/behavior/translate_c_macros.zig index 94590d3159..6f5ccc737f 100644 --- a/test/behavior/translate_c_macros.zig +++ b/test/behavior/translate_c_macros.zig @@ -21,7 +21,6 @@ test "casting to void with a macro" { } test "initializer list expression" { - 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 @@ -48,7 +47,6 @@ test "reference to a struct type" { test "cast negative integer to pointer" { 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 @@ -93,7 +91,6 @@ test "casting or calling a value with a paren-surrounded macro" { test "nested comma operator" { 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 @@ -117,7 +114,6 @@ test "cast functions" { test "large integer macro" { 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/type.zig b/test/behavior/type.zig index 1b71d67df3..42cb67ba2f 100644 --- a/test/behavior/type.zig +++ b/test/behavior/type.zig @@ -200,7 +200,6 @@ test "Type.ErrorUnion" { test "Type.Opaque" { 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 @@ -350,7 +349,6 @@ test "Type.Struct" { } test "Type.Enum" { - 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 @@ -395,7 +393,6 @@ test "Type.Enum" { test "Type.Union" { 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/behavior/union.zig b/test/behavior/union.zig index 91dea304c7..2540d780a6 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1056,7 +1056,6 @@ test "containers with single-field enums" { } test "@unionInit on union with tag but no fields" { - 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 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO From 4765294ca4bd7dd54207892b199f3053702d9a2a Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 19 Oct 2022 07:56:02 -0400 Subject: [PATCH 32/47] 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. --- lib/include/zig.h | 7 +- lib/std/debug.zig | 8 +- lib/std/os/linux/x86_64.zig | 17 +- lib/std/start.zig | 158 +++++++------- lib/test_runner.zig | 3 +- src/codegen/c.zig | 312 +++++++++++++++++---------- src/main.zig | 5 +- test/behavior/bugs/3742.zig | 1 - test/behavior/fn.zig | 1 - test/behavior/struct.zig | 2 - test/behavior/translate_c_macros.zig | 1 - 11 files changed, 319 insertions(+), 196 deletions(-) diff --git a/lib/include/zig.h b/lib/include/zig.h index ee9e31496d..41b7406fa2 100644 --- a/lib/include/zig.h +++ b/lib/include/zig.h @@ -14,6 +14,12 @@ #define zig_threadlocal zig_threadlocal_unavailable #endif +#if defined(_MSC_VER) +#define ZIG_NAKED __declspec(naked) +#else +#define ZIG_NAKED __attribute__((naked)) +#endif + #if __GNUC__ #define ZIG_COLD __attribute__ ((cold)) #else @@ -165,7 +171,6 @@ #include #include #include -#include #define int128_t __int128 #define uint128_t unsigned __int128 diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 21b05249a1..de7aa07b8b 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1222,7 +1222,13 @@ pub const DebugInfo = struct { } pub fn getModuleForAddress(self: *DebugInfo, address: usize) !*ModuleDebugInfo { - if (comptime builtin.target.isDarwin()) { + if (builtin.zig_backend == .stage2_c) { + return @as(error{ + InvalidDebugInfo, + MissingDebugInfo, + UnsupportedBackend, + }, error.UnsupportedBackend); + } else if (comptime builtin.target.isDarwin()) { return self.lookupModuleDyld(address); } else if (native_os == .windows) { return self.lookupModuleWin32(address); diff --git a/lib/std/os/linux/x86_64.zig b/lib/std/os/linux/x86_64.zig index 010ecce054..7f320c010a 100644 --- a/lib/std/os/linux/x86_64.zig +++ b/lib/std/os/linux/x86_64.zig @@ -108,11 +108,18 @@ pub extern fn clone(func: CloneFn, stack: usize, flags: usize, arg: usize, ptid: pub const restore = restore_rt; pub fn restore_rt() callconv(.Naked) void { - return asm volatile ("syscall" - : - : [number] "{rax}" (@enumToInt(SYS.rt_sigreturn)), - : "rcx", "r11", "memory" - ); + switch (@import("builtin").zig_backend) { + .stage2_c => return asm volatile (std.fmt.comptimePrint( + \\ movl ${d}, %%eax + \\ syscall + \\ retq + , .{@enumToInt(SYS.rt_sigreturn)}) ::: "rcx", "r11", "memory"), + else => return asm volatile ("syscall" + : + : [number] "{rax}" (@enumToInt(SYS.rt_sigreturn)), + : "rcx", "r11", "memory" + ), + } } pub const mode_t = usize; diff --git a/lib/std/start.zig b/lib/std/start.zig index 9f70cce1ea..0563e2adad 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -23,7 +23,6 @@ comptime { // Until then, we have simplified logic here for self-hosted. TODO remove this once // self-hosted is capable enough to handle all of the real start.zig logic. if (builtin.zig_backend == .stage2_wasm or - builtin.zig_backend == .stage2_c or builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_x86 or builtin.zig_backend == .stage2_aarch64 or @@ -265,75 +264,90 @@ fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) callconv } fn _start() callconv(.Naked) noreturn { - switch (native_arch) { - .x86_64 => { - argc_argv_ptr = asm volatile ( - \\ xor %%rbp, %%rbp - : [argc] "={rsp}" (-> [*]usize), - ); + switch (builtin.zig_backend) { + .stage2_c => switch (native_arch) { + .x86_64 => { + @export(argc_argv_ptr, .{ .name = "argc_argv_ptr" }); + @export(posixCallMainAndExit, .{ .name = "_posixCallMainAndExit" }); + asm volatile ( + \\ xor %%rbp, %%rbp + \\ mov %%rsp, argc_argv_ptr + \\ call _posixCallMainAndExit + ); + unreachable; + }, + else => @compileError("unsupported arch"), }, - .i386 => { - argc_argv_ptr = asm volatile ( - \\ xor %%ebp, %%ebp - : [argc] "={esp}" (-> [*]usize), - ); + else => switch (native_arch) { + .x86_64 => { + argc_argv_ptr = asm volatile ( + \\ xor %%rbp, %%rbp + : [argc] "={rsp}" (-> [*]usize), + ); + }, + .i386 => { + argc_argv_ptr = asm volatile ( + \\ xor %%ebp, %%ebp + : [argc] "={esp}" (-> [*]usize), + ); + }, + .aarch64, .aarch64_be, .arm, .armeb, .thumb => { + argc_argv_ptr = asm volatile ( + \\ mov fp, #0 + \\ mov lr, #0 + : [argc] "={sp}" (-> [*]usize), + ); + }, + .riscv64 => { + argc_argv_ptr = asm volatile ( + \\ li s0, 0 + \\ li ra, 0 + : [argc] "={sp}" (-> [*]usize), + ); + }, + .mips, .mipsel => { + // The lr is already zeroed on entry, as specified by the ABI. + argc_argv_ptr = asm volatile ( + \\ move $fp, $0 + : [argc] "={sp}" (-> [*]usize), + ); + }, + .powerpc => { + // Setup the initial stack frame and clear the back chain pointer. + argc_argv_ptr = asm volatile ( + \\ mr 4, 1 + \\ li 0, 0 + \\ stwu 1,-16(1) + \\ stw 0, 0(1) + \\ mtlr 0 + : [argc] "={r4}" (-> [*]usize), + : + : "r0" + ); + }, + .powerpc64le => { + // Setup the initial stack frame and clear the back chain pointer. + // TODO: Support powerpc64 (big endian) on ELFv2. + argc_argv_ptr = asm volatile ( + \\ mr 4, 1 + \\ li 0, 0 + \\ stdu 0, -32(1) + \\ mtlr 0 + : [argc] "={r4}" (-> [*]usize), + : + : "r0" + ); + }, + .sparc64 => { + // argc is stored after a register window (16 registers) plus stack bias + argc_argv_ptr = asm ( + \\ mov %%g0, %%i6 + \\ add %%o6, 2175, %[argc] + : [argc] "=r" (-> [*]usize), + ); + }, + else => @compileError("unsupported arch"), }, - .aarch64, .aarch64_be, .arm, .armeb, .thumb => { - argc_argv_ptr = asm volatile ( - \\ mov fp, #0 - \\ mov lr, #0 - : [argc] "={sp}" (-> [*]usize), - ); - }, - .riscv64 => { - argc_argv_ptr = asm volatile ( - \\ li s0, 0 - \\ li ra, 0 - : [argc] "={sp}" (-> [*]usize), - ); - }, - .mips, .mipsel => { - // The lr is already zeroed on entry, as specified by the ABI. - argc_argv_ptr = asm volatile ( - \\ move $fp, $0 - : [argc] "={sp}" (-> [*]usize), - ); - }, - .powerpc => { - // Setup the initial stack frame and clear the back chain pointer. - argc_argv_ptr = asm volatile ( - \\ mr 4, 1 - \\ li 0, 0 - \\ stwu 1,-16(1) - \\ stw 0, 0(1) - \\ mtlr 0 - : [argc] "={r4}" (-> [*]usize), - : - : "r0" - ); - }, - .powerpc64le => { - // Setup the initial stack frame and clear the back chain pointer. - // TODO: Support powerpc64 (big endian) on ELFv2. - argc_argv_ptr = asm volatile ( - \\ mr 4, 1 - \\ li 0, 0 - \\ stdu 0, -32(1) - \\ mtlr 0 - : [argc] "={r4}" (-> [*]usize), - : - : "r0" - ); - }, - .sparc64 => { - // argc is stored after a register window (16 registers) plus stack bias - argc_argv_ptr = asm ( - \\ mov %%g0, %%i6 - \\ add %%o6, 2175, %[argc] - : [argc] "=r" (-> [*]usize), - ); - }, - else => @compileError("unsupported arch"), } // If LLVM inlines stack variables into _start, they will overwrite // the command line argument data. @@ -363,7 +377,7 @@ fn wWinMainCRTStartup() callconv(std.os.windows.WINAPI) noreturn { std.os.windows.kernel32.ExitProcess(@bitCast(std.os.windows.UINT, result)); } -fn posixCallMainAndExit() noreturn { +fn posixCallMainAndExit() callconv(.C) noreturn { @setAlignStack(16); const argc = argc_argv_ptr[0]; @@ -462,7 +476,7 @@ fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 { return initEventLoopAndCallMain(); } -fn main(c_argc: i32, c_argv: [*][*:0]u8, c_envp: [*:null]?[*:0]u8) callconv(.C) i32 { +fn main(c_argc: c_int, c_argv: [*c][*c]u8, c_envp: [*c][*c]u8) callconv(.C) c_int { var env_count: usize = 0; while (c_envp[env_count] != null) : (env_count += 1) {} 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) expandStackSize(phdrs); } - return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp }); + return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), @ptrCast([*][*:0]u8, c_argv), envp }); } -fn mainWithoutEnv(c_argc: i32, c_argv: [*][*:0]u8) callconv(.C) usize { +fn mainWithoutEnv(c_argc: c_int, c_argv: [*c][*c]u8) callconv(.C) c_int { std.os.argv = c_argv[0..@intCast(usize, c_argc)]; return @call(.{ .modifier = .always_inline }, callMain, .{}); } diff --git a/lib/test_runner.zig b/lib/test_runner.zig index aafaf1b073..079d9c813a 100644 --- a/lib/test_runner.zig +++ b/lib/test_runner.zig @@ -8,7 +8,8 @@ var log_err_count: usize = 0; pub fn main() void { if (builtin.zig_backend != .stage1 and - (builtin.zig_backend != .stage2_llvm or builtin.cpu.arch == .wasm32)) + (builtin.zig_backend != .stage2_llvm or builtin.cpu.arch == .wasm32) and + builtin.zig_backend != .stage2_c) { return main2() catch @panic("test failure"); } diff --git a/src/codegen/c.zig b/src/codegen/c.zig index c9e77d0df4..6b89d37dd9 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -34,8 +34,8 @@ pub const CValue = union(enum) { /// By-value decl: Decl.Index, decl_ref: Decl.Index, - /// An undefined (void *) pointer (cannot be dereferenced) - undefined_ptr: Type, + /// An undefined value (cannot be dereferenced) + undef: Type, /// Render the slice as an identifier (using fmtIdent) identifier: []const u8, /// Render these bytes literally. @@ -342,8 +342,9 @@ pub const Function = struct { const float_bits = float_ty.floatBits(target); const is_longdouble = float_bits == CType.longdouble.sizeInBits(target); const writer = f.object.writer(); - if (!is_longdouble and float_bits == 80) { - try writer.writeAll("__"); + try writer.writeAll("__"); + if (is_longdouble or float_bits != 80) { + try writer.writeAll("builtin_"); } try writer.writeAll(fn_name); if (is_longdouble) { @@ -410,10 +411,17 @@ pub const DeclGen = struct { const decl = dg.module.declPtr(decl_index); assert(decl.has_tv); + // Render an undefined pointer if we have a pointer to a zero-bit or comptime type. if (ty.isPtrAtRuntime() and !decl.ty.isFnOrHasRuntimeBits()) { - return dg.writeCValue(writer, CValue{ .undefined_ptr = ty }); + return dg.writeCValue(writer, CValue{ .undef = ty }); } + // Chase function values in order to be able to reference the original function. + inline for (.{ .function, .extern_fn }) |tag| + if (decl.val.castTag(tag)) |func| + if (func.data.owner_decl != decl_index) + return dg.renderDeclValue(writer, ty, val, func.data.owner_decl); + if (ty.isSlice()) { try writer.writeByte('('); try dg.renderTypecast(writer, ty); @@ -588,9 +596,9 @@ pub const DeclGen = struct { try writer.writeByte('('); try dg.renderTypecast(writer, ty); try writer.writeAll("){ .payload = "); - try dg.renderValue(writer, Type.bool, val, location); - try writer.writeAll(", .is_null = "); try dg.renderValue(writer, payload_ty, val, location); + try writer.writeAll(", .is_null = "); + try dg.renderValue(writer, Type.bool, val, location); return writer.writeAll(" }"); }, .Struct => { @@ -635,7 +643,7 @@ pub const DeclGen = struct { try writer.writeAll("){ .payload = "); try dg.renderValue(writer, ty.errorUnionPayload(), val, location); return writer.print(", .error = {x} }}", .{ - try dg.fmtIntLiteral(ty.errorUnionSet(), Value.undef), + try dg.fmtIntLiteral(ty.errorUnionSet(), val), }); }, .Array => { @@ -844,13 +852,13 @@ pub const DeclGen = struct { } }, .ErrorUnion => { - const error_type = ty.errorUnionSet(); - const payload_type = ty.errorUnionPayload(); + const error_ty = ty.errorUnionSet(); + const payload_ty = ty.errorUnionPayload(); - if (!payload_type.hasRuntimeBits()) { + if (!payload_ty.hasRuntimeBits()) { // We use the error type directly as the type. const err_val = if (val.errorUnionIsPayload()) Value.initTag(.zero) else val; - return dg.renderValue(writer, error_type, err_val, location); + return dg.renderValue(writer, error_ty, err_val, location); } try writer.writeByte('('); @@ -859,11 +867,15 @@ pub const DeclGen = struct { if (val.castTag(.eu_payload)) |pl| { const payload_val = pl.data; try writer.writeAll(" .payload = "); - try dg.renderValue(writer, payload_type, payload_val, location); - try writer.writeAll(", .error = 0 }"); + try dg.renderValue(writer, payload_ty, payload_val, location); + try writer.print(", .error = {} }}", .{ + try dg.fmtIntLiteral(error_ty, Value.zero), + }); } else { - try writer.writeAll(" .error = "); - try dg.renderValue(writer, error_type, val, location); + try writer.writeAll(" .payload = "); + try dg.renderValue(writer, payload_ty, Value.undef, location); + try writer.writeAll(", .error = "); + try dg.renderValue(writer, error_ty, val, location); try writer.writeAll(" }"); } }, @@ -987,13 +999,16 @@ pub const DeclGen = struct { if (!is_global) { try w.writeAll("static "); } + const fn_info = dg.decl.ty.fnInfo(); + if (fn_info.cc == .Naked) { + try w.writeAll("ZIG_NAKED "); + } if (dg.decl.val.castTag(.function)) |func_payload| { const func: *Module.Fn = func_payload.data; if (func.is_cold) { try w.writeAll("ZIG_COLD "); } } - const fn_info = dg.decl.ty.fnInfo(); if (fn_info.return_type.hasRuntimeBits()) { try dg.renderType(w, fn_info.return_type); } else if (fn_info.return_type.isError()) { @@ -1007,21 +1022,21 @@ pub const DeclGen = struct { try dg.renderDeclName(w, dg.decl_index); try w.writeByte('('); - var params_written: usize = 0; - for (fn_info.param_types) |param_type, index| { + var index: usize = 0; + for (fn_info.param_types) |param_type| { if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; - if (params_written > 0) { + if (index > 0) { try w.writeAll(", "); } const name = CValue{ .arg = index }; try dg.renderTypeAndName(w, param_type, name, .Mut, 0); - params_written += 1; + index += 1; } if (fn_info.is_var_args) { - if (params_written != 0) try w.writeAll(", "); + if (index > 0) try w.writeAll(", "); try w.writeAll("..."); - } else if (params_written == 0) { + } else if (index == 0) { try w.writeAll("void"); } try w.writeByte(')'); @@ -1307,7 +1322,7 @@ pub const DeclGen = struct { fn renderErrorUnionTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { const payload_ty = t.errorUnionPayload(); - const error_ty = t.errorUnionSet(); + assert(t.errorUnionSet().tag() == .anyerror); var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); defer buffer.deinit(); @@ -1328,15 +1343,7 @@ pub const DeclGen = struct { } const name_begin = buffer.items.len; - if (error_ty.castTag(.error_set_inferred)) |inf_err_set_payload| { - const func = inf_err_set_payload.data.func; - try bw.writeAll("zig_E_"); - try dg.renderDeclName(bw, func.owner_decl); - } else { - try bw.print("zig_E_{}_{}", .{ - typeToCIdentifier(error_ty, dg.module), typeToCIdentifier(payload_ty, dg.module), - }); - } + try bw.print("zig_E_{}", .{typeToCIdentifier(payload_ty, dg.module)}); const name_end = buffer.items.len; try bw.writeAll(";\n"); @@ -1505,11 +1512,11 @@ pub const DeclGen = struct { }, .Pointer => { if (t.isSlice()) { - var slice_ty_pl = Type.Payload.ElemType{ + var slice_pl = Type.Payload.ElemType{ .base = .{ .tag = if (t.ptrIsMutable()) .mut_slice else .const_slice }, .data = t.childType(), }; - const slice_ty = Type.initPayload(&slice_ty_pl.base); + const slice_ty = Type.initPayload(&slice_pl.base); const name = dg.getTypedefName(slice_ty) orelse try dg.renderSliceTypedef(slice_ty); @@ -1525,7 +1532,9 @@ pub const DeclGen = struct { } const child_ty = t.childType(); - if (t.isCPtr() and child_ty.eql(Type.u8, dg.module) and dg.decl.val.tag() == .extern_fn) { + if (t.isCPtr() and child_ty.eql(Type.u8, dg.module) and + (dg.decl.val.tag() == .extern_fn or std.mem.eql(u8, std.mem.span(dg.decl.name), "main"))) + { // This is a hack, since the c compiler expects a lot of external // library functions to have char pointers in their signatures, but // u8 and i8 produce unsigned char and signed char respectively, @@ -1543,11 +1552,11 @@ pub const DeclGen = struct { return w.writeAll(" *"); }, .Array => { - var array_ty_pl = Type.Payload.Array{ .base = .{ .tag = .array }, .data = .{ + var array_pl = Type.Payload.Array{ .base = .{ .tag = .array }, .data = .{ .len = t.arrayLenIncludingSentinel(), .elem_type = t.childType(), } }; - const array_ty = Type.initPayload(&array_ty_pl.base); + const array_ty = Type.initPayload(&array_pl.base); const name = dg.getTypedefName(array_ty) orelse try dg.renderArrayTypedef(array_ty); @@ -1582,8 +1591,13 @@ pub const DeclGen = struct { return dg.renderType(w, Type.anyerror); } - const name = dg.getTypedefName(t) orelse - try dg.renderErrorUnionTypedef(t); + var error_union_pl = Type.Payload.ErrorUnion{ + .data = .{ .error_set = Type.anyerror, .payload = payload_ty }, + }; + const error_union_ty = Type.initPayload(&error_union_pl.base); + + const name = dg.getTypedefName(error_union_ty) orelse + try dg.renderErrorUnionTypedef(error_union_ty); return w.writeAll(name); }, @@ -1603,8 +1617,8 @@ pub const DeclGen = struct { }, .Enum => { // For enums, we simply use the integer tag type. - var int_tag_ty_buffer: Type.Payload.Bits = undefined; - const int_tag_ty = t.intTagType(&int_tag_ty_buffer); + var int_tag_buf: Type.Payload.Bits = undefined; + const int_tag_ty = t.intTagType(&int_tag_buf); try dg.renderType(w, int_tag_ty); }, @@ -1801,11 +1815,7 @@ pub const DeclGen = struct { try w.writeByte('&'); return dg.renderDeclName(w, decl); }, - .undefined_ptr => |ty| { - try w.writeAll("(("); - try dg.renderTypecast(w, ty); - return w.print("){x})", .{try dg.fmtIntLiteral(Type.usize, Value.undef)}); - }, + .undef => |ty| return dg.renderValue(w, ty, Value.undef, .Other), .identifier => |ident| return w.print("{ }", .{fmtIdent(ident)}), .bytes => |bytes| return w.writeAll(bytes), } @@ -1824,7 +1834,7 @@ pub const DeclGen = struct { return w.writeByte(')'); }, .decl_ref => |decl| return dg.renderDeclName(w, decl), - .undefined_ptr => unreachable, + .undef => unreachable, .identifier => |ident| return w.print("(*{ })", .{fmtIdent(ident)}), .bytes => |bytes| { try w.writeAll("(*"); @@ -2151,10 +2161,10 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .mul_add => try airMulAdd(f, inst), - .add_with_overflow => try airOverflow(f, inst, "addo_"), - .sub_with_overflow => try airOverflow(f, inst, "subo_"), - .mul_with_overflow => try airOverflow(f, inst, "mulo_"), - .shl_with_overflow => try airOverflow(f, inst, "shlo_"), + .add_with_overflow => try airOverflow(f, inst, "addo_", .range), + .sub_with_overflow => try airOverflow(f, inst, "subo_", .range), + .mul_with_overflow => try airOverflow(f, inst, "mulo_", .range), + .shl_with_overflow => try airOverflow(f, inst, "shlo_", .bits), .min => try airMinMax(f, inst, "<"), .max => try airMinMax(f, inst, ">"), @@ -2284,8 +2294,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .slice_ptr => try airSliceField(f, inst, " = ", ".ptr;\n"), .slice_len => try airSliceField(f, inst, " = ", ".len;\n"), - .ptr_slice_len_ptr => try airSliceField(f, inst, " = &", ".len;\n"), - .ptr_slice_ptr_ptr => try airSliceField(f, inst, " = &", ".ptr;\n"), + .ptr_slice_len_ptr => try airSliceField(f, inst, " = &", "->len;\n"), + .ptr_slice_ptr_ptr => try airSliceField(f, inst, " = &", "->ptr;\n"), .ptr_elem_val => try airPtrElemVal(f, inst), .ptr_elem_ptr => try airPtrElemPtr(f, inst), @@ -2459,7 +2469,7 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { const elem_type = inst_ty.elemType(); const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut; if (!elem_type.isFnOrHasRuntimeBitsIgnoreComptime()) { - return CValue{ .undefined_ptr = inst_ty }; + return CValue{ .undef = inst_ty }; } const target = f.object.dg.module.getTarget(); @@ -2474,9 +2484,13 @@ fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const inst_ty = f.air.typeOfIndex(inst); + const elem_ty = inst_ty.elemType(); + if (!elem_ty.hasRuntimeBitsIgnoreComptime()) { + return CValue{ .undef = inst_ty }; + } + // First line: the variable used as data storage. - const elem_type = inst_ty.elemType(); - const local = try f.allocLocal(elem_type, .Mut); + const local = try f.allocLocal(elem_ty, .Mut); try writer.writeAll(";\n"); return CValue{ .local_ref = local.local }; @@ -2507,8 +2521,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { if (is_array) { // Insert a memcpy to initialize this array. The source operand is always a pointer // and thus we only need to know size/type information from the local type/dest. - try writer.writeByte(';'); - try f.object.indent_writer.insertNewline(); + try writer.writeAll(";\n"); try writer.writeAll("memcpy("); try f.writeCValue(writer, local); try writer.writeAll(", "); @@ -2535,7 +2548,8 @@ fn airRet(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(";\n"); } else if (ret_ty.isError()) { try writer.writeAll("return 0;"); - } else { + } else if (f.object.dg.decl.ty.fnCallingConvention() != .Naked) { + // Not even allowed to return void in a naked function. try writer.writeAll("return;\n"); } return CValue.none; @@ -2847,7 +2861,7 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue { return ret; } -fn airOverflow(f: *Function, inst: Air.Inst.Index, op_abbrev: [*:0]const u8) !CValue { +fn airOverflow(f: *Function, inst: Air.Inst.Index, op_abbrev: [*:0]const u8, kind: enum { range, bits }) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; @@ -2879,23 +2893,29 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, op_abbrev: [*:0]const u8) !CV try w.writeAll(", &"); try f.writeCValue(w, ret); try w.writeAll(".field_0, "); - { - var arena = std.heap.ArenaAllocator.init(f.object.dg.module.gpa); - defer arena.deinit(); + switch (kind) { + .range => { + var arena = std.heap.ArenaAllocator.init(f.object.dg.module.gpa); + defer arena.deinit(); - const expected_contents = union { u: Value.Payload.U64, i: Value.Payload.I64 }; - var stack align(@alignOf(expected_contents)) = - std.heap.stackFallback(@sizeOf(expected_contents), arena.allocator()); + const expected_contents = union { u: Value.Payload.U64, i: Value.Payload.I64 }; + var stack align(@alignOf(expected_contents)) = + std.heap.stackFallback(@sizeOf(expected_contents), arena.allocator()); - if (int_info.signedness == .signed) { - const min_val = try scalar_ty.minInt(stack.get(), target); - try w.print("{}, ", .{try f.fmtIntLiteral(scalar_ty, min_val)}); - } + if (int_info.signedness == .signed) { + const min_val = try scalar_ty.minInt(stack.get(), target); + try w.print("{}, ", .{try f.fmtIntLiteral(scalar_ty, min_val)}); + } - const max_val = try scalar_ty.maxInt(stack.get(), target); - try w.print("{});", .{try f.fmtIntLiteral(scalar_ty, max_val)}); + const max_val = try scalar_ty.maxInt(stack.get(), target); + try w.print("{});\n", .{try f.fmtIntLiteral(scalar_ty, max_val)}); + }, + .bits => { + var bits_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = int_info.bits }; + const bits_val = Value.initPayload(&bits_pl.base); + try w.print("{x});\n", .{try f.fmtIntLiteral(Type.u8, bits_val)}); + }, } - try f.object.indent_writer.insertNewline(); return ret; } @@ -3075,6 +3095,9 @@ fn airCall( inst: Air.Inst.Index, modifier: std.builtin.CallOptions.Modifier, ) !CValue { + // Not even allowed to call panic in a naked function. + if (f.object.dg.decl.ty.fnCallingConvention() == .Naked) return .none; + switch (modifier) { .auto => {}, .always_tail => return f.fail("TODO: C backend: call with always_tail attribute", .{}), @@ -3094,10 +3117,10 @@ fn airCall( const writer = f.object.writer(); const result_local: CValue = r: { - if (f.liveness.isUnused(inst)) { - if (loweredFnRetTyHasBits(fn_ty)) { - try writer.print("(void)", .{}); - } + if (!loweredFnRetTyHasBits(fn_ty)) { + break :r .none; + } else if (f.liveness.isUnused(inst)) { + try writer.print("(void)", .{}); break :r .none; } else { const local = try f.allocLocal(fn_ty.fnReturnType(), .Const); @@ -3107,6 +3130,7 @@ fn airCall( }; var is_extern = false; + var name: [*:0]const u8 = ""; callee: { known: { const fn_decl = fn_decl: { @@ -3121,6 +3145,7 @@ fn airCall( else => break :known, }; }; + name = f.object.dg.module.declPtr(fn_decl).name; try f.object.dg.renderDeclName(writer, fn_decl); break :callee; } @@ -3137,7 +3162,9 @@ fn airCall( if (args_written != 0) { try writer.writeAll(", "); } - if (is_extern and ty.isCPtr() and ty.childType().tag() == .u8) { + if ((is_extern or std.mem.eql(u8, std.mem.span(name), "main")) and + ty.isCPtr() and ty.childType().tag() == .u8) + { // Corresponds with hack in renderType .Pointer case. try writer.writeAll("(char"); if (ty.isConstPtr()) try writer.writeAll(" const"); @@ -3286,15 +3313,27 @@ fn lowerTry( } } - const local = try f.allocLocal(result_ty, .Const); - if (operand_is_ptr or isByRef(payload_ty)) { - try writer.writeAll(" = &"); + const is_array = payload_ty.zigTypeTag() == .Array; + const local = try f.allocLocal(result_ty, if (is_array) .Mut else .Const); + if (is_array) { + try writer.writeAll(";\n"); + try writer.writeAll("memcpy("); + try f.writeCValue(writer, local); + try writer.writeAll(", "); try f.writeCValue(writer, err_union); - try writer.writeAll("->payload;\n"); + try writer.writeAll(".payload, sizeof("); + try f.writeCValue(writer, local); + try writer.writeAll("));\n"); } else { - try writer.writeAll(" = "); - try f.writeCValue(writer, err_union); - try writer.writeAll(".payload;\n"); + if (operand_is_ptr or isByRef(payload_ty)) { + try writer.writeAll(" = &"); + try f.writeCValue(writer, err_union); + try writer.writeAll("->payload;\n"); + } else { + try writer.writeAll(" = "); + try f.writeCValue(writer, err_union); + try writer.writeAll(".payload;\n"); + } } return local; } @@ -3361,15 +3400,21 @@ fn airBreakpoint(f: *Function) !CValue { fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; + const writer = f.object.writer(); const local = try f.allocLocal(Type.usize, .Const); - try f.object.writer().writeAll(" = zig_return_address();\n"); + try writer.writeAll(" = ("); + try f.renderTypecast(writer, Type.usize); + try writer.writeAll(")zig_return_address();\n"); return local; } fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; + const writer = f.object.writer(); const local = try f.allocLocal(Type.usize, .Const); - try f.object.writer().writeAll(" = zig_frame_address();\n"); + try writer.writeAll(" = ("); + try f.renderTypecast(writer, Type.usize); + try writer.writeAll(")zig_frame_address();\n"); return local; } @@ -3385,6 +3430,9 @@ fn airFence(f: *Function, inst: Air.Inst.Index) !CValue { } fn airUnreach(f: *Function) !CValue { + // Not even allowed to call unreachable in a naked function. + if (f.object.dg.decl.ty.fnCallingConvention() == .Naked) return .none; + try f.object.writer().writeAll("zig_unreachable();\n"); return CValue.none; } @@ -3479,15 +3527,12 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const local = try f.allocLocal(inst_ty, .Mut); if (f.wantSafety()) { try writer.writeAll(" = "); - try f.object.dg.renderValue(writer, inst_ty, Value.undef, .Other); + try f.writeCValue(writer, .{ .undef = inst_ty }); } try writer.writeAll(";\n"); break :local local; } else .none; - try writer.writeAll("{\n"); - f.object.indent_writer.pushIndent(); - const output_locals_begin = f.next_local_index; f.next_local_index += outputs.len; const constraints_extra_begin = extra_i; @@ -3510,7 +3555,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll("\")"); if (f.wantSafety()) { try writer.writeAll(" = "); - try f.object.dg.renderValue(writer, output_ty, Value.undef, .Other); + try f.writeCValue(writer, .{ .undef = output_ty }); } try writer.writeAll(";\n"); } else if (constraint.len < 2 or constraint[0] != '=') { @@ -3605,7 +3650,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { } else { const input_val = try f.resolveInst(input); try writer.print("{s}(", .{fmtStringLiteral(constraint)}); - try f.writeCValue(writer, if (input_val == .constant) .{ + try f.writeCValue(writer, if (input_val == .constant) CValue{ .local = input_locals_begin + index, } else input_val); } @@ -3647,9 +3692,6 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { } } - f.object.indent_writer.popIndent(); - try writer.writeAll("}\n"); - return local; } @@ -3728,7 +3770,7 @@ fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.air.typeOfIndex(inst); if (!inst_ty.childType().hasRuntimeBitsIgnoreComptime()) { - return CValue{ .undefined_ptr = inst_ty }; + return CValue{ .undef = inst_ty }; } if (opt_ty.optionalReprIsPayload()) { @@ -3996,7 +4038,9 @@ fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { } const local = try f.allocLocal(err_un_ty, .Const); - try writer.writeAll(" = { .error = "); + try writer.writeAll(" = { .payload = "); + try f.writeCValue(writer, .{ .undef = payload_ty }); + try writer.writeAll(", .error = "); try f.writeCValue(writer, operand); try writer.writeAll(" };\n"); return local; @@ -4059,10 +4103,25 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(ty_op.operand); const inst_ty = f.air.typeOfIndex(inst); - const local = try f.allocLocal(inst_ty, .Const); - try writer.writeAll(" = { .error = 0, .payload = "); - try f.writeCValue(writer, operand); - try writer.writeAll(" };\n"); + const payload_ty = inst_ty.errorUnionPayload(); + const is_array = payload_ty.zigTypeTag() == .Array; + const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const); + try writer.writeAll(" = { .payload = "); + try f.writeCValue(writer, if (is_array) CValue{ .undef = payload_ty } else operand); + try writer.print(", .error = {} }};\n", .{ + try f.fmtIntLiteral(inst_ty.errorUnionSet(), Value.zero), + }); + + if (is_array) { + try writer.writeAll("memcpy("); + try f.writeCValue(writer, local); + try writer.writeAll(".payload, "); + try f.writeCValue(writer, operand); + try writer.writeAll(", sizeof("); + try f.writeCValue(writer, local); + try writer.writeAll(".payload));\n"); + } + return local; } @@ -4114,11 +4173,11 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { const array_len = f.air.typeOf(ty_op.operand).elemType().arrayLen(); try writer.writeAll(" = { .ptr = "); - if (operand == .undefined_ptr) { + if (operand == .undef) { // Unfortunately, C does not support any equivalent to // &(*(void *)p)[0], although LLVM does via GetElementPtr var buf: Type.SlicePtrFieldTypeBuffer = undefined; - try f.writeCValue(writer, CValue{ .undefined_ptr = inst_ty.slicePtrFieldType(&buf) }); + try f.writeCValue(writer, CValue{ .undef = inst_ty.slicePtrFieldType(&buf) }); } else { try writer.writeAll("&("); try f.writeCValueDeref(writer, operand); @@ -4528,9 +4587,12 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const len = @intCast(usize, inst_ty.arrayLen()); const elements = @ptrCast([]const Air.Inst.Ref, f.air.extra[ty_pl.payload..][0..len]); + const mutability: Mutability = for (elements) |element| { + if (f.air.typeOf(element).zigTypeTag() == .Array) break .Mut; + } else .Const; const writer = f.object.writer(); - const local = try f.allocLocal(inst_ty, .Const); + const local = try f.allocLocal(inst_ty, mutability); try writer.writeAll(" = {"); switch (inst_ty.zigTypeTag()) { .Array => { @@ -4547,6 +4609,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { empty = false; } if (empty) try writer.print("{}", .{try f.fmtIntLiteral(Type.u8, Value.zero)}); + try writer.writeAll("};\n"); }, .Struct => { var empty = true; @@ -4557,15 +4620,44 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { if (!inst_ty.isTupleOrAnonStruct()) { try writer.print(".{ } = ", .{fmtIdent(inst_ty.structFieldName(index))}); } - try f.writeCValue(writer, try f.resolveInst(element)); + + const element_ty = f.air.typeOf(element); + try f.writeCValue(writer, switch (element_ty.zigTypeTag()) { + .Array => CValue{ .undef = element_ty }, + else => try f.resolveInst(element), + }); empty = false; } if (empty) try writer.print("{}", .{try f.fmtIntLiteral(Type.u8, Value.zero)}); + try writer.writeAll("};\n"); + + 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_storage: ?[]u8 = null; + defer if (field_name_storage) |storage| f.object.dg.gpa.free(storage); + const field_name = if (inst_ty.isTupleOrAnonStruct()) field_name: { + const name = try std.fmt.allocPrint(f.object.dg.gpa, "field_{d}", .{index}); + field_name_storage = name; + break :field_name name; + } else inst_ty.structFieldName(index); + + try writer.writeAll(";\n"); + try writer.writeAll("memcpy("); + try f.writeCValue(writer, local); + try writer.print(".{ }, ", .{fmtIdent(field_name)}); + try f.writeCValue(writer, try f.resolveInst(element)); + try writer.writeAll(", sizeof("); + try f.writeCValue(writer, local); + try writer.print(".{ }));\n", .{fmtIdent(field_name)}); + } }, .Vector => return f.fail("TODO: C backend: implement airAggregateInit for vectors", .{}), else => unreachable, } - try writer.writeAll("};\n"); return local; } @@ -4732,8 +4824,8 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 { return switch (order) { - .Unordered => "memory_order_relaxed", - .Monotonic => "memory_order_consume", + // Note: unordered is actually even less atomic than relaxed + .Unordered, .Monotonic => "memory_order_relaxed", .Acquire => "memory_order_acquire", .Release => "memory_order_release", .AcqRel => "memory_order_acq_rel", diff --git a/src/main.zig b/src/main.zig index 2885cdba95..6bde852eee 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3018,7 +3018,10 @@ fn buildOutputType( const c_code_path = try fs.path.join(arena, &[_][]const u8{ c_code_directory.path orelse ".", c_code_loc.basename, }); - try test_exec_args.appendSlice(&.{ self_exe_path, "run", "-lc", c_code_path }); + try test_exec_args.append(self_exe_path); + try test_exec_args.append("run"); + if (link_libc) try test_exec_args.append("-lc"); + try test_exec_args.append(c_code_path); } const run_or_test = switch (arg_mode) { diff --git a/test/behavior/bugs/3742.zig b/test/behavior/bugs/3742.zig index 4ade6895f8..2d28e59f4d 100644 --- a/test/behavior/bugs/3742.zig +++ b/test/behavior/bugs/3742.zig @@ -38,6 +38,5 @@ test "fixed" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; ArgSerializer.serializeCommand(GET.init("banana")); } diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index 22935feaa1..ad92cb8962 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -272,7 +272,6 @@ test "void parameters" { 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; // TODO try voidFun(1, void{}, 2, {}); } fn voidFun(a: i32, b: void, c: i32, d: void) !void { diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index f9c1aec1e4..a6faee9858 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -1163,7 +1163,6 @@ test "for loop over pointers to struct, getting field from struct pointer" { test "anon init through error unions and optionals" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - 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_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -1234,7 +1233,6 @@ test "anon init through error union" { test "typed init through error unions and optionals" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - 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_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/translate_c_macros.zig b/test/behavior/translate_c_macros.zig index 6f5ccc737f..07d8442844 100644 --- a/test/behavior/translate_c_macros.zig +++ b/test/behavior/translate_c_macros.zig @@ -100,7 +100,6 @@ test "nested comma operator" { test "cast functions" { 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 From 4fdac5f1c9cad90450cbad5a8dd276a597aba870 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 19 Oct 2022 23:18:46 -0400 Subject: [PATCH 33/47] cbe: fix C syntax when rendering initializers --- src/codegen/c.zig | 878 +++++++++++++++++++++++----------------------- 1 file changed, 448 insertions(+), 430 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 6b89d37dd9..b5705ad6da 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -63,6 +63,7 @@ const FormatTypeAsCIdentContext = struct { const ValueRenderLocation = enum { FunctionArgument, + Initializer, Other, }; @@ -256,7 +257,7 @@ pub const Function = struct { 0, ); try writer.writeAll(" = "); - try f.object.dg.renderValue(writer, ty, val, .Other); + try f.object.dg.renderValue(writer, ty, val, .Initializer); try writer.writeAll(";\n "); return decl_c_value; }, @@ -297,13 +298,14 @@ pub const Function = struct { return local_value; } - fn writeCValue(f: *Function, w: anytype, c_value: CValue) !void { + fn writeCValue(f: *Function, w: anytype, c_value: CValue, location: ValueRenderLocation) !void { switch (c_value) { .constant => |inst| { const ty = f.air.typeOf(inst); const val = f.air.value(inst).?; - return f.object.dg.renderValue(w, ty, val, .Other); + return f.object.dg.renderValue(w, ty, val, location); }, + .undef => |ty| return f.object.dg.renderValue(w, ty, Value.undef, location), else => return f.object.dg.writeCValue(w, c_value), } } @@ -425,13 +427,17 @@ pub const DeclGen = struct { if (ty.isSlice()) { try writer.writeByte('('); try dg.renderTypecast(writer, ty); - try writer.writeAll("){"); + try writer.writeAll("){ .ptr = "); + var buf: Type.SlicePtrFieldTypeBuffer = undefined; - try dg.renderValue(writer, ty.slicePtrFieldType(&buf), val.slicePtr(), .Other); - try writer.writeAll(", "); - try writer.print("{d}", .{val.sliceLen(dg.module)}); - try writer.writeByte('}'); - return; + try dg.renderValue(writer, ty.slicePtrFieldType(&buf), val.slicePtr(), .Initializer); + + var len_pl: Value.Payload.U64 = .{ + .base = .{ .tag = .int_u64 }, + .data = val.sliceLen(dg.module), + }; + const len_val = Value.initPayload(&len_pl.base); + return writer.print(", .len = {} }}", .{try dg.fmtIntLiteral(Type.usize, len_val)}); } // We shouldn't cast C function pointers as this is UB (when you call @@ -567,9 +573,13 @@ pub const DeclGen = struct { }, .Pointer => switch (ty.ptrSize()) { .Slice => { - try writer.writeByte('('); - try dg.renderTypecast(writer, ty); - try writer.writeAll("){("); + if (location != .Initializer) { + try writer.writeByte('('); + try dg.renderTypecast(writer, ty); + try writer.writeByte(')'); + } + + try writer.writeAll("{("); var buf: Type.SlicePtrFieldTypeBuffer = undefined; const ptr_ty = ty.slicePtrFieldType(&buf); try dg.renderTypecast(writer, ptr_ty); @@ -593,69 +603,86 @@ pub const DeclGen = struct { return dg.renderValue(writer, payload_ty, val, location); } - try writer.writeByte('('); - try dg.renderTypecast(writer, ty); - try writer.writeAll("){ .payload = "); - try dg.renderValue(writer, payload_ty, val, location); + if (location != .Initializer) { + try writer.writeByte('('); + try dg.renderTypecast(writer, ty); + try writer.writeByte(')'); + } + + try writer.writeAll("{ .payload = "); + try dg.renderValue(writer, payload_ty, val, .Initializer); try writer.writeAll(", .is_null = "); - try dg.renderValue(writer, Type.bool, val, location); + try dg.renderValue(writer, Type.bool, val, .Initializer); return writer.writeAll(" }"); }, .Struct => { - try writer.writeByte('('); - try dg.renderTypecast(writer, ty); - try writer.writeAll("){"); + if (location != .Initializer) { + try writer.writeByte('('); + try dg.renderTypecast(writer, ty); + try writer.writeByte(')'); + } + try writer.writeByte('{'); var empty = true; for (ty.structFields().values()) |field| { if (!field.ty.hasRuntimeBits()) continue; if (!empty) try writer.writeByte(','); - try dg.renderValue(writer, field.ty, val, location); + try dg.renderValue(writer, field.ty, val, .Initializer); empty = false; } if (empty) try writer.print("{x}", .{try dg.fmtIntLiteral(Type.u8, Value.undef)}); - return writer.writeByte('}'); }, .Union => { - try writer.writeByte('('); - try dg.renderTypecast(writer, ty); - try writer.writeAll("){"); + if (location != .Initializer) { + try writer.writeByte('('); + try dg.renderTypecast(writer, ty); + try writer.writeByte(')'); + } + try writer.writeByte('{'); if (ty.unionTagTypeSafety()) |tag_ty| { - try writer.writeAll(".tag = "); - try dg.renderValue(writer, tag_ty, val, location); + try writer.writeAll(" .tag = "); + try dg.renderValue(writer, tag_ty, val, .Initializer); try writer.writeAll(", .payload = {"); } for (ty.unionFields().values()) |field| { if (!field.ty.hasRuntimeBits()) continue; - try dg.renderValue(writer, field.ty, val, location); + try dg.renderValue(writer, field.ty, val, .Initializer); break; } else try writer.print("{x}", .{try dg.fmtIntLiteral(Type.u8, Value.undef)}); if (ty.unionTagTypeSafety()) |_| try writer.writeByte('}'); return writer.writeByte('}'); }, .ErrorUnion => { - try writer.writeByte('('); - try dg.renderTypecast(writer, ty); - try writer.writeAll("){ .payload = "); - try dg.renderValue(writer, ty.errorUnionPayload(), val, location); + if (location != .Initializer) { + try writer.writeByte('('); + try dg.renderTypecast(writer, ty); + try writer.writeByte(')'); + } + + try writer.writeAll("{ .payload = "); + try dg.renderValue(writer, ty.errorUnionPayload(), val, .Initializer); return writer.print(", .error = {x} }}", .{ try dg.fmtIntLiteral(ty.errorUnionSet(), val), }); }, .Array => { - try writer.writeByte('{'); + if (location != .Initializer) { + try writer.writeByte('('); + try dg.renderTypecast(writer, ty); + try writer.writeByte(')'); + } + try writer.writeByte('{'); const c_len = ty.arrayLenIncludingSentinel(); var index: usize = 0; while (index < c_len) : (index += 1) { if (index > 0) try writer.writeAll(", "); - try dg.renderValue(writer, ty.childType(), val, location); + try dg.renderValue(writer, ty.childType(), val, .Initializer); } - return writer.writeByte('}'); }, .ComptimeInt, @@ -696,21 +723,21 @@ pub const DeclGen = struct { // just generate a bit cast (exactly like we do in airBitcast) switch (ty.tag()) { .f32 => { - var bitcast_val_pl = Value.Payload.U64{ + var bitcast_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = @bitCast(u32, val.toFloat(f32)), }; - const bitcast_val = Value.initPayload(&bitcast_val_pl.base); + const bitcast_val = Value.initPayload(&bitcast_pl.base); return writer.print("zig_bitcast_f32_u32({x})", .{ try dg.fmtIntLiteral(Type.u32, bitcast_val), }); }, .f64 => { - var bitcast_val_pl = Value.Payload.U64{ + var bitcast_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = @bitCast(u64, val.toFloat(f64)), }; - const bitcast_val = Value.initPayload(&bitcast_val_pl.base); + const bitcast_val = Value.initPayload(&bitcast_pl.base); return writer.print("zig_bitcast_f64_u64({x})", .{ try dg.fmtIntLiteral(Type.u64, bitcast_val), }); @@ -737,12 +764,16 @@ pub const DeclGen = struct { const slice = val.castTag(.slice).?.data; var buf: Type.SlicePtrFieldTypeBuffer = undefined; - try writer.writeByte('('); - try dg.renderTypecast(writer, ty); - try writer.writeAll("){"); - try dg.renderValue(writer, ty.slicePtrFieldType(&buf), slice.ptr, location); + if (location != .Initializer) { + try writer.writeByte('('); + try dg.renderTypecast(writer, ty); + try writer.writeByte(')'); + } + + try writer.writeByte('{'); + try dg.renderValue(writer, ty.slicePtrFieldType(&buf), slice.ptr, .Initializer); try writer.writeAll(", "); - try dg.renderValue(writer, Type.usize, slice.len, location); + try dg.renderValue(writer, Type.usize, slice.len, .Initializer); try writer.writeByte('}'); }, .function => { @@ -768,13 +799,19 @@ pub const DeclGen = struct { else => unreachable, }, .Array => { + if (location == .FunctionArgument) { + try writer.writeByte('('); + try dg.renderTypecast(writer, ty); + try writer.writeByte(')'); + } + // First try specific tag representations for more efficiency. switch (val.tag()) { .undef, .empty_struct_value, .empty_array => { try writer.writeByte('{'); const ai = ty.arrayInfo(); if (ai.sentinel) |s| { - try dg.renderValue(writer, ai.elem_type, s, location); + try dg.renderValue(writer, ai.elem_type, s, .Initializer); } else { try writer.writeByte('0'); } @@ -786,23 +823,17 @@ pub const DeclGen = struct { defer arena.deinit(); const arena_allocator = arena.allocator(); - if (location == .FunctionArgument) { - try writer.writeByte('('); - try dg.renderTypecast(writer, ty); - try writer.writeByte(')'); - } - try writer.writeByte('{'); const ai = ty.arrayInfo(); var index: usize = 0; while (index < ai.len) : (index += 1) { if (index != 0) try writer.writeByte(','); const elem_val = try val.elemValue(dg.module, arena_allocator, index); - try dg.renderValue(writer, ai.elem_type, elem_val, .Other); + try dg.renderValue(writer, ai.elem_type, elem_val, .Initializer); } if (ai.sentinel) |s| { if (index != 0) try writer.writeByte(','); - try dg.renderValue(writer, ai.elem_type, s, .Other); + try dg.renderValue(writer, ai.elem_type, s, .Initializer); } try writer.writeByte('}'); }, @@ -823,19 +854,17 @@ pub const DeclGen = struct { return dg.renderValue(writer, payload_ty, payload_val, location); } - try writer.writeByte('('); - try dg.renderTypecast(writer, ty); - try writer.writeAll("){"); - if (val.castTag(.opt_payload)) |pl| { - const payload_val = pl.data; - try writer.writeAll(" .payload = "); - try dg.renderValue(writer, payload_ty, payload_val, location); - try writer.writeAll(", .is_null = false }"); - } else { - try writer.writeAll(" .payload = "); - try dg.renderValue(writer, payload_ty, Value.undef, location); - try writer.writeAll(", .is_null = true }"); + if (location != .Initializer) { + try writer.writeByte('('); + try dg.renderTypecast(writer, ty); + try writer.writeByte(')'); } + + const payload_val = if (val.castTag(.opt_payload)) |pl| pl.data else Value.undef; + + try writer.writeAll("{ .payload = "); + try dg.renderValue(writer, payload_ty, payload_val, .Initializer); + try writer.print(", .is_null = {} }}", .{val.tag() == .null_value}); }, .ErrorSet => { switch (val.tag()) { @@ -861,23 +890,20 @@ pub const DeclGen = struct { return dg.renderValue(writer, error_ty, err_val, location); } - try writer.writeByte('('); - try dg.renderTypecast(writer, ty); - try writer.writeAll("){"); - if (val.castTag(.eu_payload)) |pl| { - const payload_val = pl.data; - try writer.writeAll(" .payload = "); - try dg.renderValue(writer, payload_ty, payload_val, location); - try writer.print(", .error = {} }}", .{ - try dg.fmtIntLiteral(error_ty, Value.zero), - }); - } else { - try writer.writeAll(" .payload = "); - try dg.renderValue(writer, payload_ty, Value.undef, location); - try writer.writeAll(", .error = "); - try dg.renderValue(writer, error_ty, val, location); - try writer.writeAll(" }"); + if (location != .Initializer) { + try writer.writeByte('('); + try dg.renderTypecast(writer, ty); + try writer.writeByte(')'); } + + const payload_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.undef; + const error_val = if (val.tag() == .eu_payload) Value.zero else val; + + try writer.writeAll("{ .payload = "); + try dg.renderValue(writer, payload_ty, payload_val, .Initializer); + try writer.writeAll(", .error = "); + try dg.renderValue(writer, error_ty, error_val, .Initializer); + try writer.writeAll(" }"); }, .Enum => { switch (val.tag()) { @@ -927,36 +953,41 @@ pub const DeclGen = struct { .Struct => { const field_vals = val.castTag(.aggregate).?.data; - try writer.writeByte('('); - try dg.renderTypecast(writer, ty); - try writer.writeAll("){"); + if (location != .Initializer) { + try writer.writeByte('('); + try dg.renderTypecast(writer, ty); + try writer.writeByte(')'); + } + try writer.writeByte('{'); var empty = true; for (field_vals) |field_val, field_index| { const field_ty = ty.structFieldType(field_index); if (!field_ty.hasRuntimeBits()) continue; if (!empty) try writer.writeByte(','); - try dg.renderValue(writer, field_ty, field_val, location); + try dg.renderValue(writer, field_ty, field_val, .Initializer); empty = false; } if (empty) try writer.print("{}", .{try dg.fmtIntLiteral(Type.u8, Value.zero)}); - try writer.writeByte('}'); }, .Union => { const union_obj = val.castTag(.@"union").?.data; const layout = ty.unionGetLayout(target); - try writer.writeByte('('); - try dg.renderTypecast(writer, ty); - try writer.writeAll("){"); + if (location != .Initializer) { + try writer.writeByte('('); + try dg.renderTypecast(writer, ty); + try writer.writeByte(')'); + } + try writer.writeByte('{'); if (ty.unionTagTypeSafety()) |tag_ty| { if (layout.tag_size != 0) { try writer.writeAll(".tag = "); - try dg.renderValue(writer, tag_ty, union_obj.tag, location); + try dg.renderValue(writer, tag_ty, union_obj.tag, .Initializer); try writer.writeAll(", "); } try writer.writeAll(".payload = {"); @@ -967,11 +998,9 @@ pub const DeclGen = struct { const field_name = ty.unionFields().keys()[index]; if (field_ty.hasRuntimeBits()) { try writer.print(".{ } = ", .{fmtIdent(field_name)}); - try dg.renderValue(writer, field_ty, union_obj.val, location); + try dg.renderValue(writer, field_ty, union_obj.val, .Initializer); } else try writer.writeByte('0'); - if (ty.unionTagTypeSafety()) |_| { - try writer.writeByte('}'); - } + if (ty.unionTagTypeSafety()) |_| try writer.writeByte('}'); try writer.writeByte('}'); }, @@ -1029,7 +1058,7 @@ pub const DeclGen = struct { try w.writeAll(", "); } const name = CValue{ .arg = index }; - try dg.renderTypeAndName(w, param_type, name, .Mut, 0); + try dg.renderTypeAndName(w, param_type, name, .Const, 0); index += 1; } @@ -1737,28 +1766,28 @@ pub const DeclGen = struct { defer dg.typedefs.allocator.free(name_z); const name_bytes = name_z[0 .. name_z.len + 1]; - var tag_val_pl: Value.Payload.U32 = .{ + var tag_pl: Value.Payload.U32 = .{ .base = .{ .tag = .enum_field_index }, .data = @intCast(u32, index), }; - const tag_val = Value.initPayload(&tag_val_pl.base); + const tag_val = Value.initPayload(&tag_pl.base); - var int_val_pl: Value.Payload.U64 = undefined; - const int_val = tag_val.enumToInt(enum_ty, &int_val_pl); + var int_pl: Value.Payload.U64 = undefined; + const int_val = tag_val.enumToInt(enum_ty, &int_pl); var name_ty_pl = Type.Payload.Len{ .base = .{ .tag = .array_u8_sentinel_0 }, .data = name.len }; const name_ty = Type.initPayload(&name_ty_pl.base); - var name_val_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name_bytes }; - const name_val = Value.initPayload(&name_val_pl.base); + var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name_bytes }; + const name_val = Value.initPayload(&name_pl.base); - var len_val_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len }; - const len_val = Value.initPayload(&len_val_pl.base); + var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len }; + const len_val = Value.initPayload(&len_pl.base); try bw.print(" case {}: {{\n static ", .{try dg.fmtIntLiteral(enum_ty, int_val)}); try dg.renderTypeAndName(bw, name_ty, .{ .identifier = "name" }, .Const, 0); try buffer.appendSlice(" = "); - try dg.renderValue(bw, name_ty, name_val, .Other); + try dg.renderValue(bw, name_ty, name_val, .Initializer); try buffer.appendSlice(";\n return ("); try dg.renderTypecast(bw, name_slice_ty); try bw.print("){{{}, {}}};\n", .{ @@ -1893,8 +1922,8 @@ pub fn genErrDecls(o: *Object) !void { var max_name_len: usize = 0; for (o.dg.module.error_name_list.items) |name, value| { max_name_len = std.math.max(name.len, max_name_len); - var err_val_pl = Value.Payload.Error{ .data = .{ .name = name } }; - try o.dg.renderValue(writer, Type.anyerror, Value.initPayload(&err_val_pl.base), .Other); + var err_pl = Value.Payload.Error{ .data = .{ .name = name } }; + try o.dg.renderValue(writer, Type.anyerror, Value.initPayload(&err_pl.base), .Other); try writer.print(" = {d}u,\n", .{value}); } o.indent_writer.popIndent(); @@ -1915,13 +1944,13 @@ pub fn genErrDecls(o: *Object) !void { var name_ty_pl = Type.Payload.Len{ .base = .{ .tag = .array_u8_sentinel_0 }, .data = name.len }; const name_ty = Type.initPayload(&name_ty_pl.base); - var name_val_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name_z }; - const name_val = Value.initPayload(&name_val_pl.base); + var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name_z }; + const name_val = Value.initPayload(&name_pl.base); try writer.writeAll("static "); try o.dg.renderTypeAndName(writer, name_ty, .{ .identifier = identifier }, .Const, 0); try writer.writeAll(" = "); - try o.dg.renderValue(writer, name_ty, name_val, .Other); + try o.dg.renderValue(writer, name_ty, name_val, .Initializer); try writer.writeAll(";\n"); } @@ -1937,8 +1966,8 @@ pub fn genErrDecls(o: *Object) !void { for (o.dg.module.error_name_list.items) |name, value| { if (value != 0) try writer.writeByte(','); - var len_val_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len }; - const len_val = Value.initPayload(&len_val_pl.base); + var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len }; + const len_val = Value.initPayload(&len_pl.base); try writer.print("{{" ++ name_prefix ++ "_{}, {}}}", .{ fmtIdent(name), @@ -2031,7 +2060,7 @@ pub fn genDecl(o: *Object) !void { try o.dg.renderTypeAndName(w, o.dg.decl.ty, decl_c_value, .Mut, o.dg.decl.@"align"); try w.writeAll(" = "); if (variable.init.tag() != .unreachable_value) { - try o.dg.renderValue(w, tv.ty, variable.init, .Other); + try o.dg.renderValue(w, tv.ty, variable.init, .Initializer); } try w.writeByte(';'); try o.indent_writer.insertNewline(); @@ -2046,7 +2075,7 @@ pub fn genDecl(o: *Object) !void { try o.dg.renderTypeAndName(writer, tv.ty, decl_c_value, .Mut, o.dg.decl.@"align"); try writer.writeAll(" = "); - try o.dg.renderValue(writer, tv.ty, tv.val, .Other); + try o.dg.renderValue(writer, tv.ty, tv.val, .Initializer); try writer.writeAll(";\n"); } } @@ -2099,15 +2128,15 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .unreach => try airUnreach(f), .fence => try airFence(f, inst), - .ptr_add => try airPtrAddSub(f, inst, " + "), - .ptr_sub => try airPtrAddSub(f, inst, " - "), + .ptr_add => try airPtrAddSub(f, inst, '+'), + .ptr_sub => try airPtrAddSub(f, inst, '-'), // TODO use a different strategy for add, sub, mul, div // that communicates to the optimizer that wrapping is UB. - .add => try airBinOp (f, inst, " + "), - .sub => try airBinOp (f, inst, " - "), - .mul => try airBinOp (f, inst, " * "), - .div_float, .div_exact => try airBinOp( f, inst, " / "), + .add => try airBinOp(f, inst, "+"), + .sub => try airBinOp(f, inst, "-"), + .mul => try airBinOp(f, inst, "*"), + .div_float, .div_exact => try airBinOp(f, inst, "/"), .rem => blk: { const bin_op = f.air.instructions.items(.data)[inst].bin_op; @@ -2115,7 +2144,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO // For binary operations @TypeOf(lhs)==@TypeOf(rhs), // so we only check one. break :blk if (lhs_ty.isInt()) - try airBinOp(f, inst, " % ") + try airBinOp(f, inst, "%") else try airBinFloatOp(f, inst, "fmod"); // yes, @rem() => fmod() }, @@ -2125,21 +2154,21 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO // For binary operations @TypeOf(lhs)==@TypeOf(rhs), // so we only check one. break :blk if (lhs_ty.isInt()) - try airBinOp(f, inst, " / ") + try airBinOp(f, inst, "/") else try airBinOpBuiltinCall(f, inst, "div_trunc"); }, .div_floor => try airBinOpBuiltinCall(f, inst, "div_floor"), .mod => try airBinOpBuiltinCall(f, inst, "mod"), - .addwrap => try airWrapOp(f, inst, " + ", "addw_"), - .subwrap => try airWrapOp(f, inst, " - ", "subw_"), - .mulwrap => try airWrapOp(f, inst, " * ", "mulw_"), + .addwrap => try airWrapOp(f, inst, "+", "add"), + .subwrap => try airWrapOp(f, inst, "-", "sub"), + .mulwrap => try airWrapOp(f, inst, "*", "mul"), - .add_sat => try airSatOp(f, inst, "adds_"), - .sub_sat => try airSatOp(f, inst, "subs_"), - .mul_sat => try airSatOp(f, inst, "muls_"), - .shl_sat => try airSatOp(f, inst, "shls_"), + .add_sat => try airSatOp(f, inst, "add"), + .sub_sat => try airSatOp(f, inst, "sub"), + .mul_sat => try airSatOp(f, inst, "mul"), + .shl_sat => try airSatOp(f, inst, "shl"), .neg => try airNeg(f, inst), @@ -2161,20 +2190,20 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .mul_add => try airMulAdd(f, inst), - .add_with_overflow => try airOverflow(f, inst, "addo_", .range), - .sub_with_overflow => try airOverflow(f, inst, "subo_", .range), - .mul_with_overflow => try airOverflow(f, inst, "mulo_", .range), - .shl_with_overflow => try airOverflow(f, inst, "shlo_", .bits), + .add_with_overflow => try airOverflow(f, inst, "add", .range), + .sub_with_overflow => try airOverflow(f, inst, "sub", .range), + .mul_with_overflow => try airOverflow(f, inst, "mul", .range), + .shl_with_overflow => try airOverflow(f, inst, "shl", .bits), - .min => try airMinMax(f, inst, "<"), - .max => try airMinMax(f, inst, ">"), + .min => try airMinMax(f, inst, '<'), + .max => try airMinMax(f, inst, '>'), .slice => try airSlice(f, inst), - .cmp_gt => try airBinOp(f, inst, " > "), - .cmp_gte => try airBinOp(f, inst, " >= "), - .cmp_lt => try airBinOp(f, inst, " < "), - .cmp_lte => try airBinOp(f, inst, " <= "), + .cmp_gt => try airBinOp(f, inst, ">"), + .cmp_gte => try airBinOp(f, inst, ">="), + .cmp_lt => try airBinOp(f, inst, "<"), + .cmp_lte => try airBinOp(f, inst, "<="), .cmp_eq => try airEquality(f, inst, "((", "=="), .cmp_neq => try airEquality(f, inst, "!((", "!="), @@ -2183,13 +2212,11 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .cmp_lt_errors_len => return f.fail("TODO: C backend: implement cmp_lt_errors_len", .{}), // bool_and and bool_or are non-short-circuit operations - .bool_and => try airBinOp(f, inst, " & "), - .bool_or => try airBinOp(f, inst, " | "), - .bit_and => try airBinOp(f, inst, " & "), - .bit_or => try airBinOp(f, inst, " | "), - .xor => try airBinOp(f, inst, " ^ "), - .shr, .shr_exact => try airBinOp(f, inst, " >> "), - .shl, .shl_exact => try airBinOp(f, inst, " << "), + .bool_and, .bit_and => try airBinOp(f, inst, "&"), + .bool_or, .bit_or => try airBinOp(f, inst, "|"), + .xor => try airBinOp(f, inst, "^"), + .shr, .shr_exact => try airBinOp(f, inst, ">>"), + .shl, .shl_exact => try airBinOp(f, inst, "<<"), .not => try airNot (f, inst), .optional_payload => try airOptionalPayload(f, inst), @@ -2202,10 +2229,10 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .is_err_ptr => try airIsErr(f, inst, true, "!="), .is_non_err_ptr => try airIsErr(f, inst, true, "=="), - .is_null => try airIsNull(f, inst, "==", ""), - .is_non_null => try airIsNull(f, inst, "!=", ""), - .is_null_ptr => try airIsNull(f, inst, "==", "[0]"), - .is_non_null_ptr => try airIsNull(f, inst, "!=", "[0]"), + .is_null => try airIsNull(f, inst, "==", false), + .is_non_null => try airIsNull(f, inst, "!=", false), + .is_null_ptr => try airIsNull(f, inst, "==", true), + .is_non_null_ptr => try airIsNull(f, inst, "!=", true), .alloc => try airAlloc(f, inst), .ret_ptr => try airRetPtr(f, inst), @@ -2291,11 +2318,11 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .field_parent_ptr => try airFieldParentPtr(f, inst), .struct_field_val => try airStructFieldVal(f, inst), - .slice_ptr => try airSliceField(f, inst, " = ", ".ptr;\n"), - .slice_len => try airSliceField(f, inst, " = ", ".len;\n"), + .slice_ptr => try airSliceField(f, inst, false, "ptr"), + .slice_len => try airSliceField(f, inst, false, "len"), - .ptr_slice_len_ptr => try airSliceField(f, inst, " = &", "->len;\n"), - .ptr_slice_ptr_ptr => try airSliceField(f, inst, " = &", "->ptr;\n"), + .ptr_slice_len_ptr => try airSliceField(f, inst, true, "len"), + .ptr_slice_ptr_ptr => try airSliceField(f, inst, true, "ptr"), .ptr_elem_val => try airPtrElemVal(f, inst), .ptr_elem_ptr => try airPtrElemPtr(f, inst), @@ -2303,8 +2330,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .slice_elem_ptr => try airSliceElemPtr(f, inst), .array_elem_val => try airArrayElemVal(f, inst), - .unwrap_errunion_payload => try airUnwrapErrUnionPay(f, inst, ""), - .unwrap_errunion_payload_ptr => try airUnwrapErrUnionPay(f, inst, "&"), + .unwrap_errunion_payload => try airUnwrapErrUnionPay(f, inst, false), + .unwrap_errunion_payload_ptr => try airUnwrapErrUnionPay(f, inst, true), .unwrap_errunion_err => try airUnwrapErrUnionErr(f, inst), .unwrap_errunion_err_ptr => try airUnwrapErrUnionErr(f, inst), .wrap_errunion_payload => try airWrapErrUnionPay(f, inst), @@ -2355,7 +2382,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO try writer.writeByte('}'); } -fn airSliceField(f: *Function, inst: Air.Inst.Index, prefix: []const u8, suffix: []const u8) !CValue { +fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: []const u8) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; const inst_ty = f.air.typeOfIndex(inst); @@ -2363,9 +2390,12 @@ fn airSliceField(f: *Function, inst: Air.Inst.Index, prefix: []const u8, suffix: const operand = try f.resolveInst(ty_op.operand); const writer = f.object.writer(); const local = try f.allocLocal(inst_ty, .Const); - try writer.writeAll(prefix); - try f.writeCValue(writer, operand); - try writer.writeAll(suffix); + try writer.writeAll(" = "); + if (is_ptr) try writer.writeByte('&'); + try f.writeCValue(writer, operand, .Other); + try if (is_ptr) writer.writeAll("->") else writer.writeByte('.'); + try writer.writeAll(field_name); + try writer.writeAll(";\n"); return local; } @@ -2379,9 +2409,9 @@ fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const); try writer.writeAll(" = "); - try f.writeCValue(writer, ptr); + try f.writeCValue(writer, ptr, .Other); try writer.writeByte('['); - try f.writeCValue(writer, index); + try f.writeCValue(writer, index, .Other); try writer.writeAll("];\n"); return local; } @@ -2403,10 +2433,10 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { // It's a pointer to an array, so we need to de-reference. try f.writeCValueDeref(writer, ptr); } else { - try f.writeCValue(writer, ptr); + try f.writeCValue(writer, ptr, .Other); } try writer.writeAll(")["); - try f.writeCValue(writer, index); + try f.writeCValue(writer, index, .Other); try writer.writeAll("];\n"); return local; } @@ -2421,9 +2451,9 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const); try writer.writeAll(" = "); - try f.writeCValue(writer, slice); + try f.writeCValue(writer, slice, .Other); try writer.writeAll(".ptr["); - try f.writeCValue(writer, index); + try f.writeCValue(writer, index, .Other); try writer.writeAll("];\n"); return local; } @@ -2439,9 +2469,9 @@ fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const); try writer.writeAll(" = &"); - try f.writeCValue(writer, slice); + try f.writeCValue(writer, slice, .Other); try writer.writeAll(".ptr["); - try f.writeCValue(writer, index); + try f.writeCValue(writer, index, .Other); try writer.writeAll("];\n"); return local; } @@ -2455,9 +2485,9 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const); try writer.writeAll(" = "); - try f.writeCValue(writer, array); + try f.writeCValue(writer, array, .Other); try writer.writeByte('['); - try f.writeCValue(writer, index); + try f.writeCValue(writer, index, .Other); try writer.writeAll("];\n"); return local; } @@ -2523,11 +2553,11 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { // and thus we only need to know size/type information from the local type/dest. try writer.writeAll(";\n"); try writer.writeAll("memcpy("); - try f.writeCValue(writer, local); + try f.writeCValue(writer, local, .FunctionArgument); try writer.writeAll(", "); - try f.writeCValue(writer, operand); + try f.writeCValue(writer, operand, .FunctionArgument); try writer.writeAll(", sizeof("); - try f.writeCValue(writer, local); + try f.renderTypecast(writer, inst_ty); try writer.writeAll("));\n"); } else { try writer.writeAll(" = "); @@ -2544,7 +2574,7 @@ fn airRet(f: *Function, inst: Air.Inst.Index) !CValue { if (ret_ty.isFnOrHasRuntimeBitsIgnoreComptime()) { const operand = try f.resolveInst(un_op); try writer.writeAll("return "); - try f.writeCValue(writer, operand); + try f.writeCValue(writer, operand, .Other); try writer.writeAll(";\n"); } else if (ret_ty.isError()) { try writer.writeAll("return 0;"); @@ -2563,7 +2593,7 @@ fn airRetLoad(f: *Function, inst: Air.Inst.Index) !CValue { if (ret_ty.isFnOrHasRuntimeBitsIgnoreComptime()) { const ptr = try f.resolveInst(un_op); try writer.writeAll("return *"); - try f.writeCValue(writer, ptr); + try f.writeCValue(writer, ptr, .Other); try writer.writeAll(";\n"); } else if (ret_ty.isError()) { try writer.writeAll("return 0;\n"); @@ -2586,7 +2616,7 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(" = ("); try f.renderTypecast(writer, inst_ty); try writer.writeByte(')'); - try f.writeCValue(writer, operand); + try f.writeCValue(writer, operand, .Other); try writer.writeAll(";\n"); return local; } @@ -2603,32 +2633,44 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { const dest_int_info = inst_ty.intInfo(target); const dest_bits = dest_int_info.bits; - try writer.writeAll(" = "); + try writer.writeAll(" = ("); + try f.renderTypecast(writer, inst_ty); + try writer.writeByte(')'); if (dest_bits >= 8 and std.math.isPowerOfTwo(dest_bits)) { - try f.writeCValue(writer, operand); + try f.writeCValue(writer, operand, .Other); try writer.writeAll(";\n"); - return local; - } - - switch (dest_int_info.signedness) { + } else switch (dest_int_info.signedness) { .unsigned => { - try f.writeCValue(writer, operand); - const mask = (@as(u65, 1) << @intCast(u7, dest_bits)) - 1; - try writer.print(" & {d}ULL;\n", .{mask}); - return local; + var arena = std.heap.ArenaAllocator.init(f.object.dg.module.gpa); + defer arena.deinit(); + + const expected_contents = union { u: Value.Payload.U64, i: Value.Payload.I64 }; + var stack align(@alignOf(expected_contents)) = + std.heap.stackFallback(@sizeOf(expected_contents), arena.allocator()); + + const mask_val = try inst_ty.maxInt(stack.get(), target); + + try writer.writeByte('('); + try f.writeCValue(writer, operand, .Other); + try writer.print(" & {x});\n", .{try f.fmtIntLiteral(inst_ty, mask_val)}); }, .signed => { const operand_ty = f.air.typeOf(ty_op.operand); const c_bits = toCIntBits(operand_ty.intInfo(target).bits) orelse return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); - const shift_rhs = c_bits - dest_bits; - try writer.print("(int{d}_t)((uint{d}_t)", .{ c_bits, c_bits }); - try f.writeCValue(writer, operand); - try writer.print(" << {d}) >> {d};\n", .{ shift_rhs, shift_rhs }); - return local; + var shift_pl = Value.Payload.U64{ + .base = .{ .tag = .int_u64 }, + .data = c_bits - dest_bits, + }; + const shift_val = Value.initPayload(&shift_pl.base); + + try writer.print("((int{d}_t)((uint{0d}_t)", .{c_bits}); + try f.writeCValue(writer, operand, .Other); + try writer.print(" << {}) >> {0});\n", .{try f.fmtIntLiteral(Type.u8, shift_val)}); }, } + return local; } fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue { @@ -2640,18 +2682,18 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(un_op); const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = "); - try f.writeCValue(writer, operand); + try f.writeCValue(writer, operand, .Other); try writer.writeAll(";\n"); return local; } -fn airStoreUndefined(f: *Function, dest_ptr: CValue) !CValue { +fn airStoreUndefined(f: *Function, lhs_child_ty: Type, dest_ptr: CValue) !CValue { if (f.wantSafety()) { const writer = f.object.writer(); try writer.writeAll("memset("); - try f.writeCValue(writer, dest_ptr); + try f.writeCValue(writer, dest_ptr, .FunctionArgument); try writer.print(", {x}, sizeof(", .{try f.fmtIntLiteral(Type.u8, Value.undef)}); - try f.writeCValueDeref(writer, dest_ptr); + try f.renderTypecast(writer, lhs_child_ty); try writer.writeAll("));\n"); } return CValue.none; @@ -2660,8 +2702,8 @@ fn airStoreUndefined(f: *Function, dest_ptr: CValue) !CValue { fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { // *a = b; const bin_op = f.air.instructions.items(.data)[inst].bin_op; - const lhs_child_type = f.air.typeOf(bin_op.lhs).childType(); - if (!lhs_child_type.hasRuntimeBitsIgnoreComptime()) return CValue.none; + const lhs_child_ty = f.air.typeOf(bin_op.lhs).childType(); + if (!lhs_child_ty.hasRuntimeBitsIgnoreComptime()) return CValue.none; const dest_ptr = try f.resolveInst(bin_op.lhs); const src_val = try f.resolveInst(bin_op.rhs); @@ -2671,14 +2713,14 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { const src_val_is_undefined = if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false; if (src_val_is_undefined) - return try airStoreUndefined(f, dest_ptr); + return try airStoreUndefined(f, lhs_child_ty, dest_ptr); const writer = f.object.writer(); - if (lhs_child_type.zigTypeTag() == .Array) { + if (lhs_child_ty.zigTypeTag() == .Array) { // For this memcpy to safely work we need the rhs to have the same // underlying type as the lhs (i.e. they must both be arrays of the same underlying type). const rhs_type = f.air.typeOf(bin_op.rhs); - assert(rhs_type.eql(lhs_child_type, f.object.dg.module)); + assert(rhs_type.eql(lhs_child_ty, f.object.dg.module)); // If the source is a constant, writeCValue will emit a brace initialization // so work around this by initializing into new local. @@ -2686,37 +2728,30 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { const array_src = if (src_val == .constant) blk: { const new_local = try f.allocLocal(rhs_type, .Const); try writer.writeAll(" = "); - try f.writeCValue(writer, src_val); - try writer.writeByte(';'); - try f.object.indent_writer.insertNewline(); + try f.writeCValue(writer, src_val, .Initializer); + try writer.writeAll(";\n"); break :blk new_local; } else src_val; try writer.writeAll("memcpy("); - try f.writeCValue(writer, dest_ptr); + try f.writeCValue(writer, dest_ptr, .FunctionArgument); try writer.writeAll(", "); - try f.writeCValue(writer, array_src); + try f.writeCValue(writer, array_src, .FunctionArgument); try writer.writeAll(", sizeof("); - try f.writeCValue(writer, array_src); + try f.renderTypecast(writer, lhs_child_ty); try writer.writeAll("));\n"); } else { try f.writeCValueDeref(writer, dest_ptr); try writer.writeAll(" = "); - try f.writeCValue(writer, src_val); + try f.writeCValue(writer, src_val, .Other); try writer.writeAll(";\n"); } return CValue.none; } -fn airWrapOp( - f: *Function, - inst: Air.Inst.Index, - str_op: [*:0]const u8, - fn_op: [*:0]const u8, -) !CValue { - if (f.liveness.isUnused(inst)) - return CValue.none; +fn airWrapOp(f: *Function, inst: Air.Inst.Index, operator: []const u8, fn_name: []const u8) !CValue { + if (f.liveness.isUnused(inst)) return CValue.none; const bin_op = f.air.instructions.items(.data)[inst].bin_op; const inst_ty = f.air.typeOfIndex(inst); @@ -2725,26 +2760,18 @@ fn airWrapOp( const bits = int_info.bits; // if it's an unsigned int with non-arbitrary bit size then we can just add - if (int_info.signedness == .unsigned) { - const ok_bits = switch (bits) { - 8, 16, 32, 64, 128 => true, - else => false, - }; - if (ok_bits or inst_ty.tag() != .int_unsigned) { - return try airBinOp(f, inst, str_op); - } + if (int_info.signedness == .unsigned and bits == toCIntBits(bits)) { + return try airBinOp(f, inst, operator); } - if (bits > 64) { - return f.fail("TODO: C backend: airWrapOp for large integers", .{}); - } + if (bits > 64) return f.fail("TODO: C backend: airWrapOp for large integers", .{}); const lhs = try f.resolveInst(bin_op.lhs); const rhs = try f.resolveInst(bin_op.rhs); const w = f.object.writer(); - const ret = try f.allocLocal(inst_ty, .Mut); - try w.print(" = zig_{s}", .{fn_op}); + const local = try f.allocLocal(inst_ty, .Mut); + try w.print(" = zig_{s}w_", .{fn_name}); switch (inst_ty.tag()) { .isize => try w.writeAll("isize"), @@ -2766,9 +2793,9 @@ fn airWrapOp( } try w.writeByte('('); - try f.writeCValue(w, lhs); + try f.writeCValue(w, lhs, .FunctionArgument); try w.writeAll(", "); - try f.writeCValue(w, rhs); + try f.writeCValue(w, rhs, .FunctionArgument); { var arena = std.heap.ArenaAllocator.init(f.object.dg.module.gpa); defer arena.deinit(); @@ -2787,12 +2814,11 @@ fn airWrapOp( } try f.object.indent_writer.insertNewline(); - return ret; + return local; } -fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue { - if (f.liveness.isUnused(inst)) - return CValue.none; +fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_name: []const u8) !CValue { + if (f.liveness.isUnused(inst)) return CValue.none; const bin_op = f.air.instructions.items(.data)[inst].bin_op; const inst_ty = f.air.typeOfIndex(inst); @@ -2800,22 +2826,14 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue { const int_info = inst_ty.intInfo(target); const bits = int_info.bits; - switch (bits) { - 8, 16, 32, 64, 128 => {}, - else => return f.object.dg.fail("TODO: C backend: airSatOp for non power of 2 integers", .{}), - } - - // if it's an unsigned int with non-arbitrary bit size then we can just add - if (bits > 64) { - return f.object.dg.fail("TODO: C backend: airSatOp for large integers", .{}); - } + if (bits > 64) return f.object.dg.fail("TODO: C backend: airSatOp for large integers", .{}); const lhs = try f.resolveInst(bin_op.lhs); const rhs = try f.resolveInst(bin_op.rhs); const w = f.object.writer(); - const ret = try f.allocLocal(inst_ty, .Mut); - try w.print(" = zig_{s}", .{fn_op}); + const local = try f.allocLocal(inst_ty, .Mut); + try w.print(" = zig_{s}s_", .{fn_name}); switch (inst_ty.tag()) { .isize => try w.writeAll("isize"), @@ -2837,9 +2855,9 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue { } try w.writeByte('('); - try f.writeCValue(w, lhs); + try f.writeCValue(w, lhs, .FunctionArgument); try w.writeAll(", "); - try f.writeCValue(w, rhs); + try f.writeCValue(w, rhs, .FunctionArgument); { var arena = std.heap.ArenaAllocator.init(f.object.dg.module.gpa); defer arena.deinit(); @@ -2858,10 +2876,10 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue { } try f.object.indent_writer.insertNewline(); - return ret; + return local; } -fn airOverflow(f: *Function, inst: Air.Inst.Index, op_abbrev: [*:0]const u8, kind: enum { range, bits }) !CValue { +fn airOverflow(f: *Function, inst: Air.Inst.Index, fn_name: []const u8, kind: enum { range, bits }) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; @@ -2879,19 +2897,18 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, op_abbrev: [*:0]const u8, kin const c_bits = toCIntBits(int_info.bits) orelse return f.fail("TODO: C backend: implement integer arithmetic larger than 128 bits", .{}); - const ret = try f.allocLocal(inst_ty, .Mut); - try w.writeByte(';'); - try f.object.indent_writer.insertNewline(); - try f.writeCValue(w, ret); + const local = try f.allocLocal(inst_ty, .Mut); + try w.writeAll(";\n"); - try w.print(".field_1 = zig_{s}{c}{d}(", .{ - op_abbrev, signAbbrev(int_info.signedness), c_bits, + try f.writeCValue(w, local, .Other); + try w.print(".field_1 = zig_{s}o_{c}{d}(", .{ + fn_name, signAbbrev(int_info.signedness), c_bits, }); - try f.writeCValue(w, lhs); + try f.writeCValue(w, lhs, .FunctionArgument); try w.writeAll(", "); - try f.writeCValue(w, rhs); + try f.writeCValue(w, rhs, .FunctionArgument); try w.writeAll(", &"); - try f.writeCValue(w, ret); + try f.writeCValue(w, local, .Other); try w.writeAll(".field_0, "); switch (kind) { .range => { @@ -2916,7 +2933,7 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, op_abbrev: [*:0]const u8, kin try w.print("{x});\n", .{try f.fmtIntLiteral(Type.u8, bits_val)}); }, } - return ret; + return local; } fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { @@ -2932,13 +2949,13 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(" = "); try writer.writeByte(if (inst_ty.tag() == .bool) '!' else '~'); - try f.writeCValue(writer, op); + try f.writeCValue(writer, op, .Other); try writer.writeAll(";\n"); return local; } -fn airBinOp(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue { +fn airBinOp(f: *Function, inst: Air.Inst.Index, operator: []const u8) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; @@ -2951,9 +2968,11 @@ fn airBinOp(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = "); - try f.writeCValue(writer, lhs); - try writer.print("{s}", .{operator}); - try f.writeCValue(writer, rhs); + try f.writeCValue(writer, lhs, .Other); + try writer.writeByte(' '); + try writer.writeAll(operator); + try writer.writeByte(' '); + try f.writeCValue(writer, rhs, .Other); try writer.writeAll(";\n"); return local; @@ -2983,31 +3002,31 @@ fn airEquality( // A = lhs.is_null ; B = rhs.is_null ; C = rhs.payload == lhs.payload try writer.writeAll(negate_prefix); - try f.writeCValue(writer, lhs); + try f.writeCValue(writer, lhs, .Other); try writer.writeAll(".is_null && "); - try f.writeCValue(writer, rhs); + try f.writeCValue(writer, rhs, .Other); try writer.writeAll(".is_null) || ("); - try f.writeCValue(writer, lhs); + try f.writeCValue(writer, lhs, .Other); try writer.writeAll(".payload == "); - try f.writeCValue(writer, rhs); + try f.writeCValue(writer, rhs, .Other); try writer.writeAll(".payload && "); - try f.writeCValue(writer, lhs); + try f.writeCValue(writer, lhs, .Other); try writer.writeAll(".is_null == "); - try f.writeCValue(writer, rhs); + try f.writeCValue(writer, rhs, .Other); try writer.writeAll(".is_null));\n"); return local; } - try f.writeCValue(writer, lhs); + try f.writeCValue(writer, lhs, .Other); try writer.writeAll(eq_op_str); - try f.writeCValue(writer, rhs); + try f.writeCValue(writer, rhs, .Other); try writer.writeAll(";\n"); return local; } -fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue { +fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; @@ -3031,17 +3050,19 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CV try writer.writeAll(" = ("); try f.renderTypecast(writer, inst_ty); try writer.writeAll(")(((uintptr_t)"); - try f.writeCValue(writer, lhs); - try writer.print("){s}(", .{operator}); - try f.writeCValue(writer, rhs); + try f.writeCValue(writer, lhs, .Other); + try writer.writeAll(") "); + try writer.writeByte(operator); + try writer.writeAll(" ("); + try f.writeCValue(writer, rhs, .Other); try writer.writeAll("*sizeof("); try f.renderTypecast(writer, elem_ty); - try writer.print(")));\n", .{}); + try writer.writeAll(")));\n"); return local; } -fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue { +fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; const bin_op = f.air.instructions.items(.data)[inst].bin_op; @@ -3054,13 +3075,15 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValu // (lhs <> rhs) ? lhs : rhs try writer.writeAll(" = ("); - try f.writeCValue(writer, lhs); - try writer.print("{s}", .{operator}); - try f.writeCValue(writer, rhs); + try f.writeCValue(writer, lhs, .Other); + try writer.writeByte(' '); + try writer.writeByte(operator); + try writer.writeByte(' '); + try f.writeCValue(writer, rhs, .Other); try writer.writeAll(") ? "); - try f.writeCValue(writer, lhs); + try f.writeCValue(writer, lhs, .Other); try writer.writeAll(" : "); - try f.writeCValue(writer, rhs); + try f.writeCValue(writer, rhs, .Other); try writer.writeAll(";\n"); return local; @@ -3082,9 +3105,9 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue { var buf: Type.SlicePtrFieldTypeBuffer = undefined; try f.renderTypecast(writer, inst_ty.slicePtrFieldType(&buf)); try writer.writeByte(')'); - try f.writeCValue(writer, ptr); + try f.writeCValue(writer, ptr, .Other); try writer.writeAll(", "); - try f.writeCValue(writer, len); + try f.writeCValue(writer, len, .Initializer); try writer.writeAll("};\n"); return local; @@ -3151,7 +3174,7 @@ fn airCall( } // Fall back to function pointer call. const callee = try f.resolveInst(pl_op.operand); - try f.writeCValue(writer, callee); + try f.writeCValue(writer, callee, .Other); } try writer.writeByte('('); @@ -3171,12 +3194,7 @@ fn airCall( if (ty.isVolatilePtr()) try writer.writeAll(" volatile"); try writer.writeAll(" *)"); } - if (f.air.value(arg)) |val| { - try f.object.dg.renderValue(writer, f.air.typeOf(arg), val, .FunctionArgument); - } else { - const val = try f.resolveInst(arg); - try f.writeCValue(writer, val); - } + try f.writeCValue(writer, try f.resolveInst(arg), .FunctionArgument); args_written += 1; } try writer.writeAll(");\n"); @@ -3286,18 +3304,18 @@ fn lowerTry( } else { try writer.writeAll("if("); } - try f.writeCValue(writer, err_union); + try f.writeCValue(writer, err_union, .Other); try writer.writeByte(')'); break :err; } if (operand_is_ptr or isByRef(err_union_ty)) { try writer.writeAll("if("); - try f.writeCValue(writer, err_union); + try f.writeCValue(writer, err_union, .Other); try writer.writeAll("->error)"); break :err; } try writer.writeAll("if("); - try f.writeCValue(writer, err_union); + try f.writeCValue(writer, err_union, .Other); try writer.writeAll(".error)"); } @@ -3318,20 +3336,20 @@ fn lowerTry( if (is_array) { try writer.writeAll(";\n"); try writer.writeAll("memcpy("); - try f.writeCValue(writer, local); + try f.writeCValue(writer, local, .FunctionArgument); try writer.writeAll(", "); - try f.writeCValue(writer, err_union); + try f.writeCValue(writer, err_union, .Other); try writer.writeAll(".payload, sizeof("); - try f.writeCValue(writer, local); + try f.renderTypecast(writer, payload_ty); try writer.writeAll("));\n"); } else { if (operand_is_ptr or isByRef(payload_ty)) { try writer.writeAll(" = &"); - try f.writeCValue(writer, err_union); + try f.writeCValue(writer, err_union, .Other); try writer.writeAll("->payload;\n"); } else { try writer.writeAll(" = "); - try f.writeCValue(writer, err_union); + try f.writeCValue(writer, err_union, .Other); try writer.writeAll(".payload;\n"); } } @@ -3347,9 +3365,9 @@ 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); + try f.writeCValue(writer, result, .Other); try writer.writeAll(" = "); - try f.writeCValue(writer, operand); + try f.writeCValue(writer, operand, .Other); try writer.writeAll(";\n"); } @@ -3374,7 +3392,7 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { try f.renderTypecast(writer, inst_ty); try writer.writeByte(')'); - try f.writeCValue(writer, operand); + try f.writeCValue(writer, operand, .Other); try writer.writeAll(";\n"); return local; } @@ -3383,11 +3401,11 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(";\n"); try writer.writeAll("memcpy(&"); - try f.writeCValue(writer, local); + try f.writeCValue(writer, local, .Other); try writer.writeAll(", &"); - try f.writeCValue(writer, operand); + try f.writeCValue(writer, operand, .Other); try writer.writeAll(", sizeof("); - try f.writeCValue(writer, local); + try f.renderTypecast(writer, inst_ty); try writer.writeAll("));\n"); return local; @@ -3456,7 +3474,7 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); try writer.writeAll("if ("); - try f.writeCValue(writer, cond); + try f.writeCValue(writer, cond, .Other); try writer.writeAll(") "); try genBody(f, then_body); try writer.writeAll(" else "); @@ -3475,7 +3493,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll("switch ("); if (condition_ty.tag() == .bool) try writer.writeAll("(int)"); - try f.writeCValue(writer, condition); + try f.writeCValue(writer, condition, .Other); try writer.writeAll(") {"); f.object.indent_writer.pushIndent(); @@ -3527,7 +3545,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const local = try f.allocLocal(inst_ty, .Mut); if (f.wantSafety()) { try writer.writeAll(" = "); - try f.writeCValue(writer, .{ .undef = inst_ty }); + try f.writeCValue(writer, .{ .undef = inst_ty }, .Initializer); } try writer.writeAll(";\n"); break :local local; @@ -3555,7 +3573,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll("\")"); if (f.wantSafety()) { try writer.writeAll(" = "); - try f.writeCValue(writer, .{ .undef = output_ty }); + try f.writeCValue(writer, .{ .undef = output_ty }, .Initializer); } try writer.writeAll(";\n"); } else if (constraint.len < 2 or constraint[0] != '=') { @@ -3581,7 +3599,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(" __asm(\""); try writer.writeAll(constraint["{".len .. constraint.len - "}".len]); try writer.writeAll("\") = "); - try f.writeCValue(writer, try f.resolveInst(input)); + try f.writeCValue(writer, try f.resolveInst(input), .Initializer); try writer.writeAll(";\n"); } else if (constraint.len >= 1 and std.mem.indexOfScalar(u8, "=+&%", constraint[0]) == null) { const input_val = try f.resolveInst(input); @@ -3590,7 +3608,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { .local = input_locals_begin + index, }, .Const, 0); try writer.writeAll(" = "); - try f.writeCValue(writer, input_val); + try f.writeCValue(writer, input_val, .Initializer); try writer.writeAll(";\n"); } } else { @@ -3626,7 +3644,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeByte(' '); if (constraint[1] == '{') { try writer.print("{s}(", .{fmtStringLiteral("=r")}); - try f.writeCValue(writer, .{ .local = output_locals_begin + index }); + try f.writeCValue(writer, .{ .local = output_locals_begin + index }, .Other); } else { try writer.print("{s}(", .{fmtStringLiteral(constraint)}); try f.writeCValueDeref(writer, try f.resolveInst(output)); @@ -3646,13 +3664,14 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeByte(' '); if (constraint[0] == '{') { try writer.print("{s}(", .{fmtStringLiteral("r")}); - try f.writeCValue(writer, .{ .local = input_locals_begin + index }); + try f.writeCValue(writer, .{ .local = input_locals_begin + index }, .Other); } else { const input_val = try f.resolveInst(input); try writer.print("{s}(", .{fmtStringLiteral(constraint)}); - try f.writeCValue(writer, if (input_val == .constant) CValue{ - .local = input_locals_begin + index, - } else input_val); + try f.writeCValue(writer, if (input_val == .constant) + CValue{ .local = input_locals_begin + index } + else + input_val, .Other); } try writer.writeByte(')'); } @@ -3683,11 +3702,12 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { extra_i += (constraint.len + name.len + (2 + 3)) / 4; if (constraint[1] == '{') { - try f.writeCValueDeref(writer, if (output == .none) CValue{ - .local_ref = local.local, - } else try f.resolveInst(output)); + try f.writeCValueDeref(writer, if (output == .none) + CValue{ .local_ref = local.local } + else + try f.resolveInst(output)); try writer.writeAll(" = "); - try f.writeCValue(writer, .{ .local = output_locals_begin + index }); + try f.writeCValue(writer, .{ .local = output_locals_begin + index }, .Other); try writer.writeAll(";\n"); } } @@ -3698,8 +3718,8 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { fn airIsNull( f: *Function, inst: Air.Inst.Index, - operator: [*:0]const u8, - deref_suffix: [*:0]const u8, + operator: []const u8, + is_ptr: bool, ) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; @@ -3709,25 +3729,23 @@ fn airIsNull( const operand = try f.resolveInst(un_op); const local = try f.allocLocal(Type.initTag(.bool), .Const); - try writer.writeAll(" = ("); - try f.writeCValue(writer, operand); + try writer.writeAll(" = "); + try if (is_ptr) f.writeCValueDeref(writer, operand) else f.writeCValue(writer, operand, .Other); - const ty = f.air.typeOf(un_op); - var opt_buf: Type.Payload.ElemType = undefined; - const payload_ty = if (deref_suffix[0] != 0) - ty.childType().optionalChild(&opt_buf) - else - ty.optionalChild(&opt_buf); + const operand_ty = f.air.typeOf(un_op); + const optional_ty = if (is_ptr) operand_ty.childType() else operand_ty; + var payload_buf: Type.Payload.ElemType = undefined; + const payload_ty = optional_ty.optionalChild(&payload_buf); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { - try writer.print("){s} {s} true;\n", .{ deref_suffix, operator }); - } else if (ty.isPtrLikeOptional()) { + try writer.print(" {s} true;\n", .{operator}); + } else if (operand_ty.isPtrLikeOptional()) { // operand is a regular pointer, test `operand !=/== NULL` - try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator }); + try writer.print(" {s} NULL;\n", .{operator}); } else if (payload_ty.zigTypeTag() == .ErrorSet) { - try writer.print("){s} {s} 0;\n", .{ deref_suffix, operator }); + try writer.print(" {s} 0;\n", .{operator}); } else { - try writer.print("){s}.is_null {s} true;\n", .{ deref_suffix, operator }); + try writer.print(".is_null {s} true;\n", .{operator}); } return local; } @@ -3754,7 +3772,7 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.air.typeOfIndex(inst); const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = ("); - try f.writeCValue(writer, operand); + try f.writeCValue(writer, operand, .Other); try writer.writeAll(").payload;\n"); return local; } @@ -3781,7 +3799,7 @@ fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue { const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = &("); - try f.writeCValue(writer, operand); + try f.writeCValue(writer, operand, .Other); try writer.writeAll(")->payload;\n"); return local; } @@ -3882,7 +3900,7 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc try writer.writeAll(" = ("); try f.renderTypecast(writer, inst_ty); try writer.writeByte(')'); - try f.writeCValue(writer, struct_ptr); + try f.writeCValue(writer, struct_ptr, .Other); try writer.writeAll(";\n"); } return local; @@ -3920,15 +3938,15 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { if (is_array) { try writer.writeAll(";\n"); try writer.writeAll("memcpy("); - try f.writeCValue(writer, local); + try f.writeCValue(writer, local, .FunctionArgument); try writer.writeAll(", "); - try f.writeCValue(writer, struct_byval); + try f.writeCValue(writer, struct_byval, .Other); try writer.print(".{s}{ }, sizeof(", .{ payload, fmtIdent(field_name) }); - try f.writeCValue(writer, local); + try f.renderTypecast(writer, inst_ty); try writer.writeAll("));\n"); } else { try writer.writeAll(" = "); - try f.writeCValue(writer, struct_byval); + try f.writeCValue(writer, struct_byval, .Other); try writer.print(".{s}{ };\n", .{ payload, fmtIdent(field_name) }); } return local; @@ -3956,7 +3974,7 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { } const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = *"); - try f.writeCValue(writer, operand); + try f.writeCValue(writer, operand, .Other); try writer.writeAll(";\n"); return local; } @@ -3972,13 +3990,13 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { if (operand_ty.zigTypeTag() == .Pointer) { try f.writeCValueDeref(writer, operand); } else { - try f.writeCValue(writer, operand); + try f.writeCValue(writer, operand, .Other); } try writer.writeAll(".error;\n"); return local; } -fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, maybe_addrof: [*:0]const u8) !CValue { +fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; @@ -3994,13 +4012,15 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, maybe_addrof: [*:0]c } const inst_ty = f.air.typeOfIndex(inst); - const maybe_deref = if (operand_is_ptr) "->" else "."; const local = try f.allocLocal(inst_ty, .Const); - try writer.print(" = {s}(", .{maybe_addrof}); - try f.writeCValue(writer, operand); - - try writer.print("){s}payload;\n", .{maybe_deref}); + try writer.writeAll(" = "); + if (is_ptr) try writer.writeByte('&'); + try writer.writeByte('('); + try f.writeCValue(writer, operand, .Other); + try writer.writeByte(')'); + try if (operand_is_ptr) writer.writeAll("->") else writer.writeByte('.'); + try writer.writeAll("payload;\n"); return local; } @@ -4020,7 +4040,7 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { // .wrap_optional is used to convert non-optionals into optionals so it can never be null. const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = { .payload = "); - try f.writeCValue(writer, operand); + try f.writeCValue(writer, operand, .Initializer); try writer.writeAll(", .is_null = false };\n"); return local; } @@ -4039,9 +4059,9 @@ fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { const local = try f.allocLocal(err_un_ty, .Const); try writer.writeAll(" = { .payload = "); - try f.writeCValue(writer, .{ .undef = payload_ty }); + try f.writeCValue(writer, .{ .undef = payload_ty }, .Initializer); try writer.writeAll(", .error = "); - try f.writeCValue(writer, operand); + try f.writeCValue(writer, operand, .Initializer); try writer.writeAll(" };\n"); return local; } @@ -4104,33 +4124,29 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.air.typeOfIndex(inst); const payload_ty = inst_ty.errorUnionPayload(); + const error_ty = inst_ty.errorUnionSet(); const is_array = payload_ty.zigTypeTag() == .Array; const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const); try writer.writeAll(" = { .payload = "); - try f.writeCValue(writer, if (is_array) CValue{ .undef = payload_ty } else operand); - try writer.print(", .error = {} }};\n", .{ - try f.fmtIntLiteral(inst_ty.errorUnionSet(), Value.zero), - }); + try f.writeCValue(writer, if (is_array) CValue{ .undef = payload_ty } else operand, .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); + try f.writeCValue(writer, local, .Other); try writer.writeAll(".payload, "); - try f.writeCValue(writer, operand); + try f.writeCValue(writer, operand, .FunctionArgument); try writer.writeAll(", sizeof("); - try f.writeCValue(writer, local); - try writer.writeAll(".payload));\n"); + try f.renderTypecast(writer, payload_ty); + try writer.writeAll("));\n"); } return local; } -fn airIsErr( - f: *Function, - inst: Air.Inst.Index, - is_ptr: bool, - op_str: [*:0]const u8, -) !CValue { +fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const u8) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; @@ -4146,18 +4162,17 @@ fn airIsErr( try writer.writeAll(" = "); if (error_ty.errorSetIsEmpty()) { - try writer.print("0 {s} 0;\n", .{op_str}); + try writer.writeByte('0'); } else { - if (is_ptr) { - try f.writeCValueDeref(writer, operand); - } else { - try f.writeCValue(writer, operand); - } + try f.writeCValue(writer, operand, .Other); if (payload_ty.hasRuntimeBits()) { - try writer.writeAll(".error"); + try if (is_ptr) writer.writeAll("->") else writer.writeByte('.'); + try writer.writeAll("error"); } - try writer.print(" {s} 0;\n", .{op_str}); } + try writer.writeByte(' '); + try writer.writeAll(operator); + try writer.writeAll(" 0;\n"); return local; } @@ -4177,13 +4192,16 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { // Unfortunately, C does not support any equivalent to // &(*(void *)p)[0], although LLVM does via GetElementPtr var buf: Type.SlicePtrFieldTypeBuffer = undefined; - try f.writeCValue(writer, CValue{ .undef = inst_ty.slicePtrFieldType(&buf) }); + try f.writeCValue(writer, CValue{ .undef = inst_ty.slicePtrFieldType(&buf) }, .Initializer); } else { try writer.writeAll("&("); try f.writeCValueDeref(writer, operand); try writer.writeAll(")[0]"); } - try writer.print(", .len = {d} }};\n", .{array_len}); + + var len_pl: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = array_len }; + const len_val = Value.initPayload(&len_pl.base); + try writer.print(", .len = {} }};\n", .{try f.fmtIntLiteral(Type.usize, len_val)}); return local; } @@ -4199,7 +4217,7 @@ fn airSimpleCast(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(ty_op.operand); try writer.writeAll(" = "); - try f.writeCValue(writer, operand); + try f.writeCValue(writer, operand, .Other); try writer.writeAll(";\n"); return local; } @@ -4216,7 +4234,7 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(" = ("); try f.renderTypecast(writer, inst_ty); try writer.writeByte(')'); - try f.writeCValue(writer, operand); + try f.writeCValue(writer, operand, .Other); try writer.writeAll(";\n"); return local; } @@ -4237,7 +4255,7 @@ fn airBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u8) !C try writer.print(" = zig_{s}_", .{fn_name}); try writer.print("{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits }); - try f.writeCValue(writer, try f.resolveInst(operand)); + try f.writeCValue(writer, try f.resolveInst(operand), .FunctionArgument); try writer.print(", {d});\n", .{int_info.bits}); return local; } @@ -4266,9 +4284,9 @@ fn airBinOpBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u } try writer.writeByte('('); - try f.writeCValue(writer, try f.resolveInst(bin_op.lhs)); + try f.writeCValue(writer, try f.resolveInst(bin_op.lhs), .FunctionArgument); try writer.writeAll(", "); - try f.writeCValue(writer, try f.resolveInst(bin_op.rhs)); + try f.writeCValue(writer, try f.resolveInst(bin_op.rhs), .FunctionArgument); try writer.writeAll(");\n"); return local; } @@ -4287,12 +4305,12 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue const local = try f.allocLocal(inst_ty, .Mut); try writer.writeAll(" = "); if (is_struct) try writer.writeAll("{ .payload = "); - try f.writeCValue(writer, expected_value); + try f.writeCValue(writer, expected_value, .Initializer); if (is_struct) try writer.writeAll(", .is_null = false }"); try writer.writeAll(";\n"); if (is_struct) { - try f.writeCValue(writer, local); + try f.writeCValue(writer, local, .Other); try writer.writeAll(".is_null = "); } else { try writer.writeAll("if ("); @@ -4302,14 +4320,14 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue try writer.writeByte(')'); if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); try writer.writeAll(" *)"); - try f.writeCValue(writer, ptr); + try f.writeCValue(writer, ptr, .Other); try writer.writeAll(", "); - try f.writeCValue(writer, local); + try f.writeCValue(writer, local, .FunctionArgument); if (is_struct) { try writer.writeAll(".payload"); } try writer.writeAll(", "); - try f.writeCValue(writer, new_value); + try f.writeCValue(writer, new_value, .FunctionArgument); try writer.writeAll(", "); try writeMemoryOrder(writer, extra.successOrder()); try writer.writeAll(", "); @@ -4320,7 +4338,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue } else { try writer.writeAll(") {\n"); f.object.indent_writer.pushIndent(); - try f.writeCValue(writer, local); + try f.writeCValue(writer, local, .Other); try writer.writeAll(" = NULL;\n"); f.object.indent_writer.popIndent(); try writer.writeAll("}\n"); @@ -4353,9 +4371,9 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { } if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); try writer.writeAll(" *)"); - try f.writeCValue(writer, ptr); + try f.writeCValue(writer, ptr, .Other); try writer.writeAll(", "); - try f.writeCValue(writer, operand); + try f.writeCValue(writer, operand, .FunctionArgument); try writer.writeAll(", "); try writeMemoryOrder(writer, extra.ordering()); try writer.writeAll(");\n"); @@ -4379,7 +4397,7 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeByte(')'); if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); try writer.writeAll(" *)"); - try f.writeCValue(writer, ptr); + try f.writeCValue(writer, ptr, .Other); try writer.writeAll(", "); try writeMemoryOrder(writer, atomic_load.order); try writer.writeAll(");\n"); @@ -4399,9 +4417,9 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa try writer.writeByte(')'); if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); try writer.writeAll(" *)"); - try f.writeCValue(writer, ptr); + try f.writeCValue(writer, ptr, .Other); try writer.writeAll(", "); - try f.writeCValue(writer, element); + try f.writeCValue(writer, element, .FunctionArgument); try writer.print(", {s});\n", .{order}); return CValue.none; @@ -4416,11 +4434,11 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); try writer.writeAll("memset("); - try f.writeCValue(writer, dest_ptr); + try f.writeCValue(writer, dest_ptr, .FunctionArgument); try writer.writeAll(", "); - try f.writeCValue(writer, value); + try f.writeCValue(writer, value, .FunctionArgument); try writer.writeAll(", "); - try f.writeCValue(writer, len); + try f.writeCValue(writer, len, .FunctionArgument); try writer.writeAll(");\n"); return CValue.none; @@ -4435,11 +4453,11 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); try writer.writeAll("memcpy("); - try f.writeCValue(writer, dest_ptr); + try f.writeCValue(writer, dest_ptr, .FunctionArgument); try writer.writeAll(", "); - try f.writeCValue(writer, src_ptr); + try f.writeCValue(writer, src_ptr, .FunctionArgument); try writer.writeAll(", "); - try f.writeCValue(writer, len); + try f.writeCValue(writer, len, .FunctionArgument); try writer.writeAll(");\n"); return CValue.none; @@ -4457,9 +4475,9 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { if (layout.tag_size == 0) return CValue.none; try writer.writeByte('('); - try f.writeCValue(writer, union_ptr); + try f.writeCValue(writer, union_ptr, .Other); try writer.writeAll(")->tag = "); - try f.writeCValue(writer, new_tag); + try f.writeCValue(writer, new_tag, .Other); try writer.writeAll(";\n"); return CValue.none; @@ -4481,7 +4499,7 @@ fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { if (layout.tag_size == 0) return CValue.none; try writer.writeAll(" = "); - try f.writeCValue(writer, operand); + try f.writeCValue(writer, operand, .Other); try writer.writeAll(".tag;\n"); return local; } @@ -4497,7 +4515,7 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const local = try f.allocLocal(inst_ty, .Const); try writer.print(" = {s}(", .{try f.object.dg.getTagNameFn(enum_ty)}); - try f.writeCValue(writer, operand); + try f.writeCValue(writer, operand, .Other); try writer.writeAll(");\n"); try f.object.dg.fwd_decl.writer().writeAll("// This is where the fwd decl for tagName ended up\n"); @@ -4515,7 +4533,7 @@ fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue { const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = zig_errorName["); - try f.writeCValue(writer, operand); + try f.writeCValue(writer, operand, .Other); try writer.writeAll("];\n"); return local; } @@ -4600,12 +4618,12 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { var empty = true; for (elements) |element| { if (!empty) try writer.writeAll(", "); - try f.writeCValue(writer, try f.resolveInst(element)); + try f.writeCValue(writer, try f.resolveInst(element), .Initializer); empty = false; } if (inst_ty.sentinel()) |sentinel| { if (!empty) try writer.writeAll(", "); - try f.object.dg.renderValue(writer, elem_ty, sentinel, .Other); + try f.object.dg.renderValue(writer, elem_ty, sentinel, .Initializer); empty = false; } if (empty) try writer.print("{}", .{try f.fmtIntLiteral(Type.u8, Value.zero)}); @@ -4625,7 +4643,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, switch (element_ty.zigTypeTag()) { .Array => CValue{ .undef = element_ty }, else => try f.resolveInst(element), - }); + }, .Initializer); empty = false; } if (empty) try writer.print("{}", .{try f.fmtIntLiteral(Type.u8, Value.zero)}); @@ -4647,12 +4665,12 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(";\n"); try writer.writeAll("memcpy("); - try f.writeCValue(writer, local); + try f.writeCValue(writer, local, .Other); try writer.print(".{ }, ", .{fmtIdent(field_name)}); - try f.writeCValue(writer, try f.resolveInst(element)); + try f.writeCValue(writer, try f.resolveInst(element), .FunctionArgument); try writer.writeAll(", sizeof("); - try f.writeCValue(writer, local); - try writer.print(".{ }));\n", .{fmtIdent(field_name)}); + try f.renderTypecast(writer, element_ty); + try writer.writeAll("));\n"); } }, .Vector => return f.fail("TODO: C backend: implement airAggregateInit for vectors", .{}), @@ -4681,14 +4699,14 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { if (layout.tag_size != 0) { const field_index = tag_ty.enumFieldIndex(field_name).?; - var tag_val_pl: Value.Payload.U32 = .{ + var tag_pl: Value.Payload.U32 = .{ .base = .{ .tag = .enum_field_index }, .data = @intCast(u32, field_index), }; - const tag_val = Value.initPayload(&tag_val_pl.base); + const tag_val = Value.initPayload(&tag_pl.base); - var int_val_pl: Value.Payload.U64 = undefined; - const int_val = tag_val.enumToInt(tag_ty, &int_val_pl); + var int_pl: Value.Payload.U64 = undefined; + const int_val = tag_val.enumToInt(tag_ty, &int_pl); try writer.print(".tag = {}, ", .{try f.fmtIntLiteral(tag_ty, int_val)}); } @@ -4696,7 +4714,7 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { } try writer.print(".{ } = ", .{fmtIdent(field_name)}); - try f.writeCValue(writer, payload); + try f.writeCValue(writer, payload, .Initializer); if (union_ty.unionTagTypeSafety()) |_| try writer.writeByte('}'); try writer.writeAll("};\n"); @@ -4716,7 +4734,7 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { const ptr = try f.resolveInst(prefetch.ptr); const writer = f.object.writer(); try writer.writeAll("zig_prefetch("); - try f.writeCValue(writer, ptr); + try f.writeCValue(writer, ptr, .FunctionArgument); try writer.print(", {d}, {d});\n", .{ @enumToInt(prefetch.rw), prefetch.locality, }); @@ -4748,7 +4766,7 @@ fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(" = "); try writer.print("zig_wasm_memory_grow({d}, ", .{pl_op.payload}); - try f.writeCValue(writer, operand); + try f.writeCValue(writer, operand, .FunctionArgument); try writer.writeAll(");\n"); return local; } @@ -4762,7 +4780,7 @@ fn airNeg(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(un_op); const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = -"); - try f.writeCValue(writer, operand); + try f.writeCValue(writer, operand, .Other); try writer.writeAll(";\n"); return local; } @@ -4777,7 +4795,7 @@ fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, fn_name: []const u8) !CValue try writer.writeAll(" = "); try f.renderFloatFnName(fn_name, inst_ty); try writer.writeByte('('); - try f.writeCValue(writer, operand); + try f.writeCValue(writer, operand, .FunctionArgument); try writer.writeAll(");\n"); return local; } @@ -4793,9 +4811,9 @@ fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, fn_name: []const u8) !CValu try writer.writeAll(" = "); try f.renderFloatFnName(fn_name, inst_ty); try writer.writeByte('('); - try f.writeCValue(writer, lhs); + try f.writeCValue(writer, lhs, .FunctionArgument); try writer.writeAll(", "); - try f.writeCValue(writer, rhs); + try f.writeCValue(writer, rhs, .FunctionArgument); try writer.writeAll(");\n"); return local; } @@ -4813,11 +4831,11 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(" = "); try f.renderFloatFnName("fma", inst_ty); try writer.writeByte('('); - try f.writeCValue(writer, mulend1); + try f.writeCValue(writer, mulend1, .FunctionArgument); try writer.writeAll(", "); - try f.writeCValue(writer, mulend2); + try f.writeCValue(writer, mulend2, .FunctionArgument); try writer.writeAll(", "); - try f.writeCValue(writer, addend); + try f.writeCValue(writer, addend, .FunctionArgument); try writer.writeAll(");\n"); return local; } @@ -5013,15 +5031,15 @@ fn formatIntLiteral( } const split = std.math.min(int.limbs.len, limbs_count_64); - var upper_val_pl = Value.Payload.BigInt{ + var upper_pl = Value.Payload.BigInt{ .base = .{ .tag = .int_big_positive }, .data = int.limbs[split..], }; - const have_upper = !upper_val_pl.asBigInt().eqZero(); + const have_upper = !upper_pl.asBigInt().eqZero(); if (have_upper) try writer.writeByte('('); if (have_upper or !int.positive) try writer.writeAll("(uint128_t)"); if (have_upper) { - const upper_val = Value.initPayload(&upper_val_pl.base); + const upper_val = Value.initPayload(&upper_pl.base); try formatIntLiteral(.{ .ty = Type.u64, .val = upper_val, @@ -5030,11 +5048,11 @@ fn formatIntLiteral( try writer.writeAll("<<64|"); } - var lower_val_pl = Value.Payload.BigInt{ + var lower_pl = Value.Payload.BigInt{ .base = .{ .tag = .int_big_positive }, .data = int.limbs[0..split], }; - const lower_val = Value.initPayload(&lower_val_pl.base); + const lower_val = Value.initPayload(&lower_pl.base); try formatIntLiteral(.{ .ty = Type.u64, .val = lower_val, From 3d90ee50ff184dd7a94005f2769d055e460ad290 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 20 Oct 2022 02:04:40 -0400 Subject: [PATCH 34/47] cbe: allow immediate and register asm constraints in naked functions --- src/codegen/c.zig | 116 +++++++++++++++++++++++++--------------------- 1 file changed, 63 insertions(+), 53 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index b5705ad6da..d0bcba4ab2 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -3526,6 +3526,14 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { return CValue.none; } +fn asmInputNeedsLocal(constraint: []const u8, value: CValue) bool { + return switch (constraint[0]) { + '{' => true, + 'i', 'r' => false, + else => value == .constant, + }; +} + fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const extra = f.air.extraData(Air.Asm, ty_pl.payload); @@ -3551,10 +3559,9 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { break :local local; } else .none; - const output_locals_begin = f.next_local_index; - f.next_local_index += outputs.len; + const locals_begin = f.next_local_index; const constraints_extra_begin = extra_i; - for (outputs) |output, index| { + for (outputs) |output| { const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); const constraint = std.mem.sliceTo(extra_bytes, 0); const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); @@ -3562,12 +3569,17 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { // for the string, we still use the next u32 for the null terminator. extra_i += (constraint.len + name.len + (2 + 3)) / 4; - const output_ty = if (output == .none) inst_ty else f.air.typeOf(output).childType(); - if (std.mem.startsWith(u8, constraint, "={") and std.mem.endsWith(u8, constraint, "}")) { + if (constraint.len < 2 or constraint[0] != '=' or + (constraint[1] == '{' and constraint[constraint.len - 1] != '}')) + { + return f.fail("CBE: constraint not supported: '{s}'", .{constraint}); + } + + const is_reg = constraint[1] == '{'; + if (is_reg) { + const output_ty = if (output == .none) inst_ty else f.air.typeOf(output).childType(); try writer.writeAll("register "); - try f.object.dg.renderTypeAndName(writer, output_ty, .{ - .local = output_locals_begin + index, - }, .Mut, 0); + _ = try f.allocLocal(output_ty, .Mut); try writer.writeAll(" __asm(\""); try writer.writeAll(constraint["={".len .. constraint.len - "}".len]); try writer.writeAll("\")"); @@ -3576,13 +3588,9 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, .{ .undef = output_ty }, .Initializer); } try writer.writeAll(";\n"); - } else if (constraint.len < 2 or constraint[0] != '=') { - return f.fail("CBE: constraint not supported: '{s}'", .{constraint}); } } - const input_locals_begin = f.next_local_index; - f.next_local_index += inputs.len; - for (inputs) |input, index| { + for (inputs) |input| { const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); const constraint = std.mem.sliceTo(extra_bytes, 0); const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); @@ -3590,30 +3598,27 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { // for the string, we still use the next u32 for the null terminator. extra_i += (constraint.len + name.len + (2 + 3)) / 4; - const input_ty = f.air.typeOf(input); - if (std.mem.startsWith(u8, constraint, "{") and std.mem.endsWith(u8, constraint, "}")) { - try writer.writeAll("register "); - try f.object.dg.renderTypeAndName(writer, input_ty, .{ - .local = input_locals_begin + index, - }, .Const, 0); - try writer.writeAll(" __asm(\""); - try writer.writeAll(constraint["{".len .. constraint.len - "}".len]); - try writer.writeAll("\") = "); - try f.writeCValue(writer, try f.resolveInst(input), .Initializer); - try writer.writeAll(";\n"); - } else if (constraint.len >= 1 and std.mem.indexOfScalar(u8, "=+&%", constraint[0]) == null) { - const input_val = try f.resolveInst(input); - if (input_val == .constant) { - try f.object.dg.renderTypeAndName(writer, input_ty, .{ - .local = input_locals_begin + index, - }, .Const, 0); - try writer.writeAll(" = "); - try f.writeCValue(writer, input_val, .Initializer); - try writer.writeAll(";\n"); - } - } else { + if (constraint.len < 1 or std.mem.indexOfScalar(u8, "=+&%", constraint[0]) != null or + (constraint[0] == '{' and constraint[constraint.len - 1] != '}')) + { return f.fail("CBE: constraint not supported: '{s}'", .{constraint}); } + + const is_reg = constraint[0] == '{'; + const input_val = try f.resolveInst(input); + if (asmInputNeedsLocal(constraint, input_val)) { + const input_ty = f.air.typeOf(input); + if (is_reg) try writer.writeAll("register "); + _ = try f.allocLocal(input_ty, .Const); + if (is_reg) { + try writer.writeAll(" __asm(\""); + try writer.writeAll(constraint["{".len .. constraint.len - "}".len]); + try writer.writeAll("\")"); + } + try writer.writeAll(" = "); + try f.writeCValue(writer, input_val, .Initializer); + try writer.writeAll(";\n"); + } } { var clobber_i: u32 = 0; @@ -3631,6 +3636,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try writer.print("({s}", .{fmtStringLiteral(asm_source)}); extra_i = constraints_extra_begin; + var locals_index = locals_begin; try writer.writeByte(':'); for (outputs) |output, index| { const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); @@ -3642,11 +3648,13 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { if (index > 0) try writer.writeByte(','); try writer.writeByte(' '); - if (constraint[1] == '{') { - try writer.print("{s}(", .{fmtStringLiteral("=r")}); - try f.writeCValue(writer, .{ .local = output_locals_begin + index }, .Other); + if (!std.mem.eql(u8, name, "_")) try writer.print("[{s}]", .{name}); + const is_reg = constraint[1] == '{'; + try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "=r" else constraint)}); + if (is_reg) { + try f.writeCValue(writer, .{ .local = locals_index }, .Other); + locals_index += 1; } else { - try writer.print("{s}(", .{fmtStringLiteral(constraint)}); try f.writeCValueDeref(writer, try f.resolveInst(output)); } try writer.writeByte(')'); @@ -3662,17 +3670,16 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { if (index > 0) try writer.writeByte(','); try writer.writeByte(' '); - if (constraint[0] == '{') { - try writer.print("{s}(", .{fmtStringLiteral("r")}); - try f.writeCValue(writer, .{ .local = input_locals_begin + index }, .Other); - } else { - const input_val = try f.resolveInst(input); - try writer.print("{s}(", .{fmtStringLiteral(constraint)}); - try f.writeCValue(writer, if (input_val == .constant) - CValue{ .local = input_locals_begin + index } - else - input_val, .Other); - } + if (!std.mem.eql(u8, name, "_")) try writer.print("[{s}]", .{name}); + + const is_reg = constraint[0] == '{'; + const input_val = try f.resolveInst(input); + try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "r" else constraint)}); + try f.writeCValue(writer, if (asmInputNeedsLocal(constraint, input_val)) local: { + const input_local = CValue{ .local = locals_index }; + locals_index += 1; + break :local input_local; + } else input_val, .Other); try writer.writeByte(')'); } try writer.writeByte(':'); @@ -3693,7 +3700,8 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(");\n"); extra_i = constraints_extra_begin; - for (outputs) |output, index| { + locals_index = locals_begin; + for (outputs) |output| { const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); const constraint = std.mem.sliceTo(extra_bytes, 0); const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); @@ -3701,13 +3709,15 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { // for the string, we still use the next u32 for the null terminator. extra_i += (constraint.len + name.len + (2 + 3)) / 4; - if (constraint[1] == '{') { + const is_reg = constraint[1] == '{'; + if (is_reg) { try f.writeCValueDeref(writer, if (output == .none) CValue{ .local_ref = local.local } else try f.resolveInst(output)); try writer.writeAll(" = "); - try f.writeCValue(writer, .{ .local = output_locals_begin + index }, .Other); + try f.writeCValue(writer, .{ .local = locals_index }, .Other); + locals_index += 1; try writer.writeAll(";\n"); } } From 8b6a3ba74ea50602284543382ff1729a60b75122 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 20 Oct 2022 05:47:54 -0400 Subject: [PATCH 35/47] cbe: fix typedef declaration order --- src/codegen/c.zig | 299 ++++++++++++------------ test/behavior/bugs/1735.zig | 1 - test/behavior/bugs/1914.zig | 1 - test/behavior/error.zig | 1 - test/behavior/saturating_arithmetic.zig | 1 - 5 files changed, 153 insertions(+), 150 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index d0bcba4ab2..c12b028d0c 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -48,6 +48,11 @@ const BlockData = struct { result: CValue, }; +const TypedefKind = enum { + Forward, + Complete, +}; + pub const CValueMap = std.AutoHashMap(Air.Inst.Ref, CValue); pub const TypedefMap = std.ArrayHashMap( Type, @@ -249,13 +254,7 @@ pub const Function = struct { const decl_c_value = f.allocLocalValue(); gop.value_ptr.* = decl_c_value; try writer.writeAll("static "); - try f.object.dg.renderTypeAndName( - writer, - ty, - decl_c_value, - .Const, - 0, - ); + try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, .Const, 0, .Complete); try writer.writeAll(" = "); try f.object.dg.renderValue(writer, ty, val, .Initializer); try writer.writeAll(";\n "); @@ -294,6 +293,7 @@ pub const Function = struct { local_value, mutability, alignment, + .Complete, ); return local_value; } @@ -328,7 +328,7 @@ pub const Function = struct { } fn renderType(f: *Function, w: anytype, t: Type) !void { - return f.object.dg.renderType(w, t); + return f.object.dg.renderType(w, t, .Complete); } fn renderTypecast(f: *Function, w: anytype, t: Type) !void { @@ -1024,10 +1024,7 @@ pub const DeclGen = struct { } } - fn renderFunctionSignature(dg: *DeclGen, w: anytype, is_global: bool) !void { - if (!is_global) { - try w.writeAll("static "); - } + fn renderFunctionSignature(dg: *DeclGen, w: anytype, kind: TypedefKind) !void { const fn_info = dg.decl.ty.fnInfo(); if (fn_info.cc == .Naked) { try w.writeAll("ZIG_NAKED "); @@ -1039,9 +1036,9 @@ pub const DeclGen = struct { } } if (fn_info.return_type.hasRuntimeBits()) { - try dg.renderType(w, fn_info.return_type); + try dg.renderType(w, fn_info.return_type, kind); } else if (fn_info.return_type.isError()) { - try dg.renderType(w, Type.anyerror); + try dg.renderType(w, Type.anyerror, kind); } else if (fn_info.return_type.zigTypeTag() == .NoReturn) { try w.writeAll("zig_noreturn void"); } else { @@ -1058,7 +1055,7 @@ pub const DeclGen = struct { try w.writeAll(", "); } const name = CValue{ .arg = index }; - try dg.renderTypeAndName(w, param_type, name, .Const, 0); + try dg.renderTypeAndName(w, param_type, name, .Const, 0, kind); index += 1; } @@ -1071,15 +1068,15 @@ pub const DeclGen = struct { try w.writeByte(')'); } - fn renderPtrToFnTypedef(dg: *DeclGen, t: Type, fn_ty: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { + fn renderPtrToFnTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); defer buffer.deinit(); const bw = buffer.writer(); - const fn_info = fn_ty.fnInfo(); + const fn_info = t.fnInfo(); try bw.writeAll("typedef "); - try dg.renderType(bw, fn_info.return_type); + try dg.renderType(bw, fn_info.return_type, .Forward); try bw.writeAll(" (*"); const name_begin = buffer.items.len; @@ -1092,11 +1089,12 @@ pub const DeclGen = struct { var params_written: usize = 0; var index: usize = 0; while (index < param_len) : (index += 1) { - if (!fn_info.param_types[index].hasRuntimeBitsIgnoreComptime()) continue; + const param_ty = fn_info.param_types[index]; + if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; if (params_written > 0) { try bw.writeAll(", "); } - try dg.renderTypecast(bw, fn_info.param_types[index]); + try dg.renderTypeAndName(bw, param_ty, .{ .bytes = "" }, .Mut, 0, .Forward); params_written += 1; } @@ -1133,7 +1131,7 @@ pub const DeclGen = struct { var ptr_type_buf: Type.SlicePtrFieldTypeBuffer = undefined; const ptr_type = t.slicePtrFieldType(&ptr_type_buf); const ptr_name = CValue{ .bytes = "ptr" }; - try dg.renderTypeAndName(bw, ptr_type, ptr_name, .Mut, 0); + try dg.renderTypeAndName(bw, ptr_type, ptr_name, .Mut, 0, .Complete); try bw.writeAll("; size_t len; } "); const name_begin = buffer.items.len; @@ -1157,27 +1155,33 @@ pub const DeclGen = struct { return name; } - fn renderStructTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { - const struct_obj = t.castTag(.@"struct").?.data; // Handle 0 bit types elsewhere. - const fqn = try struct_obj.getFullyQualifiedName(dg.module); - defer dg.typedefs.allocator.free(fqn); + fn renderFwdTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { + // The forward declaration for T is stored with a key of *const T. + const child_ty = t.childType(); + + var fqn_buf = std.ArrayList(u8).init(dg.typedefs.allocator); + defer fqn_buf.deinit(); + + const owner_decl = dg.module.declPtr(child_ty.getOwnerDecl()); + try owner_decl.renderFullyQualifiedName(dg.module, fqn_buf.writer()); var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); defer buffer.deinit(); - const tag = "struct"; - const tagged_name_begin = buffer.items.len + "typedef ".len + tag.len + " ".len; - try buffer.writer().print("typedef " ++ tag ++ " zig_S_{} ", .{fmtIdent(fqn)}); - const tagged_name_end = buffer.items.len - " ".len; - try buffer.ensureUnusedCapacity(tagged_name_end - tagged_name_begin + ";\n".len); - const name_begin = buffer.items.len; - buffer.appendSliceAssumeCapacity(buffer.items[tagged_name_begin..tagged_name_end]); - const name_end = buffer.items.len; + const tag = switch (child_ty.zigTypeTag()) { + .Struct => "struct ", + .Union => if (child_ty.unionTagTypeSafety()) |_| "struct " else "union ", + 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) }); + const name_end = buffer.items.len - " ".len; + try buffer.ensureUnusedCapacity((name_end - name_begin) + ";\n".len); + buffer.appendSliceAssumeCapacity(buffer.items[name_begin..name_end]); buffer.appendSliceAssumeCapacity(";\n"); const rendered = buffer.toOwnedSlice(); errdefer dg.typedefs.allocator.free(rendered); - const tagged_name = rendered[tagged_name_begin..tagged_name_end]; const name = rendered[name_begin..name_end]; try dg.typedefs.ensureUnusedCapacity(1); @@ -1186,20 +1190,32 @@ pub const DeclGen = struct { .{ .name = name, .rendered = rendered }, ); - try buffer.appendSlice(tag ++ " "); - try buffer.appendSlice(tagged_name); + return name; + } + + fn renderStructTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { + var ptr_pl = Type.Payload.ElemType{ .base = .{ .tag = .single_const_pointer }, .data = t }; + const ptr_ty = Type.initPayload(&ptr_pl.base); + const name = dg.getTypedefName(ptr_ty) orelse + try dg.renderFwdTypedef(ptr_ty); + + var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); + defer buffer.deinit(); + + try buffer.appendSlice("struct "); + try buffer.appendSlice(name); try buffer.appendSlice(" {\n"); { - var it = struct_obj.fields.iterator(); + var it = t.structFields().iterator(); var empty = true; - while (it.next()) |entry| { - const field_ty = entry.value_ptr.ty; + while (it.next()) |field| { + const field_ty = field.value_ptr.ty; if (!field_ty.hasRuntimeBits()) continue; - const alignment = entry.value_ptr.abi_align; - const field_name: CValue = .{ .identifier = entry.key_ptr.* }; + const alignment = field.value_ptr.abi_align; + const field_name = CValue{ .identifier = field.key_ptr.* }; try buffer.append(' '); - try dg.renderTypeAndName(buffer.writer(), field_ty, field_name, .Mut, alignment); + try dg.renderTypeAndName(buffer.writer(), field_ty, field_name, .Mut, alignment, .Complete); try buffer.appendSlice(";\n"); empty = false; @@ -1208,16 +1224,13 @@ pub const DeclGen = struct { } try buffer.appendSlice("};\n"); - const rendered_body = buffer.toOwnedSlice(); - errdefer dg.typedefs.allocator.free(rendered_body); + const rendered = buffer.toOwnedSlice(); + errdefer dg.typedefs.allocator.free(rendered); - // We need to add another item to the TypedefMap, so we need a distinct - // type that is not used anywhere, but is still uniquely associated with - // this type, so use an empty struct which references our unique decls. try dg.typedefs.ensureUnusedCapacity(1); dg.typedefs.putAssumeCapacityNoClobber( - try Type.Tag.empty_struct.create(dg.typedefs_arena, &struct_obj.namespace), - .{ .name = undefined, .rendered = rendered_body }, + try t.copy(dg.typedefs_arena), + .{ .name = name, .rendered = rendered }, ); return name; @@ -1240,7 +1253,7 @@ pub const DeclGen = struct { defer dg.typedefs.allocator.free(field_name); try buffer.append(' '); - try dg.renderTypeAndName(buffer.writer(), field_ty, .{ .identifier = field_name }, .Mut, 0); + try dg.renderTypeAndName(buffer.writer(), field_ty, .{ .identifier = field_name }, .Mut, 0, .Complete); try buffer.appendSlice(";\n"); empty = false; @@ -1265,37 +1278,16 @@ pub const DeclGen = struct { } fn renderUnionTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { - const union_obj = t.cast(Type.Payload.Union).?.data; - const fqn = try union_obj.getFullyQualifiedName(dg.module); - defer dg.typedefs.allocator.free(fqn); + var ptr_pl = Type.Payload.ElemType{ .base = .{ .tag = .single_const_pointer }, .data = t }; + const ptr_ty = Type.initPayload(&ptr_pl.base); + const name = dg.getTypedefName(ptr_ty) orelse + try dg.renderFwdTypedef(ptr_ty); var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); defer buffer.deinit(); - const tag: []const u8 = if (t.unionTagTypeSafety()) |_| "struct" else "union"; - const tagged_name_begin = buffer.items.len + "typedef ".len + tag.len + " ".len; - try buffer.writer().print("typedef {s} zig_S_{} ", .{ tag, fmtIdent(fqn) }); - const tagged_name_end = buffer.items.len - " ".len; - try buffer.ensureUnusedCapacity(tagged_name_end - tagged_name_begin + ";\n".len); - const name_begin = buffer.items.len; - buffer.appendSliceAssumeCapacity(buffer.items[tagged_name_begin..tagged_name_end]); - const name_end = buffer.items.len; - buffer.appendSliceAssumeCapacity(";\n"); - - const rendered = buffer.toOwnedSlice(); - errdefer dg.typedefs.allocator.free(rendered); - const tagged_name = rendered[tagged_name_begin..tagged_name_end]; - const name = rendered[name_begin..name_end]; - - try dg.typedefs.ensureUnusedCapacity(1); - dg.typedefs.putAssumeCapacityNoClobber( - try t.copy(dg.typedefs_arena), - .{ .name = name, .rendered = rendered }, - ); - - try buffer.appendSlice(tag); - try buffer.append(' '); - try buffer.appendSlice(tagged_name); + try buffer.appendSlice(if (t.unionTagTypeSafety()) |_| "struct " else "union "); + try buffer.appendSlice(name); try buffer.appendSlice(" {\n"); const indent = if (t.unionTagTypeSafety()) |tag_ty| indent: { @@ -1303,7 +1295,7 @@ pub const DeclGen = struct { const layout = t.unionGetLayout(target); if (layout.tag_size != 0) { try buffer.append(' '); - try dg.renderTypeAndName(buffer.writer(), tag_ty, .{ .identifier = "tag" }, .Mut, 0); + try dg.renderTypeAndName(buffer.writer(), tag_ty, .{ .identifier = "tag" }, .Mut, 0, .Complete); try buffer.appendSlice(";\n"); } try buffer.appendSlice(" union {\n"); @@ -1313,14 +1305,14 @@ pub const DeclGen = struct { { var it = t.unionFields().iterator(); var empty = true; - while (it.next()) |entry| { - const field_ty = entry.value_ptr.ty; + while (it.next()) |field| { + const field_ty = field.value_ptr.ty; if (!field_ty.hasRuntimeBits()) continue; - const alignment = entry.value_ptr.abi_align; - const field_name: CValue = .{ .identifier = entry.key_ptr.* }; + const alignment = field.value_ptr.abi_align; + const field_name = CValue{ .identifier = field.key_ptr.* }; try buffer.appendSlice(indent); - try dg.renderTypeAndName(buffer.writer(), field_ty, field_name, .Mut, alignment); + try dg.renderTypeAndName(buffer.writer(), field_ty, field_name, .Mut, alignment, .Complete); try buffer.appendSlice(";\n"); empty = false; @@ -1334,16 +1326,13 @@ pub const DeclGen = struct { if (t.unionTagTypeSafety()) |_| try buffer.appendSlice(" } payload;\n"); try buffer.appendSlice("};\n"); - const rendered_body = buffer.toOwnedSlice(); - errdefer dg.typedefs.allocator.free(rendered_body); + const rendered = buffer.toOwnedSlice(); + errdefer dg.typedefs.allocator.free(rendered); - // We need to add another item to the TypedefMap, so we need a distinct - // type that is not used anywhere, but is still uniquely associated with - // this type, so use an empty struct which references our unique decls. try dg.typedefs.ensureUnusedCapacity(1); dg.typedefs.putAssumeCapacityNoClobber( - try Type.Tag.empty_struct.create(dg.typedefs_arena, &union_obj.namespace), - .{ .name = undefined, .rendered = rendered_body }, + try t.copy(dg.typedefs_arena), + .{ .name = name, .rendered = rendered }, ); return name; @@ -1363,11 +1352,11 @@ pub const DeclGen = struct { const error_align = Type.anyerror.abiAlignment(target); if (error_align > payload_align) { try bw.writeAll("typedef struct { "); - try dg.renderTypeAndName(bw, payload_ty, payload_name, .Mut, 0); + try dg.renderTypeAndName(bw, payload_ty, payload_name, .Mut, 0, .Complete); try bw.writeAll("; uint16_t error; } "); } else { try bw.writeAll("typedef struct { uint16_t error; "); - try dg.renderTypeAndName(bw, payload_ty, payload_name, .Mut, 0); + try dg.renderTypeAndName(bw, payload_ty, payload_name, .Mut, 0, .Complete); try bw.writeAll("; } "); } @@ -1398,7 +1387,7 @@ pub const DeclGen = struct { const bw = buffer.writer(); try bw.writeAll("typedef "); - try dg.renderType(bw, info.elem_type); + try dg.renderType(bw, info.elem_type, .Complete); const name_begin = buffer.items.len + " ".len; try bw.print(" zig_A_{}_{d}", .{ typeToCIdentifier(info.elem_type, dg.module), info.len }); @@ -1426,7 +1415,7 @@ pub const DeclGen = struct { try bw.writeAll("typedef struct { "); const payload_name = CValue{ .bytes = "payload" }; - try dg.renderTypeAndName(bw, child_type, payload_name, .Mut, 0); + try dg.renderTypeAndName(bw, child_type, payload_name, .Mut, 0, .Complete); try bw.writeAll("; bool is_null; } "); const name_begin = buffer.items.len; @@ -1488,7 +1477,12 @@ pub const DeclGen = struct { /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | /// | `renderType` | "uint8_t *" | "zig_A_uint8_t_10" | /// - fn renderType(dg: *DeclGen, w: anytype, t: Type) error{ OutOfMemory, AnalysisFail }!void { + fn renderType( + dg: *DeclGen, + w: anytype, + t: Type, + kind: TypedefKind, + ) error{ OutOfMemory, AnalysisFail }!void { const target = dg.module.getTarget(); switch (t.zigTypeTag()) { @@ -1540,10 +1534,11 @@ pub const DeclGen = struct { } }, .Pointer => { + const child_ty = t.childType(); if (t.isSlice()) { var slice_pl = Type.Payload.ElemType{ .base = .{ .tag = if (t.ptrIsMutable()) .mut_slice else .const_slice }, - .data = t.childType(), + .data = child_ty, }; const slice_ty = Type.initPayload(&slice_pl.base); @@ -1553,31 +1548,27 @@ pub const DeclGen = struct { return w.writeAll(name); } - if (t.castPtrToFn()) |fn_ty| { - const name = dg.getTypedefName(t) orelse - try dg.renderPtrToFnTypedef(t, fn_ty); + if (child_ty.zigTypeTag() == .Fn) { + const name = dg.getTypedefName(child_ty) orelse + try dg.renderPtrToFnTypedef(child_ty); return w.writeAll(name); } - const child_ty = t.childType(); if (t.isCPtr() and child_ty.eql(Type.u8, dg.module) and - (dg.decl.val.tag() == .extern_fn or std.mem.eql(u8, std.mem.span(dg.decl.name), "main"))) + (dg.decl.val.tag() == .extern_fn or + std.mem.eql(u8, std.mem.span(dg.decl.name), "main"))) { // This is a hack, since the c compiler expects a lot of external // library functions to have char pointers in their signatures, but // u8 and i8 produce unsigned char and signed char respectively, - // which in C are not very usefully different than char. + // which in C are (not very usefully) different than char. try w.writeAll("char"); } else { - try dg.renderType(w, child_ty); - } - if (t.isConstPtr()) { - try w.writeAll(" const"); - } - if (t.isVolatilePtr()) { - try w.writeAll(" volatile"); + try dg.renderType(w, child_ty, .Forward); } + if (t.isConstPtr()) try w.writeAll(" const"); + if (t.isVolatilePtr()) try w.writeAll(" volatile"); return w.writeAll(" *"); }, .Array => { @@ -1601,7 +1592,7 @@ pub const DeclGen = struct { } if (t.optionalReprIsPayload()) { - return dg.renderType(w, child_type); + return dg.renderType(w, child_type, .Complete); } const name = dg.getTypedefName(t) orelse @@ -1617,7 +1608,7 @@ pub const DeclGen = struct { const payload_ty = t.errorUnionPayload(); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { - return dg.renderType(w, Type.anyerror); + return dg.renderType(w, Type.anyerror, .Complete); } var error_union_pl = Type.Payload.ErrorUnion{ @@ -1630,17 +1621,26 @@ pub const DeclGen = struct { return w.writeAll(name); }, - .Struct => { - const name = dg.getTypedefName(t) orelse if (t.isTupleOrAnonStruct()) - try dg.renderTupleTypedef(t) - else - try dg.renderStructTypedef(t); + .Struct, .Union => |tag| if (kind == .Complete or t.isTupleOrAnonStruct()) { + const name = dg.getTypedefName(t) orelse switch (tag) { + .Struct => if (t.isTupleOrAnonStruct()) + try dg.renderTupleTypedef(t) + else + try dg.renderStructTypedef(t), + .Union => try dg.renderUnionTypedef(t), + else => unreachable, + }; return w.writeAll(name); - }, - .Union => { - const name = dg.getTypedefName(t) orelse - try dg.renderUnionTypedef(t); + } else { + var ptr_pl = Type.Payload.ElemType{ + .base = .{ .tag = .single_const_pointer }, + .data = t, + }; + const ptr_ty = Type.initPayload(&ptr_pl.base); + + const name = dg.getTypedefName(ptr_ty) orelse + try dg.renderFwdTypedef(ptr_ty); return w.writeAll(name); }, @@ -1649,7 +1649,7 @@ pub const DeclGen = struct { var int_tag_buf: Type.Payload.Bits = undefined; const int_tag_ty = t.intTagType(&int_tag_buf); - try dg.renderType(w, int_tag_ty); + try dg.renderType(w, int_tag_ty, kind); }, .Opaque => switch (t.tag()) { .anyopaque => try w.writeAll("void"), @@ -1701,7 +1701,7 @@ pub const DeclGen = struct { ty: Type, ) error{ OutOfMemory, AnalysisFail }!void { const name = CValue{ .bytes = "" }; - return renderTypeAndName(dg, w, ty, name, .Mut, 0); + return renderTypeAndName(dg, w, ty, name, .Mut, 0, .Complete); } /// Renders a type and name in field declaration/definition format. @@ -1720,6 +1720,7 @@ pub const DeclGen = struct { name: CValue, mutability: Mutability, alignment: u32, + kind: TypedefKind, ) error{ OutOfMemory, AnalysisFail }!void { var suffix = std.ArrayList(u8).init(dg.gpa); defer suffix.deinit(); @@ -1736,7 +1737,7 @@ pub const DeclGen = struct { if (alignment != 0) try w.print("ZIG_ALIGN({}) ", .{alignment}); - try dg.renderType(w, render_ty); + try dg.renderType(w, render_ty, kind); const const_prefix = switch (mutability) { .Const => "const ", @@ -1755,11 +1756,11 @@ pub const DeclGen = struct { const name_slice_ty = Type.initTag(.const_slice_u8_sentinel_0); try buffer.appendSlice("static "); - try dg.renderType(bw, name_slice_ty); + try dg.renderType(bw, name_slice_ty, .Complete); const name_begin = buffer.items.len + " ".len; try bw.print(" zig_tagName_{}(", .{typeToCIdentifier(enum_ty, dg.module)}); const name_end = buffer.items.len - "(".len; - try dg.renderTypeAndName(bw, enum_ty, .{ .identifier = "tag" }, .Const, 0); + try dg.renderTypeAndName(bw, enum_ty, .{ .identifier = "tag" }, .Const, 0, .Complete); try buffer.appendSlice(") {\n switch (tag) {\n"); for (enum_ty.enumFields().keys()) |name, index| { const name_z = try dg.typedefs.allocator.dupeZ(u8, name); @@ -1785,7 +1786,7 @@ pub const DeclGen = struct { const len_val = Value.initPayload(&len_pl.base); try bw.print(" case {}: {{\n static ", .{try dg.fmtIntLiteral(enum_ty, int_val)}); - try dg.renderTypeAndName(bw, name_ty, .{ .identifier = "name" }, .Const, 0); + try dg.renderTypeAndName(bw, name_ty, .{ .identifier = "name" }, .Const, 0, .Complete); try buffer.appendSlice(" = "); try dg.renderValue(bw, name_ty, name_val, .Initializer); try buffer.appendSlice(";\n return ("); @@ -1948,7 +1949,7 @@ pub fn genErrDecls(o: *Object) !void { const name_val = Value.initPayload(&name_pl.base); try writer.writeAll("static "); - try o.dg.renderTypeAndName(writer, name_ty, .{ .identifier = identifier }, .Const, 0); + try o.dg.renderTypeAndName(writer, name_ty, .{ .identifier = identifier }, .Const, 0, .Complete); try writer.writeAll(" = "); try o.dg.renderValue(writer, name_ty, name_val, .Initializer); try writer.writeAll(";\n"); @@ -1961,7 +1962,7 @@ pub fn genErrDecls(o: *Object) !void { const name_array_ty = Type.initPayload(&name_array_ty_pl.base); try writer.writeAll("static "); - try o.dg.renderTypeAndName(writer, name_array_ty, .{ .identifier = name_prefix }, .Const, 0); + try o.dg.renderTypeAndName(writer, name_array_ty, .{ .identifier = name_prefix }, .Const, 0, .Complete); try writer.writeAll(" = {"); for (o.dg.module.error_name_list.items) |name, value| { if (value != 0) try writer.writeByte(','); @@ -1988,14 +1989,13 @@ pub fn genFunc(f: *Function) !void { const is_global = o.dg.module.decl_exports.contains(f.func.owner_decl); const fwd_decl_writer = o.dg.fwd_decl.writer(); - if (is_global) { - try fwd_decl_writer.writeAll("ZIG_EXTERN_C "); - } - try o.dg.renderFunctionSignature(fwd_decl_writer, is_global); + try fwd_decl_writer.writeAll(if (is_global) "ZIG_EXTERN_C " else "static "); + try o.dg.renderFunctionSignature(fwd_decl_writer, .Forward); try fwd_decl_writer.writeAll(";\n"); try o.indent_writer.insertNewline(); - try o.dg.renderFunctionSignature(o.writer(), is_global); + if (!is_global) try o.writer().writeAll("static "); + try o.dg.renderFunctionSignature(o.writer(), .Complete); try o.writer().writeByte(' '); // In case we need to use the header, populate it with a copy of the function @@ -2029,7 +2029,7 @@ pub fn genDecl(o: *Object) !void { if (tv.val.tag() == .extern_fn) { const writer = o.writer(); try writer.writeAll("ZIG_EXTERN_C "); - try o.dg.renderFunctionSignature(writer, true); + try o.dg.renderFunctionSignature(writer, .Forward); try writer.writeAll(";\n"); } else if (tv.val.castTag(.variable)) |var_payload| { const variable: *Module.Var = var_payload.data; @@ -2048,7 +2048,7 @@ pub fn genDecl(o: *Object) !void { .decl = o.dg.decl_index, }; - try o.dg.renderTypeAndName(fwd_decl_writer, o.dg.decl.ty, decl_c_value, .Mut, o.dg.decl.@"align"); + 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"); if (variable.is_extern or variable.init.isUndefDeep()) { @@ -2057,7 +2057,7 @@ pub fn genDecl(o: *Object) !void { try o.indent_writer.insertNewline(); const w = o.writer(); - try o.dg.renderTypeAndName(w, o.dg.decl.ty, decl_c_value, .Mut, o.dg.decl.@"align"); + 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) { try o.dg.renderValue(w, tv.ty, variable.init, .Initializer); @@ -2072,7 +2072,7 @@ pub fn genDecl(o: *Object) !void { // https://github.com/ziglang/zig/issues/7582 const decl_c_value: CValue = .{ .decl = o.dg.decl_index }; - try o.dg.renderTypeAndName(writer, tv.ty, decl_c_value, .Mut, o.dg.decl.@"align"); + try o.dg.renderTypeAndName(writer, tv.ty, decl_c_value, .Mut, o.dg.decl.@"align", .Complete); try writer.writeAll(" = "); try o.dg.renderValue(writer, tv.ty, tv.val, .Initializer); @@ -2095,7 +2095,7 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { const is_global = dg.declIsGlobal(tv); if (is_global) { try writer.writeAll("ZIG_EXTERN_C "); - try dg.renderFunctionSignature(writer, is_global); + try dg.renderFunctionSignature(writer, .Complete); try dg.fwd_decl.appendSlice(";\n"); } }, @@ -2760,8 +2760,10 @@ fn airWrapOp(f: *Function, inst: Air.Inst.Index, operator: []const u8, fn_name: const bits = int_info.bits; // if it's an unsigned int with non-arbitrary bit size then we can just add - if (int_info.signedness == .unsigned and bits == toCIntBits(bits)) { - return try airBinOp(f, inst, operator); + if (toCIntBits(bits)) |c_bits| { + if (int_info.signedness == .unsigned and bits == c_bits) { + return try airBinOp(f, inst, operator); + } } if (bits > 64) return f.fail("TODO: C backend: airWrapOp for large integers", .{}); @@ -3899,9 +3901,11 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc } const payload = if (struct_ty.tag() == .union_tagged or struct_ty.tag() == .union_safety_tagged) "payload." else ""; + // Ensure complete type definition is visible before accessing fields. + try f.renderType(std.io.null_writer, struct_ty); + const inst_ty = f.air.typeOfIndex(inst); const local = try f.allocLocal(inst_ty, .Const); - if (field_val_ty.hasRuntimeBitsIgnoreComptime()) { try writer.writeAll(" = &"); try f.writeCValueDeref(writer, struct_ptr); @@ -3941,6 +3945,9 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { }; const payload = if (struct_ty.tag() == .union_tagged or struct_ty.tag() == .union_safety_tagged) "payload." else ""; + // Ensure complete type definition is visible before accessing fields. + try f.renderType(std.io.null_writer, struct_ty); + const inst_ty = f.air.typeOfIndex(inst); const is_array = inst_ty.zigTypeTag() == .Array; const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const); @@ -4326,7 +4333,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue try writer.writeAll("if ("); } try writer.print("zig_cmpxchg_{s}((zig_atomic(", .{flavor}); - try f.object.dg.renderTypecast(writer, ptr_ty.elemType()); + try f.renderTypecast(writer, ptr_ty.elemType()); try writer.writeByte(')'); if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); try writer.writeAll(" *)"); @@ -4371,12 +4378,12 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { switch (extra.op()) { else => { try writer.writeAll("zig_atomic("); - try f.object.dg.renderTypecast(writer, ptr_ty.elemType()); + try f.renderTypecast(writer, ptr_ty.elemType()); try writer.writeByte(')'); }, .Nand, .Min, .Max => { // These are missing from stdatomic.h, so no atomic types for now. - try f.object.dg.renderTypecast(writer, ptr_ty.elemType()); + try f.renderTypecast(writer, ptr_ty.elemType()); }, } if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); @@ -4403,7 +4410,7 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); try writer.writeAll(" = zig_atomic_load((zig_atomic("); - try f.object.dg.renderTypecast(writer, ptr_ty.elemType()); + try f.renderTypecast(writer, ptr_ty.elemType()); try writer.writeByte(')'); if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); try writer.writeAll(" *)"); @@ -4423,7 +4430,7 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa const writer = f.object.writer(); try writer.writeAll("zig_atomic_store((zig_atomic("); - try f.object.dg.renderTypecast(writer, ptr_ty.elemType()); + try f.renderTypecast(writer, ptr_ty.elemType()); try writer.writeByte(')'); if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); try writer.writeAll(" *)"); diff --git a/test/behavior/bugs/1735.zig b/test/behavior/bugs/1735.zig index b94b4257b6..5deca6bfa5 100644 --- a/test/behavior/bugs/1735.zig +++ b/test/behavior/bugs/1735.zig @@ -43,7 +43,6 @@ const a = struct { test "initialization" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; var t = a.init(); try std.testing.expect(t.foo.len == 0); } diff --git a/test/behavior/bugs/1914.zig b/test/behavior/bugs/1914.zig index 67b2e90930..bd3f830e60 100644 --- a/test/behavior/bugs/1914.zig +++ b/test/behavior/bugs/1914.zig @@ -12,7 +12,6 @@ const b_list: []B = &[_]B{}; const a = A{ .b_list_pointer = &b_list }; test "segfault bug" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const assert = std.debug.assert; const obj = B{ .a_pointer = &a }; assert(obj.a_pointer == &a); // this makes zig crash diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 25ac894c6c..2013cbcfa3 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -470,7 +470,6 @@ test "function pointer with return type that is error union with payload which i return error.SkipZigTest; } - 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_aarch64) return error.SkipZigTest; // TODO diff --git a/test/behavior/saturating_arithmetic.zig b/test/behavior/saturating_arithmetic.zig index 16bc32a5f1..4ab01ecddc 100644 --- a/test/behavior/saturating_arithmetic.zig +++ b/test/behavior/saturating_arithmetic.zig @@ -8,7 +8,6 @@ test "saturating add" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { From 65a48df5324278c3d876e168b523894c8662e546 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 23 Oct 2022 08:12:10 -0400 Subject: [PATCH 36/47] cbe: fix globals that reference functions Global constant initializers can reference functions, so forward declare the constants and initialize them later with the function definitions, which guarantees that they appear after all declarations. --- src/codegen/c.zig | 20 +++++++++++--------- src/link/C.zig | 14 ++++---------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index c12b028d0c..5f62b93922 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -2027,10 +2027,10 @@ pub fn genDecl(o: *Object) !void { .val = o.dg.decl.val, }; if (tv.val.tag() == .extern_fn) { - const writer = o.writer(); - try writer.writeAll("ZIG_EXTERN_C "); - try o.dg.renderFunctionSignature(writer, .Forward); - try writer.writeAll(";\n"); + const fwd_decl_writer = o.dg.fwd_decl.writer(); + try fwd_decl_writer.writeAll("ZIG_EXTERN_C "); + try o.dg.renderFunctionSignature(fwd_decl_writer, .Forward); + try fwd_decl_writer.writeAll(";\n"); } else if (tv.val.castTag(.variable)) |var_payload| { const variable: *Module.Var = var_payload.data; const is_global = o.dg.declIsGlobal(tv) or variable.is_extern; @@ -2055,7 +2055,6 @@ pub fn genDecl(o: *Object) !void { return; } - try o.indent_writer.insertNewline(); const w = o.writer(); try o.dg.renderTypeAndName(w, o.dg.decl.ty, decl_c_value, .Mut, o.dg.decl.@"align", .Complete); try w.writeAll(" = "); @@ -2065,15 +2064,18 @@ pub fn genDecl(o: *Object) !void { try w.writeByte(';'); try o.indent_writer.insertNewline(); } else { + const decl_c_value: CValue = .{ .decl = o.dg.decl_index }; + + const fwd_decl_writer = o.dg.fwd_decl.writer(); + try fwd_decl_writer.writeAll("static "); + try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, .Mut, o.dg.decl.@"align", .Complete); + try fwd_decl_writer.writeAll(";\n"); + const writer = o.writer(); try writer.writeAll("static "); - // TODO ask the Decl if it is const // https://github.com/ziglang/zig/issues/7582 - - const decl_c_value: CValue = .{ .decl = o.dg.decl_index }; try o.dg.renderTypeAndName(writer, tv.ty, decl_c_value, .Mut, o.dg.decl.@"align", .Complete); - try writer.writeAll(" = "); try o.dg.renderValue(writer, tv.ty, tv.val, .Initializer); try writer.writeAll(";\n"); diff --git a/src/link/C.zig b/src/link/C.zig index 8509e4ae8a..a52ac63df5 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -301,11 +301,10 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) }; f.file_size += f.typedef_buf.items.len; - // Now the function bodies. - try f.all_buffers.ensureUnusedCapacity(gpa, f.fn_count); - for (decl_keys) |decl_index, i| - if (module.declPtr(decl_index).getFunction() != null) - f.appendBufAssumeCapacity(decl_values[i].code.items); + // Now the code. + try f.all_buffers.ensureUnusedCapacity(gpa, decl_values.len); + for (decl_values) |decl| + f.appendBufAssumeCapacity(decl.code.items); const file = self.base.file.?; try file.setEndPos(f.file_size); @@ -322,7 +321,6 @@ const Flush = struct { all_buffers: std.ArrayListUnmanaged(std.os.iovec_const) = .{}, /// Keeps track of the total bytes of `all_buffers`. file_size: u64 = 0, - fn_count: usize = 0, const Typedefs = std.HashMapUnmanaged( Type, @@ -435,10 +433,6 @@ fn flushDecl(self: *C, f: *Flush, decl_index: Module.Decl.Index) FlushDeclError! try self.flushTypedefs(f, decl_block.typedefs); try f.all_buffers.ensureUnusedCapacity(gpa, 2); f.appendBufAssumeCapacity(decl_block.fwd_decl.items); - if (decl.getFunction()) |_| - f.fn_count += 1 - else - f.appendBufAssumeCapacity(decl_block.code.items); } pub fn flushEmitH(module: *Module) !void { From 1bab8548688ec13d29abbf3820bb2f9723e09c66 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 23 Oct 2022 12:55:20 -0400 Subject: [PATCH 37/47] cbe: implement 128-bit and fix smaller integer builtins --- lib/include/zig.h | 2546 +++++++++++------------ src/codegen/c.zig | 1117 +++++----- test/behavior/align.zig | 2 - test/behavior/bugs/2114.zig | 1 - test/behavior/cast.zig | 2 - test/behavior/floatop.zig | 3 - test/behavior/int128.zig | 1 - test/behavior/math.zig | 10 - test/behavior/muladd.zig | 1 - test/behavior/packed-struct.zig | 1 - test/behavior/ptrcast.zig | 1 - test/behavior/saturating_arithmetic.zig | 5 - test/behavior/sizeof_and_typeof.zig | 1 - test/behavior/widening.zig | 2 - 14 files changed, 1734 insertions(+), 1959 deletions(-) diff --git a/lib/include/zig.h b/lib/include/zig.h index 41b7406fa2..45e573d092 100644 --- a/lib/include/zig.h +++ b/lib/include/zig.h @@ -1,103 +1,108 @@ #undef linux +#define __STDC_WANT_IEC_60559_TYPES_EXT__ +#include +#include +#include + +#if defined(__has_builtin) +#define zig_has_builtin(builtin) __has_builtin(__builtin_##builtin) +#else +#define zig_has_builtin(builtin) 0 +#endif + +#if defined(__has_attribute) +#define zig_has_attribute(attribute) __has_attribute(attribute) +#else +#define zig_has_attribute(attribute) 0 +#endif + #if __STDC_VERSION__ >= 201112L -#define zig_noreturn _Noreturn #define zig_threadlocal thread_local #elif __GNUC__ -#define zig_noreturn __attribute__ ((noreturn)) #define zig_threadlocal __thread #elif _MSC_VER -#define zig_noreturn __declspec(noreturn) #define zig_threadlocal __declspec(thread) #else -#define zig_noreturn #define zig_threadlocal zig_threadlocal_unavailable #endif -#if defined(_MSC_VER) -#define ZIG_NAKED __declspec(naked) +#if zig_has_attribute(naked) +#define zig_naked __attribute__((naked)) +#elif defined(_MSC_VER) +#define zig_naked __declspec(naked) #else -#define ZIG_NAKED __attribute__((naked)) +#define zig_naked zig_naked_unavailable #endif -#if __GNUC__ -#define ZIG_COLD __attribute__ ((cold)) +#if zig_has_attribute(cold) +#define zig_cold __attribute__((cold)) #else -#define ZIG_COLD +#define zig_cold #endif #if __STDC_VERSION__ >= 199901L -#define ZIG_RESTRICT restrict +#define zig_restrict restrict #elif defined(__GNUC__) -#define ZIG_RESTRICT __restrict +#define zig_restrict __restrict #else -#define ZIG_RESTRICT +#define zig_restrict #endif #if __STDC_VERSION__ >= 201112L -#include -#define ZIG_ALIGN(alignment) alignas(alignment) -#elif defined(__GNUC__) -#define ZIG_ALIGN(alignment) __attribute__((aligned(alignment))) +#define zig_align(alignment) _Alignas(alignment) +#elif zig_has_attribute(aligned) +#define zig_align(alignment) __attribute__((aligned(alignment))) +#elif _MSC_VER #else -#define ZIG_ALIGN(alignment) zig_compile_error("the C compiler being used does not support aligning variables") +#error the C compiler being used does not support aligning variables #endif -#if __STDC_VERSION__ >= 199901L -#include -#else -#define bool unsigned char -#define true 1 -#define false 0 -#endif - -#if defined(__GNUC__) +#if zig_has_builtin(unreachable) #define zig_unreachable() __builtin_unreachable() #else #define zig_unreachable() #endif -#ifdef __cplusplus -#define ZIG_EXTERN_C extern "C" +#if defined(__cplusplus) +#define zig_extern_c extern "C" #else -#define ZIG_EXTERN_C +#define zig_extern_c #endif -#if defined(_MSC_VER) -#define zig_breakpoint() __debugbreak() -#elif defined(__MINGW32__) || defined(__MINGW64__) -#define zig_breakpoint() __debugbreak() -#elif defined(__clang__) +#if zig_has_builtin(debugtrap) #define zig_breakpoint() __builtin_debugtrap() -#elif defined(__GNUC__) +#elif zig_has_builtin(trap) #define zig_breakpoint() __builtin_trap() +#elif defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__) +#define zig_breakpoint() __debugbreak() #elif defined(__i386__) || defined(__x86_64__) #define zig_breakpoint() __asm__ volatile("int $0x03"); #else #define zig_breakpoint() raise(SIGTRAP) #endif -#if defined(_MSC_VER) -#define zig_return_address() _ReturnAddress() -#elif defined(__GNUC__) +#if zig_has_builtin(return_address) #define zig_return_address() __builtin_extract_return_addr(__builtin_return_address(0)) +#elif defined(_MSC_VER) +#define zig_return_address() _ReturnAddress() #else #define zig_return_address() 0 #endif -#if defined(__GNUC__) +#if zig_has_builtin(frame_address) #define zig_frame_address() __builtin_frame_address(0) #else #define zig_frame_address() 0 #endif -#if defined(__GNUC__) +#if zig_has_builtin(prefetch) #define zig_prefetch(addr, rw, locality) __builtin_prefetch(addr, rw, locality) #else #define zig_prefetch(addr, rw, locality) #endif -#if defined(__clang__) +#if zig_has_builtin(memory_size) && zig_has_builtin(memory_grow) #define zig_wasm_memory_size(index) __builtin_wasm_memory_size(index) #define zig_wasm_memory_grow(index, delta) __builtin_wasm_memory_grow(index, delta) #else @@ -130,8 +135,8 @@ #define memory_order_acq_rel __ATOMIC_ACQ_REL #define memory_order_seq_cst __ATOMIC_SEQ_CST #define zig_atomic(type) type -#define zig_cmpxchg_strong(obj, expected, desired, succ, fail) __atomic_compare_exchange_n(obj, &(expected), desired, false, succ, fail) -#define zig_cmpxchg_weak(obj, expected, desired, succ, fail) __atomic_compare_exchange_n(obj, &(expected), desired, true , succ, fail) +#define zig_cmpxchg_strong(obj, expected, desired, succ, fail) __atomic_compare_exchange_n(obj, &(expected), desired, zig_false, succ, fail) +#define zig_cmpxchg_weak(obj, expected, desired, succ, fail) __atomic_compare_exchange_n(obj, &(expected), desired, zig_true , succ, fail) #define zig_atomicrmw_xchg(obj, arg, order) __atomic_exchange_n(obj, arg, order) #define zig_atomicrmw_add(obj, arg, order) __atomic_fetch_add (obj, arg, order) #define zig_atomicrmw_sub(obj, arg, order) __atomic_fetch_sub (obj, arg, order) @@ -168,1367 +173,1259 @@ #define zig_fence(order) zig_unimplemented() #endif -#include -#include -#include +#if __STDC_VERSION__ >= 201112L +#define zig_noreturn _Noreturn void +#define zig_threadlocal thread_local +#elif __GNUC__ +#define zig_noreturn __attribute__ ((noreturn)) void +#define zig_threadlocal __thread +#elif _MSC_VER +#define zig_noreturn __declspec(noreturn) void +#define zig_threadlocal __declspec(thread) +#else +#define zig_noreturn void +#define zig_threadlocal zig_threadlocal_unavailable +#endif -#define int128_t __int128 -#define uint128_t unsigned __int128 -#define UINT128_MAX (((uint128_t)UINT64_MAX<<64|UINT64_MAX)) -ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t); -ZIG_EXTERN_C void *memset (void *, int, size_t); -ZIG_EXTERN_C int64_t __addodi4(int64_t lhs, int64_t rhs, int *overflow); -ZIG_EXTERN_C int128_t __addoti4(int128_t lhs, int128_t rhs, int *overflow); -ZIG_EXTERN_C uint64_t __uaddodi4(uint64_t lhs, uint64_t rhs, int *overflow); -ZIG_EXTERN_C uint128_t __uaddoti4(uint128_t lhs, uint128_t rhs, int *overflow); -ZIG_EXTERN_C int32_t __subosi4(int32_t lhs, int32_t rhs, int *overflow); -ZIG_EXTERN_C int64_t __subodi4(int64_t lhs, int64_t rhs, int *overflow); -ZIG_EXTERN_C int128_t __suboti4(int128_t lhs, int128_t rhs, int *overflow); -ZIG_EXTERN_C uint32_t __usubosi4(uint32_t lhs, uint32_t rhs, int *overflow); -ZIG_EXTERN_C uint64_t __usubodi4(uint64_t lhs, uint64_t rhs, int *overflow); -ZIG_EXTERN_C uint128_t __usuboti4(uint128_t lhs, uint128_t rhs, int *overflow); -ZIG_EXTERN_C int64_t __mulodi4(int64_t lhs, int64_t rhs, int *overflow); -ZIG_EXTERN_C int128_t __muloti4(int128_t lhs, int128_t rhs, int *overflow); -ZIG_EXTERN_C uint64_t __umulodi4(uint64_t lhs, uint64_t rhs, int *overflow); -ZIG_EXTERN_C uint128_t __umuloti4(uint128_t lhs, uint128_t rhs, int *overflow); +#define zig_bitSizeOf(T) (CHAR_BIT * sizeof(T)) +typedef void zig_void; -static inline uint8_t zig_addw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) { - uint8_t thresh = max - rhs; - if (lhs > thresh) { - return lhs - thresh - 1; - } else { - return lhs + rhs; +#if defined(__cplusplus) +typedef bool zig_bool; +#define zig_false false +#define zig_true true +#else +#if __STDC_VERSION__ >= 199901L +typedef _Bool zig_bool; +#else +typedef char zig_bool; +#endif +#define zig_false ((zig_bool)0) +#define zig_true ((zig_bool)1) +#endif + +typedef uintptr_t zig_usize; +typedef intptr_t zig_isize; +typedef signed short int zig_c_short; +typedef unsigned short int zig_c_ushort; +typedef signed int zig_c_int; +typedef unsigned int zig_c_uint; +typedef signed long int zig_c_long; +typedef unsigned long int zig_c_ulong; +typedef signed long long int zig_c_longlong; +typedef unsigned long long int zig_c_ulonglong; +typedef long double zig_c_longdouble; + +typedef uint8_t zig_u8; +typedef int8_t zig_i8; +typedef uint16_t zig_u16; +typedef int16_t zig_i16; +typedef uint16_t zig_u16; +typedef int16_t zig_i16; +typedef uint32_t zig_u32; +typedef int32_t zig_i32; +typedef uint64_t zig_u64; +typedef int64_t zig_i64; + +#define zig_as_u8(val) UINT8_C(val) +#define zig_as_i8(val) INT8_C(val) +#define zig_as_u16(val) UINT16_C(val) +#define zig_as_i16(val) INT16_C(val) +#define zig_as_u32(val) UINT32_C(val) +#define zig_as_i32(val) INT32_C(val) +#define zig_as_u64(val) UINT64_C(val) +#define zig_as_i64(val) INT64_C(val) + +#define zig_minInt_u8 zig_as_u8(0) +#define zig_maxInt_u8 UINT8_MAX +#define zig_minInt_i8 INT8_MIN +#define zig_maxInt_i8 INT8_MAX +#define zig_minInt_u16 zig_as_u16(0) +#define zig_maxInt_u16 UINT16_MAX +#define zig_minInt_i16 INT16_MIN +#define zig_maxInt_i16 INT16_MAX +#define zig_minInt_u32 zig_as_u32(0) +#define zig_maxInt_u32 UINT32_MAX +#define zig_minInt_i32 INT32_MIN +#define zig_maxInt_i32 INT32_MAX +#define zig_minInt_u64 zig_as_u64(0) +#define zig_maxInt_u64 UINT64_MAX +#define zig_minInt_i64 INT64_MIN +#define zig_maxInt_i64 INT64_MAX + +#if FLT_MANT_DIG == 11 +typedef float zig_f16; +#elif DBL_MANT_DIG == 11 +typedef double zig_f16; +#elif LDBL_MANT_DIG == 11 +typedef long double zig_f16; +#elif FLT16_MANT_DIG == 11 +typedef _Float16 zig_f16; +#endif + +#if FLT_MANT_DIG == 24 +typedef float zig_f32; +#elif DBL_MANT_DIG == 24 +typedef double zig_f32; +#elif LDBL_MANT_DIG == 24 +typedef long double zig_f32; +#elif FLT32_MANT_DIG == 24 +typedef _Float32 zig_f32; +#endif + +#if FLT_MANT_DIG == 53 +typedef float zig_f64; +#elif DBL_MANT_DIG == 53 +typedef double zig_f64; +#elif LDBL_MANT_DIG == 53 +typedef long double zig_f64; +#elif FLT64_MANT_DIG == 53 +typedef _Float64 zig_f64; +#endif + +#if FLT_MANT_DIG == 64 +typedef float zig_f80; +#elif DBL_MANT_DIG == 64 +typedef double zig_f80; +#elif LDBL_MANT_DIG == 64 +typedef long double zig_f80; +#elif FLT80_MANT_DIG == 64 +typedef _Float80 zig_f80; +#elif defined(__SIZEOF_FLOAT80__) +typedef __float80 zig_f80; +#endif + +#if FLT_MANT_DIG == 113 +typedef float zig_f128; +#elif DBL_MANT_DIG == 113 +typedef double zig_f128; +#elif LDBL_MANT_DIG == 113 +typedef long double zig_f128; +#elif FLT128_MANT_DIG == 113 +typedef _Float128 zig_f128; +#elif defined(__SIZEOF_FLOAT128__) +typedef __float128 zig_f128; +#endif + +zig_extern_c void *memcpy (void *zig_restrict, void const *zig_restrict, zig_usize); +zig_extern_c void *memset (void *, int, zig_usize); + +/* ==================== 8/16/32/64-bit Integer Routines ===================== */ + +#define zig_maxInt(Type, bits) zig_shr_##Type(zig_maxInt_##Type, (zig_bitSizeOf(zig_##Type) - bits)) +#define zig_minInt(Type, bits) zig_not_##Type(zig_maxInt(Type, bits), bits) + +#define zig_int_helpers(w) \ + static inline zig_u##w zig_shl_u##w(zig_u##w lhs, zig_u8 rhs) { \ + return lhs << rhs; \ + } \ +\ + static inline zig_i##w zig_shl_i##w(zig_i##w lhs, zig_u8 rhs) { \ + return lhs << rhs; \ + } \ +\ + static inline zig_u##w zig_shr_u##w(zig_u##w lhs, zig_u8 rhs) { \ + return lhs >> rhs; \ + } \ +\ + static inline zig_i##w zig_shr_i##w(zig_i##w lhs, zig_u8 rhs) { \ + zig_i##w sign_mask = lhs < zig_as_i##w(0) ? zig_as_i##w(-1) : zig_as_i##w(0); \ + return ((lhs ^ sign_mask) >> rhs) ^ sign_mask; \ + } \ +\ + static inline zig_u##w zig_not_u##w(zig_u##w val, zig_u8 bits) { \ + return val ^ zig_maxInt(u##w, bits); \ + } \ +\ + static inline zig_i##w zig_not_i##w(zig_i##w val, zig_u8 bits) { \ + (void)bits; \ + return ~val; \ + } \ +\ + static inline zig_u##w zig_wrap_u##w(zig_u##w val, zig_u8 bits) { \ + return val & zig_maxInt(u##w, bits); \ + } \ +\ + static inline zig_i##w zig_wrap_i##w(zig_i##w val, zig_u8 bits) { \ + return (val & zig_as_u##w(1) << (bits - zig_as_u8(1))) != 0 \ + ? val | zig_minInt(i##w, bits) : val & zig_maxInt(i##w, bits); \ + } \ +\ + static inline zig_u##w zig_div_floor_u##w(zig_u##w lhs, zig_u##w rhs) { \ + return lhs / rhs; \ + } \ +\ + static inline zig_i##w zig_div_floor_i##w(zig_i##w lhs, zig_i##w rhs) { \ + return lhs / rhs - (((lhs ^ rhs) & (lhs % rhs)) < zig_as_i##w(0)); \ + } \ +\ + static inline zig_u##w zig_mod_u##w(zig_u##w lhs, zig_u##w rhs) { \ + return lhs % rhs; \ + } \ +\ + static inline zig_i##w zig_mod_i##w(zig_i##w lhs, zig_i##w rhs) { \ + zig_i##w rem = lhs % rhs; \ + return rem + (((lhs ^ rhs) & rem) < zig_as_i##w(0) ? rhs : zig_as_i##w(0)); \ } +zig_int_helpers(8) +zig_int_helpers(16) +zig_int_helpers(32) +zig_int_helpers(64) + +static inline zig_bool zig_addo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) { +#if zig_has_builtin(add_overflow) + zig_u32 full_res; + zig_bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u32(full_res, bits); + return overflow || full_res < zig_minInt(u32, bits) || full_res > zig_maxInt(u32, bits); +#else + *res = zig_addw_u32(lhs, rhs, bits); + return *res < lhs; +#endif } -static inline int8_t zig_addw_i8(int8_t lhs, int8_t rhs, int8_t min, int8_t max) { - if ((lhs > 0) && (rhs > 0)) { - int8_t thresh = max - rhs; - if (lhs > thresh) { - return min + lhs - thresh - 1; - } - } else if ((lhs < 0) && (rhs < 0)) { - int8_t thresh = min - rhs; - if (lhs < thresh) { - return max + lhs - thresh + 1; - } +zig_extern_c zig_i32 __addosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow); +static inline zig_bool zig_addo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) { +#if zig_has_builtin(add_overflow) + zig_i32 full_res; + zig_bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); +#else + zig_c_int overflow_int; + zig_u32 full_res = __addosi4(lhs, rhs, &overflow_int); + zig_bool overflow = overflow_int != 0; +#endif + *res = zig_wrap_i32(full_res, bits); + return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); +} + +static inline zig_bool zig_addo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) { +#if zig_has_builtin(add_overflow) + zig_u64 full_res; + zig_bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u64(full_res, bits); + return overflow || full_res < zig_minInt(u64, bits) || full_res > zig_maxInt(u64, bits); +#else + *res = zig_addw_u64(lhs, rhs, bits); + return *res < lhs; +#endif +} + +zig_extern_c zig_i64 __addodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow); +static inline zig_bool zig_addo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) { +#if zig_has_builtin(add_overflow) + zig_i64 full_res; + zig_bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); +#else + zig_c_int overflow_int; + zig_u64 full_res = __addodi4(lhs, rhs, &overflow_int); + zig_bool overflow = overflow_int != 0; +#endif + *res = zig_wrap_i64(full_res, bits); + return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); +} + +static inline zig_bool zig_addo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) { +#if zig_has_builtin(add_overflow) + zig_u8 full_res; + zig_bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u8(full_res, bits); + return overflow || full_res < zig_minInt(u8, bits) || full_res > zig_maxInt(u8, bits); +#else + return zig_addo_u32(res, lhs, rhs, bits); +#endif +} + +static inline zig_bool zig_addo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) { +#if zig_has_builtin(add_overflow) + zig_i8 full_res; + zig_bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); + *res = zig_wrap_i8(full_res, bits); + return overflow || full_res < zig_minInt(i8, bits) || full_res > zig_maxInt(i8, bits); +#else + return zig_addo_i32(res, lhs, rhs, bits); +#endif +} + +static inline zig_bool zig_addo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) { +#if zig_has_builtin(add_overflow) + zig_u16 full_res; + zig_bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u16(full_res, bits); + return overflow || full_res < zig_minInt(u16, bits) || full_res > zig_maxInt(u16, bits); +#else + return zig_addo_u32(res, lhs, rhs, bits); +#endif +} + +static inline zig_bool zig_addo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) { +#if zig_has_builtin(add_overflow) + zig_i16 full_res; + zig_bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); + *res = zig_wrap_i16(full_res, bits); + return overflow || full_res < zig_minInt(i16, bits) || full_res > zig_maxInt(i16, bits); +#else + return zig_addo_i32(res, lhs, rhs, bits); +#endif +} + +static inline zig_bool zig_subo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) { +#if zig_has_builtin(sub_overflow) + zig_u32 full_res; + zig_bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u32(full_res, bits); + return overflow || full_res < zig_minInt(u32, bits) || full_res > zig_maxInt(u32, bits); +#else + *res = zig_subw_u32(lhs, rhs, bits); + return *res > lhs; +#endif +} + +zig_extern_c zig_i32 __subosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow); +static inline zig_bool zig_subo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) { +#if zig_has_builtin(sub_overflow) + zig_i32 full_res; + zig_bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); +#else + zig_c_int overflow_int; + zig_u32 full_res = __subosi4(lhs, rhs, &overflow_int); + zig_bool overflow = overflow_int != 0; +#endif + *res = zig_wrap_i32(full_res, bits); + return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); +} + +static inline zig_bool zig_subo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) { +#if zig_has_builtin(sub_overflow) + zig_u64 full_res; + zig_bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u64(full_res, bits); + return overflow || full_res < zig_minInt(u64, bits) || full_res > zig_maxInt(u64, bits); +#else + *res = zig_subw_u64(lhs, rhs, bits); + return *res > lhs; +#endif +} + +zig_extern_c zig_i64 __subodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow); +static inline zig_bool zig_subo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) { +#if zig_has_builtin(sub_overflow) + zig_i64 full_res; + zig_bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); +#else + zig_c_int overflow_int; + zig_u64 full_res = __subodi4(lhs, rhs, &overflow_int); + zig_bool overflow = overflow_int != 0; +#endif + *res = zig_wrap_i64(full_res, bits); + return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); +} + +static inline zig_bool zig_subo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) { +#if zig_has_builtin(sub_overflow) + zig_u8 full_res; + zig_bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u8(full_res, bits); + return overflow || full_res < zig_minInt(u8, bits) || full_res > zig_maxInt(u8, bits); +#else + return zig_subo_u32(res, lhs, rhs, bits); +#endif +} + +static inline zig_bool zig_subo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) { +#if zig_has_builtin(sub_overflow) + zig_i8 full_res; + zig_bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); + *res = zig_wrap_i8(full_res, bits); + return overflow || full_res < zig_minInt(i8, bits) || full_res > zig_maxInt(i8, bits); +#else + return zig_subo_i32(res, lhs, rhs, bits); +#endif +} + +static inline zig_bool zig_subo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) { +#if zig_has_builtin(sub_overflow) + zig_u16 full_res; + zig_bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u16(full_res, bits); + return overflow || full_res < zig_minInt(u16, bits) || full_res > zig_maxInt(u16, bits); +#else + return zig_subo_u32(res, lhs, rhs, bits); +#endif +} + +static inline zig_bool zig_subo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) { +#if zig_has_builtin(sub_overflow) + zig_i16 full_res; + zig_bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); + *res = zig_wrap_i16(full_res, bits); + return overflow || full_res < zig_minInt(i16, bits) || full_res > zig_maxInt(i16, bits); +#else + return zig_subo_i32(res, lhs, rhs, bits); +#endif +} + +static inline zig_bool zig_mulo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) { +#if zig_has_builtin(mul_overflow) + zig_u32 full_res; + zig_bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u32(full_res, bits); + return overflow || full_res < zig_minInt(u32, bits) || full_res > zig_maxInt(u32, bits); +#else + *res = zig_mulw_u32(lhs, rhs, bits); + return rhs != zig_as_u32(0) && lhs > zig_maxInt(u32, bits) / rhs; +#endif +} + +zig_extern_c zig_i32 __mulosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow); +static inline zig_bool zig_mulo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) { +#if zig_has_builtin(mul_overflow) + zig_i32 full_res; + zig_bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); +#else + zig_c_int overflow_int; + zig_u32 full_res = __mulosi4(lhs, rhs, &overflow_int); + zig_bool overflow = overflow_int != 0; +#endif + *res = zig_wrap_i32(full_res, bits); + return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); +} + +static inline zig_bool zig_mulo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) { +#if zig_has_builtin(mul_overflow) + zig_u64 full_res; + zig_bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u64(full_res, bits); + return overflow || full_res < zig_minInt(u64, bits) || full_res > zig_maxInt(u64, bits); +#else + *res = zig_mulw_u64(lhs, rhs, bits); + return rhs != zig_as_u64(0) && lhs > zig_maxInt(u64, bits) / rhs; +#endif +} + +zig_extern_c zig_i64 __mulodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow); +static inline zig_bool zig_mulo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) { +#if zig_has_builtin(mul_overflow) + zig_i64 full_res; + zig_bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); +#else + zig_c_int overflow_int; + zig_u64 full_res = __mulodi4(lhs, rhs, &overflow_int); + zig_bool overflow = overflow_int != 0; +#endif + *res = zig_wrap_i64(full_res, bits); + return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); +} + +static inline zig_bool zig_mulo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) { +#if zig_has_builtin(mul_overflow) + zig_u8 full_res; + zig_bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u8(full_res, bits); + return overflow || full_res < zig_minInt(u8, bits) || full_res > zig_maxInt(u8, bits); +#else + return zig_mulo_u32(res, lhs, rhs, bits); +#endif +} + +static inline zig_bool zig_mulo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) { +#if zig_has_builtin(mul_overflow) + zig_i8 full_res; + zig_bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); + *res = zig_wrap_i8(full_res, bits); + return overflow || full_res < zig_minInt(i8, bits) || full_res > zig_maxInt(i8, bits); +#else + return zig_mulo_i32(res, lhs, rhs, bits); +#endif +} + +static inline zig_bool zig_mulo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) { +#if zig_has_builtin(mul_overflow) + zig_u16 full_res; + zig_bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u16(full_res, bits); + return overflow || full_res < zig_minInt(u16, bits) || full_res > zig_maxInt(u16, bits); +#else + return zig_mulo_u32(res, lhs, rhs, bits); +#endif +} + +static inline zig_bool zig_mulo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) { +#if zig_has_builtin(mul_overflow) + zig_i16 full_res; + zig_bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); + *res = zig_wrap_i16(full_res, bits); + return overflow || full_res < zig_minInt(i16, bits) || full_res > zig_maxInt(i16, bits); +#else + return zig_mulo_i32(res, lhs, rhs, bits); +#endif +} + +#define zig_int_builtins(w) \ + static inline zig_u##w zig_shlw_u##w(zig_u##w lhs, zig_u8 rhs, zig_u8 bits) { \ + return zig_wrap_u##w(zig_shl_u##w(lhs, rhs), bits); \ + } \ +\ + static inline zig_i##w zig_shlw_i##w(zig_i##w lhs, zig_u8 rhs, zig_u8 bits) { \ + return zig_wrap_i##w((zig_i##w)zig_shl_u##w((zig_u##w)lhs, (zig_u##w)rhs), bits); \ + } \ +\ + static inline zig_u##w zig_addw_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ + return zig_wrap_u##w(lhs + rhs, bits); \ + } \ +\ + static inline zig_i##w zig_addw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ + return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs + (zig_u##w)rhs), bits); \ + } \ +\ + static inline zig_u##w zig_subw_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ + return zig_wrap_u##w(lhs - rhs, bits); \ + } \ +\ + static inline zig_i##w zig_subw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ + return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs - (zig_u##w)rhs), bits); \ + } \ +\ + static inline zig_u##w zig_mulw_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ + return zig_wrap_u##w(lhs * rhs, bits); \ + } \ +\ + static inline zig_i##w zig_mulw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ + return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs * (zig_u##w)rhs), bits); \ + } \ +\ + static inline zig_bool zig_shlo_u##w(zig_u##w *res, zig_u##w lhs, zig_u8 rhs, zig_u8 bits) { \ + *res = zig_shlw_u##w(lhs, rhs, bits); \ + return (lhs & zig_maxInt_u##w << (bits - rhs)) != zig_as_u##w(0); \ + } \ +\ + static inline zig_bool zig_shlo_i##w(zig_i##w *res, zig_i##w lhs, zig_u8 rhs, zig_u8 bits) { \ + *res = zig_shlw_i##w(lhs, rhs, bits); \ + zig_i##w mask = (zig_i##w)(zig_maxInt_u##w << (bits - rhs - 1)); \ + return (lhs & mask) != zig_as_i##w(0) && (lhs & mask) != mask; \ + } \ +\ + static inline zig_u##w zig_shls_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ + zig_u##w res; \ + if (rhs >= bits) return lhs != zig_as_u##w(0) ? zig_maxInt(u##w, bits) : lhs; \ + return zig_shlo_u##w(&res, lhs, (zig_u8)rhs, bits) ? zig_maxInt(u##w, bits) : res; \ + } \ +\ + static inline zig_i##w zig_shls_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ + zig_i##w res; \ + if ((zig_u##w)rhs < (zig_u##w)bits && !zig_shlo_i##w(&res, lhs, rhs, bits)) return res; \ + return lhs < zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ + } \ +\ + static inline zig_u##w zig_adds_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ + zig_u##w res; \ + return zig_addo_u##w(&res, lhs, rhs, bits) ? zig_maxInt(u##w, bits) : res; \ + } \ +\ + static inline zig_i##w zig_adds_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ + zig_i##w res; \ + if (!zig_addo_i##w(&res, lhs, rhs, bits)) return res; \ + return res >= zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ + } \ +\ + static inline zig_u##w zig_subs_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ + zig_u##w res; \ + return zig_subo_u##w(&res, lhs, rhs, bits) ? zig_minInt(u##w, bits) : res; \ + } \ +\ + static inline zig_i##w zig_subs_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ + zig_i##w res; \ + if (!zig_subo_i##w(&res, lhs, rhs, bits)) return res; \ + return res >= zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ + } \ +\ + static inline zig_u##w zig_muls_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ + zig_u##w res; \ + return zig_mulo_u##w(&res, lhs, rhs, bits) ? zig_maxInt(u##w, bits) : res; \ + } \ +\ + static inline zig_i##w zig_muls_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ + zig_i##w res; \ + if (!zig_mulo_i##w(&res, lhs, rhs, bits)) return res; \ + return (lhs ^ rhs) < zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ } +zig_int_builtins(8) +zig_int_builtins(16) +zig_int_builtins(32) +zig_int_builtins(64) + +#define zig_builtin8(name, val) __builtin_##name(val) +typedef zig_c_uint zig_Builtin8; + +#define zig_builtin16(name, val) __builtin_##name(val) +typedef zig_c_uint zig_Builtin16; + +#if INT_MIN <= INT32_MIN +#define zig_builtin32(name, val) __builtin_##name(val) +typedef zig_c_uint zig_Builtin32; +#elif LONG_MIN <= INT32_MIN +#define zig_builtin32(name, val) __builtin_##name##l(val) +typedef zig_c_ulong zig_Builtin32; +#endif + +#if INT_MIN <= INT64_MIN +#define zig_builtin64(name, val) __builtin_##name(val) +typedef zig_c_uint zig_Builtin64; +#elif LONG_MIN <= INT64_MIN +#define zig_builtin64(name, val) __builtin_##name##l(val) +typedef zig_c_ulong zig_Builtin64; +#elif LLONG_MIN <= INT64_MIN +#define zig_builtin64(name, val) __builtin_##name##ll(val) +typedef zig_c_ulonglong zig_Builtin64; +#endif + +#if zig_has_builtin(clz) +#define zig_builtin_clz(w) \ + static inline zig_u8 zig_clz_u##w(zig_u##w val, zig_u8 bits) { \ + if (val == 0) return bits; \ + return zig_builtin##w(clz, val) - (zig_bitSizeOf(zig_Builtin##w) - bits); \ + } \ +\ + static inline zig_u8 zig_clz_i##w(zig_i##w val, zig_u8 bits) { \ + return zig_clz_u##w((zig_u##w)val, bits); \ + } +zig_builtin_clz(8) +zig_builtin_clz(16) +zig_builtin_clz(32) +zig_builtin_clz(64) +#endif + +#if zig_has_builtin(ctz) +#define zig_builtin_ctz(w) \ + static inline zig_u8 zig_ctz_u##w(zig_u##w val, zig_u8 bits) { \ + if (val == 0) return bits; \ + return zig_builtin##w(ctz, val); \ + } \ +\ + static inline zig_u8 zig_ctz_i##w(zig_i##w val, zig_u8 bits) { \ + return zig_ctz_u##w((zig_u##w)val, bits); \ + } +zig_builtin_ctz(8) +zig_builtin_ctz(16) +zig_builtin_ctz(32) +zig_builtin_ctz(64) +#endif + +#if zig_has_builtin(popcount) +#define zig_builtin_popcount(w) \ + static inline zig_u8 zig_popcount_u##w(zig_u##w val, zig_u8 bits) { \ + (void)bits; \ + return zig_builtin##w(popcount, val); \ + } \ +\ + static inline zig_u8 zig_popcount_i##w(zig_i##w val, zig_u8 bits) { \ + \ + return zig_popcount_u##w((zig_u##w)val, bits); \ + } +zig_builtin_popcount(8) +zig_builtin_popcount(16) +zig_builtin_popcount(32) +zig_builtin_popcount(64) +#endif + +static inline zig_u8 zig_byte_swap_u8(zig_u8 val, zig_u8 bits) { + return zig_wrap_u8(val >> (8 - bits), bits); +} + +static inline zig_i8 zig_byte_swap_i8(zig_i8 val, zig_u8 bits) { + return zig_wrap_i8((zig_i8)zig_byte_swap_u8((zig_u8)val, bits), bits); +} + +static inline zig_u16 zig_byte_swap_u16(zig_u16 val, zig_u8 bits) { + zig_u16 full_res; +#if zig_has_builtin(bswap16) + full_res = __builtin_bswap16(val); +#else + full_res = (zig_u16)zig_byte_swap_u8((zig_u8)(val >> 0)) << 8 | + (zig_u16)zig_byte_swap_u8((zig_u8)(val >> 8)) >> 0; +#endif + return zig_wrap_u16(full_res >> (16 - bits), bits); +} + +static inline zig_i16 zig_byte_swap_i16(zig_i16 val, zig_u8 bits) { + return zig_wrap_i16((zig_i16)zig_byte_swap_u16((zig_u16)val, bits), bits); +} + +static inline zig_u32 zig_byte_swap_u32(zig_u32 val, zig_u8 bits) { + zig_u32 full_res; +#if zig_has_builtin(bswap32) + full_res = __builtin_bswap32(val); +#else + full_res = (zig_u32)zig_byte_swap_u16((zig_u16)(val >> 0)) << 16 | + (zig_u32)zig_byte_swap_u16((zig_u16)(val >> 16)) >> 0; +#endif + return zig_wrap_u32(full_res >> (32 - bits), bits); +} + +static inline zig_i32 zig_byte_swap_i32(zig_i32 val, zig_u8 bits) { + return zig_wrap_i32((zig_i32)zig_byte_swap_u32((zig_u32)val, bits), bits); +} + +static inline zig_u64 zig_byte_swap_u64(zig_u64 val, zig_u8 bits) { + zig_u64 full_res; +#if zig_has_builtin(bswap64) + full_res = __builtin_bswap64(val); +#else + full_res = (zig_u64)zig_byte_swap_u32((zig_u32)(val >> 0)) << 32 | + (zig_u64)zig_byte_swap_u32((zig_u32)(val >> 32)) >> 0; +#endif + return zig_wrap_u64(full_res >> (64 - bits), bits); +} + +static inline zig_i64 zig_byte_swap_i64(zig_i64 val, zig_u8 bits) { + return zig_wrap_i64((zig_i64)zig_byte_swap_u64((zig_u64)val, bits), bits); +} + +static inline zig_u8 zig_bit_reverse_u8(zig_u8 val, zig_u8 bits) { + zig_u8 full_res; +#if zig_has_builtin(bitreverse8) + full_res = __builtin_bitreverse8(val); +#else + static zig_u8 const lut[0x10] = { + 0b0000, 0b1000, 0b0100, 0b1100, + 0b0010, 0b1010, 0b0110, 0b1110, + 0b0001, 0b1001, 0b0101, 0b1101, + 0b0011, 0b1011, 0b0111, 0b1111, + }; + full_res = lut[val >> 0 & 0xF] << 4 | lut[val >> 4 & 0xF] << 0; +#endif + return zig_wrap_u8(full_res >> (8 - bits), bits); +} + +static inline zig_i8 zig_bit_reverse_i8(zig_i8 val, zig_u8 bits) { + return zig_wrap_i8((zig_i8)zig_bit_reverse_u8((zig_u8)val, bits), bits); +} + +static inline zig_u16 zig_bit_reverse_u16(zig_u16 val, zig_u8 bits) { + zig_u16 full_res; +#if zig_has_builtin(bitreverse16) + full_res = __builtin_bitreverse16(val); +#else + full_res = (zig_u16)zig_bit_reverse_u8((zig_u8)(val >> 0)) << 8 | + (zig_u16)zig_bit_reverse_u8((zig_u8)(val >> 8)) >> 0; +#endif + return zig_wrap_u16(full_res >> (16 - bits), bits); +} + +static inline zig_i16 zig_bit_reverse_i16(zig_i16 val, zig_u8 bits) { + return zig_wrap_i16((zig_i16)zig_bit_reverse_u16((zig_u16)val, bits), bits); +} + +static inline zig_u32 zig_bit_reverse_u32(zig_u32 val, zig_u8 bits) { + zig_u32 full_res; +#if zig_has_builtin(bitreverse32) + full_res = __builtin_bitreverse32(val); +#else + full_res = (zig_u32)zig_bit_reverse_u16((zig_u16)(val >> 0)) << 16 | + (zig_u32)zig_bit_reverse_u16((zig_u16)(val >> 16)) >> 0; +#endif + return zig_wrap_u32(full_res >> (32 - bits), bits); +} + +static inline zig_i32 zig_bit_reverse_i32(zig_i32 val, zig_u8 bits) { + return zig_wrap_i32((zig_i32)zig_bit_reverse_u32((zig_u32)val, bits), bits); +} + +static inline zig_u64 zig_bit_reverse_u64(zig_u64 val, zig_u8 bits) { + zig_u64 full_res; +#if zig_has_builtin(bitreverse64) + full_res = __builtin_bitreverse64(val); +#else + full_res = (zig_u64)zig_bit_reverse_u32((zig_u32)(val >> 0)) << 32 | + (zig_u64)zig_bit_reverse_u32((zig_u32)(val >> 32)) >> 0; +#endif + return zig_wrap_u64(full_res >> (64 - bits), bits); +} + +static inline zig_i64 zig_bit_reverse_i64(zig_i64 val, zig_u8 bits) { + return zig_wrap_i64((zig_i64)zig_bit_reverse_u64((zig_u64)val, bits), bits); +} + +/* ======================== 128-bit Integer Routines ======================== */ + +#if !defined(zig_has_int128) +# if defined(__SIZEOF_INT128__) +# define zig_has_int128 1 +# else +# define zig_has_int128 0 +# endif +#endif + +#if zig_has_int128 + +typedef unsigned __int128 zig_u128; +typedef signed __int128 zig_i128; + +#define zig_as_u128(hi, lo) ((zig_u128)(hi)<<64|(lo)) +#define zig_as_i128(hi, lo) ((zig_i128)zig_as_u128(hi, lo)) +#define zig_hi_u128(val) ((zig_u64)((val) >> 64)) +#define zig_lo_u128(val) ((zig_u64)((val) >> 0)) +#define zig_hi_i128(val) ((zig_i64)((val) >> 64)) +#define zig_lo_i128(val) ((zig_u64)((val) >> 0)) +#define zig_bitcast_u128(val) ((zig_u128)(val)) +#define zig_bitcast_i128(val) ((zig_i128)(val)) +#define zig_cmp_int128(ZigType, CType) \ + static inline zig_i8 zig_cmp_##ZigType(CType lhs, CType rhs) { \ + return (lhs > rhs) - (lhs < rhs); \ + } +#define zig_bit_int128(ZigType, CType, operation, operator) \ + static inline CType zig_##operation##_##ZigType(CType lhs, CType rhs) { \ + return lhs operator rhs; \ + } + +#else /* zig_has_int128 */ + +#if __LITTLE_ENDIAN__ || _MSC_VER +typedef struct { zig_align(16) zig_u64 lo; zig_u64 hi; } zig_u128; +typedef struct { zig_align(16) zig_u64 lo; zig_i64 hi; } zig_i128; +#else +typedef struct { zig_align(16) zig_u64 hi; zig_u64 lo; } zig_u128; +typedef struct { zig_align(16) zig_i64 hi; zig_u64 lo; } zig_i128; +#endif + +#define zig_as_u128(hi, lo) ((zig_u128){ .h##i = (hi), .l##o = (lo) }) +#define zig_as_i128(hi, lo) ((zig_i128){ .h##i = (hi), .l##o = (lo) }) +#define zig_hi_u128(val) ((val).hi) +#define zig_lo_u128(val) ((val).lo) +#define zig_hi_i128(val) ((val).hi) +#define zig_lo_i128(val) ((val).lo) +#define zig_bitcast_u128(val) zig_as_u128((zig_u64)(val).hi, (val).lo) +#define zig_bitcast_i128(val) zig_as_i128((zig_i64)(val).hi, (val).lo) +#define zig_cmp_int128(ZigType, CType) \ + static inline zig_c_int zig_cmp_##ZigType(CType lhs, CType rhs) { \ + return (lhs.hi == rhs.hi) \ + ? (lhs.lo > rhs.lo) - (lhs.lo < rhs.lo) \ + : (lhs.hi > rhs.hi) - (lhs.hi < rhs.hi); \ + } +#define zig_bit_int128(ZigType, CType, operation, operator) \ + static inline CType zig_##operation##_##ZigType(CType lhs, CType rhs) { \ + return (CType){ .hi = lhs.hi operator rhs.hi, .lo = lhs.lo operator rhs.lo }; \ + } + +#endif /* zig_has_int128 */ + +#define zig_minInt_u128 zig_as_u128(zig_minInt_u64, zig_minInt_u64) +#define zig_maxInt_u128 zig_as_u128(zig_maxInt_u64, zig_maxInt_u64) +#define zig_minInt_i128 zig_as_i128(zig_minInt_i64, zig_minInt_u64) +#define zig_maxInt_i128 zig_as_i128(zig_maxInt_i64, zig_maxInt_u64) + +zig_cmp_int128(u128, zig_u128) +zig_cmp_int128(i128, zig_i128) + +zig_bit_int128(u128, zig_u128, and, &) +zig_bit_int128(i128, zig_i128, and, &) + +zig_bit_int128(u128, zig_u128, or, |) +zig_bit_int128(i128, zig_i128, or, |) + +zig_bit_int128(u128, zig_u128, xor, ^) +zig_bit_int128(i128, zig_i128, xor, ^) + +static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs); + +#if zig_has_int128 + +static inline zig_u128 zig_not_u128(zig_u128 val, zig_u8 bits) { + return val ^ zig_maxInt(u128, bits); +} + +static inline zig_i128 zig_not_i128(zig_i128 val, zig_u8 bits) { + (void)bits; + return ~val; +} + +static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs) { + return lhs >> rhs; +} + +static inline zig_u128 zig_shl_u128(zig_u128 lhs, zig_u8 rhs) { + return lhs << rhs; +} + +static inline zig_i128 zig_shl_i128(zig_i128 lhs, zig_u8 rhs) { + return lhs << rhs; +} + +static inline zig_u128 zig_add_u128(zig_u128 lhs, zig_u128 rhs) { return lhs + rhs; } -static inline uint16_t zig_addw_u16(uint16_t lhs, uint16_t rhs, uint16_t max) { - uint16_t thresh = max - rhs; - if (lhs > thresh) { - return lhs - thresh - 1; - } else { - return lhs + rhs; - } -} - -static inline int16_t zig_addw_i16(int16_t lhs, int16_t rhs, int16_t min, int16_t max) { - if ((lhs > 0) && (rhs > 0)) { - int16_t thresh = max - rhs; - if (lhs > thresh) { - return min + lhs - thresh - 1; - } - } else if ((lhs < 0) && (rhs < 0)) { - int16_t thresh = min - rhs; - if (lhs < thresh) { - return max + lhs - thresh + 1; - } - } +static inline zig_i128 zig_add_i128(zig_i128 lhs, zig_i128 rhs) { return lhs + rhs; } -static inline uint32_t zig_addw_u32(uint32_t lhs, uint32_t rhs, uint32_t max) { - uint32_t thresh = max - rhs; - if (lhs > thresh) { - return lhs - thresh - 1; - } else { - return lhs + rhs; - } -} - -static inline int32_t zig_addw_i32(int32_t lhs, int32_t rhs, int32_t min, int32_t max) { - if ((lhs > 0) && (rhs > 0)) { - int32_t thresh = max - rhs; - if (lhs > thresh) { - return min + lhs - thresh - 1; - } - } else if ((lhs < 0) && (rhs < 0)) { - int32_t thresh = min - rhs; - if (lhs < thresh) { - return max + lhs - thresh + 1; - } - } - return lhs + rhs; -} - -static inline uint64_t zig_addw_u64(uint64_t lhs, uint64_t rhs, uint64_t max) { - uint64_t thresh = max - rhs; - if (lhs > thresh) { - return lhs - thresh - 1; - } else { - return lhs + rhs; - } -} - -static inline int64_t zig_addw_i64(int64_t lhs, int64_t rhs, int64_t min, int64_t max) { - if ((lhs > 0) && (rhs > 0)) { - int64_t thresh = max - rhs; - if (lhs > thresh) { - return min + lhs - thresh - 1; - } - } else if ((lhs < 0) && (rhs < 0)) { - int64_t thresh = min - rhs; - if (lhs < thresh) { - return max + lhs - thresh + 1; - } - } - return lhs + rhs; -} - -static inline intptr_t zig_addw_isize(intptr_t lhs, intptr_t rhs, intptr_t min, intptr_t max) { - return (intptr_t)(((uintptr_t)lhs) + ((uintptr_t)rhs)); -} - -static inline short zig_addw_short(short lhs, short rhs, short min, short max) { - return (short)(((unsigned short)lhs) + ((unsigned short)rhs)); -} - -static inline int zig_addw_int(int lhs, int rhs, int min, int max) { - return (int)(((unsigned)lhs) + ((unsigned)rhs)); -} - -static inline long zig_addw_long(long lhs, long rhs, long min, long max) { - return (long)(((unsigned long)lhs) + ((unsigned long)rhs)); -} - -static inline long long zig_addw_longlong(long long lhs, long long rhs, long long min, long long max) { - return (long long)(((unsigned long long)lhs) + ((unsigned long long)rhs)); -} - -static inline uint8_t zig_subw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) { - if (lhs < rhs) { - return max - rhs - lhs + 1; - } else { - return lhs - rhs; - } -} - -static inline int8_t zig_subw_i8(int8_t lhs, int8_t rhs, int8_t min, int8_t max) { - if ((lhs > 0) && (rhs < 0)) { - int8_t thresh = lhs - max; - if (rhs < thresh) { - return min + (thresh - rhs - 1); - } - } else if ((lhs < 0) && (rhs > 0)) { - int8_t thresh = lhs - min; - if (rhs > thresh) { - return max - (rhs - thresh - 1); - } - } +static inline zig_u128 zig_sub_u128(zig_u128 lhs, zig_u128 rhs) { return lhs - rhs; } -static inline uint16_t zig_subw_u16(uint16_t lhs, uint16_t rhs, uint16_t max) { - if (lhs < rhs) { - return max - rhs - lhs + 1; - } else { - return lhs - rhs; - } -} - -static inline int16_t zig_subw_i16(int16_t lhs, int16_t rhs, int16_t min, int16_t max) { - if ((lhs > 0) && (rhs < 0)) { - int16_t thresh = lhs - max; - if (rhs < thresh) { - return min + (thresh - rhs - 1); - } - } else if ((lhs < 0) && (rhs > 0)) { - int16_t thresh = lhs - min; - if (rhs > thresh) { - return max - (rhs - thresh - 1); - } - } +static inline zig_i128 zig_sub_i128(zig_i128 lhs, zig_i128 rhs) { return lhs - rhs; } -static inline uint32_t zig_subw_u32(uint32_t lhs, uint32_t rhs, uint32_t max) { - if (lhs < rhs) { - return max - rhs - lhs + 1; - } else { - return lhs - rhs; - } +static inline zig_u128 zig_mul_u128(zig_u128 lhs, zig_u128 rhs) { + return lhs * rhs; } -static inline int32_t zig_subw_i32(int32_t lhs, int32_t rhs, int32_t min, int32_t max) { - if ((lhs > 0) && (rhs < 0)) { - int32_t thresh = lhs - max; - if (rhs < thresh) { - return min + (thresh - rhs - 1); - } - } else if ((lhs < 0) && (rhs > 0)) { - int32_t thresh = lhs - min; - if (rhs > thresh) { - return max - (rhs - thresh - 1); - } - } - return lhs - rhs; +static inline zig_i128 zig_mul_i128(zig_i128 lhs, zig_i128 rhs) { + return lhs * rhs; } -static inline uint64_t zig_subw_u64(uint64_t lhs, uint64_t rhs, uint64_t max) { - if (lhs < rhs) { - return max - rhs - lhs + 1; - } else { - return lhs - rhs; - } +static inline zig_u128 zig_div_trunc_u128(zig_u128 lhs, zig_u128 rhs) { + return lhs / rhs; } -static inline int64_t zig_subw_i64(int64_t lhs, int64_t rhs, int64_t min, int64_t max) { - if ((lhs > 0) && (rhs < 0)) { - int64_t thresh = lhs - max; - if (rhs < thresh) { - return min + (thresh - rhs - 1); - } - } else if ((lhs < 0) && (rhs > 0)) { - int64_t thresh = lhs - min; - if (rhs > thresh) { - return max - (rhs - thresh - 1); - } - } - return lhs - rhs; +static inline zig_i128 zig_div_trunc_i128(zig_i128 lhs, zig_i128 rhs) { + return lhs / rhs; } -static inline intptr_t zig_subw_isize(intptr_t lhs, intptr_t rhs, intptr_t min, intptr_t max) { - return (intptr_t)(((uintptr_t)lhs) - ((uintptr_t)rhs)); +static inline zig_u128 zig_rem_u128(zig_u128 lhs, zig_u128 rhs) { + return lhs % rhs; } -static inline short zig_subw_short(short lhs, short rhs, short min, short max) { - return (short)(((unsigned short)lhs) - ((unsigned short)rhs)); +static inline zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) { + return lhs % rhs; } -static inline int zig_subw_int(int lhs, int rhs, int min, int max) { - return (int)(((unsigned)lhs) - ((unsigned)rhs)); +static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) { + return zig_div_trunc_i128(lhs, rhs) - (((lhs ^ rhs) & zig_rem_i128(lhs, rhs)) < zig_as_i128(0, 0)); } -static inline long zig_subw_long(long lhs, long rhs, long min, long max) { - return (long)(((unsigned long)lhs) - ((unsigned long)rhs)); +static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { + zig_i128 rem = zig_rem_i128(lhs, rhs); + return rem + (((lhs ^ rhs) & rem) < zig_as_i128(0, 0) ? rhs : zig_as_i128(0, 0)); } -static inline long long zig_subw_longlong(long long lhs, long long rhs, long long min, long long max) { - return (long long)(((unsigned long long)lhs) - ((unsigned long long)rhs)); +#else /* zig_has_int128 */ + +static inline zig_u128 zig_not_u128(zig_u128 val, zig_u8 bits) { + return (zig_u128){ .hi = zig_not_u64(val.hi, bits - zig_as_u8(64)), .lo = zig_not_u64(val.lo, zig_as_u8(64)) }; } -static inline bool zig_addo_i8(int8_t lhs, int8_t rhs, int8_t *res, int8_t min, int8_t max) { -#if defined(__GNUC__) && INT8_MAX == INT_MAX - if (min == INT8_MIN && max == INT8_MAX) { - return __builtin_sadd_overflow(lhs, rhs, (int*)res); - } -#elif defined(__GNUC__) && INT8_MAX == LONG_MAX - if (min == INT8_MIN && max == INT8_MAX) { - return __builtin_saddl_overflow(lhs, rhs, (long*)res); - } -#elif defined(__GNUC__) && INT8_MAX == LLONG_MAX - if (min == INT8_MIN && max == INT8_MAX) { - return __builtin_saddll_overflow(lhs, rhs, (long long*)res); - } -#endif - int16_t big_result = (int16_t)lhs + (int16_t)rhs; - if (big_result > max) { - *res = big_result - ((int16_t)max - (int16_t)min); - return true; - } - if (big_result < min) { - *res = big_result + ((int16_t)max - (int16_t)min); - return true; - } - *res = big_result; - return false; +static inline zig_i128 zig_not_i128(zig_i128 val, zig_u8 bits) { + return (zig_i128){ .hi = zig_not_i64(val.hi, bits - zig_as_u8(64)), .lo = zig_not_u64(val.lo, zig_as_u8(64)) }; } -static inline bool zig_addo_i16(int16_t lhs, int16_t rhs, int16_t *res, int16_t min, int16_t max) { -#if defined(__GNUC__) && INT16_MAX == INT_MAX - if (min == INT16_MIN && max == INT16_MAX) { - return __builtin_sadd_overflow(lhs, rhs, (int*)res); - } -#elif defined(__GNUC__) && INT16_MAX == LONG_MAX - if (min == INT16_MIN && max == INT16_MAX) { - return __builtin_saddl_overflow(lhs, rhs, (long*)res); - } -#elif defined(__GNUC__) && INT16_MAX == LLONG_MAX - if (min == INT16_MIN && max == INT16_MAX) { - return __builtin_saddll_overflow(lhs, rhs, (long long*)res); - } -#endif - int32_t big_result = (int32_t)lhs + (int32_t)rhs; - if (big_result > max) { - *res = big_result - ((int32_t)max - (int32_t)min); - return true; - } - if (big_result < min) { - *res = big_result + ((int32_t)max - (int32_t)min); - return true; - } - *res = big_result; - return false; +static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs) { + if (rhs >= zig_as_u8(64)) return (zig_u128){ .hi = lhs.hi << (rhs - zig_as_u8(64)), .lo = zig_minInt_u64 }; + return (zig_u128){ .hi = lhs.hi << rhs | lhs.lo >> (zig_as_u8(64) - rhs), .lo = lhs.lo << rhs }; } -static inline bool zig_addo_i32(int32_t lhs, int32_t rhs, int32_t *res, int32_t min, int32_t max) { -#if defined(__GNUC__) && INT32_MAX == INT_MAX - if (min == INT32_MIN && max == INT32_MAX) { - return __builtin_sadd_overflow(lhs, rhs, (int*)res); - } -#elif defined(__GNUC__) && INT32_MAX == LONG_MAX - if (min == INT32_MIN && max == INT32_MAX) { - return __builtin_saddl_overflow(lhs, rhs, (long*)res); - } -#elif defined(__GNUC__) && INT32_MAX == LLONG_MAX - if (min == INT32_MIN && max == INT32_MAX) { - return __builtin_saddll_overflow(lhs, rhs, (long long*)res); - } -#endif - int64_t big_result = (int64_t)lhs + (int64_t)rhs; - if (big_result > max) { - *res = big_result - ((int64_t)max - (int64_t)min); - return true; - } - if (big_result < min) { - *res = big_result + ((int64_t)max - (int64_t)min); - return true; - } - *res = big_result; - return false; +static inline zig_u128 zig_shl_u128(zig_u128 lhs, zig_u8 rhs) { + if (rhs >= zig_as_u8(64)) return (zig_u128){ .hi = lhs.hi << (rhs - zig_as_u8(64)), .lo = zig_minInt_u64 }; + return (zig_u128){ .hi = lhs.hi << rhs | lhs.lo >> (zig_as_u8(64) - rhs), .lo = lhs.lo << rhs }; } -static inline bool zig_addo_i64(int64_t lhs, int64_t rhs, int64_t *res, int64_t min, int64_t max) { - bool overflow; -#if defined(__GNUC__) && INT64_MAX == INT_MAX - overflow = __builtin_sadd_overflow(lhs, rhs, (int*)res); -#elif defined(__GNUC__) && INT64_MAX == LONG_MAX - overflow = __builtin_saddl_overflow(lhs, rhs, (long*)res); -#elif defined(__GNUC__) && INT64_MAX == LLONG_MAX - overflow = __builtin_saddll_overflow(lhs, rhs, (long long*)res); +static inline zig_i128 zig_shl_i128(zig_i128 lhs, zig_u8 rhs) { + if (rhs >= zig_as_u8(64)) return (zig_i128){ .hi = lhs.hi << (rhs - zig_as_u8(64)), .lo = zig_minInt_u64 }; + return (zig_i128){ .hi = lhs.hi << rhs | lhs.lo >> (zig_as_u8(64) - rhs), .lo = lhs.lo << rhs }; +} + +static inline zig_u128 zig_add_u128(zig_u128 lhs, zig_u128 rhs) { + zig_u128 res; + res.hi = lhs.hi + rhs.hi + zig_addo_u64(&res.lo, lhs.lo, rhs.lo, zig_maxInt_u64); + return res; +} + +static inline zig_i128 zig_add_i128(zig_i128 lhs, zig_i128 rhs) { + zig_i128 res; + res.hi = lhs.hi + rhs.hi + zig_addo_u64(&res.lo, lhs.lo, rhs.lo, zig_maxInt_u64); + return res; +} + +static inline zig_u128 zig_sub_u128(zig_u128 lhs, zig_u128 rhs) { + zig_u128 res; + res.hi = lhs.hi - rhs.hi - zig_subo_u64(&res.lo, lhs.lo, rhs.lo, zig_maxInt_u64); + return res; +} + +static inline zig_i128 zig_sub_i128(zig_i128 lhs, zig_i128 rhs) { + zig_i128 res; + res.hi = lhs.hi - rhs.hi - zig_subo_u64(&res.lo, lhs.lo, rhs.lo, zig_maxInt_u64); + return res; +} + +static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) { + return zig_sub_i128(zig_div_trunc_i128(lhs, rhs), (((lhs.hi ^ rhs.hi) & zig_rem_i128(lhs, rhs).hi) < zig_as_i64(0)) ? zig_as_i128(0, 1) : zig_as_i128(0, 0)); +} + +static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { + zig_i128 rem = zig_rem_i128(lhs, rhs); + return rem + (((lhs.hi ^ rhs.hi) & rem.hi) < zig_as_i64(0) ? rhs : zig_as_i128(0, 0)); +} + +#endif /* zig_has_int128 */ + +#define zig_div_floor_u128 zig_div_trunc_u128 +#define zig_mod_u128 zig_rem_u128 + +static inline zig_i128 zig_shr_i128(zig_i128 lhs, zig_u8 rhs) { + zig_i128 sign_mask = zig_cmp_i128(lhs, zig_as_i128(0, 0)) < 0 ? zig_as_i128(-1, UINT64_MAX) : zig_as_i128(0, 0); + return zig_xor_i128(zig_bitcast_i128(zig_shr_u128(zig_bitcast_u128(zig_xor_i128(lhs, sign_mask)), rhs)), sign_mask); +} + +static inline zig_u128 zig_wrap_u128(zig_u128 val, zig_u8 bits) { + return zig_and_u128(val, zig_maxInt(u128, bits)); +} + +static inline zig_i128 zig_wrap_i128(zig_i128 val, zig_u8 bits) { + return zig_as_i128(zig_wrap_i64(zig_hi_i128(val), bits - zig_as_u8(64)), zig_lo_i128(val)); +} + +static inline zig_u128 zig_shlw_u128(zig_u128 lhs, zig_u8 rhs, zig_u8 bits) { + return zig_wrap_u128(zig_shl_u128(lhs, rhs), bits); +} + +static inline zig_i128 zig_shlw_i128(zig_i128 lhs, zig_u8 rhs, zig_u8 bits) { + return zig_wrap_i128(zig_bitcast_i128(zig_shl_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); +} + +static inline zig_u128 zig_addw_u128(zig_u128 lhs, zig_u8 rhs, zig_u8 bits) { + return zig_wrap_u128(zig_add_u128(lhs, rhs), bits); +} + +static inline zig_i128 zig_addw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { + return zig_wrap_i128(zig_bitcast_i128(zig_add_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); +} + +static inline zig_u128 zig_subw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { + return zig_wrap_u128(zig_sub_u128(lhs, rhs), bits); +} + +static inline zig_i128 zig_subw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { + return zig_wrap_i128(zig_bitcast_i128(zig_sub_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); +} + +static inline zig_u128 zig_mulw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { + return zig_wrap_u128(zig_mul_u128(lhs, rhs), bits); +} + +static inline zig_i128 zig_mulw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { + return zig_wrap_i128(zig_bitcast_i128(zig_mul_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); +} + +#if zig_has_int128 + +static inline zig_bool zig_shlo_u128(zig_u128 *res, zig_u128 lhs, zig_u8 rhs, zig_u8 bits) { + *res = zig_shlw_u128(lhs, rhs, bits); + return zig_and_u128(lhs, zig_shl_u128(zig_maxInt_u128, bits - rhs)) != zig_as_u128(0, 0); +} + +static inline zig_bool zig_shlo_i128(zig_i128 *res, zig_i128 lhs, zig_u8 rhs, zig_u8 bits) { + *res = zig_shlw_i128(lhs, rhs, bits); + zig_i128 mask = zig_bitcast_i128(zig_shl_u128(zig_maxInt_u128, bits - rhs - zig_as_u8(1))); + return zig_cmp_i128(zig_and_i128(lhs, mask), zig_as_i128(0, 0)) != 0 && + zig_cmp_i128(zig_and_i128(lhs, mask), mask) != 0; +} + +static inline zig_bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +#if zig_has_builtin(add_overflow) + zig_u128 full_res; + zig_bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u128(full_res, bits); + return overflow || full_res < zig_minInt(u128, bits) || full_res > zig_maxInt(u128, bits); #else - int int_overflow; - *res = __addodi4(lhs, rhs, &int_overflow); - overflow = int_overflow != 0; + *res = zig_addw_u128(lhs, rhs, bits); + return *res < lhs; #endif - if (!overflow) { - if (*res > max) { - // TODO adjust the result to be the truncated bits - return true; - } else if (*res < min) { - // TODO adjust the result to be the truncated bits - return true; - } - } - return overflow; } -static inline bool zig_addo_i128(int128_t lhs, int128_t rhs, int128_t *res, int128_t min, int128_t max) { - bool overflow; -#if defined(__GNUC__) && INT128_MAX == INT_MAX - overflow = __builtin_sadd_overflow(lhs, rhs, (int*)res); -#elif defined(__GNUC__) && INT128_MAX == LONG_MAX - overflow = __builtin_saddl_overflow(lhs, rhs, (long*)res); -#elif defined(__GNUC__) && INT128_MAX == LLONG_MAX - overflow = __builtin_saddll_overflow(lhs, rhs, (long long*)res); +zig_extern_c zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); +static inline zig_bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +#if zig_has_builtin(add_overflow) + zig_i128 full_res; + zig_bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); #else - int int_overflow; - *res = __addoti4(lhs, rhs, &int_overflow); - overflow = int_overflow != 0; + zig_c_int overflow_int; + zig_i128 full_res = __addoti4(lhs, rhs, &overflow_int); + zig_bool overflow = overflow_int != 0; #endif - if (!overflow) { - if (*res > max) { - // TODO adjust the result to be the truncated bits - return true; - } else if (*res < min) { - // TODO adjust the result to be the truncated bits - return true; - } - } - return overflow; + *res = zig_wrap_i128(full_res, bits); + return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits); } -static inline bool zig_addo_u8(uint8_t lhs, uint8_t rhs, uint8_t *res, uint8_t max) { -#if defined(__GNUC__) && UINT8_MAX == UINT_MAX - if (max == UINT8_MAX) { - return __builtin_uadd_overflow(lhs, rhs, (unsigned int*)res); - } -#elif defined(__GNUC__) && UINT8_MAX == ULONG_MAX - if (max == UINT8_MAX) { - return __builtin_uaddl_overflow(lhs, rhs, (unsigned long*)res); - } -#elif defined(__GNUC__) && UINT8_MAX == ULLONG_MAX - if (max == UINT8_MAX) { - return __builtin_uaddll_overflow(lhs, rhs, (unsigned long long*)res); - } -#endif - uint16_t big_result = (uint16_t)lhs + (uint16_t)rhs; - if (big_result > max) { - *res = big_result - max - 1; - return true; - } - *res = big_result; - return false; -} - -static inline uint16_t zig_addo_u16(uint16_t lhs, uint16_t rhs, uint16_t *res, uint16_t max) { -#if defined(__GNUC__) && UINT16_MAX == UINT_MAX - if (max == UINT16_MAX) { - return __builtin_uadd_overflow(lhs, rhs, (unsigned int*)res); - } -#elif defined(__GNUC__) && UINT16_MAX == ULONG_MAX - if (max == UINT16_MAX) { - return __builtin_uaddl_overflow(lhs, rhs, (unsigned long*)res); - } -#elif defined(__GNUC__) && UINT16_MAX == ULLONG_MAX - if (max == UINT16_MAX) { - return __builtin_uaddll_overflow(lhs, rhs, (unsigned long long*)res); - } -#endif - uint32_t big_result = (uint32_t)lhs + (uint32_t)rhs; - if (big_result > max) { - *res = big_result - max - 1; - return true; - } - *res = big_result; - return false; -} - -static inline uint32_t zig_addo_u32(uint32_t lhs, uint32_t rhs, uint32_t *res, uint32_t max) { -#if defined(__GNUC__) && UINT32_MAX == UINT_MAX - if (max == UINT32_MAX) { - return __builtin_uadd_overflow(lhs, rhs, (unsigned int*)res); - } -#elif defined(__GNUC__) && UINT32_MAX == ULONG_MAX - if (max == UINT32_MAX) { - return __builtin_uaddl_overflow(lhs, rhs, (unsigned long*)res); - } -#elif defined(__GNUC__) && UINT32_MAX == ULLONG_MAX - if (max == UINT32_MAX) { - return __builtin_uaddll_overflow(lhs, rhs, (unsigned long long*)res); - } -#endif - uint64_t big_result = (uint64_t)lhs + (uint64_t)rhs; - if (big_result > max) { - *res = big_result - max - 1; - return true; - } - *res = big_result; - return false; -} - -static inline uint64_t zig_addo_u64(uint64_t lhs, uint64_t rhs, uint64_t *res, uint64_t max) { - bool overflow; -#if defined(__GNUC__) && UINT64_MAX == UINT_MAX - overflow = __builtin_uadd_overflow(lhs, rhs, (unsigned int*)res); -#elif defined(__GNUC__) && UINT64_MAX == ULONG_MAX - overflow = __builtin_uaddl_overflow(lhs, rhs, (unsigned long*)res); -#elif defined(__GNUC__) && UINT64_MAX == ULLONG_MAX - overflow = __builtin_uaddll_overflow(lhs, rhs, (unsigned long long*)res); +static inline zig_bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +#if zig_has_builtin(sub_overflow) + zig_u128 full_res; + zig_bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u128(full_res, bits); + return overflow || full_res < zig_minInt(u128, bits) || full_res > zig_maxInt(u128, bits); #else - int int_overflow; - *res = __uaddodi4(lhs, rhs, &int_overflow); - overflow = int_overflow != 0; + *res = zig_subw_u128(lhs, rhs, bits); + return *res > lhs; #endif - if (*res > max && !overflow) { - *res -= max - 1; - return true; - } - return overflow; } -static inline uint128_t zig_addo_u128(uint128_t lhs, uint128_t rhs, uint128_t *res, uint128_t max) { - int overflow; - *res = __uaddoti4(lhs, rhs, &overflow); - if (*res > max && overflow == 0) { - *res -= max - 1; - return true; - } - return overflow != 0; -} - -static inline bool zig_subo_i8(int8_t lhs, int8_t rhs, int8_t *res, int8_t min, int8_t max) { -#if defined(__GNUC__) && INT8_MAX == INT_MAX - if (min == INT8_MIN && max == INT8_MAX) { - return __builtin_ssub_overflow(lhs, rhs, (int*)res); - } -#elif defined(__GNUC__) && INT8_MAX == LONG_MAX - if (min == INT8_MIN && max == INT8_MAX) { - return __builtin_ssubl_overflow(lhs, rhs, (long*)res); - } -#elif defined(__GNUC__) && INT8_MAX == LLONG_MAX - if (min == INT8_MIN && max == INT8_MAX) { - return __builtin_ssubll_overflow(lhs, rhs, (long long*)res); - } -#endif - int16_t big_result = (int16_t)lhs - (int16_t)rhs; - if (big_result > max) { - *res = big_result - ((int16_t)max - (int16_t)min); - return true; - } - if (big_result < min) { - *res = big_result + ((int16_t)max - (int16_t)min); - return true; - } - *res = big_result; - return false; -} - -static inline bool zig_subo_i16(int16_t lhs, int16_t rhs, int16_t *res, int16_t min, int16_t max) { -#if defined(__GNUC__) && INT16_MAX == INT_MAX - if (min == INT16_MIN && max == INT16_MAX) { - return __builtin_ssub_overflow(lhs, rhs, (int*)res); - } -#elif defined(__GNUC__) && INT16_MAX == LONG_MAX - if (min == INT16_MIN && max == INT16_MAX) { - return __builtin_ssubl_overflow(lhs, rhs, (long*)res); - } -#elif defined(__GNUC__) && INT16_MAX == LLONG_MAX - if (min == INT16_MIN && max == INT16_MAX) { - return __builtin_ssubll_overflow(lhs, rhs, (long long*)res); - } -#endif - int32_t big_result = (int32_t)lhs - (int32_t)rhs; - if (big_result > max) { - *res = big_result - ((int32_t)max - (int32_t)min); - return true; - } - if (big_result < min) { - *res = big_result + ((int32_t)max - (int32_t)min); - return true; - } - *res = big_result; - return false; -} - -static inline bool zig_subo_i32(int32_t lhs, int32_t rhs, int32_t *res, int32_t min, int32_t max) { -#if defined(__GNUC__) && INT32_MAX == INT_MAX - if (min == INT32_MIN && max == INT32_MAX) { - return __builtin_ssub_overflow(lhs, rhs, (int*)res); - } -#elif defined(__GNUC__) && INT32_MAX == LONG_MAX - if (min == INT32_MIN && max == INT32_MAX) { - return __builtin_ssubl_overflow(lhs, rhs, (long*)res); - } -#elif defined(__GNUC__) && INT32_MAX == LLONG_MAX - if (min == INT32_MIN && max == INT32_MAX) { - return __builtin_ssubll_overflow(lhs, rhs, (long long*)res); - } -#endif - int64_t big_result = (int64_t)lhs - (int64_t)rhs; - if (big_result > max) { - *res = big_result - ((int64_t)max - (int64_t)min); - return true; - } - if (big_result < min) { - *res = big_result + ((int64_t)max - (int64_t)min); - return true; - } - *res = big_result; - return false; -} - -static inline bool zig_subo_i64(int64_t lhs, int64_t rhs, int64_t *res, int64_t min, int64_t max) { - bool overflow; -#if defined(__GNUC__) && INT64_MAX == INT_MAX - overflow = __builtin_ssub_overflow(lhs, rhs, (int*)res); -#elif defined(__GNUC__) && INT64_MAX == LONG_MAX - overflow = __builtin_ssubl_overflow(lhs, rhs, (long*)res); -#elif defined(__GNUC__) && INT64_MAX == LLONG_MAX - overflow = __builtin_ssubll_overflow(lhs, rhs, (long long*)res); +zig_extern_c zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); +static inline zig_bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +#if zig_has_builtin(sub_overflow) + zig_i128 full_res; + zig_bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); #else - int int_overflow; - *res = __subodi4(lhs, rhs, &int_overflow); - overflow = int_overflow != 0; + zig_c_int overflow_int; + zig_i128 full_res = __suboti4(lhs, rhs, &overflow_int); + zig_bool overflow = overflow_int != 0; #endif - if (!overflow) { - if (*res > max) { - // TODO adjust the result to be the truncated bits - return true; - } else if (*res < min) { - // TODO adjust the result to be the truncated bits - return true; - } - } - return overflow; + *res = zig_wrap_i128(full_res, bits); + return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits); } -static inline bool zig_subo_i128(int128_t lhs, int128_t rhs, int128_t *res, int128_t min, int128_t max) { - bool overflow; -#if defined(__GNUC__) && INT128_MAX == INT_MAX - overflow = __builtin_ssub_overflow(lhs, rhs, (int*)res); -#elif defined(__GNUC__) && INT128_MAX == LONG_MAX - overflow = __builtin_ssubl_overflow(lhs, rhs, (long*)res); -#elif defined(__GNUC__) && INT128_MAX == LLONG_MAX - overflow = __builtin_ssubll_overflow(lhs, rhs, (long long*)res); +static inline zig_bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +#if zig_has_builtin(mul_overflow) + zig_u128 full_res; + zig_bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u128(full_res, bits); + return overflow || full_res < zig_minInt(u128, bits) || full_res > zig_maxInt(u128, bits); #else - int int_overflow; - *res = __suboti4(lhs, rhs, &int_overflow); - overflow = int_overflow != 0; + *res = zig_mulw_u128(lhs, rhs, bits); + return rhs != zig_as_u128(0, 0) && lhs > zig_maxInt(u128, bits) / rhs; #endif - if (!overflow) { - if (*res > max) { - // TODO adjust the result to be the truncated bits - return true; - } else if (*res < min) { - // TODO adjust the result to be the truncated bits - return true; - } - } - return overflow; } -static inline bool zig_subo_u8(uint8_t lhs, uint8_t rhs, uint8_t *res, uint8_t max) { -#if defined(__GNUC__) && UINT8_MAX == UINT_MAX - return __builtin_usub_overflow(lhs, rhs, (unsigned int*)res); -#elif defined(__GNUC__) && UINT8_MAX == ULONG_MAX - return __builtin_usubl_overflow(lhs, rhs, (unsigned long*)res); -#elif defined(__GNUC__) && UINT8_MAX == ULLONG_MAX - return __builtin_usubll_overflow(lhs, rhs, (unsigned long long*)res); -#endif - if (rhs > lhs) { - *res = max - (rhs - lhs - 1); - return true; - } - *res = lhs - rhs; - return false; -} - -static inline uint16_t zig_subo_u16(uint16_t lhs, uint16_t rhs, uint16_t *res, uint16_t max) { -#if defined(__GNUC__) && UINT16_MAX == UINT_MAX - return __builtin_usub_overflow(lhs, rhs, (unsigned int*)res); -#elif defined(__GNUC__) && UINT16_MAX == ULONG_MAX - return __builtin_usubl_overflow(lhs, rhs, (unsigned long*)res); -#elif defined(__GNUC__) && UINT16_MAX == ULLONG_MAX - return __builtin_usubll_overflow(lhs, rhs, (unsigned long long*)res); -#endif - if (rhs > lhs) { - *res = max - (rhs - lhs - 1); - return true; - } - *res = lhs - rhs; - return false; -} - -static inline uint32_t zig_subo_u32(uint32_t lhs, uint32_t rhs, uint32_t *res, uint32_t max) { - if (max == UINT32_MAX) { -#if defined(__GNUC__) && UINT32_MAX == UINT_MAX - return __builtin_usub_overflow(lhs, rhs, (unsigned int*)res); -#elif defined(__GNUC__) && UINT32_MAX == ULONG_MAX - return __builtin_usubl_overflow(lhs, rhs, (unsigned long*)res); -#elif defined(__GNUC__) && UINT32_MAX == ULLONG_MAX - return __builtin_usubll_overflow(lhs, rhs, (unsigned long long*)res); -#endif - int int_overflow; - *res = __usubosi4(lhs, rhs, &int_overflow); - return int_overflow != 0; - } else { - if (rhs > lhs) { - *res = max - (rhs - lhs - 1); - return true; - } - *res = lhs - rhs; - return false; - } -} - -static inline uint64_t zig_subo_u64(uint64_t lhs, uint64_t rhs, uint64_t *res, uint64_t max) { - if (max == UINT64_MAX) { -#if defined(__GNUC__) && UINT64_MAX == UINT_MAX - return __builtin_usub_overflow(lhs, rhs, (unsigned int*)res); -#elif defined(__GNUC__) && UINT64_MAX == ULONG_MAX - return __builtin_usubl_overflow(lhs, rhs, (unsigned long*)res); -#elif defined(__GNUC__) && UINT64_MAX == ULLONG_MAX - return __builtin_usubll_overflow(lhs, rhs, (unsigned long long*)res); +zig_extern_c zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); +static inline zig_bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +#if zig_has_builtin(mul_overflow) + zig_i128 full_res; + zig_bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); #else - int int_overflow; - *res = __usubodi4(lhs, rhs, &int_overflow); - return int_overflow != 0; + zig_c_int overflow_int; + zig_i128 full_res = __muloti4(lhs, rhs, &overflow); + zig_bool overflow = overflow_int != 0; #endif - } else { - if (rhs > lhs) { - *res = max - (rhs - lhs - 1); - return true; - } - *res = lhs - rhs; - return false; - } + *res = zig_wrap_i128(full_res, bits); + return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits); } -static inline uint128_t zig_subo_u128(uint128_t lhs, uint128_t rhs, uint128_t *res, uint128_t max) { - if (max == UINT128_MAX) { - int int_overflow; - *res = __usuboti4(lhs, rhs, &int_overflow); - return int_overflow != 0; - } else { - if (rhs > lhs) { - *res = max - (rhs - lhs - 1); - return true; - } - *res = lhs - rhs; - return false; - } +#else /* zig_has_int128 */ + +static inline zig_bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs) { + return zig_addo_u64(&res->hi, lhs.hi, rhs.hi, UINT64_MAX) | + zig_addo_u64(&res->hi, res->hi, zig_addo_u64(&res->lo, lhs.lo, rhs.lo, UINT64_MAX)); } -static inline bool zig_mulo_i8(int8_t lhs, int8_t rhs, int8_t *res, int8_t min, int8_t max) { -#if defined(__GNUC__) && INT8_MAX == INT_MAX - if (min == INT8_MIN && max == INT8_MAX) { - return __builtin_smul_overflow(lhs, rhs, (int*)res); - } -#elif defined(__GNUC__) && INT8_MAX == LONG_MAX - if (min == INT8_MIN && max == INT8_MAX) { - return __builtin_smull_overflow(lhs, rhs, (long*)res); - } -#elif defined(__GNUC__) && INT8_MAX == LLONG_MAX - if (min == INT8_MIN && max == INT8_MAX) { - return __builtin_smulll_overflow(lhs, rhs, (long long*)res); - } -#endif - int16_t big_result = (int16_t)lhs * (int16_t)rhs; - if (big_result > max) { - *res = big_result - ((int16_t)max - (int16_t)min); - return true; - } - if (big_result < min) { - *res = big_result + ((int16_t)max - (int16_t)min); - return true; - } - *res = big_result; - return false; +static inline zig_bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs) { + return zig_subo_u64(&res->hi, lhs.hi, rhs.hi, UINT64_MAX) | + zig_subo_u64(&res->hi, res->hi, zig_subo_u64(&res->lo, lhs.lo, rhs.lo, UINT64_MAX)); } -static inline bool zig_mulo_i16(int16_t lhs, int16_t rhs, int16_t *res, int16_t min, int16_t max) { -#if defined(__GNUC__) && INT16_MAX == INT_MAX - if (min == INT16_MIN && max == INT16_MAX) { - return __builtin_smul_overflow(lhs, rhs, (int*)res); - } -#elif defined(__GNUC__) && INT16_MAX == LONG_MAX - if (min == INT16_MIN && max == INT16_MAX) { - return __builtin_smull_overflow(lhs, rhs, (long*)res); - } -#elif defined(__GNUC__) && INT16_MAX == LLONG_MAX - if (min == INT16_MIN && max == INT16_MAX) { - return __builtin_smulll_overflow(lhs, rhs, (long long*)res); - } -#endif - int32_t big_result = (int32_t)lhs * (int32_t)rhs; - if (big_result > max) { - *res = big_result - ((int32_t)max - (int32_t)min); - return true; - } - if (big_result < min) { - *res = big_result + ((int32_t)max - (int32_t)min); - return true; - } - *res = big_result; - return false; +#endif /* zig_has_int128 */ + +static inline zig_u128 zig_shls_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { + zig_u128 res; + if (zig_cmp_u128(rhs, zig_as_u128(0, bits)) >= 0) + return zig_cmp_u128(lhs, zig_as_u128(0, 0)) != 0 ? zig_maxInt(u128, bits) : lhs; + return zig_shlo_u128(&res, lhs, (zig_u8)rhs, bits) ? zig_maxInt(u128, bits) : res; } -static inline bool zig_mulo_i32(int32_t lhs, int32_t rhs, int32_t *res, int32_t min, int32_t max) { -#if defined(__GNUC__) && INT32_MAX == INT_MAX - if (min == INT32_MIN && max == INT32_MAX) { - return __builtin_smul_overflow(lhs, rhs, (int*)res); - } -#elif defined(__GNUC__) && INT32_MAX == LONG_MAX - if (min == INT32_MIN && max == INT32_MAX) { - return __builtin_smull_overflow(lhs, rhs, (long*)res); - } -#elif defined(__GNUC__) && INT32_MAX == LLONG_MAX - if (min == INT32_MIN && max == INT32_MAX) { - return __builtin_smulll_overflow(lhs, rhs, (long long*)res); - } -#endif - int64_t big_result = (int64_t)lhs * (int64_t)rhs; - if (big_result > max) { - *res = big_result - ((int64_t)max - (int64_t)min); - return true; - } - if (big_result < min) { - *res = big_result + ((int64_t)max - (int64_t)min); - return true; - } - *res = big_result; - return false; +static inline zig_i128 zig_shls_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { + zig_i128 res; + if (zig_cmp_u128(zig_bitcast_u128(rhs), zig_as_u128(0, bits)) < 0 && !zig_shlo_i128(&res, lhs, rhs, bits)) return res; + return zig_cmp_i128(lhs, zig_as_i128(0, 0)) < 0 ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); } -static inline bool zig_mulo_i64(int64_t lhs, int64_t rhs, int64_t *res, int64_t min, int64_t max) { - bool overflow; -#if defined(__GNUC__) && INT64_MAX == INT_MAX - overflow = __builtin_smul_overflow(lhs, rhs, (int*)res); -#elif defined(__GNUC__) && INT64_MAX == LONG_MAX - overflow = __builtin_smull_overflow(lhs, rhs, (long*)res); -#elif defined(__GNUC__) && INT64_MAX == LLONG_MAX - overflow = __builtin_smulll_overflow(lhs, rhs, (long long*)res); +static inline zig_u128 zig_adds_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { + zig_u128 res; + return zig_addo_u128(&res, lhs, rhs, bits) ? zig_maxInt(u128, bits) : res; +} + +static inline zig_i128 zig_adds_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { + zig_i128 res; + if (!zig_addo_i128(&res, lhs, rhs, bits)) return res; + return zig_cmp_i128(res, zig_as_i128(0, 0)) >= 0 ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); +} + +static inline zig_u128 zig_subs_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { + zig_u128 res; + return zig_subo_u128(&res, lhs, rhs, bits) ? zig_minInt(u128, bits) : res; +} + +static inline zig_i128 zig_subs_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { + zig_i128 res; + if (!zig_subo_i128(&res, lhs, rhs, bits)) return res; + return zig_cmp_i128(res, zig_as_i128(0, 0)) >= 0 ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); +} + +static inline zig_u128 zig_muls_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { + zig_u128 res; + return zig_mulo_u128(&res, lhs, rhs, bits) ? zig_maxInt(u128, bits) : res; +} + +static inline zig_i128 zig_muls_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { + zig_i128 res; + if (!zig_mulo_i128(&res, lhs, rhs, bits)) return res; + return zig_cmp_i128(zig_xor_i128(lhs, rhs), zig_as_i128(0, 0)) < 0 ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); +} + +static inline zig_u8 zig_clz_u128(zig_u128 val, zig_u8 bits) { + if (zig_hi_u128(val) != 0) return zig_clz_u64(zig_hi_u128(val), bits - zig_as_u8(64)); + return zig_clz_u64(zig_lo_u128(val), zig_as_u8(64)) + zig_as_u8(64); +} + +static inline zig_u8 zig_clz_i128(zig_i128 val, zig_u8 bits) { + return zig_clz_u128(zig_bitcast_u128(val), bits); +} + +static inline zig_u8 zig_ctz_u128(zig_u128 val, zig_u8 bits) { + if (zig_lo_u128(val) != 0) return zig_ctz_u64(zig_lo_u128(val), zig_as_u8(64)); + return zig_ctz_u64(zig_hi_u128(val), bits - zig_as_u8(64)) + zig_as_u8(64); +} + +static inline zig_u8 zig_ctz_i128(zig_i128 val, zig_u8 bits) { + return zig_ctz_u128(zig_bitcast_u128(val), bits); +} + +static inline zig_u8 zig_popcount_u128(zig_u128 val, zig_u8 bits) { + return zig_popcount_u64(zig_hi_u128(val), bits - zig_as_u8(64)) + + zig_popcount_u64(zig_lo_u128(val), zig_as_u8(64)); +} + +static inline zig_u8 zig_popcount_i128(zig_i128 val, zig_u8 bits) { + return zig_popcount_u128(zig_bitcast_u128(val), bits); +} + +static inline zig_u128 zig_byte_swap_u128(zig_u128 val, zig_u8 bits) { + zig_u128 full_res; +#if zig_has_builtin(bswap128) + full_res = __builtin_bswap128(val); #else - int int_overflow; - *res = __mulodi4(lhs, rhs, &int_overflow); - overflow = int_overflow != 0; + full_res = zig_as_u128(zig_byte_swap_u64(zig_lo_u128(val), zig_as_u8(64)), + zig_byte_swap_u64(zig_hi_u128(val), zig_as_u8(64))); #endif - if (!overflow) { - if (*res > max) { - // TODO adjust the result to be the truncated bits - return true; - } else if (*res < min) { - // TODO adjust the result to be the truncated bits - return true; - } - } - return overflow; + return zig_shr_u128(full_res, zig_as_u8(128) - bits); } -static inline bool zig_mulo_i128(int128_t lhs, int128_t rhs, int128_t *res, int128_t min, int128_t max) { - bool overflow; -#if defined(__GNUC__) && INT128_MAX == INT_MAX - overflow = __builtin_smul_overflow(lhs, rhs, (int*)res); -#elif defined(__GNUC__) && INT128_MAX == LONG_MAX - overflow = __builtin_smull_overflow(lhs, rhs, (long*)res); -#elif defined(__GNUC__) && INT128_MAX == LLONG_MAX - overflow = __builtin_smulll_overflow(lhs, rhs, (long long*)res); -#else - int int_overflow; - *res = __muloti4(lhs, rhs, &int_overflow); - overflow = int_overflow != 0; -#endif - if (!overflow) { - if (*res > max) { - // TODO adjust the result to be the truncated bits - return true; - } else if (*res < min) { - // TODO adjust the result to be the truncated bits - return true; - } - } - return overflow; +static inline zig_i128 zig_byte_swap_i128(zig_i128 val, zig_u8 bits) { + return zig_byte_swap_u128(zig_bitcast_u128(val), bits); } -static inline bool zig_mulo_u8(uint8_t lhs, uint8_t rhs, uint8_t *res, uint8_t max) { -#if defined(__GNUC__) && UINT8_MAX == UINT_MAX - if (max == UINT8_MAX) { - return __builtin_umul_overflow(lhs, rhs, (unsigned int*)res); - } -#elif defined(__GNUC__) && UINT8_MAX == ULONG_MAX - if (max == UINT8_MAX) { - return __builtin_umull_overflow(lhs, rhs, (unsigned long*)res); - } -#elif defined(__GNUC__) && UINT8_MAX == ULLONG_MAX - if (max == UINT8_MAX) { - return __builtin_umulll_overflow(lhs, rhs, (unsigned long long*)res); - } -#endif - uint16_t big_result = (uint16_t)lhs * (uint16_t)rhs; - if (big_result > max) { - *res = big_result - max - 1; - return true; - } - *res = big_result; - return false; +static inline zig_u128 zig_bit_reverse_u128(zig_u128 val, zig_u8 bits) { + return zig_shr_u128(zig_as_u128(zig_bit_reverse_u64(zig_lo_u128(val), zig_as_u8(64)), + zig_bit_reverse_u64(zig_hi_u128(val), zig_as_u8(64))), + zig_as_u8(128) - bits); } -static inline uint16_t zig_mulo_u16(uint16_t lhs, uint16_t rhs, uint16_t *res, uint16_t max) { -#if defined(__GNUC__) && UINT16_MAX == UINT_MAX - if (max == UINT16_MAX) { - return __builtin_umul_overflow(lhs, rhs, (unsigned int*)res); - } -#elif defined(__GNUC__) && UINT16_MAX == ULONG_MAX - if (max == UINT16_MAX) { - return __builtin_umull_overflow(lhs, rhs, (unsigned long*)res); - } -#elif defined(__GNUC__) && UINT16_MAX == ULLONG_MAX - if (max == UINT16_MAX) { - return __builtin_umulll_overflow(lhs, rhs, (unsigned long long*)res); - } -#endif - uint32_t big_result = (uint32_t)lhs * (uint32_t)rhs; - if (big_result > max) { - *res = big_result - max - 1; - return true; - } - *res = big_result; - return false; +static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, zig_u8 bits) { + return zig_bit_reverse_u128(zig_bitcast_u128(val), bits); } -static inline uint32_t zig_mulo_u32(uint32_t lhs, uint32_t rhs, uint32_t *res, uint32_t max) { -#if defined(__GNUC__) && UINT32_MAX == UINT_MAX - if (max == UINT32_MAX) { - return __builtin_umul_overflow(lhs, rhs, (unsigned int*)res); - } -#elif defined(__GNUC__) && UINT32_MAX == ULONG_MAX - if (max == UINT32_MAX) { - return __builtin_umull_overflow(lhs, rhs, (unsigned long*)res); - } -#elif defined(__GNUC__) && UINT32_MAX == ULLONG_MAX - if (max == UINT32_MAX) { - return __builtin_umulll_overflow(lhs, rhs, (unsigned long long*)res); - } -#endif - uint64_t big_result = (uint64_t)lhs * (uint64_t)rhs; - if (big_result > max) { - *res = big_result - max - 1; - return true; - } - *res = big_result; - return false; -} +/* ========================== Float Point Routines ========================== */ -static inline uint64_t zig_mulo_u64(uint64_t lhs, uint64_t rhs, uint64_t *res, uint64_t max) { - bool overflow; -#if defined(__GNUC__) && UINT64_MAX == UINT_MAX - overflow = __builtin_umul_overflow(lhs, rhs, (unsigned int*)res); -#elif defined(__GNUC__) && UINT64_MAX == ULONG_MAX - overflow = __builtin_umull_overflow(lhs, rhs, (unsigned long*)res); -#elif defined(__GNUC__) && UINT64_MAX == ULLONG_MAX - overflow = __builtin_umulll_overflow(lhs, rhs, (unsigned long long*)res); -#else - int int_overflow; - *res = __umulodi4(lhs, rhs, &int_overflow); - overflow = int_overflow != 0; -#endif - if (*res > max && !overflow) { - *res -= max - 1; - return true; - } - return overflow; -} - -static inline uint128_t zig_mulo_u128(uint128_t lhs, uint128_t rhs, uint128_t *res, uint128_t max) { - int overflow; - *res = __umuloti4(lhs, rhs, &overflow); - if (*res > max && overflow == 0) { - *res -= max - 1; - return true; - } - return overflow != 0; -} - -static inline float zig_bitcast_f32_u32(uint32_t arg) { - float dest; +static inline zig_f32 zig_bitcast_f32_u32(zig_u32 arg) { + zig_f32 dest; memcpy(&dest, &arg, sizeof dest); return dest; } -static inline float zig_bitcast_f64_u64(uint64_t arg) { - double dest; +static inline zig_f64 zig_bitcast_f64_u64(zig_u64 arg) { + zig_f64 dest; memcpy(&dest, &arg, sizeof dest); return dest; } -#define zig_add_sat_u(ZT, T) static inline T zig_adds_##ZT(T x, T y, T max) { \ - return (x > max - y) ? max : x + y; \ -} - -#define zig_add_sat_s(ZT, T, T2) static inline T zig_adds_##ZT(T2 x, T2 y, T2 min, T2 max) { \ - T2 res = x + y; \ - return (res < min) ? min : (res > max) ? max : res; \ -} - -zig_add_sat_u( u8, uint8_t) -zig_add_sat_s( i8, int8_t, int16_t) -zig_add_sat_u(u16, uint16_t) -zig_add_sat_s(i16, int16_t, int32_t) -zig_add_sat_u(u32, uint32_t) -zig_add_sat_s(i32, int32_t, int64_t) -zig_add_sat_u(u64, uint64_t) -zig_add_sat_s(i64, int64_t, int128_t) -zig_add_sat_s(isize, intptr_t, int128_t) -zig_add_sat_s(short, short, int) -zig_add_sat_s(int, int, long) -zig_add_sat_s(long, long, long long) - -#define zig_sub_sat_u(ZT, T) static inline T zig_subs_##ZT(T x, T y, T max) { \ - return (x > max + y) ? max : x - y; \ -} - -#define zig_sub_sat_s(ZT, T, T2) static inline T zig_subs_##ZT(T2 x, T2 y, T2 min, T2 max) { \ - T2 res = x - y; \ - return (res < min) ? min : (res > max) ? max : res; \ -} - -zig_sub_sat_u( u8, uint8_t) -zig_sub_sat_s( i8, int8_t, int16_t) -zig_sub_sat_u(u16, uint16_t) -zig_sub_sat_s(i16, int16_t, int32_t) -zig_sub_sat_u(u32, uint32_t) -zig_sub_sat_s(i32, int32_t, int64_t) -zig_sub_sat_u(u64, uint64_t) -zig_sub_sat_s(i64, int64_t, int128_t) -zig_sub_sat_s(isize, intptr_t, int128_t) -zig_sub_sat_s(short, short, int) -zig_sub_sat_s(int, int, long) -zig_sub_sat_s(long, long, long long) - - -#define zig_mul_sat_u(ZT, T, T2) static inline T zig_muls_##ZT(T2 x, T2 y, T2 max) { \ - T2 res = x * y; \ - return (res > max) ? max : res; \ -} - -#define zig_mul_sat_s(ZT, T, T2) static inline T zig_muls_##ZT(T2 x, T2 y, T2 min, T2 max) { \ - T2 res = x * y; \ - return (res < min) ? min : (res > max) ? max : res; \ -} - -zig_mul_sat_u(u8, uint8_t, uint16_t) -zig_mul_sat_s(i8, int8_t, int16_t) -zig_mul_sat_u(u16, uint16_t, uint32_t) -zig_mul_sat_s(i16, int16_t, int32_t) -zig_mul_sat_u(u32, uint32_t, uint64_t) -zig_mul_sat_s(i32, int32_t, int64_t) -zig_mul_sat_u(u64, uint64_t, uint128_t) -zig_mul_sat_s(i64, int64_t, int128_t) -zig_mul_sat_s(isize, intptr_t, int128_t) -zig_mul_sat_s(short, short, int) -zig_mul_sat_s(int, int, long) -zig_mul_sat_s(long, long, long long) - -#define zig_shl_sat_u(ZT, T, bits) static inline T zig_shls_##ZT(T x, T y, T max) { \ - if(x == 0) return 0; \ - T bits_set = 64 - __builtin_clzll(x); \ - return (bits_set + y > bits) ? max : x << y; \ -} - -#define zig_shl_sat_s(ZT, T, bits) static inline T zig_shls_##ZT(T x, T y, T min, T max) { \ - if(x == 0) return 0; \ - T x_twos_comp = x < 0 ? -x : x; \ - T bits_set = 64 - __builtin_clzll(x_twos_comp); \ - T min_or_max = (x < 0) ? min : max; \ - return (y + bits_set > bits ) ? min_or_max : x << y; \ -} - -zig_shl_sat_u(u8, uint8_t, 8) -zig_shl_sat_s(i8, int8_t, 7) -zig_shl_sat_u(u16, uint16_t, 16) -zig_shl_sat_s(i16, int16_t, 15) -zig_shl_sat_u(u32, uint32_t, 32) -zig_shl_sat_s(i32, int32_t, 31) -zig_shl_sat_u(u64, uint64_t, 64) -zig_shl_sat_s(i64, int64_t, 63) -zig_shl_sat_s(isize, intptr_t, ((sizeof(intptr_t)) * CHAR_BIT - 1)) -zig_shl_sat_s(short, short, ((sizeof(short )) * CHAR_BIT - 1)) -zig_shl_sat_s(int, int, ((sizeof(int )) * CHAR_BIT - 1)) -zig_shl_sat_s(long, long, ((sizeof(long )) * CHAR_BIT - 1)) - -#define zig_bitsizeof(T) (CHAR_BIT * sizeof(T)) -#define zig_bit_mask(T, bit_width) \ - ((bit_width) == zig_bitsizeof(T) \ - ? ((T)-1) \ - : (((T)1 << (T)(bit_width)) - 1)) - -static inline int zig_clz(unsigned int value, uint8_t zig_type_bit_width) { - if (value == 0) return zig_type_bit_width; - return __builtin_clz(value) - zig_bitsizeof(unsigned int) + zig_type_bit_width; -} - -static inline int zig_clzl(unsigned long value, uint8_t zig_type_bit_width) { - if (value == 0) return zig_type_bit_width; - return __builtin_clzl(value) - zig_bitsizeof(unsigned long) + zig_type_bit_width; -} - -static inline int zig_clzll(unsigned long long value, uint8_t zig_type_bit_width) { - if (value == 0) return zig_type_bit_width; - return __builtin_clzll(value) - zig_bitsizeof(unsigned long long) + zig_type_bit_width; -} - -#define zig_clz_u8 zig_clz -#define zig_clz_i8 zig_clz -#define zig_clz_u16 zig_clz -#define zig_clz_i16 zig_clz -#define zig_clz_u32 zig_clzl -#define zig_clz_i32 zig_clzl -#define zig_clz_u64 zig_clzll -#define zig_clz_i64 zig_clzll - -static inline int zig_clz_u128(uint128_t value, uint8_t zig_type_bit_width) { - if (value == 0) return zig_type_bit_width; - const uint128_t mask = zig_bit_mask(uint128_t, zig_type_bit_width); - const uint64_t hi = (value & mask) >> 64; - const uint64_t lo = (value & mask); - const int leading_zeroes = ( - hi != 0 ? __builtin_clzll(hi) : 64 + (lo != 0 ? __builtin_clzll(lo) : 64)); - return leading_zeroes - zig_bitsizeof(uint128_t) + zig_type_bit_width; -} - -#define zig_clz_i128 zig_clz_u128 - -static inline int zig_ctz(unsigned int value, uint8_t zig_type_bit_width) { - if (value == 0) return zig_type_bit_width; - return __builtin_ctz(value & zig_bit_mask(unsigned int, zig_type_bit_width)); -} - -static inline int zig_ctzl(unsigned long value, uint8_t zig_type_bit_width) { - if (value == 0) return zig_type_bit_width; - return __builtin_ctzl(value & zig_bit_mask(unsigned long, zig_type_bit_width)); -} - -static inline int zig_ctzll(unsigned long value, uint8_t zig_type_bit_width) { - if (value == 0) return zig_type_bit_width; - return __builtin_ctzll(value & zig_bit_mask(unsigned long, zig_type_bit_width)); -} - -#define zig_ctz_u8 zig_ctz -#define zig_ctz_i8 zig_ctz -#define zig_ctz_u16 zig_ctz -#define zig_ctz_i16 zig_ctz -#define zig_ctz_u32 zig_ctzl -#define zig_ctz_i32 zig_ctzl -#define zig_ctz_u64 zig_ctzll -#define zig_ctz_i64 zig_ctzll - -static inline int zig_ctz_u128(uint128_t value, uint8_t zig_type_bit_width) { - const uint128_t mask = zig_bit_mask(uint128_t, zig_type_bit_width); - const uint64_t hi = (value & mask) >> 64; - const uint64_t lo = (value & mask); - return (lo != 0 ? __builtin_ctzll(lo) : 64 + (hi != 0 ? __builtin_ctzll(hi) : 64)); -} - -#define zig_ctz_i128 zig_ctz_u128 - -static inline int zig_popcount(unsigned int value, uint8_t zig_type_bit_width) { - return __builtin_popcount(value & zig_bit_mask(unsigned int, zig_type_bit_width)); -} - -static inline int zig_popcountl(unsigned long value, uint8_t zig_type_bit_width) { - return __builtin_popcountl(value & zig_bit_mask(unsigned long, zig_type_bit_width)); -} - -static inline int zig_popcountll(unsigned long value, uint8_t zig_type_bit_width) { - return __builtin_popcountll(value & zig_bit_mask(unsigned long, zig_type_bit_width)); -} - -#define zig_popcount_u8 zig_popcount -#define zig_popcount_i8 zig_popcount -#define zig_popcount_u16 zig_popcount -#define zig_popcount_i16 zig_popcount -#define zig_popcount_u32 zig_popcountl -#define zig_popcount_i32 zig_popcountl -#define zig_popcount_u64 zig_popcountll -#define zig_popcount_i64 zig_popcountll - -static inline int zig_popcount_u128(uint128_t value, uint8_t zig_type_bit_width) { - const uint128_t mask = zig_bit_mask(uint128_t, zig_type_bit_width); - const uint64_t hi = (value & mask) >> 64; - const uint64_t lo = (value & mask); - return __builtin_popcountll(hi) + __builtin_popcountll(lo); -} - -#define zig_popcount_i128 zig_popcount_u128 - -static inline bool zig_shlo_i8(int8_t lhs, int8_t rhs, int8_t *res, uint8_t bits) { - *res = lhs << rhs; - if (zig_clz_i8(lhs, bits) >= rhs) return false; - *res &= UINT8_MAX >> (8 - bits); - return true; -} - -static inline bool zig_shlo_i16(int16_t lhs, int16_t rhs, int16_t *res, uint8_t bits) { - *res = lhs << rhs; - if (zig_clz_i16(lhs, bits) >= rhs) return false; - *res &= UINT16_MAX >> (16 - bits); - return true; -} - -static inline bool zig_shlo_i32(int32_t lhs, int32_t rhs, int32_t *res, uint8_t bits) { - *res = lhs << rhs; - if (zig_clz_i32(lhs, bits) >= rhs) return false; - *res &= UINT32_MAX >> (32 - bits); - return true; -} - -static inline bool zig_shlo_i64(int64_t lhs, int64_t rhs, int64_t *res, uint8_t bits) { - *res = lhs << rhs; - if (zig_clz_i64(lhs, bits) >= rhs) return false; - *res &= UINT64_MAX >> (64 - bits); - return true; -} - -static inline bool zig_shlo_i128(int128_t lhs, int128_t rhs, int128_t *res, uint8_t bits) { - *res = lhs << rhs; - if (zig_clz_i128(lhs, bits) >= rhs) return false; - *res &= UINT128_MAX >> (128 - bits); - return true; -} - -static inline bool zig_shlo_u8(uint8_t lhs, uint8_t rhs, uint8_t *res, uint8_t bits) { - *res = lhs << rhs; - if (zig_clz_u8(lhs, bits) >= rhs) return false; - *res &= UINT8_MAX >> (8 - bits); - return true; -} - -static inline uint16_t zig_shlo_u16(uint16_t lhs, uint16_t rhs, uint16_t *res, uint8_t bits) { - *res = lhs << rhs; - if (zig_clz_u16(lhs, bits) >= rhs) return false; - *res &= UINT16_MAX >> (16 - bits); - return true; -} - -static inline uint32_t zig_shlo_u32(uint32_t lhs, uint32_t rhs, uint32_t *res, uint8_t bits) { - *res = lhs << rhs; - if (zig_clz_u32(lhs, bits) >= rhs) return false; - *res &= UINT32_MAX >> (32 - bits); - return true; -} - -static inline uint64_t zig_shlo_u64(uint64_t lhs, uint64_t rhs, uint64_t *res, uint8_t bits) { - *res = lhs << rhs; - if (zig_clz_u64(lhs, bits) >= rhs) return false; - *res &= UINT64_MAX >> (64 - bits); - return true; -} - -static inline uint128_t zig_shlo_u128(uint128_t lhs, uint128_t rhs, uint128_t *res, uint8_t bits) { - *res = lhs << rhs; - if (zig_clz_u128(lhs, bits) >= rhs) return false; - *res &= UINT128_MAX >> (128 - bits); - return true; -} - -#define zig_sign_extend(T) \ - static inline T zig_sign_extend_##T(T value, uint8_t zig_type_bit_width) { \ - const T m = (T)1 << (T)(zig_type_bit_width - 1); \ - return (value ^ m) - m; \ - } - -zig_sign_extend(uint8_t) -zig_sign_extend(uint16_t) -zig_sign_extend(uint32_t) -zig_sign_extend(uint64_t) -zig_sign_extend(uint128_t) - -#define zig_byte_swap_u(ZigTypeBits, CTypeBits) \ - static inline uint##CTypeBits##_t zig_byte_swap_u##ZigTypeBits(uint##CTypeBits##_t value, uint8_t zig_type_bit_width) { \ - return __builtin_bswap##CTypeBits(value) >> (CTypeBits - zig_type_bit_width); \ - } - -#define zig_byte_swap_s(ZigTypeBits, CTypeBits) \ - static inline int##CTypeBits##_t zig_byte_swap_i##ZigTypeBits(int##CTypeBits##_t value, uint8_t zig_type_bit_width) { \ - const uint##CTypeBits##_t swapped = zig_byte_swap_u##ZigTypeBits(value, zig_type_bit_width); \ - return zig_sign_extend_uint##CTypeBits##_t(swapped, zig_type_bit_width); \ - } - -#define zig_byte_swap(ZigTypeBits, CTypeBits) \ - zig_byte_swap_u(ZigTypeBits, CTypeBits) \ - zig_byte_swap_s(ZigTypeBits, CTypeBits) - -zig_byte_swap( 8, 16) -zig_byte_swap(16, 16) -zig_byte_swap(32, 32) -zig_byte_swap(64, 64) - -static inline uint128_t zig_byte_swap_u128(uint128_t value, uint8_t zig_type_bit_width) { - const uint128_t mask = zig_bit_mask(uint128_t, zig_type_bit_width); - const uint128_t hi = __builtin_bswap64((uint64_t)(value >> 64)); - const uint128_t lo = __builtin_bswap64((uint64_t)value); - return (((lo << 64 | hi) >> (128 - zig_type_bit_width))) & mask; -} - -zig_byte_swap_s(128, 128) - -static const uint8_t zig_bit_reverse_lut[256] = { - 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, - 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, - 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4, - 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, - 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, - 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, - 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, - 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, - 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, - 0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, - 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1, - 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, - 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, - 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, - 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd, - 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, - 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, - 0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, - 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7, - 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, - 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, - 0x3f, 0xbf, 0x7f, 0xff -}; - -static inline uint8_t zig_bit_reverse_u8(uint8_t value, uint8_t zig_type_bit_width) { - const uint8_t reversed = zig_bit_reverse_lut[value] >> (8 - zig_type_bit_width); - return zig_sign_extend_uint8_t(reversed, zig_type_bit_width); -} - -#define zig_bit_reverse_i8 zig_bit_reverse_u8 - -static inline uint16_t zig_bit_reverse_u16(uint16_t value, uint8_t zig_type_bit_width) { - const uint16_t swapped = zig_byte_swap_u16(value, zig_type_bit_width); - const uint16_t reversed = ( - ((uint16_t)zig_bit_reverse_lut[(swapped >> 0x08) & 0xff] << 0x08) | - ((uint16_t)zig_bit_reverse_lut[(swapped >> 0x00) & 0xff] << 0x00)); - return zig_sign_extend_uint16_t( - reversed & zig_bit_mask(uint16_t, zig_type_bit_width), - zig_type_bit_width); -} - -#define zig_bit_reverse_i16 zig_bit_reverse_u16 - -static inline uint32_t zig_bit_reverse_u32(uint32_t value, uint8_t zig_type_bit_width) { - const uint32_t swapped = zig_byte_swap_u32(value, zig_type_bit_width); - const uint32_t reversed = ( - ((uint32_t)zig_bit_reverse_lut[(swapped >> 0x18) & 0xff] << 0x18) | - ((uint32_t)zig_bit_reverse_lut[(swapped >> 0x10) & 0xff] << 0x10) | - ((uint32_t)zig_bit_reverse_lut[(swapped >> 0x08) & 0xff] << 0x08) | - ((uint32_t)zig_bit_reverse_lut[(swapped >> 0x00) & 0xff] << 0x00)); - return zig_sign_extend_uint32_t( - reversed & zig_bit_mask(uint32_t, zig_type_bit_width), - zig_type_bit_width); -} - -#define zig_bit_reverse_i32 zig_bit_reverse_u32 - -static inline uint64_t zig_bit_reverse_u64(uint64_t value, uint8_t zig_type_bit_width) { - const uint64_t swapped = zig_byte_swap_u64(value, zig_type_bit_width); - const uint64_t reversed = ( - ((uint64_t)zig_bit_reverse_lut[(swapped >> 0x38) & 0xff] << 0x38) | - ((uint64_t)zig_bit_reverse_lut[(swapped >> 0x30) & 0xff] << 0x30) | - ((uint64_t)zig_bit_reverse_lut[(swapped >> 0x28) & 0xff] << 0x28) | - ((uint64_t)zig_bit_reverse_lut[(swapped >> 0x20) & 0xff] << 0x20) | - ((uint64_t)zig_bit_reverse_lut[(swapped >> 0x18) & 0xff] << 0x18) | - ((uint64_t)zig_bit_reverse_lut[(swapped >> 0x10) & 0xff] << 0x10) | - ((uint64_t)zig_bit_reverse_lut[(swapped >> 0x08) & 0xff] << 0x08) | - ((uint64_t)zig_bit_reverse_lut[(swapped >> 0x00) & 0xff] << 0x00)); - return zig_sign_extend_uint64_t( - reversed & zig_bit_mask(uint64_t, zig_type_bit_width), - zig_type_bit_width); -} - -#define zig_bit_reverse_i64 zig_bit_reverse_u64 - -static inline uint128_t zig_bit_reverse_u128(uint128_t value, uint8_t zig_type_bit_width) { - const uint128_t swapped = zig_byte_swap_u128(value, zig_type_bit_width); - const uint128_t reversed = ( - ((uint128_t)zig_bit_reverse_lut[(swapped >> 0x78) & 0xff] << 0x78) | - ((uint128_t)zig_bit_reverse_lut[(swapped >> 0x70) & 0xff] << 0x70) | - ((uint128_t)zig_bit_reverse_lut[(swapped >> 0x68) & 0xff] << 0x68) | - ((uint128_t)zig_bit_reverse_lut[(swapped >> 0x60) & 0xff] << 0x60) | - ((uint128_t)zig_bit_reverse_lut[(swapped >> 0x58) & 0xff] << 0x58) | - ((uint128_t)zig_bit_reverse_lut[(swapped >> 0x50) & 0xff] << 0x50) | - ((uint128_t)zig_bit_reverse_lut[(swapped >> 0x48) & 0xff] << 0x48) | - ((uint128_t)zig_bit_reverse_lut[(swapped >> 0x40) & 0xff] << 0x40) | - ((uint128_t)zig_bit_reverse_lut[(swapped >> 0x38) & 0xff] << 0x38) | - ((uint128_t)zig_bit_reverse_lut[(swapped >> 0x30) & 0xff] << 0x30) | - ((uint128_t)zig_bit_reverse_lut[(swapped >> 0x28) & 0xff] << 0x28) | - ((uint128_t)zig_bit_reverse_lut[(swapped >> 0x20) & 0xff] << 0x20) | - ((uint128_t)zig_bit_reverse_lut[(swapped >> 0x18) & 0xff] << 0x18) | - ((uint128_t)zig_bit_reverse_lut[(swapped >> 0x10) & 0xff] << 0x10) | - ((uint128_t)zig_bit_reverse_lut[(swapped >> 0x08) & 0xff] << 0x08) | - ((uint128_t)zig_bit_reverse_lut[(swapped >> 0x00) & 0xff] << 0x00)); - return zig_sign_extend_uint128_t( - reversed & zig_bit_mask(uint128_t, zig_type_bit_width), - zig_type_bit_width); -} - -#define zig_bit_reverse_i128 zig_bit_reverse_u128 - static inline float zig_div_truncf(float numerator, float denominator) { return __builtin_truncf(numerator / denominator); } @@ -1562,17 +1459,6 @@ static inline long double zig_div_truncl(long double numerator, long double deno #define zig_div_floor_f80 zig_div_floorl #define zig_div_floor_f128 zig_div_floorl -#define zig_div_floor_u8 zig_div_floorf -#define zig_div_floor_i8 zig_div_floorf -#define zig_div_floor_u16 zig_div_floorf -#define zig_div_floor_i16 zig_div_floorf -#define zig_div_floor_u32 zig_div_floor -#define zig_div_floor_i32 zig_div_floor -#define zig_div_floor_u64 zig_div_floor -#define zig_div_floor_i64 zig_div_floor -#define zig_div_floor_u128 zig_div_floorl -#define zig_div_floor_i128 zig_div_floorl - static inline float zig_modf(float numerator, float denominator) { return (numerator - (zig_div_floorf(numerator, denominator) * denominator)); } @@ -1590,19 +1476,3 @@ static inline long double zig_modl(long double numerator, long double denominato #define zig_mod_f64 zig_mod #define zig_mod_f80 zig_modl #define zig_mod_f128 zig_modl - -#define zig_mod_int(ZigType, CType) \ - static inline CType zig_mod_##ZigType(CType numerator, CType denominator) { \ - return (numerator - (zig_div_floor_##ZigType(numerator, denominator) * denominator)); \ - } - -zig_mod_int( u8, uint8_t) -zig_mod_int( i8, int8_t) -zig_mod_int( u16, uint16_t) -zig_mod_int( i16, int16_t) -zig_mod_int( u32, uint32_t) -zig_mod_int( i32, int32_t) -zig_mod_int( u64, uint64_t) -zig_mod_int( i64, int64_t) -zig_mod_int(u128, uint128_t) -zig_mod_int(i128, int128_t) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 5f62b93922..00fc70ff2a 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -72,6 +72,12 @@ const ValueRenderLocation = enum { Other, }; +const BuiltinInfo = enum { + None, + Range, + Bits, +}; + /// TODO make this not cut off at 128 bytes fn formatTypeAsCIdentifier( data: FormatTypeAsCIdentContext, @@ -323,6 +329,33 @@ pub const Function = struct { } } + fn writeCValueMember(f: *Function, w: anytype, c_value: CValue, member: CValue) !void { + switch (c_value) { + .constant => |inst| { + const ty = f.air.typeOf(inst); + const val = f.air.value(inst).?; + try f.object.dg.renderValue(w, ty, val, .Other); + try w.writeByte('.'); + return f.writeCValue(w, member, .Other); + }, + else => return f.object.dg.writeCValueMember(w, c_value, member), + } + } + + fn writeCValueDerefMember(f: *Function, w: anytype, c_value: CValue, member: CValue) !void { + switch (c_value) { + .constant => |inst| { + const ty = f.air.typeOf(inst); + const val = f.air.value(inst).?; + try w.writeByte('('); + try f.object.dg.renderValue(w, ty, val, .Other); + try w.writeAll(")->"); + return f.writeCValue(w, member, .Other); + }, + else => return f.object.dg.writeCValueDerefMember(w, c_value, member), + } + } + fn fail(f: *Function, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { return f.object.dg.fail(format, args); } @@ -339,16 +372,63 @@ pub const Function = struct { return f.object.dg.fmtIntLiteral(ty, val); } - fn renderFloatFnName(f: *Function, fn_name: []const u8, float_ty: Type) !void { + fn renderTypeForBuiltinFnName(f: *Function, writer: anytype, ty: Type) !void { + const target = f.object.dg.module.getTarget(); + const c_bits = if (ty.isInt()) c_bits: { + const int_info = ty.intInfo(target); + try writer.writeByte(signAbbrev(int_info.signedness)); + break :c_bits toCIntBits(int_info.bits) orelse + return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); + } else if (ty.isRuntimeFloat()) c_bits: { + try writer.writeByte('f'); + break :c_bits ty.floatBits(target); + } else return f.fail("TODO: CBE: implement renderTypeForBuiltinFnName for type {}", .{ + ty.fmt(f.object.dg.module), + }); + try writer.print("{d}", .{c_bits}); + } + + fn renderBuiltinInfo(f: *Function, writer: anytype, ty: Type, info: BuiltinInfo) !void { + const target = f.object.dg.module.getTarget(); + switch (info) { + .None => {}, + .Range => { + var arena = std.heap.ArenaAllocator.init(f.object.dg.module.gpa); + defer arena.deinit(); + + const expected_contents = union { u: Value.Payload.U64, i: Value.Payload.I64 }; + var stack align(@alignOf(expected_contents)) = + std.heap.stackFallback(@sizeOf(expected_contents), arena.allocator()); + + const int_info = ty.intInfo(target); + if (int_info.signedness == .signed) { + const min_val = try ty.minInt(stack.get(), target); + try writer.print(", {x}", .{try f.fmtIntLiteral(ty, min_val)}); + } + + const max_val = try ty.maxInt(stack.get(), target); + try writer.print(", {x}", .{try f.fmtIntLiteral(ty, max_val)}); + }, + .Bits => { + var bits_pl = Value.Payload.U64{ + .base = .{ .tag = .int_u64 }, + .data = ty.bitSize(target), + }; + const bits_val = Value.initPayload(&bits_pl.base); + try writer.print(", {}", .{try f.fmtIntLiteral(Type.u8, bits_val)}); + }, + } + } + + fn renderFloatFnName(f: *Function, writer: anytype, operation: []const u8, float_ty: Type) !void { const target = f.object.dg.module.getTarget(); const float_bits = float_ty.floatBits(target); const is_longdouble = float_bits == CType.longdouble.sizeInBits(target); - const writer = f.object.writer(); try writer.writeAll("__"); if (is_longdouble or float_bits != 80) { try writer.writeAll("builtin_"); } - try writer.writeAll(fn_name); + try writer.writeAll(operation); if (is_longdouble) { try writer.writeByte('l'); } else switch (float_bits) { @@ -558,9 +638,8 @@ pub const DeclGen = struct { const target = dg.module.getTarget(); if (val.isUndefDeep()) { switch (ty.zigTypeTag()) { - // Using '{}' for integer and floats seemed to error C compilers (both GCC and Clang) - // with 'error: expected expression' (including when built with 'zig cc') - .Bool => return writer.writeAll("false"), + // bool b = 0xaa; evals to true, but memcpy(&b, 0xaa, 1); evals to false. + .Bool => return dg.renderValue(writer, ty, Value.@"false", location), .Int, .Enum, .ErrorSet => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, val)}), .Float => switch (ty.tag()) { .f32 => return writer.print("zig_bitcast_f32_u32({x})", .{ @@ -839,15 +918,14 @@ pub const DeclGen = struct { }, } }, - .Bool => return writer.print("{}", .{val.toBool()}), + .Bool => return writer.print("zig_{}", .{val.toBool()}), .Optional => { var opt_buf: Type.Payload.ElemType = undefined; const payload_ty = ty.optionalChild(&opt_buf); - if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { - const is_null = val.castTag(.opt_payload) == null; - return writer.print("{}", .{is_null}); - } + const is_null_val = Value.makeBool(val.tag() == .null_value); + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) + return dg.renderValue(writer, Type.bool, is_null_val, location); if (ty.optionalReprIsPayload()) { const payload_val = if (val.castTag(.opt_payload)) |pl| pl.data else val; @@ -864,21 +942,17 @@ pub const DeclGen = struct { try writer.writeAll("{ .payload = "); try dg.renderValue(writer, payload_ty, payload_val, .Initializer); - try writer.print(", .is_null = {} }}", .{val.tag() == .null_value}); + try writer.writeAll(", .is_null = "); + try dg.renderValue(writer, Type.bool, is_null_val, .Initializer); + try writer.writeAll(" }"); }, .ErrorSet => { - switch (val.tag()) { - .@"error" => { - const payload = val.castTag(.@"error").?; - // error values will be #defined at the top of the file - return writer.print("zig_error_{s}", .{fmtIdent(payload.data.name)}); - }, - else => { - // In this case we are rendering an error union which has a - // 0 bits payload. - return writer.writeByte('0'); - }, - } + const error_name = if (val.castTag(.@"error")) |error_pl| + error_pl.data.name + else + dg.module.error_name_list.items[0]; + // Error values are already defined by genErrDecls. + try writer.print("zig_error_{}", .{fmtIdent(error_name)}); }, .ErrorUnion => { const error_ty = ty.errorUnionSet(); @@ -897,7 +971,7 @@ pub const DeclGen = struct { } const payload_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.undef; - const error_val = if (val.tag() == .eu_payload) Value.zero else val; + const error_val = if (val.errorUnionIsPayload()) Value.zero else val; try writer.writeAll("{ .payload = "); try dg.renderValue(writer, payload_ty, payload_val, .Initializer); @@ -1026,24 +1100,14 @@ pub const DeclGen = struct { fn renderFunctionSignature(dg: *DeclGen, w: anytype, kind: TypedefKind) !void { const fn_info = dg.decl.ty.fnInfo(); - if (fn_info.cc == .Naked) { - try w.writeAll("ZIG_NAKED "); - } - if (dg.decl.val.castTag(.function)) |func_payload| { - const func: *Module.Fn = func_payload.data; - if (func.is_cold) { - try w.writeAll("ZIG_COLD "); - } - } - if (fn_info.return_type.hasRuntimeBits()) { - try dg.renderType(w, fn_info.return_type, kind); - } else if (fn_info.return_type.isError()) { - try dg.renderType(w, Type.anyerror, kind); - } else if (fn_info.return_type.zigTypeTag() == .NoReturn) { - try w.writeAll("zig_noreturn void"); - } else { - try w.writeAll("void"); - } + if (fn_info.cc == .Naked) try w.writeAll("zig_naked "); + if (dg.decl.val.castTag(.function)) |func_payload| + if (func_payload.data.is_cold) try w.writeAll("zig_cold "); + const ret_ty = fn_info.return_type; + try dg.renderType(w, if (ret_ty.tag() == .noreturn or ret_ty.hasRuntimeBitsIgnoreComptime()) + ret_ty + else + Type.void, kind); try w.writeByte(' '); try dg.renderDeclName(w, dg.decl_index); try w.writeByte('('); @@ -1051,9 +1115,7 @@ pub const DeclGen = struct { var index: usize = 0; for (fn_info.param_types) |param_type| { if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; - if (index > 0) { - try w.writeAll(", "); - } + if (index > 0) try w.writeAll(", "); const name = CValue{ .arg = index }; try dg.renderTypeAndName(w, param_type, name, .Const, 0, kind); index += 1; @@ -1063,7 +1125,7 @@ pub const DeclGen = struct { if (index > 0) try w.writeAll(", "); try w.writeAll("..."); } else if (index == 0) { - try w.writeAll("void"); + try dg.renderType(w, Type.void, kind); } try w.writeByte(')'); } @@ -1102,7 +1164,7 @@ pub const DeclGen = struct { if (params_written != 0) try bw.writeAll(", "); try bw.writeAll("..."); } else if (params_written == 0) { - try bw.writeAll("void"); + try dg.renderType(bw, Type.void, .Forward); } try bw.writeAll(");\n"); @@ -1126,14 +1188,18 @@ pub const DeclGen = struct { defer buffer.deinit(); const bw = buffer.writer(); - try bw.writeAll("typedef struct { "); + var ptr_ty_buf: Type.SlicePtrFieldTypeBuffer = undefined; + const ptr_ty = t.slicePtrFieldType(&ptr_ty_buf); + const ptr_name = CValue{ .identifier = "ptr" }; + const len_ty = Type.usize; + const len_name = CValue{ .identifier = "len" }; - var ptr_type_buf: Type.SlicePtrFieldTypeBuffer = undefined; - const ptr_type = t.slicePtrFieldType(&ptr_type_buf); - const ptr_name = CValue{ .bytes = "ptr" }; - try dg.renderTypeAndName(bw, ptr_type, ptr_name, .Mut, 0, .Complete); + try bw.writeAll("typedef struct {\n "); + try dg.renderTypeAndName(bw, ptr_ty, ptr_name, .Mut, 0, .Complete); + try bw.writeAll(";\n "); + try dg.renderTypeAndName(bw, len_ty, len_name, .Mut, 0, .Complete); - try bw.writeAll("; size_t len; } "); + try bw.writeAll(";\n} "); const name_begin = buffer.items.len; try bw.print("zig_{c}_{}", .{ @as(u8, if (t.isConstPtr()) 'L' else 'M'), @@ -1339,27 +1405,31 @@ pub const DeclGen = struct { } fn renderErrorUnionTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { - const payload_ty = t.errorUnionPayload(); assert(t.errorUnionSet().tag() == .anyerror); var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); defer buffer.deinit(); const bw = buffer.writer(); - const payload_name = CValue{ .bytes = "payload" }; + const payload_ty = t.errorUnionPayload(); + const payload_name = CValue{ .identifier = "payload" }; + const error_ty = t.errorUnionSet(); + const error_name = CValue{ .identifier = "error" }; + const target = dg.module.getTarget(); const payload_align = payload_ty.abiAlignment(target); - const error_align = Type.anyerror.abiAlignment(target); + const error_align = error_ty.abiAlignment(target); + try bw.writeAll("typedef struct {\n "); if (error_align > payload_align) { - try bw.writeAll("typedef struct { "); try dg.renderTypeAndName(bw, payload_ty, payload_name, .Mut, 0, .Complete); - try bw.writeAll("; uint16_t error; } "); + try bw.writeAll(";\n "); + try dg.renderTypeAndName(bw, error_ty, error_name, .Mut, 0, .Complete); } else { - try bw.writeAll("typedef struct { uint16_t error; "); + try dg.renderTypeAndName(bw, error_ty, error_name, .Mut, 0, .Complete); + try bw.writeAll(";\n "); try dg.renderTypeAndName(bw, payload_ty, payload_name, .Mut, 0, .Complete); - try bw.writeAll("; } "); } - + try bw.writeAll(";\n} "); const name_begin = buffer.items.len; try bw.print("zig_E_{}", .{typeToCIdentifier(payload_ty, dg.module)}); const name_end = buffer.items.len; @@ -1413,11 +1483,11 @@ pub const DeclGen = struct { defer buffer.deinit(); const bw = buffer.writer(); - try bw.writeAll("typedef struct { "); - const payload_name = CValue{ .bytes = "payload" }; - try dg.renderTypeAndName(bw, child_type, payload_name, .Mut, 0, .Complete); - try bw.writeAll("; bool is_null; } "); - + try bw.writeAll("typedef struct {\n "); + try dg.renderTypeAndName(bw, child_type, .{ .identifier = "payload" }, .Mut, 0, .Complete); + try bw.writeAll(";\n "); + try dg.renderTypeAndName(bw, Type.bool, .{ .identifier = "is_null" }, .Mut, 0, .Complete); + try bw.writeAll("; } "); const name_begin = buffer.items.len; try bw.print("zig_Q_{}", .{typeToCIdentifier(child_type, dg.module)}); const name_end = buffer.items.len; @@ -1486,51 +1556,20 @@ pub const DeclGen = struct { const target = dg.module.getTarget(); switch (t.zigTypeTag()) { - .NoReturn, .Void => try w.writeAll("void"), - .Bool => try w.writeAll("bool"), - .Int => { - switch (t.tag()) { - .u1, .u8 => try w.writeAll("uint8_t"), - .i8 => try w.writeAll("int8_t"), - .u16 => try w.writeAll("uint16_t"), - .i16 => try w.writeAll("int16_t"), - .u32 => try w.writeAll("uint32_t"), - .i32 => try w.writeAll("int32_t"), - .u64 => try w.writeAll("uint64_t"), - .i64 => try w.writeAll("int64_t"), - .u128 => try w.writeAll("uint128_t"), - .i128 => try w.writeAll("int128_t"), - .usize => try w.writeAll("uintptr_t"), - .isize => try w.writeAll("intptr_t"), - .c_short => try w.writeAll("short"), - .c_ushort => try w.writeAll("unsigned short"), - .c_int => try w.writeAll("int"), - .c_uint => try w.writeAll("unsigned int"), - .c_long => try w.writeAll("long"), - .c_ulong => try w.writeAll("unsigned long"), - .c_longlong => try w.writeAll("long long"), - .c_ulonglong => try w.writeAll("unsigned long long"), - .u29, .int_signed, .int_unsigned => { - const info = t.intInfo(target); - const sign_prefix = switch (info.signedness) { - .signed => "", - .unsigned => "u", - }; - const c_bits = toCIntBits(info.bits) orelse - return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); - try w.print("{s}int{d}_t", .{ sign_prefix, c_bits }); - }, - else => unreachable, - } - }, - .Float => { - switch (t.tag()) { - .f32 => try w.writeAll("float"), - .f64 => try w.writeAll("double"), - .c_longdouble => try w.writeAll("long double"), - .f16 => return dg.fail("TODO: C backend: implement float type f16", .{}), - .f128 => return dg.fail("TODO: C backend: implement float type f128", .{}), - else => unreachable, + .NoReturn, .Void, .Bool, .Int, .Float, .ErrorSet => |tag| { + const is_named = switch (tag) { + .Int => t.isNamedInt(), + .ErrorSet => false, + else => true, + }; + if (is_named) { + try w.writeAll("zig_"); + try t.print(w, dg.module); + } else { + const info = t.intInfo(target); + const c_bits = toCIntBits(info.bits) orelse + return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); + try w.print("zig_{c}{d}", .{ signAbbrev(info.signedness), c_bits }); } }, .Pointer => { @@ -1564,9 +1603,10 @@ pub const DeclGen = struct { // u8 and i8 produce unsigned char and signed char respectively, // which in C are (not very usefully) different than char. try w.writeAll("char"); - } else { - try dg.renderType(w, child_ty, .Forward); - } + } else try dg.renderType(w, switch (child_ty.tag()) { + .anyopaque => Type.void, + else => child_ty, + }, .Forward); if (t.isConstPtr()) try w.writeAll(" const"); if (t.isVolatilePtr()) try w.writeAll(" volatile"); return w.writeAll(" *"); @@ -1587,29 +1627,22 @@ pub const DeclGen = struct { var opt_buf: Type.Payload.ElemType = undefined; const child_type = t.optionalChild(&opt_buf); - if (!child_type.hasRuntimeBitsIgnoreComptime()) { - return w.writeAll("bool"); - } + if (!child_type.hasRuntimeBitsIgnoreComptime()) + return dg.renderType(w, Type.bool, kind); - if (t.optionalReprIsPayload()) { - return dg.renderType(w, child_type, .Complete); - } + if (t.optionalReprIsPayload()) + return dg.renderType(w, child_type, kind); const name = dg.getTypedefName(t) orelse try dg.renderOptionalTypedef(t, child_type); return w.writeAll(name); }, - .ErrorSet => { - comptime assert(Type.anyerror.abiSize(builtin.target) == 2); - return w.writeAll("uint16_t"); - }, .ErrorUnion => { const payload_ty = t.errorUnionPayload(); - if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { - return dg.renderType(w, Type.anyerror, .Complete); - } + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) + return dg.renderType(w, Type.anyerror, kind); var error_union_pl = Type.Payload.ErrorUnion{ .data = .{ .error_set = Type.anyerror, .payload = payload_ty }, @@ -1652,7 +1685,6 @@ pub const DeclGen = struct { try dg.renderType(w, int_tag_ty, kind); }, .Opaque => switch (t.tag()) { - .anyopaque => try w.writeAll("void"), .@"opaque" => { const name = dg.getTypedefName(t) orelse try dg.renderOpaqueTypedef(t); @@ -1695,13 +1727,8 @@ pub const DeclGen = struct { /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | /// | `renderType` | "uint8_t *" | "zig_A_uint8_t_10" | /// - fn renderTypecast( - dg: *DeclGen, - w: anytype, - ty: Type, - ) error{ OutOfMemory, AnalysisFail }!void { - const name = CValue{ .bytes = "" }; - return renderTypeAndName(dg, w, ty, name, .Mut, 0, .Complete); + fn renderTypecast(dg: *DeclGen, w: anytype, ty: Type) error{ OutOfMemory, AnalysisFail }!void { + return renderTypeAndName(dg, w, ty, .{ .bytes = "" }, .Mut, 0, .Complete); } /// Renders a type and name in field declaration/definition format. @@ -1735,8 +1762,9 @@ pub const DeclGen = struct { render_ty = render_ty.elemType(); } - if (alignment != 0) - try w.print("ZIG_ALIGN({}) ", .{alignment}); + if (alignment != 0 and alignment > ty.abiAlignment(dg.module.getTarget())) { + try w.print("zig_align({}) ", .{alignment}); + } try dg.renderType(w, render_ty, kind); const const_prefix = switch (mutability) { @@ -1792,13 +1820,16 @@ pub const DeclGen = struct { try buffer.appendSlice(";\n return ("); try dg.renderTypecast(bw, name_slice_ty); try bw.print("){{{}, {}}};\n", .{ - fmtIdent("name"), - try dg.fmtIntLiteral(Type.usize, len_val), + fmtIdent("name"), try dg.fmtIntLiteral(Type.usize, len_val), }); try buffer.appendSlice(" }\n"); } - try buffer.appendSlice(" }\n while (true) zig_breakpoint();\n}\n"); + try buffer.appendSlice(" }\n while ("); + try dg.renderValue(bw, Type.bool, Value.@"true", .Other); + try buffer.appendSlice(") "); + _ = try airBreakpoint(bw); + try buffer.appendSlice("}\n"); const rendered = buffer.toOwnedSlice(); errdefer dg.typedefs.allocator.free(rendered); @@ -1874,6 +1905,27 @@ pub const DeclGen = struct { } } + fn writeCValueMember(dg: *DeclGen, writer: anytype, c_value: CValue, member: CValue) !void { + try dg.writeCValue(writer, c_value); + try writer.writeByte('.'); + try dg.writeCValue(writer, member); + } + + fn writeCValueDerefMember(dg: *DeclGen, writer: anytype, c_value: CValue, member: CValue) !void { + switch (c_value) { + .none, .constant, .undef => unreachable, + .local, .arg, .decl, .identifier, .bytes => { + try dg.writeCValue(writer, c_value); + try writer.writeAll("->"); + }, + .local_ref, .decl_ref => { + try dg.writeCValueDeref(writer, c_value); + try writer.writeByte('.'); + }, + } + try dg.writeCValue(writer, member); + } + fn renderDeclName(dg: *DeclGen, writer: anytype, decl_index: Decl.Index) !void { const decl = dg.module.declPtr(decl_index); dg.module.markDeclAlive(decl); @@ -1971,8 +2023,7 @@ pub fn genErrDecls(o: *Object) !void { const len_val = Value.initPayload(&len_pl.base); try writer.print("{{" ++ name_prefix ++ "_{}, {}}}", .{ - fmtIdent(name), - try o.dg.fmtIntLiteral(Type.usize, len_val), + fmtIdent(name), try o.dg.fmtIntLiteral(Type.usize, len_val), }); } try writer.writeAll("};\n"); @@ -1989,7 +2040,7 @@ pub fn genFunc(f: *Function) !void { const is_global = o.dg.module.decl_exports.contains(f.func.owner_decl); const fwd_decl_writer = o.dg.fwd_decl.writer(); - try fwd_decl_writer.writeAll(if (is_global) "ZIG_EXTERN_C " else "static "); + try fwd_decl_writer.writeAll(if (is_global) "zig_extern_c " else "static "); try o.dg.renderFunctionSignature(fwd_decl_writer, .Forward); try fwd_decl_writer.writeAll(";\n"); @@ -2028,7 +2079,7 @@ pub fn genDecl(o: *Object) !void { }; if (tv.val.tag() == .extern_fn) { const fwd_decl_writer = o.dg.fwd_decl.writer(); - try fwd_decl_writer.writeAll("ZIG_EXTERN_C "); + try fwd_decl_writer.writeAll("zig_extern_c "); try o.dg.renderFunctionSignature(fwd_decl_writer, .Forward); try fwd_decl_writer.writeAll(";\n"); } else if (tv.val.castTag(.variable)) |var_payload| { @@ -2036,7 +2087,7 @@ pub fn genDecl(o: *Object) !void { 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 "); + try fwd_decl_writer.writeAll("zig_extern_c "); } if (variable.is_threadlocal) { try fwd_decl_writer.writeAll("zig_threadlocal "); @@ -2096,7 +2147,7 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { .Fn => { const is_global = dg.declIsGlobal(tv); if (is_global) { - try writer.writeAll("ZIG_EXTERN_C "); + try writer.writeAll("zig_extern_c "); try dg.renderFunctionSignature(writer, .Complete); try dg.fwd_decl.appendSlice(";\n"); } @@ -2124,7 +2175,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .const_ty => unreachable, // excluded from function bodies .arg => airArg(f), - .breakpoint => try airBreakpoint(f), + .breakpoint => try airBreakpoint(f.object.writer()), .ret_addr => try airRetAddr(f, inst), .frame_addr => try airFrameAddress(f, inst), .unreach => try airUnreach(f), @@ -2135,10 +2186,10 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO // TODO use a different strategy for add, sub, mul, div // that communicates to the optimizer that wrapping is UB. - .add => try airBinOp(f, inst, "+"), - .sub => try airBinOp(f, inst, "-"), - .mul => try airBinOp(f, inst, "*"), - .div_float, .div_exact => try airBinOp(f, inst, "/"), + .add => try airBinOp(f, inst, "+", "add", .None), + .sub => try airBinOp(f, inst, "-", "sub", .None), + .mul => try airBinOp(f, inst, "*", "mul", .None), + .div_float, .div_exact => try airBinOp(f, inst, "/", "div_trunc", .None), .rem => blk: { const bin_op = f.air.instructions.items(.data)[inst].bin_op; @@ -2146,9 +2197,9 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO // For binary operations @TypeOf(lhs)==@TypeOf(rhs), // so we only check one. break :blk if (lhs_ty.isInt()) - try airBinOp(f, inst, "%") + try airBinOp(f, inst, "%", "rem", .None) else - try airBinFloatOp(f, inst, "fmod"); // yes, @rem() => fmod() + try airBinFloatOp(f, inst, "fmod"); }, .div_trunc => blk: { const bin_op = f.air.instructions.items(.data)[inst].bin_op; @@ -2156,21 +2207,21 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO // For binary operations @TypeOf(lhs)==@TypeOf(rhs), // so we only check one. break :blk if (lhs_ty.isInt()) - try airBinOp(f, inst, "/") + try airBinOp(f, inst, "/", "div_trunc", .None) else - try airBinOpBuiltinCall(f, inst, "div_trunc"); + try airBinBuiltinCall(f, inst, "div_trunc", .None); }, - .div_floor => try airBinOpBuiltinCall(f, inst, "div_floor"), - .mod => try airBinOpBuiltinCall(f, inst, "mod"), + .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .None), + .mod => try airBinBuiltinCall(f, inst, "mod", .None), - .addwrap => try airWrapOp(f, inst, "+", "add"), - .subwrap => try airWrapOp(f, inst, "-", "sub"), - .mulwrap => try airWrapOp(f, inst, "*", "mul"), + .addwrap => try airBinBuiltinCall(f, inst, "addw", .Bits), + .subwrap => try airBinBuiltinCall(f, inst, "subw", .Bits), + .mulwrap => try airBinBuiltinCall(f, inst, "mulw", .Bits), - .add_sat => try airSatOp(f, inst, "add"), - .sub_sat => try airSatOp(f, inst, "sub"), - .mul_sat => try airSatOp(f, inst, "mul"), - .shl_sat => try airSatOp(f, inst, "shl"), + .add_sat => try airBinBuiltinCall(f, inst, "adds", .Bits), + .sub_sat => try airBinBuiltinCall(f, inst, "subs", .Bits), + .mul_sat => try airBinBuiltinCall(f, inst, "muls", .Bits), + .shl_sat => try airBinBuiltinCall(f, inst, "shls", .Bits), .neg => try airNeg(f, inst), @@ -2192,20 +2243,20 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .mul_add => try airMulAdd(f, inst), - .add_with_overflow => try airOverflow(f, inst, "add", .range), - .sub_with_overflow => try airOverflow(f, inst, "sub", .range), - .mul_with_overflow => try airOverflow(f, inst, "mul", .range), - .shl_with_overflow => try airOverflow(f, inst, "shl", .bits), + .add_with_overflow => try airOverflow(f, inst, "add", .Bits), + .sub_with_overflow => try airOverflow(f, inst, "sub", .Bits), + .mul_with_overflow => try airOverflow(f, inst, "mul", .Bits), + .shl_with_overflow => try airOverflow(f, inst, "shl", .Bits), .min => try airMinMax(f, inst, '<'), .max => try airMinMax(f, inst, '>'), .slice => try airSlice(f, inst), - .cmp_gt => try airBinOp(f, inst, ">"), - .cmp_gte => try airBinOp(f, inst, ">="), - .cmp_lt => try airBinOp(f, inst, "<"), - .cmp_lte => try airBinOp(f, inst, "<="), + .cmp_gt => try airCmpOp(f, inst, ">"), + .cmp_gte => try airCmpOp(f, inst, ">="), + .cmp_lt => try airCmpOp(f, inst, "<"), + .cmp_lte => try airCmpOp(f, inst, "<="), .cmp_eq => try airEquality(f, inst, "((", "=="), .cmp_neq => try airEquality(f, inst, "!((", "!="), @@ -2214,12 +2265,13 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .cmp_lt_errors_len => return f.fail("TODO: C backend: implement cmp_lt_errors_len", .{}), // bool_and and bool_or are non-short-circuit operations - .bool_and, .bit_and => try airBinOp(f, inst, "&"), - .bool_or, .bit_or => try airBinOp(f, inst, "|"), - .xor => try airBinOp(f, inst, "^"), - .shr, .shr_exact => try airBinOp(f, inst, ">>"), - .shl, .shl_exact => try airBinOp(f, inst, "<<"), - .not => try airNot (f, inst), + .bool_and, .bit_and => try airBinOp(f, inst, "&", "and", .None), + .bool_or, .bit_or => try airBinOp(f, inst, "|", "or", .None), + .xor => try airBinOp(f, inst, "^", "xor", .None), + .shr, .shr_exact => try airBinBuiltinCall(f, inst, "shr", .None), + .shl, => try airBinBuiltinCall(f, inst, "shl", .None), + .shl_exact => try airBinOp(f, inst, "<<", "shl", .None), + .not => try airNot (f, inst), .optional_payload => try airOptionalPayload(f, inst), .optional_payload_ptr => try airOptionalPayloadPtr(f, inst), @@ -2263,11 +2315,11 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .memcpy => try airMemcpy(f, inst), .set_union_tag => try airSetUnionTag(f, inst), .get_union_tag => try airGetUnionTag(f, inst), - .clz => try airBuiltinCall(f, inst, "clz"), - .ctz => try airBuiltinCall(f, inst, "ctz"), - .popcount => try airBuiltinCall(f, inst, "popcount"), - .byte_swap => try airBuiltinCall(f, inst, "byte_swap"), - .bit_reverse => try airBuiltinCall(f, inst, "bit_reverse"), + .clz => try airUnBuiltinCall(f, inst, "clz", .Bits), + .ctz => try airUnBuiltinCall(f, inst, "ctz", .Bits), + .popcount => try airUnBuiltinCall(f, inst, "popcount", .Bits), + .byte_swap => try airUnBuiltinCall(f, inst, "byte_swap", .Bits), + .bit_reverse => try airUnBuiltinCall(f, inst, "bit_reverse", .Bits), .tag_name => try airTagName(f, inst), .error_name => try airErrorName(f, inst), .splat => try airSplat(f, inst), @@ -2393,10 +2445,10 @@ fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: [ const writer = f.object.writer(); const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = "); - if (is_ptr) try writer.writeByte('&'); - try f.writeCValue(writer, operand, .Other); - try if (is_ptr) writer.writeAll("->") else writer.writeByte('.'); - try writer.writeAll(field_name); + if (is_ptr) { + try writer.writeByte('&'); + try f.writeCValueDerefMember(writer, operand, .{ .identifier = field_name }); + } else try f.writeCValueMember(writer, operand, .{ .identifier = field_name }); try writer.writeAll(";\n"); return local; } @@ -2752,138 +2804,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { return CValue.none; } -fn airWrapOp(f: *Function, inst: Air.Inst.Index, operator: []const u8, fn_name: []const u8) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; - - const bin_op = f.air.instructions.items(.data)[inst].bin_op; - const inst_ty = f.air.typeOfIndex(inst); - const target = f.object.dg.module.getTarget(); - const int_info = inst_ty.intInfo(target); - const bits = int_info.bits; - - // if it's an unsigned int with non-arbitrary bit size then we can just add - if (toCIntBits(bits)) |c_bits| { - if (int_info.signedness == .unsigned and bits == c_bits) { - return try airBinOp(f, inst, operator); - } - } - - if (bits > 64) return f.fail("TODO: C backend: airWrapOp for large integers", .{}); - - const lhs = try f.resolveInst(bin_op.lhs); - const rhs = try f.resolveInst(bin_op.rhs); - const w = f.object.writer(); - - const local = try f.allocLocal(inst_ty, .Mut); - try w.print(" = zig_{s}w_", .{fn_name}); - - switch (inst_ty.tag()) { - .isize => try w.writeAll("isize"), - .c_short => try w.writeAll("short"), - .c_int => try w.writeAll("int"), - .c_long => try w.writeAll("long"), - .c_longlong => try w.writeAll("longlong"), - else => { - const prefix_byte: u8 = signAbbrev(int_info.signedness); - for ([_]u8{ 8, 16, 32, 64 }) |nbits| { - if (bits <= nbits) { - try w.print("{c}{d}", .{ prefix_byte, nbits }); - break; - } - } else { - unreachable; - } - }, - } - - try w.writeByte('('); - try f.writeCValue(w, lhs, .FunctionArgument); - try w.writeAll(", "); - try f.writeCValue(w, rhs, .FunctionArgument); - { - var arena = std.heap.ArenaAllocator.init(f.object.dg.module.gpa); - defer arena.deinit(); - - const expected_contents = union { u: Value.Payload.U64, i: Value.Payload.I64 }; - var stack align(@alignOf(expected_contents)) = - std.heap.stackFallback(@sizeOf(expected_contents), arena.allocator()); - - if (int_info.signedness == .signed) { - const min_val = try inst_ty.minInt(stack.get(), target); - try w.print(", {}", .{try f.fmtIntLiteral(inst_ty, min_val)}); - } - - const max_val = try inst_ty.maxInt(stack.get(), target); - try w.print(", {});", .{try f.fmtIntLiteral(inst_ty, max_val)}); - } - try f.object.indent_writer.insertNewline(); - - return local; -} - -fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_name: []const u8) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; - - const bin_op = f.air.instructions.items(.data)[inst].bin_op; - const inst_ty = f.air.typeOfIndex(inst); - const target = f.object.dg.module.getTarget(); - const int_info = inst_ty.intInfo(target); - const bits = int_info.bits; - - if (bits > 64) return f.object.dg.fail("TODO: C backend: airSatOp for large integers", .{}); - - const lhs = try f.resolveInst(bin_op.lhs); - const rhs = try f.resolveInst(bin_op.rhs); - const w = f.object.writer(); - - const local = try f.allocLocal(inst_ty, .Mut); - try w.print(" = zig_{s}s_", .{fn_name}); - - switch (inst_ty.tag()) { - .isize => try w.writeAll("isize"), - .c_short => try w.writeAll("short"), - .c_int => try w.writeAll("int"), - .c_long => try w.writeAll("long"), - .c_longlong => try w.writeAll("longlong"), - else => { - const prefix_byte: u8 = signAbbrev(int_info.signedness); - for ([_]u8{ 8, 16, 32, 64 }) |nbits| { - if (bits <= nbits) { - try w.print("{c}{d}", .{ prefix_byte, nbits }); - break; - } - } else { - unreachable; - } - }, - } - - try w.writeByte('('); - try f.writeCValue(w, lhs, .FunctionArgument); - try w.writeAll(", "); - try f.writeCValue(w, rhs, .FunctionArgument); - { - var arena = std.heap.ArenaAllocator.init(f.object.dg.module.gpa); - defer arena.deinit(); - - const expected_contents = union { u: Value.Payload.U64, i: Value.Payload.I64 }; - var stack align(@alignOf(expected_contents)) = - std.heap.stackFallback(@sizeOf(expected_contents), arena.allocator()); - - if (int_info.signedness == .signed) { - const min_val = try inst_ty.minInt(stack.get(), target); - try w.print(", {}", .{try f.fmtIntLiteral(inst_ty, min_val)}); - } - - const max_val = try inst_ty.maxInt(stack.get(), target); - try w.print(", {});", .{try f.fmtIntLiteral(inst_ty, max_val)}); - } - try f.object.indent_writer.insertNewline(); - - return local; -} - -fn airOverflow(f: *Function, inst: Air.Inst.Index, fn_name: []const u8, kind: enum { range, bits }) !CValue { +fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: BuiltinInfo) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; @@ -2895,54 +2816,29 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, fn_name: []const u8, kind: en const inst_ty = f.air.typeOfIndex(inst); const scalar_ty = f.air.typeOf(bin_op.lhs).scalarType(); - const target = f.object.dg.module.getTarget(); - const int_info = scalar_ty.intInfo(target); const w = f.object.writer(); - const c_bits = toCIntBits(int_info.bits) orelse - return f.fail("TODO: C backend: implement integer arithmetic larger than 128 bits", .{}); const local = try f.allocLocal(inst_ty, .Mut); try w.writeAll(";\n"); try f.writeCValue(w, local, .Other); - try w.print(".field_1 = zig_{s}o_{c}{d}(", .{ - fn_name, signAbbrev(int_info.signedness), c_bits, - }); + try w.writeAll(".field_1 = zig_"); + try w.writeAll(operation); + try w.writeAll("o_"); + try f.renderTypeForBuiltinFnName(w, scalar_ty); + try w.writeAll("(&"); + try f.writeCValueMember(w, local, .{ .identifier = "field_0" }); + try w.writeAll(", "); try f.writeCValue(w, lhs, .FunctionArgument); try w.writeAll(", "); try f.writeCValue(w, rhs, .FunctionArgument); - try w.writeAll(", &"); - try f.writeCValue(w, local, .Other); - try w.writeAll(".field_0, "); - switch (kind) { - .range => { - var arena = std.heap.ArenaAllocator.init(f.object.dg.module.gpa); - defer arena.deinit(); - - const expected_contents = union { u: Value.Payload.U64, i: Value.Payload.I64 }; - var stack align(@alignOf(expected_contents)) = - std.heap.stackFallback(@sizeOf(expected_contents), arena.allocator()); - - if (int_info.signedness == .signed) { - const min_val = try scalar_ty.minInt(stack.get(), target); - try w.print("{}, ", .{try f.fmtIntLiteral(scalar_ty, min_val)}); - } - - const max_val = try scalar_ty.maxInt(stack.get(), target); - try w.print("{});\n", .{try f.fmtIntLiteral(scalar_ty, max_val)}); - }, - .bits => { - var bits_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = int_info.bits }; - const bits_val = Value.initPayload(&bits_pl.base); - try w.print("{x});\n", .{try f.fmtIntLiteral(Type.u8, bits_val)}); - }, - } + try f.renderBuiltinInfo(w, scalar_ty, info); + try w.writeAll(");\n"); return local; } fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) - return CValue.none; + if (f.liveness.isUnused(inst)) return CValue.none; const ty_op = f.air.instructions.items(.data)[inst].ty_op; const op = try f.resolveInst(ty_op.operand); @@ -2951,6 +2847,9 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.air.typeOfIndex(inst); const local = try f.allocLocal(inst_ty, .Const); + const target = f.object.dg.module.getTarget(); + if (inst_ty.bitSize(target) > 64) {} + try writer.writeAll(" = "); try writer.writeByte(if (inst_ty.tag() == .bool) '!' else '~'); try f.writeCValue(writer, op, .Other); @@ -2959,16 +2858,53 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { return local; } -fn airBinOp(f: *Function, inst: Air.Inst.Index, operator: []const u8) !CValue { - if (f.liveness.isUnused(inst)) - return CValue.none; +fn airBinOp( + f: *Function, + inst: Air.Inst.Index, + operator: []const u8, + operation: []const u8, + info: BuiltinInfo, +) !CValue { + if (f.liveness.isUnused(inst)) return CValue.none; const bin_op = f.air.instructions.items(.data)[inst].bin_op; + + const operand_ty = f.air.typeOf(bin_op.lhs); + const target = f.object.dg.module.getTarget(); + if (operand_ty.bitSize(target) > 64) return try airBinBuiltinCall(f, inst, operation, info); + + const inst_ty = f.air.typeOfIndex(inst); const lhs = try f.resolveInst(bin_op.lhs); const rhs = try f.resolveInst(bin_op.rhs); const writer = f.object.writer(); + const local = try f.allocLocal(inst_ty, .Const); + + try writer.writeAll(" = "); + try f.writeCValue(writer, lhs, .Other); + try writer.writeByte(' '); + try writer.writeAll(operator); + try writer.writeByte(' '); + try f.writeCValue(writer, rhs, .Other); + try writer.writeAll(";\n"); + + return local; +} + +fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: []const u8) !CValue { + if (f.liveness.isUnused(inst)) return CValue.none; + + const bin_op = f.air.instructions.items(.data)[inst].bin_op; + + const operand_ty = f.air.typeOf(bin_op.lhs); + const target = f.object.dg.module.getTarget(); + if (operand_ty.bitSize(target) > 64) return try airCmpBuiltinCall(f, inst, operator); + const inst_ty = f.air.typeOfIndex(inst); + const lhs = try f.resolveInst(bin_op.lhs); + const rhs = try f.resolveInst(bin_op.rhs); + + const writer = f.object.writer(); const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = "); @@ -3301,27 +3237,19 @@ fn lowerTry( const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(); if (!err_union_ty.errorUnionSet().errorSetIsEmpty()) { - err: { - if (!payload_has_bits) { - if (operand_is_ptr) { - try writer.writeAll("if(*"); - } else { - try writer.writeAll("if("); - } + try writer.writeAll("if ("); + if (!payload_has_bits) { + if (operand_is_ptr) + try f.writeCValueDeref(writer, err_union) + else try f.writeCValue(writer, err_union, .Other); - try writer.writeByte(')'); - break :err; - } - if (operand_is_ptr or isByRef(err_union_ty)) { - try writer.writeAll("if("); - try f.writeCValue(writer, err_union, .Other); - try writer.writeAll("->error)"); - break :err; - } - try writer.writeAll("if("); - try f.writeCValue(writer, err_union, .Other); - try writer.writeAll(".error)"); + } else { + if (operand_is_ptr or isByRef(err_union_ty)) + try f.writeCValueDerefMember(writer, err_union, .{ .identifier = "error" }) + else + try f.writeCValueMember(writer, err_union, .{ .identifier = "error" }); } + try writer.writeByte(')'); try genBody(f, body); try f.object.indent_writer.insertNewline(); @@ -3342,20 +3270,17 @@ fn lowerTry( try writer.writeAll("memcpy("); try f.writeCValue(writer, local, .FunctionArgument); try writer.writeAll(", "); - try f.writeCValue(writer, err_union, .Other); - try writer.writeAll(".payload, sizeof("); + try f.writeCValueMember(writer, err_union, .{ .identifier = "payload" }); + try writer.writeAll(", sizeof("); try f.renderTypecast(writer, payload_ty); try writer.writeAll("));\n"); } else { + try writer.writeAll(" = "); if (operand_is_ptr or isByRef(payload_ty)) { - try writer.writeAll(" = &"); - try f.writeCValue(writer, err_union, .Other); - try writer.writeAll("->payload;\n"); - } else { - try writer.writeAll(" = "); - try f.writeCValue(writer, err_union, .Other); - try writer.writeAll(".payload;\n"); - } + try writer.writeByte('&'); + try f.writeCValueDerefMember(writer, err_union, .{ .identifier = "payload" }); + } else try f.writeCValueMember(writer, err_union, .{ .identifier = "payload" }); + try writer.writeAll(";\n"); } return local; } @@ -3415,8 +3340,8 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { return local; } -fn airBreakpoint(f: *Function) !CValue { - try f.object.writer().writeAll("zig_breakpoint();\n"); +fn airBreakpoint(writer: anytype) !CValue { + try writer.writeAll("zig_breakpoint();\n"); return CValue.none; } @@ -3463,9 +3388,12 @@ fn airLoop(f: *Function, inst: Air.Inst.Index) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const loop = f.air.extraData(Air.Block, ty_pl.payload); const body = f.air.extra[loop.end..][0..loop.data.body_len]; - try f.object.writer().writeAll("while (true) "); + const writer = f.object.writer(); + try writer.writeAll("while ("); + try f.object.dg.renderValue(writer, Type.bool, Value.@"true", .Other); + try writer.writeAll(") "); try genBody(f, body); - try f.object.indent_writer.insertNewline(); + try writer.writeByte('\n'); return CValue.none; } @@ -3496,7 +3424,11 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); try writer.writeAll("switch ("); - if (condition_ty.tag() == .bool) try writer.writeAll("(int)"); + if (condition_ty.tag() == .bool) { + try writer.writeByte('('); + try f.renderTypecast(writer, Type.u1); + try writer.writeByte(')'); + } try f.writeCValue(writer, condition, .Other); try writer.writeAll(") {"); f.object.indent_writer.pushIndent(); @@ -3751,16 +3683,22 @@ fn airIsNull( var payload_buf: Type.Payload.ElemType = undefined; const payload_ty = optional_ty.optionalChild(&payload_buf); - if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { - try writer.print(" {s} true;\n", .{operator}); - } else if (operand_ty.isPtrLikeOptional()) { + const rhs = if (!payload_ty.hasRuntimeBitsIgnoreComptime()) + TypedValue{ .ty = Type.bool, .val = Value.@"true" } + else if (operand_ty.isPtrLikeOptional()) // operand is a regular pointer, test `operand !=/== NULL` - try writer.print(" {s} NULL;\n", .{operator}); - } else if (payload_ty.zigTypeTag() == .ErrorSet) { - try writer.print(" {s} 0;\n", .{operator}); - } else { - try writer.print(".is_null {s} true;\n", .{operator}); - } + TypedValue{ .ty = operand_ty, .val = Value.@"null" } + else if (payload_ty.zigTypeTag() == .ErrorSet) + TypedValue{ .ty = payload_ty, .val = Value.zero } + else rhs: { + try writer.writeAll(".is_null"); + break :rhs TypedValue{ .ty = Type.bool, .val = Value.@"true" }; + }; + try writer.writeByte(' '); + try writer.writeAll(operator); + try writer.writeByte(' '); + try f.object.dg.renderValue(writer, rhs.ty, rhs.val, .Other); + try writer.writeAll(";\n"); return local; } @@ -3812,9 +3750,9 @@ fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue { } const local = try f.allocLocal(inst_ty, .Const); - try writer.writeAll(" = &("); - try f.writeCValue(writer, operand, .Other); - try writer.writeAll(")->payload;\n"); + try writer.writeAll(" = &"); + try f.writeCValueDerefMember(writer, operand, .{ .identifier = "payload" }); + try writer.writeAll(";\n"); return local; } @@ -3833,7 +3771,9 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { } try f.writeCValueDeref(writer, operand); - try writer.writeAll(".is_null = false;\n"); + try writer.writeAll(".is_null = "); + try f.object.dg.renderValue(writer, Type.bool, Value.@"false", .Initializer); + try writer.writeAll(";\n"); const inst_ty = f.air.typeOfIndex(inst); const local = try f.allocLocal(inst_ty, .Const); @@ -3974,44 +3914,30 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { /// *(E!T) -> E /// Note that the result is never a pointer. fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) - return CValue.none; + if (f.liveness.isUnused(inst)) return CValue.none; const ty_op = f.air.instructions.items(.data)[inst].ty_op; const inst_ty = f.air.typeOfIndex(inst); - const writer = f.object.writer(); const operand = try f.resolveInst(ty_op.operand); const operand_ty = f.air.typeOf(ty_op.operand); - if (operand_ty.zigTypeTag() == .Pointer) { - const err_union_ty = operand_ty.childType(); - if (err_union_ty.errorUnionSet().errorSetIsEmpty()) { - return CValue{ .bytes = "0" }; - } - if (!err_union_ty.errorUnionPayload().hasRuntimeBits()) { - return operand; - } - const local = try f.allocLocal(inst_ty, .Const); - try writer.writeAll(" = *"); - try f.writeCValue(writer, operand, .Other); - try writer.writeAll(";\n"); - return local; - } - if (operand_ty.errorUnionSet().errorSetIsEmpty()) { - return CValue{ .bytes = "0" }; - } - if (!operand_ty.errorUnionPayload().hasRuntimeBits()) { - return operand; - } + const operand_is_ptr = operand_ty.zigTypeTag() == .Pointer; + const error_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; + const error_ty = error_union_ty.errorUnionSet(); + const payload_ty = error_union_ty.errorUnionPayload(); + if (!payload_ty.hasRuntimeBits()) return operand; + const writer = f.object.writer(); const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = "); - if (operand_ty.zigTypeTag() == .Pointer) { - try f.writeCValueDeref(writer, operand); - } else { - try f.writeCValue(writer, operand, .Other); - } - try writer.writeAll(".error;\n"); + if (!error_ty.errorSetIsEmpty()) + if (operand_is_ptr) + try f.writeCValueDerefMember(writer, operand, .{ .identifier = "error" }) + else + try f.writeCValueMember(writer, operand, .{ .identifier = "error" }) + else + try f.object.dg.renderValue(writer, error_ty, Value.zero, .Initializer); + try writer.writeAll(";\n"); return local; } @@ -4020,26 +3946,23 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu return CValue.none; const ty_op = f.air.instructions.items(.data)[inst].ty_op; - const writer = f.object.writer(); + const inst_ty = f.air.typeOfIndex(inst); const operand = try f.resolveInst(ty_op.operand); const operand_ty = f.air.typeOf(ty_op.operand); const operand_is_ptr = operand_ty.zigTypeTag() == .Pointer; const error_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; - if (!error_union_ty.errorUnionPayload().hasRuntimeBits()) { - return CValue.none; - } - - const inst_ty = f.air.typeOfIndex(inst); + if (!error_union_ty.errorUnionPayload().hasRuntimeBits()) return CValue.none; + const writer = f.object.writer(); const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = "); if (is_ptr) try writer.writeByte('&'); - try writer.writeByte('('); - try f.writeCValue(writer, operand, .Other); - try writer.writeByte(')'); - try if (operand_is_ptr) writer.writeAll("->") else writer.writeByte('.'); - try writer.writeAll("payload;\n"); + if (operand_is_ptr) + try f.writeCValueDerefMember(writer, operand, .{ .identifier = "payload" }) + else + try f.writeCValueMember(writer, operand, .{ .identifier = "payload" }); + try writer.writeAll(";\n"); return local; } @@ -4060,7 +3983,9 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = { .payload = "); try f.writeCValue(writer, operand, .Initializer); - try writer.writeAll(", .is_null = false };\n"); + try writer.writeAll(", .is_null = "); + try f.object.dg.renderValue(writer, Type.bool, Value.@"false", .Initializer); + try writer.writeAll(" };\n"); return local; } @@ -4070,13 +3995,11 @@ fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const ty_op = f.air.instructions.items(.data)[inst].ty_op; const operand = try f.resolveInst(ty_op.operand); - const err_un_ty = f.air.typeOfIndex(inst); - const payload_ty = err_un_ty.errorUnionPayload(); - if (!payload_ty.hasRuntimeBits()) { - return operand; - } + const error_union_ty = f.air.typeOfIndex(inst); + const payload_ty = error_union_ty.errorUnionPayload(); + if (!payload_ty.hasRuntimeBits()) return operand; - const local = try f.allocLocal(err_un_ty, .Const); + const local = try f.allocLocal(error_union_ty, .Const); try writer.writeAll(" = { .payload = "); try f.writeCValue(writer, .{ .undef = payload_ty }, .Initializer); try writer.writeAll(", .error = "); @@ -4180,18 +4103,21 @@ fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const try writer.writeAll(" = "); - if (error_ty.errorSetIsEmpty()) { - try writer.writeByte('0'); - } else { - try f.writeCValue(writer, operand, .Other); - if (payload_ty.hasRuntimeBits()) { - try if (is_ptr) writer.writeAll("->") else writer.writeByte('.'); - try writer.writeAll("error"); - } - } + if (!error_ty.errorSetIsEmpty()) + if (payload_ty.hasRuntimeBits()) + if (is_ptr) + try f.writeCValueDerefMember(writer, operand, .{ .identifier = "error" }) + else + try f.writeCValueMember(writer, operand, .{ .identifier = "error" }) + else + try f.writeCValue(writer, operand, .Other) + else + try f.object.dg.renderValue(writer, error_ty, Value.zero, .Other); try writer.writeByte(' '); try writer.writeAll(operator); - try writer.writeAll(" 0;\n"); + try writer.writeByte(' '); + try f.object.dg.renderValue(writer, error_ty, Value.zero, .Other); + try writer.writeAll(";\n"); return local; } @@ -4258,58 +4184,77 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue { return local; } -fn airBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u8) !CValue { +fn airUnBuiltinCall( + f: *Function, + inst: Air.Inst.Index, + operation: []const u8, + info: BuiltinInfo, +) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; const inst_ty = f.air.typeOfIndex(inst); - const local = try f.allocLocal(inst_ty, .Const); const operand = f.air.instructions.items(.data)[inst].ty_op.operand; const operand_ty = f.air.typeOf(operand); - const target = f.object.dg.module.getTarget(); + + const local = try f.allocLocal(inst_ty, .Const); const writer = f.object.writer(); - - const int_info = operand_ty.intInfo(target); - const c_bits = toCIntBits(int_info.bits) orelse - return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); - - try writer.print(" = zig_{s}_", .{fn_name}); - try writer.print("{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits }); + try writer.writeAll(" = zig_"); + try writer.writeAll(operation); + try writer.writeByte('_'); + try f.renderTypeForBuiltinFnName(writer, operand_ty); + try writer.writeByte('('); try f.writeCValue(writer, try f.resolveInst(operand), .FunctionArgument); - try writer.print(", {d});\n", .{int_info.bits}); + try f.renderBuiltinInfo(writer, operand_ty, info); + try writer.writeAll(");\n"); return local; } -fn airBinOpBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u8) !CValue { +fn airBinBuiltinCall( + f: *Function, + inst: Air.Inst.Index, + operation: []const u8, + info: BuiltinInfo, +) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; const inst_ty = f.air.typeOfIndex(inst); - const local = try f.allocLocal(inst_ty, .Const); const bin_op = f.air.instructions.items(.data)[inst].bin_op; - const lhs_ty = f.air.typeOf(bin_op.lhs); - const target = f.object.dg.module.getTarget(); + const operand_ty = f.air.typeOf(bin_op.lhs); + + const local = try f.allocLocal(inst_ty, .Const); const writer = f.object.writer(); - - // For binary operations @TypeOf(lhs)==@TypeOf(rhs), so we only check one. - if (lhs_ty.isInt()) { - const int_info = lhs_ty.intInfo(target); - const c_bits = toCIntBits(int_info.bits) orelse - return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); - try writer.print(" = zig_{s}_{c}{d}", .{ fn_name, signAbbrev(int_info.signedness), c_bits }); - } else if (lhs_ty.isRuntimeFloat()) { - const c_bits = lhs_ty.floatBits(target); - try writer.print(" = zig_{s}_f{d}", .{ fn_name, c_bits }); - } else { - return f.fail("TODO: C backend: implement airBinOpBuiltinCall for type {s}", .{@tagName(lhs_ty.tag())}); - } - + try writer.writeAll(" = zig_"); + try writer.writeAll(operation); + try writer.writeByte('_'); + try f.renderTypeForBuiltinFnName(writer, operand_ty); try writer.writeByte('('); try f.writeCValue(writer, try f.resolveInst(bin_op.lhs), .FunctionArgument); try writer.writeAll(", "); try f.writeCValue(writer, try f.resolveInst(bin_op.rhs), .FunctionArgument); + try f.renderBuiltinInfo(writer, operand_ty, info); try writer.writeAll(");\n"); return local; } +fn airCmpBuiltinCall(f: *Function, inst: Air.Inst.Index, operator: []const u8) !CValue { + if (f.liveness.isUnused(inst)) return CValue.none; + + const inst_ty = f.air.typeOfIndex(inst); + const bin_op = f.air.instructions.items(.data)[inst].bin_op; + const operand_ty = f.air.typeOf(bin_op.lhs); + + const local = try f.allocLocal(inst_ty, .Const); + const writer = f.object.writer(); + try writer.writeAll(" = zig_cmp_"); + try f.renderTypeForBuiltinFnName(writer, operand_ty); + try writer.writeByte('('); + try f.writeCValue(writer, try f.resolveInst(bin_op.lhs), .FunctionArgument); + try writer.writeAll(", "); + try f.writeCValue(writer, try f.resolveInst(bin_op.rhs), .FunctionArgument); + try writer.print(") {s} {};\n", .{ operator, try f.fmtIntLiteral(Type.initTag(.i8), Value.zero) }); + return local; +} + fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const extra = f.air.extraData(Air.Cmpxchg, ty_pl.payload).data; @@ -4325,7 +4270,11 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue try writer.writeAll(" = "); if (is_struct) try writer.writeAll("{ .payload = "); try f.writeCValue(writer, expected_value, .Initializer); - if (is_struct) try writer.writeAll(", .is_null = false }"); + if (is_struct) { + try writer.writeAll(", .is_null = "); + try f.object.dg.renderValue(writer, Type.bool, Value.@"false", .Initializer); + try writer.writeAll(" }"); + } try writer.writeAll(";\n"); if (is_struct) { @@ -4341,10 +4290,10 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue try writer.writeAll(" *)"); try f.writeCValue(writer, ptr, .Other); try writer.writeAll(", "); - try f.writeCValue(writer, local, .FunctionArgument); - if (is_struct) { - try writer.writeAll(".payload"); - } + if (is_struct) + try f.writeCValueMember(writer, local, .{ .identifier = "payload" }) + else + try f.writeCValue(writer, local, .FunctionArgument); try writer.writeAll(", "); try f.writeCValue(writer, new_value, .FunctionArgument); try writer.writeAll(", "); @@ -4537,8 +4486,6 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, operand, .Other); try writer.writeAll(");\n"); - try f.object.dg.fwd_decl.writer().writeAll("// This is where the fwd decl for tagName ended up\n"); - return local; } @@ -4804,7 +4751,7 @@ fn airNeg(f: *Function, inst: Air.Inst.Index) !CValue { return local; } -fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, fn_name: []const u8) !CValue { +fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; const un_op = f.air.instructions.items(.data)[inst].un_op; const writer = f.object.writer(); @@ -4812,14 +4759,14 @@ fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, fn_name: []const u8) !CValue const operand = try f.resolveInst(un_op); const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = "); - try f.renderFloatFnName(fn_name, inst_ty); + try f.renderFloatFnName(writer, operation, inst_ty); try writer.writeByte('('); try f.writeCValue(writer, operand, .FunctionArgument); try writer.writeAll(");\n"); return local; } -fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, fn_name: []const u8) !CValue { +fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; const bin_op = f.air.instructions.items(.data)[inst].bin_op; const writer = f.object.writer(); @@ -4828,7 +4775,7 @@ fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, fn_name: []const u8) !CValu const rhs = try f.resolveInst(bin_op.rhs); const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = "); - try f.renderFloatFnName(fn_name, inst_ty); + try f.renderFloatFnName(writer, operation, inst_ty); try writer.writeByte('('); try f.writeCValue(writer, lhs, .FunctionArgument); try writer.writeAll(", "); @@ -4848,7 +4795,7 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = "); - try f.renderFloatFnName("fma", inst_ty); + try f.renderFloatFnName(writer, "fma", inst_ty); try writer.writeByte('('); try f.writeCValue(writer, mulend1, .FunctionArgument); try writer.writeAll(", "); @@ -5010,8 +4957,7 @@ fn formatIntLiteral( }; undef_limbs: [limbs_count_128]Limb, - str: [worst_case_int.sizeInBaseUpperBound(base)]u8, - limbs_limbs: [expected_needed_limbs_count]Limb, + wrap_limbs: [limbs_count_128]Limb, }; var stack align(@alignOf(expected_contents)) = std.heap.stackFallback(@sizeOf(expected_contents), data.mod.gpa); @@ -5037,35 +4983,89 @@ fn formatIntLiteral( } else data.val.toBigInt(&int_buf, target); assert(int.fitsInTwosComp(int_info.signedness, int_info.bits)); - const limbs_count_64 = @divExact(64, @bitSizeOf(Limb)); const c_bits = toCIntBits(int_info.bits) orelse unreachable; - if (c_bits == 128) { - // Clang and GCC don't support 128-bit integer constants but - // will hopefully unfold them if we construct one manually. - //std.debug.todo("128-bit is unimplemented"); - try writer.writeByte('('); - if (int_info.signedness == .signed) { - try writer.writeAll("(int128_t)"); - if (!int.positive) try writer.writeByte('-'); + var one_limbs: [BigInt.calcLimbLen(1)]Limb = undefined; + const one = BigInt.Mutable.init(&one_limbs, 1).toConst(); + + const wrap_limbs = try allocator.alloc(Limb, BigInt.calcTwosCompLimbCount(c_bits)); + defer allocator.free(wrap_limbs); + var wrap = BigInt.Mutable{ .limbs = wrap_limbs, .len = undefined, .positive = undefined }; + if (wrap.addWrap(int, one, int_info.signedness, c_bits) or + int_info.signedness == .signed and wrap.subWrap(int, one, int_info.signedness, c_bits)) + { + const abbrev = switch (data.ty.tag()) { + .c_short, .c_ushort => "SHRT", + .c_int, .c_uint => "INT", + .c_long, .c_ulong => "LONG", + .c_longlong, .c_ulonglong => "LLONG", + .isize, .usize => "INTPTR", + else => return writer.print("zig_{s}Int_{c}{d}", .{ + if (int.positive) "max" else "min", signAbbrev(int_info.signedness), c_bits, + }), + }; + if (int_info.signedness == .unsigned) try writer.writeByte('U'); + return writer.print("{s}_{s}", .{ abbrev, if (int.positive) "MAX" else "MIN" }); + } + + if (!int.positive) try writer.writeByte('-'); + switch (data.ty.tag()) { + .c_short, .c_ushort, .c_int, .c_uint, .c_long, .c_ulong, .c_longlong, .c_ulonglong => {}, + else => try writer.print("zig_as_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits }), + } + + const limbs_count_64 = @divExact(64, @bitSizeOf(Limb)); + if (c_bits <= 64) { + var base: u8 = undefined; + var case: std.fmt.Case = undefined; + switch (fmt.len) { + 0 => base = 10, + 1 => switch (fmt[0]) { + 'b' => { + base = 2; + try writer.writeAll("0b"); + }, + 'o' => { + base = 8; + try writer.writeByte('0'); + }, + 'd' => base = 10, + 'x' => { + base = 16; + case = .lower; + try writer.writeAll("0x"); + }, + 'X' => { + base = 16; + case = .upper; + try writer.writeAll("0x"); + }, + else => @compileError("Invalid fmt: " ++ fmt), + }, + else => @compileError("Invalid fmt: " ++ fmt), } + var str: [64]u8 = undefined; + var limbs_buf: [BigInt.calcToStringLimbsBufferLen(limbs_count_64, 10)]Limb = undefined; + try writer.writeAll(str[0..int.abs().toString(&str, base, case, &limbs_buf)]); + } else { + assert(c_bits == 128); const split = std.math.min(int.limbs.len, limbs_count_64); + var upper_pl = Value.Payload.BigInt{ .base = .{ .tag = .int_big_positive }, .data = int.limbs[split..], }; - const have_upper = !upper_pl.asBigInt().eqZero(); - if (have_upper) try writer.writeByte('('); - if (have_upper or !int.positive) try writer.writeAll("(uint128_t)"); - if (have_upper) { - const upper_val = Value.initPayload(&upper_pl.base); - try formatIntLiteral(.{ - .ty = Type.u64, - .val = upper_val, - .mod = data.mod, - }, fmt, options, writer); - try writer.writeAll("<<64|"); - } + const upper_val = Value.initPayload(&upper_pl.base); + try formatIntLiteral(.{ + .ty = switch (int_info.signedness) { + .unsigned => Type.u64, + .signed => Type.i64, + }, + .val = upper_val, + .mod = data.mod, + }, fmt, options, writer); + + try writer.writeAll(", "); var lower_pl = Value.Payload.BigInt{ .base = .{ .tag = .int_big_positive }, @@ -5078,74 +5078,9 @@ fn formatIntLiteral( .mod = data.mod, }, fmt, options, writer); - if (have_upper) try writer.writeByte(')'); return writer.writeByte(')'); } - assert(c_bits <= 64); - var one_limbs: [BigInt.calcLimbLen(1)]Limb = undefined; - const one = BigInt.Mutable.init(&one_limbs, 1).toConst(); - - var wrap_limbs: [BigInt.calcTwosCompLimbCount(64)]Limb = undefined; - var wrap = BigInt.Mutable{ .limbs = &wrap_limbs, .len = undefined, .positive = undefined }; - if (wrap.addWrap(int, one, int_info.signedness, c_bits) or - int_info.signedness == .signed and wrap.subWrap(int, one, int_info.signedness, c_bits)) - { - if (int_info.signedness == .unsigned) try writer.writeByte('U'); - switch (data.ty.tag()) { - .c_short, .c_ushort => try writer.writeAll("SHRT"), - .c_int, .c_uint => try writer.writeAll("INT"), - .c_long, .c_ulong => try writer.writeAll("LONG"), - .c_longlong, .c_ulonglong => try writer.writeAll("LLONG"), - .isize, .usize => try writer.writeAll("INTPTR"), - else => try writer.print("INT{d}", .{c_bits}), - } - try writer.writeAll(if (int.positive) "_MAX" else "_MIN"); - return; - } - - if (!int.positive) try writer.writeByte('-'); - switch (data.ty.tag()) { - .c_short, .c_ushort, .c_int, .c_uint, .c_long, .c_ulong, .c_longlong, .c_ulonglong => {}, - else => { - if (int_info.signedness == .unsigned) try writer.writeByte('U'); - try writer.print("INT{d}_C(", .{c_bits}); - }, - } - - var base: u8 = undefined; - var case: std.fmt.Case = undefined; - switch (fmt.len) { - 0 => base = 10, - 1 => switch (fmt[0]) { - 'b' => { - base = 2; - try writer.writeAll("0b"); - }, - 'o' => { - base = 8; - try writer.writeByte('0'); - }, - 'd' => base = 10, - 'x' => { - base = 16; - case = .lower; - try writer.writeAll("0x"); - }, - 'X' => { - base = 16; - case = .upper; - try writer.writeAll("0x"); - }, - else => @compileError("Invalid fmt: " ++ fmt), - }, - else => @compileError("Invalid fmt: " ++ fmt), - } - - var str: [64]u8 = undefined; - var limbs_buf: [BigInt.calcToStringLimbsBufferLen(limbs_count_64, 10)]Limb = undefined; - try writer.writeAll(str[0..int.abs().toString(&str, base, case, &limbs_buf)]); - switch (data.ty.tag()) { .c_short, .c_ushort, .c_int => {}, .c_uint => try writer.writeAll("u"), diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 4188104459..ca2aee52dd 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -393,8 +393,6 @@ test "function callconv expression depends on generic parameter" { } test "runtime-known array index has best alignment possible" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - // take full advantage of over-alignment var array align(4) = [_]u8{ 1, 2, 3, 4 }; comptime assert(@TypeOf(&array[0]) == *align(4) u8); diff --git a/test/behavior/bugs/2114.zig b/test/behavior/bugs/2114.zig index a013688f4e..fdfb8884a5 100644 --- a/test/behavior/bugs/2114.zig +++ b/test/behavior/bugs/2114.zig @@ -12,7 +12,6 @@ test "fixed" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO try testCtz(); comptime try testCtz(); diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 3a7dda642c..0b8787e8b6 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -119,7 +119,6 @@ test "@intToFloat(f80)" { 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_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest(comptime Int: type) !void { @@ -1157,7 +1156,6 @@ fn castToOptionalSlice() ?[]const u8 { test "cast u128 to f128 and back" { 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 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index 4abd09b9c6..8524a11b6a 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -54,7 +54,6 @@ fn testFloatComparisons() !void { test "different sized float comparisons" { 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 @@ -303,7 +302,6 @@ test "@log" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO comptime try testLog(); try testLog(); @@ -543,7 +541,6 @@ fn testTrunc() !void { test "negation f16" { 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/behavior/int128.zig b/test/behavior/int128.zig index 3a1b198aa1..ceb63290cd 100644 --- a/test/behavior/int128.zig +++ b/test/behavior/int128.zig @@ -43,7 +43,6 @@ test "int128" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var buff: i128 = -1; try expect(buff < 0 and (buff + 1) == 0); diff --git a/test/behavior/math.zig b/test/behavior/math.zig index e8cecf803a..ca7c3b044a 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -452,7 +452,6 @@ fn testDivision() !void { } test "division half-precision floats" { - 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 @@ -685,7 +684,6 @@ test "basic @mulWithOverflow" { // TODO migrate to this for all backends once they handle more cases test "extensive @mulWithOverflow" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO { @@ -835,7 +833,6 @@ test "extensive @mulWithOverflow" { } test "@mulWithOverflow bitsize > 32" { - 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_wasm) return error.SkipZigTest; // TODO @@ -923,8 +920,6 @@ test "@subWithOverflow" { } test "@shlWithOverflow" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - { var result: u4 = undefined; var a: u4 = 2; @@ -1274,7 +1269,6 @@ test "@sqrt" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) 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 try testSqrt(f64, 12.0); comptime try testSqrt(f64, 12.0); @@ -1339,7 +1333,6 @@ test "@floor" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) 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 try testFloor(f64, 12.0); comptime try testFloor(f64, 12.0); @@ -1388,7 +1381,6 @@ test "@ceil" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) 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 try testCeil(f64, 12.0); comptime try testCeil(f64, 12.0); @@ -1437,7 +1429,6 @@ test "@trunc" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) 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 try testTrunc(f64, 12.0); comptime try testTrunc(f64, 12.0); @@ -1500,7 +1491,6 @@ test "@round" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) 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 try testRound(f64, 12.0); comptime try testRound(f64, 12.0); diff --git a/test/behavior/muladd.zig b/test/behavior/muladd.zig index 814e007950..c688f27593 100644 --- a/test/behavior/muladd.zig +++ b/test/behavior/muladd.zig @@ -27,7 +27,6 @@ fn testMulAdd() !void { test "@mulAdd f16" { 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/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index 1b5ee24936..dd6aa7549d 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -448,7 +448,6 @@ test "optional pointer in packed struct" { } test "nested packed struct field access test" { - 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/ptrcast.zig b/test/behavior/ptrcast.zig index 21e8b544a8..79f75e0131 100644 --- a/test/behavior/ptrcast.zig +++ b/test/behavior/ptrcast.zig @@ -133,7 +133,6 @@ test "lower reinterpreted comptime field ptr (with under-aligned fields)" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO: CBE does not yet support under-aligned fields // Test lowering a field ptr comptime var bytes align(2) = [_]u8{ 1, 2, 3, 4, 5, 6 }; diff --git a/test/behavior/saturating_arithmetic.zig b/test/behavior/saturating_arithmetic.zig index 4ab01ecddc..6dff9a9112 100644 --- a/test/behavior/saturating_arithmetic.zig +++ b/test/behavior/saturating_arithmetic.zig @@ -54,7 +54,6 @@ test "saturating add 128bit" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { try testSatAdd(i128, maxInt(i128), -maxInt(i128), 0); @@ -78,7 +77,6 @@ test "saturating subtraction" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -124,7 +122,6 @@ test "saturating subtraction 128bit" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -151,7 +148,6 @@ test "saturating multiplication" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage1 and builtin.cpu.arch == .wasm32) { // https://github.com/ziglang/zig/issues/9660 @@ -199,7 +195,6 @@ test "saturating shift-left" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { diff --git a/test/behavior/sizeof_and_typeof.zig b/test/behavior/sizeof_and_typeof.zig index 0c0069b28f..6f27d414d2 100644 --- a/test/behavior/sizeof_and_typeof.zig +++ b/test/behavior/sizeof_and_typeof.zig @@ -19,7 +19,6 @@ test "@sizeOf on compile-time types" { test "@TypeOf() with multiple arguments" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; { var var_1: u32 = undefined; var var_2: u8 = undefined; diff --git a/test/behavior/widening.zig b/test/behavior/widening.zig index 801c7d6a9b..b916e9ca87 100644 --- a/test/behavior/widening.zig +++ b/test/behavior/widening.zig @@ -40,7 +40,6 @@ test "float widening" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var a: f16 = 12.34; var b: f32 = a; @@ -60,7 +59,6 @@ test "float widening f16 to f128" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO // TODO https://github.com/ziglang/zig/issues/3282 if (builtin.cpu.arch == .aarch64) return error.SkipZigTest; From 15df64ade8e460141d6485340ca0f88d21f03c47 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 23 Oct 2022 13:21:39 -0400 Subject: [PATCH 38/47] std: add cbe hacks to more targets These are needed because clang doesn't support anything in naked functions, not even assembly register inputs. --- lib/std/os/linux/arm64.zig | 19 +++++++--- lib/std/os/linux/i386.zig | 38 ++++++++++++++----- lib/std/os/linux/x86_64.zig | 13 ++++--- lib/std/start.zig | 38 +++++++++++++------ .../hello_world_with_updates.0.zig | 2 +- .../hello_world_with_updates.0.zig | 2 +- .../hello_world_with_updates.0.zig | 2 +- .../hello_world_with_updates.0.zig | 2 +- 8 files changed, 80 insertions(+), 36 deletions(-) diff --git a/lib/std/os/linux/arm64.zig b/lib/std/os/linux/arm64.zig index 8571b26071..c3b9f09028 100644 --- a/lib/std/os/linux/arm64.zig +++ b/lib/std/os/linux/arm64.zig @@ -106,11 +106,20 @@ pub extern fn clone(func: CloneFn, stack: usize, flags: u32, arg: usize, ptid: * pub const restore = restore_rt; pub fn restore_rt() callconv(.Naked) void { - return asm volatile ("svc #0" - : - : [number] "{x8}" (@enumToInt(SYS.rt_sigreturn)), - : "memory", "cc" - ); + switch (@import("builtin").zig_backend) { + .stage2_c => return asm volatile ( + \\ mov x8, %[number] + \\ svc #0 + : + : [number] "i" (@enumToInt(SYS.rt_sigreturn)), + : "memory", "cc" + ), + else => return asm volatile ("svc #0" + : + : [number] "{x8}" (@enumToInt(SYS.rt_sigreturn)), + : "memory", "cc" + ), + } } pub const O = struct { diff --git a/lib/std/os/linux/i386.zig b/lib/std/os/linux/i386.zig index 2766393487..9357002519 100644 --- a/lib/std/os/linux/i386.zig +++ b/lib/std/os/linux/i386.zig @@ -124,19 +124,37 @@ const CloneFn = std.meta.FnPtr(fn (arg: usize) callconv(.C) u8); pub extern fn clone(func: CloneFn, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; pub fn restore() callconv(.Naked) void { - return asm volatile ("int $0x80" - : - : [number] "{eax}" (@enumToInt(SYS.sigreturn)), - : "memory" - ); + switch (@import("builtin").zig_backend) { + .stage2_c => return asm volatile ( + \\ movl %[number], %%eax + \\ int $0x80 + : + : [number] "i" (@enumToInt(SYS.sigreturn)), + : "memory" + ), + else => return asm volatile ("int $0x80" + : + : [number] "{eax}" (@enumToInt(SYS.sigreturn)), + : "memory" + ), + } } pub fn restore_rt() callconv(.Naked) void { - return asm volatile ("int $0x80" - : - : [number] "{eax}" (@enumToInt(SYS.rt_sigreturn)), - : "memory" - ); + switch (@import("builtin").zig_backend) { + .stage2_c => return asm volatile ( + \\ movl %[number], %%eax + \\ int $0x80 + : + : [number] "i" (@enumToInt(SYS.rt_sigreturn)), + : "memory" + ), + else => return asm volatile ("int $0x80" + : + : [number] "{eax}" (@enumToInt(SYS.rt_sigreturn)), + : "memory" + ), + } } pub const O = struct { diff --git a/lib/std/os/linux/x86_64.zig b/lib/std/os/linux/x86_64.zig index 7f320c010a..90aae99082 100644 --- a/lib/std/os/linux/x86_64.zig +++ b/lib/std/os/linux/x86_64.zig @@ -109,11 +109,14 @@ pub const restore = restore_rt; pub fn restore_rt() callconv(.Naked) void { switch (@import("builtin").zig_backend) { - .stage2_c => return asm volatile (std.fmt.comptimePrint( - \\ movl ${d}, %%eax - \\ syscall - \\ retq - , .{@enumToInt(SYS.rt_sigreturn)}) ::: "rcx", "r11", "memory"), + .stage2_c => return asm volatile ( + \\ movl %[number], %%eax + \\ syscall + \\ retq + : + : [number] "i" (@enumToInt(SYS.rt_sigreturn)), + : "rcx", "r11", "memory" + ), else => return asm volatile ("syscall" : : [number] "{rax}" (@enumToInt(SYS.rt_sigreturn)), diff --git a/lib/std/start.zig b/lib/std/start.zig index 0563e2adad..cc8a92d7b6 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -265,23 +265,37 @@ fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) callconv fn _start() callconv(.Naked) noreturn { switch (builtin.zig_backend) { - .stage2_c => switch (native_arch) { - .x86_64 => { - @export(argc_argv_ptr, .{ .name = "argc_argv_ptr" }); - @export(posixCallMainAndExit, .{ .name = "_posixCallMainAndExit" }); - asm volatile ( - \\ xor %%rbp, %%rbp - \\ mov %%rsp, argc_argv_ptr + .stage2_c => { + @export(argc_argv_ptr, .{ .name = "argc_argv_ptr" }); + @export(posixCallMainAndExit, .{ .name = "_posixCallMainAndExit" }); + switch (native_arch) { + .x86_64 => asm volatile ( + \\ xorl %%ebp, %%ebp + \\ movq %%rsp, argc_argv_ptr + \\ andq $-16, %%rsp \\ call _posixCallMainAndExit - ); - unreachable; - }, - else => @compileError("unsupported arch"), + ), + .i386 => asm volatile ( + \\ xorl %%ebp, %%ebp + \\ movl %%esp, argc_argv_ptr + \\ andl $-16, %%esp + \\ jmp _posixCallMainAndExit + ), + .aarch64, .aarch64_be, .arm, .armeb, .thumb => asm volatile ( + \\ mov fp, #0 + \\ mov lr, #0 + \\ str sp, argc_argv_ptr + \\ and sp, #-16 + \\ b _posixCallMainAndExit + ), + else => @compileError("unsupported arch"), + } + unreachable; }, else => switch (native_arch) { .x86_64 => { argc_argv_ptr = asm volatile ( - \\ xor %%rbp, %%rbp + \\ xor %%ebp, %%ebp : [argc] "={rsp}" (-> [*]usize), ); }, diff --git a/test/cases/aarch64-macos/hello_world_with_updates.0.zig b/test/cases/aarch64-macos/hello_world_with_updates.0.zig index 9f516ff139..a3bcb8527f 100644 --- a/test/cases/aarch64-macos/hello_world_with_updates.0.zig +++ b/test/cases/aarch64-macos/hello_world_with_updates.0.zig @@ -2,4 +2,4 @@ // output_mode=Exe // target=aarch64-macos // -// :109:9: error: root struct of file 'tmp' has no member named 'main' +// :108:9: error: root struct of file 'tmp' has no member named 'main' diff --git a/test/cases/x86_64-linux/hello_world_with_updates.0.zig b/test/cases/x86_64-linux/hello_world_with_updates.0.zig index 40abdd6c1f..70cec703da 100644 --- a/test/cases/x86_64-linux/hello_world_with_updates.0.zig +++ b/test/cases/x86_64-linux/hello_world_with_updates.0.zig @@ -2,4 +2,4 @@ // output_mode=Exe // target=x86_64-linux // -// :109:9: error: root struct of file 'tmp' has no member named 'main' +// :108:9: error: root struct of file 'tmp' has no member named 'main' diff --git a/test/cases/x86_64-macos/hello_world_with_updates.0.zig b/test/cases/x86_64-macos/hello_world_with_updates.0.zig index e0680c81d7..3b8758a0e5 100644 --- a/test/cases/x86_64-macos/hello_world_with_updates.0.zig +++ b/test/cases/x86_64-macos/hello_world_with_updates.0.zig @@ -2,4 +2,4 @@ // output_mode=Exe // target=x86_64-macos // -// :109:9: error: root struct of file 'tmp' has no member named 'main' +// :108:9: error: root struct of file 'tmp' has no member named 'main' diff --git a/test/cases/x86_64-windows/hello_world_with_updates.0.zig b/test/cases/x86_64-windows/hello_world_with_updates.0.zig index 04e1d4cfad..e9a55f6061 100644 --- a/test/cases/x86_64-windows/hello_world_with_updates.0.zig +++ b/test/cases/x86_64-windows/hello_world_with_updates.0.zig @@ -2,4 +2,4 @@ // output_mode=Exe // target=x86_64-windows // -// :130:9: error: root struct of file 'tmp' has no member named 'main' +// :129:9: error: root struct of file 'tmp' has no member named 'main' From ab468d57e3af5d158dfbe0229cc451ce62dd80dc Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 24 Oct 2022 10:42:41 -0400 Subject: [PATCH 39/47] cbe: implement packed structs Sometimes you have to break a test to make progress :) --- lib/include/zig.h | 95 ++-- src/codegen/c.zig | 959 +++++++++++++++++++++++--------- test/behavior/bitcast.zig | 2 - test/behavior/eval.zig | 2 - test/behavior/packed-struct.zig | 2 +- test/behavior/struct.zig | 2 - test/behavior/undefined.zig | 1 - 7 files changed, 732 insertions(+), 331 deletions(-) diff --git a/lib/include/zig.h b/lib/include/zig.h index 45e573d092..4ac6537188 100644 --- a/lib/include/zig.h +++ b/lib/include/zig.h @@ -316,19 +316,28 @@ zig_extern_c void *memset (void *, int, zig_usize); #define zig_maxInt(Type, bits) zig_shr_##Type(zig_maxInt_##Type, (zig_bitSizeOf(zig_##Type) - bits)) #define zig_minInt(Type, bits) zig_not_##Type(zig_maxInt(Type, bits), bits) +#define zig_int_operator(Type, RhsType, operation, operator) \ + static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##RhsType rhs) { \ + return lhs operator rhs; \ + } +#define zig_int_operators(w) \ + zig_int_operator(u##w, u##w, and, &) \ + zig_int_operator(i##w, i##w, and, &) \ + zig_int_operator(u##w, u##w, or, |) \ + zig_int_operator(i##w, i##w, or, |) \ + zig_int_operator(u##w, u##w, xor, ^) \ + zig_int_operator(i##w, i##w, xor, ^) \ + zig_int_operator(u##w, u8, shl, <<) \ + zig_int_operator(i##w, u8, shl, <<) \ + zig_int_operator(u##w, u8, shr, >>) \ + zig_int_operator(u##w, u##w, div_floor, /) \ + zig_int_operator(u##w, u##w, mod, %) +zig_int_operators(8) +zig_int_operators(16) +zig_int_operators(32) +zig_int_operators(64) + #define zig_int_helpers(w) \ - static inline zig_u##w zig_shl_u##w(zig_u##w lhs, zig_u8 rhs) { \ - return lhs << rhs; \ - } \ -\ - static inline zig_i##w zig_shl_i##w(zig_i##w lhs, zig_u8 rhs) { \ - return lhs << rhs; \ - } \ -\ - static inline zig_u##w zig_shr_u##w(zig_u##w lhs, zig_u8 rhs) { \ - return lhs >> rhs; \ - } \ -\ static inline zig_i##w zig_shr_i##w(zig_i##w lhs, zig_u8 rhs) { \ zig_i##w sign_mask = lhs < zig_as_i##w(0) ? zig_as_i##w(-1) : zig_as_i##w(0); \ return ((lhs ^ sign_mask) >> rhs) ^ sign_mask; \ @@ -351,18 +360,10 @@ zig_extern_c void *memset (void *, int, zig_usize); return (val & zig_as_u##w(1) << (bits - zig_as_u8(1))) != 0 \ ? val | zig_minInt(i##w, bits) : val & zig_maxInt(i##w, bits); \ } \ -\ - static inline zig_u##w zig_div_floor_u##w(zig_u##w lhs, zig_u##w rhs) { \ - return lhs / rhs; \ - } \ \ static inline zig_i##w zig_div_floor_i##w(zig_i##w lhs, zig_i##w rhs) { \ return lhs / rhs - (((lhs ^ rhs) & (lhs % rhs)) < zig_as_i##w(0)); \ } \ -\ - static inline zig_u##w zig_mod_u##w(zig_u##w lhs, zig_u##w rhs) { \ - return lhs % rhs; \ - } \ \ static inline zig_i##w zig_mod_i##w(zig_i##w lhs, zig_i##w rhs) { \ zig_i##w rem = lhs % rhs; \ @@ -969,12 +970,12 @@ typedef signed __int128 zig_i128; #define zig_lo_i128(val) ((zig_u64)((val) >> 0)) #define zig_bitcast_u128(val) ((zig_u128)(val)) #define zig_bitcast_i128(val) ((zig_i128)(val)) -#define zig_cmp_int128(ZigType, CType) \ - static inline zig_i8 zig_cmp_##ZigType(CType lhs, CType rhs) { \ +#define zig_cmp_int128(Type) \ + static inline zig_i8 zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \ return (lhs > rhs) - (lhs < rhs); \ } -#define zig_bit_int128(ZigType, CType, operation, operator) \ - static inline CType zig_##operation##_##ZigType(CType lhs, CType rhs) { \ +#define zig_bit_int128(Type, operation, operator) \ + static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ return lhs operator rhs; \ } @@ -996,15 +997,15 @@ typedef struct { zig_align(16) zig_i64 hi; zig_u64 lo; } zig_i128; #define zig_lo_i128(val) ((val).lo) #define zig_bitcast_u128(val) zig_as_u128((zig_u64)(val).hi, (val).lo) #define zig_bitcast_i128(val) zig_as_i128((zig_i64)(val).hi, (val).lo) -#define zig_cmp_int128(ZigType, CType) \ - static inline zig_c_int zig_cmp_##ZigType(CType lhs, CType rhs) { \ +#define zig_cmp_int128(Type) \ + static inline zig_i8 zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \ return (lhs.hi == rhs.hi) \ ? (lhs.lo > rhs.lo) - (lhs.lo < rhs.lo) \ : (lhs.hi > rhs.hi) - (lhs.hi < rhs.hi); \ } -#define zig_bit_int128(ZigType, CType, operation, operator) \ - static inline CType zig_##operation##_##ZigType(CType lhs, CType rhs) { \ - return (CType){ .hi = lhs.hi operator rhs.hi, .lo = lhs.lo operator rhs.lo }; \ +#define zig_bit_int128(Type, operation, operator) \ + static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ + return (zig_##Type){ .hi = lhs.hi operator rhs.hi, .lo = lhs.lo operator rhs.lo }; \ } #endif /* zig_has_int128 */ @@ -1014,17 +1015,17 @@ typedef struct { zig_align(16) zig_i64 hi; zig_u64 lo; } zig_i128; #define zig_minInt_i128 zig_as_i128(zig_minInt_i64, zig_minInt_u64) #define zig_maxInt_i128 zig_as_i128(zig_maxInt_i64, zig_maxInt_u64) -zig_cmp_int128(u128, zig_u128) -zig_cmp_int128(i128, zig_i128) +zig_cmp_int128(u128) +zig_cmp_int128(i128) -zig_bit_int128(u128, zig_u128, and, &) -zig_bit_int128(i128, zig_i128, and, &) +zig_bit_int128(u128, and, &) +zig_bit_int128(i128, and, &) -zig_bit_int128(u128, zig_u128, or, |) -zig_bit_int128(i128, zig_i128, or, |) +zig_bit_int128(u128, or, |) +zig_bit_int128(i128, or, |) -zig_bit_int128(u128, zig_u128, xor, ^) -zig_bit_int128(i128, zig_i128, xor, ^) +zig_bit_int128(u128, xor, ^) +zig_bit_int128(i128, xor, ^) static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs); @@ -1164,7 +1165,7 @@ static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { #define zig_mod_u128 zig_rem_u128 static inline zig_i128 zig_shr_i128(zig_i128 lhs, zig_u8 rhs) { - zig_i128 sign_mask = zig_cmp_i128(lhs, zig_as_i128(0, 0)) < 0 ? zig_as_i128(-1, UINT64_MAX) : zig_as_i128(0, 0); + zig_i128 sign_mask = zig_cmp_i128(lhs, zig_as_i128(0, 0)) < zig_as_i8(0) ? zig_as_i128(-1, UINT64_MAX) : zig_as_i128(0, 0); return zig_xor_i128(zig_bitcast_i128(zig_shr_u128(zig_bitcast_u128(zig_xor_i128(lhs, sign_mask)), rhs)), sign_mask); } @@ -1218,8 +1219,8 @@ static inline zig_bool zig_shlo_u128(zig_u128 *res, zig_u128 lhs, zig_u8 rhs, zi static inline zig_bool zig_shlo_i128(zig_i128 *res, zig_i128 lhs, zig_u8 rhs, zig_u8 bits) { *res = zig_shlw_i128(lhs, rhs, bits); zig_i128 mask = zig_bitcast_i128(zig_shl_u128(zig_maxInt_u128, bits - rhs - zig_as_u8(1))); - return zig_cmp_i128(zig_and_i128(lhs, mask), zig_as_i128(0, 0)) != 0 && - zig_cmp_i128(zig_and_i128(lhs, mask), mask) != 0; + return zig_cmp_i128(zig_and_i128(lhs, mask), zig_as_i128(0, 0)) != zig_as_i8(0) && + zig_cmp_i128(zig_and_i128(lhs, mask), mask) != zig_as_i8(0); } static inline zig_bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { @@ -1316,15 +1317,15 @@ static inline zig_bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs) static inline zig_u128 zig_shls_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { zig_u128 res; - if (zig_cmp_u128(rhs, zig_as_u128(0, bits)) >= 0) - return zig_cmp_u128(lhs, zig_as_u128(0, 0)) != 0 ? zig_maxInt(u128, bits) : lhs; + if (zig_cmp_u128(rhs, zig_as_u128(0, bits)) >= zig_as_i8(0)) + return zig_cmp_u128(lhs, zig_as_u128(0, 0)) != zig_as_i8(0) ? zig_maxInt(u128, bits) : lhs; return zig_shlo_u128(&res, lhs, (zig_u8)rhs, bits) ? zig_maxInt(u128, bits) : res; } static inline zig_i128 zig_shls_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { zig_i128 res; - if (zig_cmp_u128(zig_bitcast_u128(rhs), zig_as_u128(0, bits)) < 0 && !zig_shlo_i128(&res, lhs, rhs, bits)) return res; - return zig_cmp_i128(lhs, zig_as_i128(0, 0)) < 0 ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); + if (zig_cmp_u128(zig_bitcast_u128(rhs), zig_as_u128(0, bits)) < zig_as_i8(0) && !zig_shlo_i128(&res, lhs, rhs, bits)) return res; + return zig_cmp_i128(lhs, zig_as_i128(0, 0)) < zig_as_i8(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); } static inline zig_u128 zig_adds_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { @@ -1335,7 +1336,7 @@ static inline zig_u128 zig_adds_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { static inline zig_i128 zig_adds_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { zig_i128 res; if (!zig_addo_i128(&res, lhs, rhs, bits)) return res; - return zig_cmp_i128(res, zig_as_i128(0, 0)) >= 0 ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); + return zig_cmp_i128(res, zig_as_i128(0, 0)) >= zig_as_i8(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); } static inline zig_u128 zig_subs_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { @@ -1346,7 +1347,7 @@ static inline zig_u128 zig_subs_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { static inline zig_i128 zig_subs_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { zig_i128 res; if (!zig_subo_i128(&res, lhs, rhs, bits)) return res; - return zig_cmp_i128(res, zig_as_i128(0, 0)) >= 0 ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); + return zig_cmp_i128(res, zig_as_i128(0, 0)) >= zig_as_i8(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); } static inline zig_u128 zig_muls_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { @@ -1357,7 +1358,7 @@ static inline zig_u128 zig_muls_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { static inline zig_i128 zig_muls_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { zig_i128 res; if (!zig_mulo_i128(&res, lhs, rhs, bits)) return res; - return zig_cmp_i128(zig_xor_i128(lhs, rhs), zig_as_i128(0, 0)) < 0 ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); + return zig_cmp_i128(zig_xor_i128(lhs, rhs), zig_as_i128(0, 0)) < zig_as_i8(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); } static inline zig_u8 zig_clz_u128(zig_u128 val, zig_u8 bits) { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 00fc70ff2a..8052c8116b 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -18,7 +18,7 @@ const Air = @import("../Air.zig"); const Liveness = @import("../Liveness.zig"); const CType = @import("../type.zig").CType; -const Mutability = enum { Const, Mut }; +const Mutability = enum { Const, ConstArgument, Mut }; const BigInt = std.math.big.int; pub const CValue = union(enum) { @@ -254,24 +254,20 @@ pub const Function = struct { const val = f.air.value(inst).?; const ty = f.air.typeOf(inst); - switch (ty.zigTypeTag()) { - .Array => { - const writer = f.object.code_header.writer(); - const decl_c_value = f.allocLocalValue(); - gop.value_ptr.* = decl_c_value; - try writer.writeAll("static "); - try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, .Const, 0, .Complete); - try writer.writeAll(" = "); - try f.object.dg.renderValue(writer, ty, val, .Initializer); - try writer.writeAll(";\n "); - return decl_c_value; - }, - else => { - const result = CValue{ .constant = inst }; - gop.value_ptr.* = result; - return result; - }, - } + + const result = if (lowersToArray(ty, f.object.dg.module.getTarget())) result: { + const writer = f.object.code_header.writer(); + const decl_c_value = f.allocLocalValue(); + try writer.writeAll("static "); + try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, .Const, 0, .Complete); + try writer.writeAll(" = "); + try f.object.dg.renderValue(writer, ty, val, .Initializer); + try writer.writeAll(";\n "); + break :result decl_c_value; + } else CValue{ .constant = inst }; + + gop.value_ptr.* = result; + return result; } fn wantSafety(f: *Function) bool { @@ -374,7 +370,7 @@ pub const Function = struct { fn renderTypeForBuiltinFnName(f: *Function, writer: anytype, ty: Type) !void { const target = f.object.dg.module.getTarget(); - const c_bits = if (ty.isInt()) c_bits: { + const c_bits = if (ty.isAbiInt()) c_bits: { const int_info = ty.intInfo(target); try writer.writeByte(signAbbrev(int_info.signedness)); break :c_bits toCIntBits(int_info.bits) orelse @@ -393,21 +389,21 @@ pub const Function = struct { switch (info) { .None => {}, .Range => { - var arena = std.heap.ArenaAllocator.init(f.object.dg.module.gpa); + var arena = std.heap.ArenaAllocator.init(f.object.dg.gpa); defer arena.deinit(); - const expected_contents = union { u: Value.Payload.U64, i: Value.Payload.I64 }; - var stack align(@alignOf(expected_contents)) = - std.heap.stackFallback(@sizeOf(expected_contents), arena.allocator()); + const ExpectedContents = union { u: Value.Payload.U64, i: Value.Payload.I64 }; + var stack align(@alignOf(ExpectedContents)) = + std.heap.stackFallback(@sizeOf(ExpectedContents), arena.allocator()); const int_info = ty.intInfo(target); if (int_info.signedness == .signed) { const min_val = try ty.minInt(stack.get(), target); - try writer.print(", {x}", .{try f.fmtIntLiteral(ty, min_val)}); + try writer.print(", {x}", .{try f.object.dg.fmtIntLiteral(ty, min_val)}); } const max_val = try ty.maxInt(stack.get(), target); - try writer.print(", {x}", .{try f.fmtIntLiteral(ty, max_val)}); + try writer.print(", {x}", .{try f.object.dg.fmtIntLiteral(ty, max_val)}); }, .Bits => { var bits_pl = Value.Payload.U64{ @@ -415,7 +411,7 @@ pub const Function = struct { .data = ty.bitSize(target), }; const bits_val = Value.initPayload(&bits_pl.base); - try writer.print(", {}", .{try f.fmtIntLiteral(Type.u8, bits_val)}); + try writer.print(", {}", .{try f.object.dg.fmtIntLiteral(Type.u8, bits_val)}); }, } } @@ -471,7 +467,7 @@ pub const DeclGen = struct { @setCold(true); const src = LazySrcLoc.nodeOffset(0); const src_loc = src.toSrcLoc(dg.decl); - dg.error_msg = try Module.ErrorMsg.create(dg.module.gpa, src_loc, format, args); + dg.error_msg = try Module.ErrorMsg.create(dg.gpa, src_loc, format, args); return error.AnalysisFail; } @@ -559,14 +555,55 @@ pub const DeclGen = struct { try dg.renderDeclValue(writer, ptr_ty, ptr_val, decl_index); }, .field_ptr => { + const ptr_info = ptr_ty.ptrInfo(); const field_ptr = ptr_val.castTag(.field_ptr).?.data; const container_ty = field_ptr.container_ty; const index = field_ptr.field_index; + + var container_ptr_ty_pl: Type.Payload.ElemType = .{ + .base = .{ .tag = .c_mut_pointer }, + .data = field_ptr.container_ty, + }; + const container_ptr_ty = Type.initPayload(&container_ptr_ty_pl.base); + const FieldInfo = struct { name: []const u8, ty: Type }; const field_info: FieldInfo = switch (container_ty.zigTypeTag()) { - .Struct => FieldInfo{ - .name = container_ty.structFields().keys()[index], - .ty = container_ty.structFields().values()[index].ty, + .Struct => switch (container_ty.containerLayout()) { + .Auto, .Extern => FieldInfo{ + .name = container_ty.structFields().keys()[index], + .ty = container_ty.structFields().values()[index].ty, + }, + .Packed => if (ptr_info.data.host_size == 0) { + const target = dg.module.getTarget(); + + const byte_offset = container_ty.packedStructFieldByteOffset(index, target); + var byte_offset_pl = Value.Payload.U64{ + .base = .{ .tag = .int_u64 }, + .data = byte_offset, + }; + const byte_offset_val = Value.initPayload(&byte_offset_pl.base); + + var ptr_u8_pl = ptr_info; + ptr_u8_pl.data.pointee_type = Type.u8; + const ptr_u8_ty = Type.initPayload(&ptr_u8_pl.base); + + try writer.writeAll("&(("); + try dg.renderTypecast(writer, ptr_u8_ty); + try writer.writeByte(')'); + try dg.renderParentPtr(writer, field_ptr.container_ptr, container_ptr_ty); + return writer.print(")[{}]", .{try dg.fmtIntLiteral(Type.usize, byte_offset_val)}); + } else { + var host_pl = Type.Payload.Bits{ + .base = .{ .tag = .int_unsigned }, + .data = ptr_info.data.host_size * 8, + }; + const host_ty = Type.initPayload(&host_pl.base); + + try writer.writeByte('('); + try dg.renderTypecast(writer, ptr_ty); + try writer.writeByte(')'); + return dg.renderParentPtr(writer, field_ptr.container_ptr, host_ty); + }, }, .Union => FieldInfo{ .name = container_ty.unionFields().keys()[index], @@ -582,20 +619,16 @@ pub const DeclGen = struct { }, else => unreachable, }; - var container_ptr_ty_pl: Type.Payload.ElemType = .{ - .base = .{ .tag = .c_mut_pointer }, - .data = field_ptr.container_ty, - }; - const container_ptr_ty = Type.initPayload(&container_ptr_ty_pl.base); if (field_info.ty.hasRuntimeBitsIgnoreComptime()) { try writer.writeAll("&("); try dg.renderParentPtr(writer, field_ptr.container_ptr, container_ptr_ty); - if (field_ptr.container_ty.tag() == .union_tagged or field_ptr.container_ty.tag() == .union_safety_tagged) { - try writer.print(")->payload.{ }", .{fmtIdent(field_info.name)}); - } else { - try writer.print(")->{ }", .{fmtIdent(field_info.name)}); + try writer.writeAll(")->"); + switch (field_ptr.container_ty.tag()) { + .union_tagged, .union_safety_tagged => try writer.writeAll("payload."), + else => {}, } + try writer.print("{ }", .{fmtIdent(field_info.name)}); } else { try dg.renderParentPtr(writer, field_ptr.container_ptr, field_info.ty); } @@ -694,25 +727,28 @@ pub const DeclGen = struct { try dg.renderValue(writer, Type.bool, val, .Initializer); return writer.writeAll(" }"); }, - .Struct => { - if (location != .Initializer) { - try writer.writeByte('('); - try dg.renderTypecast(writer, ty); - try writer.writeByte(')'); - } + .Struct => switch (ty.containerLayout()) { + .Auto, .Extern => { + if (location != .Initializer) { + try writer.writeByte('('); + try dg.renderTypecast(writer, ty); + try writer.writeByte(')'); + } - try writer.writeByte('{'); - var empty = true; - for (ty.structFields().values()) |field| { - if (!field.ty.hasRuntimeBits()) continue; + try writer.writeByte('{'); + var empty = true; + for (ty.structFields().values()) |field| { + if (!field.ty.hasRuntimeBits()) continue; - if (!empty) try writer.writeByte(','); - try dg.renderValue(writer, field.ty, val, .Initializer); + if (!empty) try writer.writeByte(','); + try dg.renderValue(writer, field.ty, val, .Initializer); - empty = false; - } - if (empty) try writer.print("{x}", .{try dg.fmtIntLiteral(Type.u8, Value.undef)}); - return writer.writeByte('}'); + empty = false; + } + if (empty) try writer.print("{x}", .{try dg.fmtIntLiteral(Type.u8, Value.undef)}); + return writer.writeByte('}'); + }, + .Packed => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, Value.undef)}), }, .Union => { if (location != .Initializer) { @@ -898,7 +934,7 @@ pub const DeclGen = struct { }, else => { // Fall back to generic implementation. - var arena = std.heap.ArenaAllocator.init(dg.module.gpa); + var arena = std.heap.ArenaAllocator.init(dg.gpa); defer arena.deinit(); const arena_allocator = arena.allocator(); @@ -1024,28 +1060,63 @@ pub const DeclGen = struct { }, else => unreachable, }, - .Struct => { - const field_vals = val.castTag(.aggregate).?.data; + .Struct => switch (ty.containerLayout()) { + .Auto, .Extern => { + const field_vals = val.castTag(.aggregate).?.data; + + if (location != .Initializer) { + try writer.writeByte('('); + try dg.renderTypecast(writer, ty); + try writer.writeByte(')'); + } + + try writer.writeByte('{'); + var empty = true; + for (field_vals) |field_val, field_index| { + const field_ty = ty.structFieldType(field_index); + if (!field_ty.hasRuntimeBits()) continue; + + if (!empty) try writer.writeByte(','); + try dg.renderValue(writer, field_ty, field_val, .Initializer); + + empty = false; + } + if (empty) try writer.print("{}", .{try dg.fmtIntLiteral(Type.u8, Value.zero)}); + try writer.writeByte('}'); + }, + .Packed => { + const field_vals = val.castTag(.aggregate).?.data; + const int_info = ty.intInfo(target); + + var bit_offset_ty_pl = Type.Payload.Bits{ + .base = .{ .tag = .int_unsigned }, + .data = Type.smallestUnsignedBits(int_info.bits - 1), + }; + const bit_offset_ty = Type.initPayload(&bit_offset_ty_pl.base); + + var bit_offset_val_pl: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = 0 }; + const bit_offset_val = Value.initPayload(&bit_offset_val_pl.base); - if (location != .Initializer) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + var empty = true; + for (field_vals) |field_val, index| { + const field_ty = ty.structFieldType(index); + if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; + + if (!empty) try writer.writeAll(" | "); + try writer.writeByte('('); + try dg.renderTypecast(writer, ty); + try writer.writeByte(')'); + try dg.renderValue(writer, field_ty, field_val, .Other); + try writer.writeAll(" << "); + try dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); + + bit_offset_val_pl.data += field_ty.bitSize(target); + empty = false; + } + if (empty) try dg.renderValue(writer, ty, Value.undef, .Initializer); try writer.writeByte(')'); - } - - try writer.writeByte('{'); - var empty = true; - for (field_vals) |field_val, field_index| { - const field_ty = ty.structFieldType(field_index); - if (!field_ty.hasRuntimeBits()) continue; - - if (!empty) try writer.writeByte(','); - try dg.renderValue(writer, field_ty, field_val, .Initializer); - - empty = false; - } - if (empty) try writer.print("{}", .{try dg.fmtIntLiteral(Type.u8, Value.zero)}); - try writer.writeByte('}'); + }, }, .Union => { const union_obj = val.castTag(.@"union").?.data; @@ -1103,11 +1174,12 @@ pub const DeclGen = struct { if (fn_info.cc == .Naked) try w.writeAll("zig_naked "); if (dg.decl.val.castTag(.function)) |func_payload| if (func_payload.data.is_cold) try w.writeAll("zig_cold "); - const ret_ty = fn_info.return_type; - try dg.renderType(w, if (ret_ty.tag() == .noreturn or ret_ty.hasRuntimeBitsIgnoreComptime()) - ret_ty - else - Type.void, kind); + + const target = dg.module.getTarget(); + var ret_buf: LowerFnRetTyBuffer = undefined; + const ret_ty = lowerFnRetTy(fn_info.return_type, &ret_buf, target); + + try dg.renderType(w, ret_ty, kind); try w.writeByte(' '); try dg.renderDeclName(w, dg.decl_index); try w.writeByte('('); @@ -1117,7 +1189,7 @@ pub const DeclGen = struct { if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; if (index > 0) try w.writeAll(", "); const name = CValue{ .arg = index }; - try dg.renderTypeAndName(w, param_type, name, .Const, 0, kind); + try dg.renderTypeAndName(w, param_type, name, .ConstArgument, 0, kind); index += 1; } @@ -1137,10 +1209,13 @@ pub const DeclGen = struct { const fn_info = t.fnInfo(); - try bw.writeAll("typedef "); - try dg.renderType(bw, fn_info.return_type, .Forward); - try bw.writeAll(" (*"); + const target = dg.module.getTarget(); + var ret_buf: LowerFnRetTyBuffer = undefined; + const ret_ty = lowerFnRetTy(fn_info.return_type, &ret_buf, target); + try bw.writeAll("typedef "); + try dg.renderType(bw, ret_ty, .Forward); + try bw.writeAll(" (*"); const name_begin = buffer.items.len; try bw.print("zig_F_{}", .{typeToCIdentifier(t, dg.module)}); const name_end = buffer.items.len; @@ -1315,8 +1390,12 @@ pub const DeclGen = struct { const val = fields.values[i]; if (val.tag() != .unreachable_value) continue; - const field_name = try std.fmt.allocPrint(dg.typedefs.allocator, "field_{d}", .{i}); - defer dg.typedefs.allocator.free(field_name); + 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); try buffer.append(' '); try dg.renderTypeAndName(buffer.writer(), field_ty, .{ .identifier = field_name }, .Mut, 0, .Complete); @@ -1463,7 +1542,10 @@ pub const DeclGen = struct { try bw.print(" zig_A_{}_{d}", .{ typeToCIdentifier(info.elem_type, dg.module), info.len }); const name_end = buffer.items.len; - try bw.print("[{d}];\n", .{if (info.len > 0) info.len else 1}); + const c_len = if (info.len > 0) info.len else 1; + var c_len_pl: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = c_len }; + const c_len_val = Value.initPayload(&c_len_pl.base); + try bw.print("[{}];\n", .{try dg.fmtIntLiteral(Type.usize, c_len_val)}); const rendered = buffer.toOwnedSlice(); errdefer dg.typedefs.allocator.free(rendered); @@ -1487,7 +1569,7 @@ pub const DeclGen = struct { try dg.renderTypeAndName(bw, child_type, .{ .identifier = "payload" }, .Mut, 0, .Complete); try bw.writeAll(";\n "); try dg.renderTypeAndName(bw, Type.bool, .{ .identifier = "is_null" }, .Mut, 0, .Complete); - try bw.writeAll("; } "); + try bw.writeAll(";\n} "); const name_begin = buffer.items.len; try bw.print("zig_Q_{}", .{typeToCIdentifier(child_type, dg.module)}); const name_end = buffer.items.len; @@ -1566,18 +1648,29 @@ pub const DeclGen = struct { try w.writeAll("zig_"); try t.print(w, dg.module); } else { - const info = t.intInfo(target); - const c_bits = toCIntBits(info.bits) orelse - return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); - try w.print("zig_{c}{d}", .{ signAbbrev(info.signedness), c_bits }); + const int_info = t.intInfo(target); + if (toCIntBits(int_info.bits)) |c_bits| + return w.print("zig_{c}{d}", .{ signAbbrev(int_info.signedness), c_bits }) + else if (loweredArrayInfo(t, target)) |array_info| { + assert(array_info.sentinel == null); + var array_pl = Type.Payload.Array{ + .base = .{ .tag = .array }, + .data = .{ .len = array_info.len, .elem_type = array_info.elem_type }, + }; + const array_ty = Type.initPayload(&array_pl.base); + + return dg.renderType(w, array_ty, kind); + } else return dg.fail("C backend: Unable to lower unnamed integer type {}", .{ + t.fmt(dg.module), + }); } }, .Pointer => { - const child_ty = t.childType(); - if (t.isSlice()) { + const ptr_info = t.ptrInfo().data; + if (ptr_info.size == .Slice) { var slice_pl = Type.Payload.ElemType{ .base = .{ .tag = if (t.ptrIsMutable()) .mut_slice else .const_slice }, - .data = child_ty, + .data = ptr_info.pointee_type, }; const slice_ty = Type.initPayload(&slice_pl.base); @@ -1587,14 +1680,22 @@ pub const DeclGen = struct { return w.writeAll(name); } - if (child_ty.zigTypeTag() == .Fn) { - const name = dg.getTypedefName(child_ty) orelse - try dg.renderPtrToFnTypedef(child_ty); + if (ptr_info.pointee_type.zigTypeTag() == .Fn) { + const name = dg.getTypedefName(ptr_info.pointee_type) orelse + try dg.renderPtrToFnTypedef(ptr_info.pointee_type); return w.writeAll(name); } - if (t.isCPtr() and child_ty.eql(Type.u8, dg.module) and + if (ptr_info.host_size != 0) { + var host_pl = Type.Payload.Bits{ + .base = .{ .tag = .int_unsigned }, + .data = ptr_info.host_size * 8, + }; + const host_ty = Type.initPayload(&host_pl.base); + + try dg.renderType(w, host_ty, .Forward); + } else if (t.isCPtr() and ptr_info.pointee_type.eql(Type.u8, dg.module) and (dg.decl.val.tag() == .extern_fn or std.mem.eql(u8, std.mem.span(dg.decl.name), "main"))) { @@ -1603,9 +1704,9 @@ pub const DeclGen = struct { // u8 and i8 produce unsigned char and signed char respectively, // which in C are (not very usefully) different than char. try w.writeAll("char"); - } else try dg.renderType(w, switch (child_ty.tag()) { + } else try dg.renderType(w, switch (ptr_info.pointee_type.tag()) { .anyopaque => Type.void, - else => child_ty, + else => ptr_info.pointee_type, }, .Forward); if (t.isConstPtr()) try w.writeAll(" const"); if (t.isVolatilePtr()) try w.writeAll(" volatile"); @@ -1654,7 +1755,9 @@ pub const DeclGen = struct { return w.writeAll(name); }, - .Struct, .Union => |tag| if (kind == .Complete or t.isTupleOrAnonStruct()) { + .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()) { const name = dg.getTypedefName(t) orelse switch (tag) { .Struct => if (t.isTupleOrAnonStruct()) try dg.renderTupleTypedef(t) @@ -1664,7 +1767,7 @@ pub const DeclGen = struct { else => unreachable, }; - return w.writeAll(name); + try w.writeAll(name); } else { var ptr_pl = Type.Payload.ElemType{ .base = .{ .tag = .single_const_pointer }, @@ -1675,7 +1778,7 @@ pub const DeclGen = struct { const name = dg.getTypedefName(ptr_ty) orelse try dg.renderFwdTypedef(ptr_ty); - return w.writeAll(name); + try w.writeAll(name); }, .Enum => { // For enums, we simply use the integer tag type. @@ -1751,24 +1854,32 @@ pub const DeclGen = struct { ) error{ OutOfMemory, AnalysisFail }!void { var suffix = std.ArrayList(u8).init(dg.gpa); defer suffix.deinit(); + const suffix_writer = suffix.writer(); // Any top-level array types are rendered here as a suffix, which // avoids creating typedefs for every array type + const target = dg.module.getTarget(); var render_ty = ty; - while (render_ty.zigTypeTag() == .Array) { - const sentinel_bit = @boolToInt(render_ty.sentinel() != null); - const c_len = render_ty.arrayLen() + sentinel_bit; - try suffix.writer().print("[{d}]", .{c_len}); - render_ty = render_ty.elemType(); + var depth: u32 = 0; + while (loweredArrayInfo(render_ty, target)) |array_info| { + const c_len = array_info.len + @boolToInt(array_info.sentinel != null); + var c_len_pl: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = c_len }; + const c_len_val = Value.initPayload(&c_len_pl.base); + + try suffix_writer.writeByte('['); + if (mutability == .ConstArgument and depth == 0) try suffix_writer.writeAll("static const "); + try suffix.writer().print("{}]", .{try dg.fmtIntLiteral(Type.usize, c_len_val)}); + render_ty = array_info.elem_type; + depth += 1; } - if (alignment != 0 and alignment > ty.abiAlignment(dg.module.getTarget())) { + if (alignment != 0 and alignment > ty.abiAlignment(target)) { try w.print("zig_align({}) ", .{alignment}); } try dg.renderType(w, render_ty, kind); const const_prefix = switch (mutability) { - .Const => "const ", + .Const, .ConstArgument => "const ", .Mut => "", }; try w.print(" {s}", .{const_prefix}); @@ -1935,7 +2046,7 @@ pub const DeclGen = struct { } else if (decl.isExtern()) { return writer.writeAll(mem.sliceTo(decl.name, 0)); } else { - const gpa = dg.module.gpa; + const gpa = dg.gpa; const name = try decl.getFullyQualifiedName(dg.module); defer gpa.free(name); return writer.print("{ }", .{fmtIdent(name)}); @@ -2298,8 +2409,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .trunc => try airTrunc(f, inst), .bool_to_int => try airBoolToInt(f, inst), .load => try airLoad(f, inst), - .ret => try airRet(f, inst), - .ret_load => try airRetLoad(f, inst), + .ret => try airRet(f, inst, false), + .ret_load => try airRet(f, inst, true), .store => try airStore(f, inst), .loop => try airLoop(f, inst), .cond_br => try airCondBr(f, inst), @@ -2588,14 +2699,15 @@ fn airArg(f: *Function) CValue { fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; - const is_volatile = f.air.typeOf(ty_op.operand).isVolatilePtr(); + const ptr_info = f.air.typeOf(ty_op.operand).ptrInfo().data; const inst_ty = f.air.typeOfIndex(inst); if (!inst_ty.hasRuntimeBitsIgnoreComptime() or - !is_volatile and f.liveness.isUnused(inst)) + !ptr_info.@"volatile" and f.liveness.isUnused(inst)) return CValue.none; - const is_array = inst_ty.zigTypeTag() == .Array; + const target = f.object.dg.module.getTarget(); + const is_array = lowersToArray(inst_ty, target); const operand = try f.resolveInst(ty_op.operand); const writer = f.object.writer(); @@ -2612,26 +2724,88 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, operand, .FunctionArgument); try writer.writeAll(", sizeof("); try f.renderTypecast(writer, inst_ty); - try writer.writeAll("));\n"); + try writer.writeAll("))"); + } else if (ptr_info.host_size != 0) { + var host_pl = Type.Payload.Bits{ + .base = .{ .tag = .int_unsigned }, + .data = ptr_info.host_size * 8, + }; + const host_ty = Type.initPayload(&host_pl.base); + + var bit_offset_ty_pl = Type.Payload.Bits{ + .base = .{ .tag = .int_unsigned }, + .data = Type.smallestUnsignedBits(host_pl.data - 1), + }; + const bit_offset_ty = Type.initPayload(&bit_offset_ty_pl.base); + + var bit_offset_val_pl: Value.Payload.U64 = .{ + .base = .{ .tag = .int_u64 }, + .data = ptr_info.bit_offset, + }; + const bit_offset_val = Value.initPayload(&bit_offset_val_pl.base); + + var field_pl = Type.Payload.Bits{ + .base = .{ .tag = .int_unsigned }, + .data = @intCast(u16, inst_ty.bitSize(target)), + }; + const field_ty = Type.initPayload(&field_pl.base); + + try writer.writeAll(" = ("); + try f.renderTypecast(writer, inst_ty); + try writer.writeAll(")zig_wrap_"); + try f.renderTypeForBuiltinFnName(writer, field_ty); + try writer.writeAll("(("); + try f.renderTypecast(writer, field_ty); + try writer.writeAll(")zig_shr_"); + try f.renderTypeForBuiltinFnName(writer, host_ty); + try writer.writeByte('('); + try f.writeCValueDeref(writer, operand); + try writer.print(", {})", .{try f.fmtIntLiteral(bit_offset_ty, bit_offset_val)}); + try f.renderBuiltinInfo(writer, field_ty, .Bits); + try writer.writeByte(')'); } else { try writer.writeAll(" = "); try f.writeCValueDeref(writer, operand); - try writer.writeAll(";\n"); } + try writer.writeAll(";\n"); return local; } -fn airRet(f: *Function, inst: Air.Inst.Index) !CValue { +fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { const un_op = f.air.instructions.items(.data)[inst].un_op; const writer = f.object.writer(); - const ret_ty = f.air.typeOf(un_op); - if (ret_ty.isFnOrHasRuntimeBitsIgnoreComptime()) { + const target = f.object.dg.module.getTarget(); + const op_ty = f.air.typeOf(un_op); + const ret_ty = if (is_ptr) op_ty.childType() else op_ty; + var lowered_ret_buf: LowerFnRetTyBuffer = undefined; + const lowered_ret_ty = lowerFnRetTy(ret_ty, &lowered_ret_buf, target); + + if (lowered_ret_ty.hasRuntimeBitsIgnoreComptime()) { + var deref = is_ptr; const operand = try f.resolveInst(un_op); + const ret_val = if (lowersToArray(ret_ty, target)) ret_val: { + 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 writer.writeAll(", "); + if (deref) + try f.writeCValueDeref(writer, operand) + else + try f.writeCValue(writer, operand, .FunctionArgument); + deref = false; + try writer.writeAll(", sizeof("); + try f.renderTypecast(writer, ret_ty); + try writer.writeAll("));\n"); + break :ret_val array_local; + } else operand; + try writer.writeAll("return "); - try f.writeCValue(writer, operand, .Other); + if (deref) + try f.writeCValueDeref(writer, ret_val) + else + try f.writeCValue(writer, ret_val, .Other); try writer.writeAll(";\n"); - } else if (ret_ty.isError()) { - try writer.writeAll("return 0;"); } else if (f.object.dg.decl.ty.fnCallingConvention() != .Naked) { // Not even allowed to return void in a naked function. try writer.writeAll("return;\n"); @@ -2639,24 +2813,6 @@ fn airRet(f: *Function, inst: Air.Inst.Index) !CValue { return CValue.none; } -fn airRetLoad(f: *Function, inst: Air.Inst.Index) !CValue { - const un_op = f.air.instructions.items(.data)[inst].un_op; - const writer = f.object.writer(); - const ptr_ty = f.air.typeOf(un_op); - const ret_ty = ptr_ty.childType(); - if (ret_ty.isFnOrHasRuntimeBitsIgnoreComptime()) { - const ptr = try f.resolveInst(un_op); - try writer.writeAll("return *"); - try f.writeCValue(writer, ptr, .Other); - try writer.writeAll(";\n"); - } else if (ret_ty.isError()) { - try writer.writeAll("return 0;\n"); - } else { - try writer.writeAll("return;\n"); - } - return CValue.none; -} - fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; @@ -2696,12 +2852,12 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(";\n"); } else switch (dest_int_info.signedness) { .unsigned => { - var arena = std.heap.ArenaAllocator.init(f.object.dg.module.gpa); + var arena = std.heap.ArenaAllocator.init(f.object.dg.gpa); defer arena.deinit(); - const expected_contents = union { u: Value.Payload.U64, i: Value.Payload.I64 }; - var stack align(@alignOf(expected_contents)) = - std.heap.stackFallback(@sizeOf(expected_contents), arena.allocator()); + const ExpectedContents = union { u: Value.Payload.U64, i: Value.Payload.I64 }; + var stack align(@alignOf(ExpectedContents)) = + std.heap.stackFallback(@sizeOf(ExpectedContents), arena.allocator()); const mask_val = try inst_ty.maxInt(stack.get(), target); @@ -2756,10 +2912,11 @@ fn airStoreUndefined(f: *Function, lhs_child_ty: Type, dest_ptr: CValue) !CValue fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { // *a = b; const bin_op = f.air.instructions.items(.data)[inst].bin_op; - const lhs_child_ty = f.air.typeOf(bin_op.lhs).childType(); - if (!lhs_child_ty.hasRuntimeBitsIgnoreComptime()) return CValue.none; + const ptr_info = f.air.typeOf(bin_op.lhs).ptrInfo().data; + if (!ptr_info.pointee_type.hasRuntimeBitsIgnoreComptime()) return CValue.none; - const dest_ptr = try f.resolveInst(bin_op.lhs); + const ptr_val = try f.resolveInst(bin_op.lhs); + const src_ty = f.air.typeOf(bin_op.rhs); const src_val = try f.resolveInst(bin_op.rhs); // TODO Sema should emit a different instruction when the store should @@ -2767,20 +2924,20 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { const src_val_is_undefined = if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false; if (src_val_is_undefined) - return try airStoreUndefined(f, lhs_child_ty, dest_ptr); + return try airStoreUndefined(f, ptr_info.pointee_type, ptr_val); + const target = f.object.dg.module.getTarget(); const writer = f.object.writer(); - if (lhs_child_ty.zigTypeTag() == .Array) { + if (lowersToArray(ptr_info.pointee_type, target)) { // For this memcpy to safely work we need the rhs to have the same // underlying type as the lhs (i.e. they must both be arrays of the same underlying type). - const rhs_type = f.air.typeOf(bin_op.rhs); - assert(rhs_type.eql(lhs_child_ty, f.object.dg.module)); + assert(src_ty.eql(ptr_info.pointee_type, f.object.dg.module)); // If the source is a constant, writeCValue will emit a brace initialization // so work around this by initializing into new local. // TODO this should be done by manually initializing elements of the dest array const array_src = if (src_val == .constant) blk: { - const new_local = try f.allocLocal(rhs_type, .Const); + const new_local = try f.allocLocal(src_ty, .Const); try writer.writeAll(" = "); try f.writeCValue(writer, src_val, .Initializer); try writer.writeAll(";\n"); @@ -2789,18 +2946,69 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { } else src_val; try writer.writeAll("memcpy("); - try f.writeCValue(writer, dest_ptr, .FunctionArgument); + try f.writeCValue(writer, ptr_val, .FunctionArgument); try writer.writeAll(", "); try f.writeCValue(writer, array_src, .FunctionArgument); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, lhs_child_ty); - try writer.writeAll("));\n"); + try f.renderTypecast(writer, src_ty); + try writer.writeAll("))"); + } else if (ptr_info.host_size != 0) { + const host_bits = ptr_info.host_size * 8; + var host_pl = Type.Payload.Bits{ .base = .{ .tag = .int_unsigned }, .data = host_bits }; + const host_ty = Type.initPayload(&host_pl.base); + + var bit_offset_ty_pl = Type.Payload.Bits{ + .base = .{ .tag = .int_unsigned }, + .data = Type.smallestUnsignedBits(host_bits - 1), + }; + const bit_offset_ty = Type.initPayload(&bit_offset_ty_pl.base); + + var bit_offset_val_pl: Value.Payload.U64 = .{ + .base = .{ .tag = .int_u64 }, + .data = ptr_info.bit_offset, + }; + const bit_offset_val = Value.initPayload(&bit_offset_val_pl.base); + + const src_bits = src_ty.bitSize(target); + + const Limb = std.math.big.Limb; + const ExpectedContents = [BigInt.Managed.default_capacity]Limb; + var stack align(@alignOf(ExpectedContents)) = + std.heap.stackFallback(@sizeOf(ExpectedContents), f.object.dg.gpa); + + var mask = try BigInt.Managed.initCapacity(stack.get(), BigInt.calcTwosCompLimbCount(host_bits)); + defer mask.deinit(); + + try mask.setTwosCompIntLimit(.max, .unsigned, @intCast(usize, src_bits)); + try mask.shiftLeft(&mask, ptr_info.bit_offset); + try mask.bitNotWrap(&mask, .unsigned, host_bits); + + var mask_pl = Value.Payload.BigInt{ + .base = .{ .tag = .int_big_positive }, + .data = mask.limbs[0..mask.len()], + }; + const mask_val = Value.initPayload(&mask_pl.base); + + try f.writeCValueDeref(writer, ptr_val); + try writer.writeAll(" = zig_or_"); + try f.renderTypeForBuiltinFnName(writer, host_ty); + try writer.writeAll("(zig_and_"); + try f.renderTypeForBuiltinFnName(writer, host_ty); + try writer.writeByte('('); + try f.writeCValueDeref(writer, ptr_val); + try writer.print(", {x}), zig_shl_", .{try f.fmtIntLiteral(host_ty, mask_val)}); + try f.renderTypeForBuiltinFnName(writer, host_ty); + try writer.writeAll("(("); + try f.renderTypecast(writer, host_ty); + try writer.writeByte(')'); + try f.writeCValue(writer, src_val, .Other); + try writer.print(", {}))", .{try f.fmtIntLiteral(bit_offset_ty, bit_offset_val)}); } else { - try f.writeCValueDeref(writer, dest_ptr); + try f.writeCValueDeref(writer, ptr_val); try writer.writeAll(" = "); try f.writeCValue(writer, src_val, .Other); - try writer.writeAll(";\n"); } + try writer.writeAll(";\n"); return CValue.none; } @@ -2959,7 +3167,9 @@ fn airEquality( } try f.writeCValue(writer, lhs, .Other); + try writer.writeByte(' '); try writer.writeAll(eq_op_str); + try writer.writeByte(' '); try f.writeCValue(writer, rhs, .Other); try writer.writeAll(";\n"); @@ -3079,17 +3289,22 @@ fn airCall( }; const writer = f.object.writer(); - const result_local: CValue = r: { - if (!loweredFnRetTyHasBits(fn_ty)) { - break :r .none; - } else if (f.liveness.isUnused(inst)) { - try writer.print("(void)", .{}); - break :r .none; - } else { - const local = try f.allocLocal(fn_ty.fnReturnType(), .Const); - try writer.writeAll(" = "); - break :r local; - } + const target = f.object.dg.module.getTarget(); + const ret_ty = fn_ty.fnReturnType(); + var lowered_ret_buf: LowerFnRetTyBuffer = undefined; + const lowered_ret_ty = lowerFnRetTy(ret_ty, &lowered_ret_buf, target); + + const result_local: CValue = if (!lowered_ret_ty.hasRuntimeBitsIgnoreComptime()) + .none + else if (f.liveness.isUnused(inst)) r: { + try writer.writeByte('('); + try f.renderTypecast(writer, Type.void); + try writer.writeByte(')'); + break :r .none; + } else r: { + const local = try f.allocLocal(lowered_ret_ty, .Const); + try writer.writeAll(" = "); + break :r local; }; var is_extern = false; @@ -3138,7 +3353,19 @@ fn airCall( args_written += 1; } try writer.writeAll(");\n"); - return result_local; + + if (result_local == .none or !lowersToArray(ret_ty, target)) return result_local; + + const array_local = try f.allocLocal(ret_ty, .Mut); + try writer.writeAll(";\n"); + try writer.writeAll("memcpy("); + try f.writeCValue(writer, array_local, .FunctionArgument); + try writer.writeAll(", "); + try f.writeCValueMember(writer, result_local, .{ .identifier = "array" }); + try writer.writeAll(", sizeof("); + try f.renderTypecast(writer, ret_ty); + try writer.writeAll("));\n"); + return array_local; } fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue { @@ -3263,7 +3490,8 @@ fn lowerTry( } } - const is_array = payload_ty.zigTypeTag() == .Array; + const target = f.object.dg.module.getTarget(); + const is_array = lowersToArray(payload_ty, target); const local = try f.allocLocal(result_ty, if (is_array) .Mut else .Const); if (is_array) { try writer.writeAll(";\n"); @@ -3814,51 +4042,74 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struct_ptr: CValue, index: u32) !CValue { const writer = f.object.writer(); + const field_ptr_ty = f.air.typeOfIndex(inst); + const field_ptr_info = field_ptr_ty.ptrInfo(); const struct_ty = struct_ptr_ty.elemType(); - var field_name: []const u8 = undefined; - var field_val_ty: Type = undefined; - - var field_name_buf: []const u8 = ""; - defer f.object.dg.gpa.free(field_name_buf); - switch (struct_ty.tag()) { - .@"struct" => { - const fields = struct_ty.structFields(); - field_name = fields.keys()[index]; - field_val_ty = fields.values()[index].ty; - }, - .@"union", .union_safety_tagged, .union_tagged => { - const fields = struct_ty.unionFields(); - field_name = fields.keys()[index]; - field_val_ty = fields.values()[index].ty; - }, - .tuple, .anon_struct => { - const tuple = struct_ty.tupleFields(); - if (tuple.values[index].tag() != .unreachable_value) return CValue.none; - - field_name_buf = try std.fmt.allocPrint(f.object.dg.gpa, "field_{d}", .{index}); - field_name = field_name_buf; - field_val_ty = tuple.types[index]; - }, - else => unreachable, - } - const payload = if (struct_ty.tag() == .union_tagged or struct_ty.tag() == .union_safety_tagged) "payload." else ""; + const field_ty = struct_ty.structFieldType(index); // Ensure complete type definition is visible before accessing fields. try f.renderType(std.io.null_writer, struct_ty); - const inst_ty = f.air.typeOfIndex(inst); - const local = try f.allocLocal(inst_ty, .Const); - if (field_val_ty.hasRuntimeBitsIgnoreComptime()) { - try writer.writeAll(" = &"); - try f.writeCValueDeref(writer, struct_ptr); - try writer.print(".{s}{ };\n", .{ payload, fmtIdent(field_name) }); - } else { - try writer.writeAll(" = ("); - try f.renderTypecast(writer, inst_ty); - try writer.writeByte(')'); - try f.writeCValue(writer, struct_ptr, .Other); - try writer.writeAll(";\n"); - } + const local = try f.allocLocal(field_ptr_ty, .Const); + try writer.writeAll(" = ("); + 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, + }; + + var field_name_buf: []const u8 = &.{}; + defer f.object.dg.gpa.free(field_name_buf); + const field_name: ?[]const u8 = switch (struct_ty.tag()) { + .@"struct" => switch (struct_ty.containerLayout()) { + .Auto, .Extern => struct_ty.structFieldName(index), + .Packed => if (field_ptr_info.data.host_size == 0) { + const target = f.object.dg.module.getTarget(); + + const byte_offset = struct_ty.packedStructFieldByteOffset(index, target); + var byte_offset_pl = Value.Payload.U64{ + .base = .{ .tag = .int_u64 }, + .data = byte_offset, + }; + const byte_offset_val = Value.initPayload(&byte_offset_pl.base); + + var ptr_u8_pl = field_ptr_info; + ptr_u8_pl.data.pointee_type = Type.u8; + const ptr_u8_ty = Type.initPayload(&ptr_u8_pl.base); + + try writer.writeAll("&(("); + try f.renderTypecast(writer, ptr_u8_ty); + try writer.writeByte(')'); + try f.writeCValue(writer, struct_ptr, .Other); + try writer.print(")[{}];\n", .{try f.fmtIntLiteral(Type.usize, byte_offset_val)}); + return local; + } else null, + }, + .@"union", .union_safety_tagged, .union_tagged => struct_ty.unionFields().keys()[index], + .tuple, .anon_struct => |tag| 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; + }, + else => unreachable, + }; + + if (field_ty.hasRuntimeBitsIgnoreComptime()) { + try writer.writeByte('&'); + if (extra_name orelse field_name) |name| + try f.writeCValueDerefMember(writer, struct_ptr, .{ .identifier = 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; } @@ -3868,18 +4119,84 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const extra = f.air.extraData(Air.StructField, ty_pl.payload).data; - const writer = f.object.writer(); + 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); + const writer = f.object.writer(); + + // 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()) { - .@"struct" => struct_ty.structFields().keys()[extra.field_index], + .@"struct" => switch (struct_ty.containerLayout()) { + .Auto, .Extern => struct_ty.structFieldName(extra.field_index), + .Packed => { + const struct_obj = struct_ty.castTag(.@"struct").?.data; + const int_info = struct_ty.intInfo(target); + + var bit_offset_ty_pl = Type.Payload.Bits{ + .base = .{ .tag = .int_unsigned }, + .data = Type.smallestUnsignedBits(int_info.bits - 1), + }; + const bit_offset_ty = Type.initPayload(&bit_offset_ty_pl.base); + + var bit_offset_val_pl: Value.Payload.U64 = .{ + .base = .{ .tag = .int_u64 }, + .data = struct_obj.packedFieldBitOffset(target, extra.field_index), + }; + const bit_offset_val = Value.initPayload(&bit_offset_val_pl.base); + + const field_int_signedness = if (inst_ty.isAbiInt()) + inst_ty.intInfo(target).signedness + else + .unsigned; + var field_int_pl = Type.Payload.Bits{ + .base = .{ .tag = switch (field_int_signedness) { + .unsigned => .int_unsigned, + .signed => .int_signed, + } }, + .data = @intCast(u16, inst_ty.bitSize(target)), + }; + const field_int_ty = Type.initPayload(&field_int_pl.base); + + const temp_local = try f.allocLocal(field_int_ty, .Const); + try writer.writeAll(" = zig_wrap_"); + try f.renderTypeForBuiltinFnName(writer, field_int_ty); + try writer.writeAll("(("); + try f.renderTypecast(writer, field_int_ty); + try writer.writeAll(")zig_shr_"); + try f.renderTypeForBuiltinFnName(writer, struct_ty); + try writer.writeByte('('); + try f.writeCValue(writer, struct_byval, .Other); + try writer.writeAll(", "); + try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); + try writer.writeByte(')'); + try f.renderBuiltinInfo(writer, field_int_ty, .Bits); + try writer.writeAll(");\n"); + if (inst_ty.eql(field_int_ty, f.object.dg.module)) return temp_local; + + const local = try f.allocLocal(inst_ty, .Mut); + try writer.writeAll(";\n"); + try writer.writeAll("memcpy("); + try f.writeCValue(writer, .{ .local_ref = local.local }, .FunctionArgument); + try writer.writeAll(", "); + try f.writeCValue(writer, .{ .local_ref = temp_local.local }, .FunctionArgument); + try writer.writeAll(", sizeof("); + try f.renderTypecast(writer, inst_ty); + try writer.writeAll("));\n"); + return local; + }, + }, .@"union", .union_safety_tagged, .union_tagged => struct_ty.unionFields().keys()[extra.field_index], - .tuple, .anon_struct => blk: { + .tuple, .anon_struct => |tag| 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; }, @@ -3887,13 +4204,8 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { }; const payload = if (struct_ty.tag() == .union_tagged or struct_ty.tag() == .union_safety_tagged) "payload." else ""; - // Ensure complete type definition is visible before accessing fields. - try f.renderType(std.io.null_writer, struct_ty); - - const inst_ty = f.air.typeOfIndex(inst); - const is_array = inst_ty.zigTypeTag() == .Array; + const is_array = lowersToArray(inst_ty, target); const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const); - if (is_array) { try writer.writeAll(";\n"); try writer.writeAll("memcpy("); @@ -4067,7 +4379,8 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.air.typeOfIndex(inst); const payload_ty = inst_ty.errorUnionPayload(); const error_ty = inst_ty.errorUnionSet(); - const is_array = payload_ty.zigTypeTag() == .Array; + 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); try writer.writeAll(" = { .payload = "); try f.writeCValue(writer, if (is_array) CValue{ .undef = payload_ty } else operand, .Initializer); @@ -4141,7 +4454,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { } else { try writer.writeAll("&("); try f.writeCValueDeref(writer, operand); - try writer.writeAll(")[0]"); + try writer.print(")[{}]", .{try f.fmtIntLiteral(Type.usize, Value.zero)}); } var len_pl: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = array_len }; @@ -4571,16 +4884,18 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const len = @intCast(usize, inst_ty.arrayLen()); const elements = @ptrCast([]const Air.Inst.Ref, f.air.extra[ty_pl.payload..][0..len]); + const target = f.object.dg.module.getTarget(); const mutability: Mutability = for (elements) |element| { - if (f.air.typeOf(element).zigTypeTag() == .Array) break .Mut; + if (lowersToArray(f.air.typeOf(element), target)) break .Mut; } else .Const; const writer = f.object.writer(); const local = try f.allocLocal(inst_ty, mutability); - try writer.writeAll(" = {"); + try writer.writeAll(" = "); switch (inst_ty.zigTypeTag()) { .Array => { const elem_ty = inst_ty.childType(); + try writer.writeByte('{'); var empty = true; for (elements) |element| { if (!empty) try writer.writeAll(", "); @@ -4595,49 +4910,99 @@ 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"); }, - .Struct => { - var empty = true; - for (elements) |element, index| { - if (inst_ty.structFieldValueComptime(index)) |_| continue; + .Struct => switch (inst_ty.containerLayout()) { + .Auto, .Extern => { + try writer.writeByte('{'); + var empty = true; + for (elements) |element, index| { + if (inst_ty.structFieldValueComptime(index)) |_| continue; - if (!empty) try writer.writeAll(", "); - if (!inst_ty.isTupleOrAnonStruct()) { - try writer.print(".{ } = ", .{fmtIdent(inst_ty.structFieldName(index))}); + if (!empty) try writer.writeAll(", "); + if (!inst_ty.isTupleOrAnonStruct()) { + try writer.print(".{ } = ", .{fmtIdent(inst_ty.structFieldName(index))}); + } + + const element_ty = f.air.typeOf(element); + try f.writeCValue(writer, switch (element_ty.zigTypeTag()) { + .Array => CValue{ .undef = element_ty }, + else => try f.resolveInst(element), + }, .Initializer); + empty = false; } + if (empty) try writer.print("{}", .{try f.fmtIntLiteral(Type.u8, Value.zero)}); + try writer.writeAll("};\n"); - const element_ty = f.air.typeOf(element); - try f.writeCValue(writer, switch (element_ty.zigTypeTag()) { - .Array => CValue{ .undef = element_ty }, - else => try f.resolveInst(element), - }, .Initializer); - empty = false; - } - if (empty) try writer.print("{}", .{try f.fmtIntLiteral(Type.u8, Value.zero)}); - try writer.writeAll("};\n"); + for (elements) |element, index| { + if (inst_ty.structFieldValueComptime(index)) |_| continue; - for (elements) |element, index| { - if (inst_ty.structFieldValueComptime(index)) |_| continue; + const element_ty = f.air.typeOf(element); + if (element_ty.zigTypeTag() != .Array) 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); - var field_name_storage: ?[]u8 = null; - defer if (field_name_storage) |storage| f.object.dg.gpa.free(storage); - const field_name = if (inst_ty.isTupleOrAnonStruct()) field_name: { - const name = try std.fmt.allocPrint(f.object.dg.gpa, "field_{d}", .{index}); - field_name_storage = name; - break :field_name name; - } else 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.writeCValue(writer, try f.resolveInst(element), .FunctionArgument); + try writer.writeAll(", sizeof("); + try f.renderTypecast(writer, element_ty); + try writer.writeAll("));\n"); + } + }, + .Packed => { + const int_info = inst_ty.intInfo(target); + var bit_offset_ty_pl = Type.Payload.Bits{ + .base = .{ .tag = .int_unsigned }, + .data = Type.smallestUnsignedBits(int_info.bits - 1), + }; + const bit_offset_ty = Type.initPayload(&bit_offset_ty_pl.base); + + var bit_offset_val_pl: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = 0 }; + const bit_offset_val = Value.initPayload(&bit_offset_val_pl.base); + + var empty = true; + for (elements) |_, index| { + const field_ty = inst_ty.structFieldType(index); + if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; + + if (!empty) { + try writer.writeAll("zig_or_"); + try f.renderTypeForBuiltinFnName(writer, inst_ty); + try writer.writeByte('('); + } + empty = false; + } + empty = true; + for (elements) |element, index| { + const field_ty = inst_ty.structFieldType(index); + if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; + + if (!empty) try writer.writeAll(", "); + try writer.writeAll("zig_shlw_"); + try f.renderTypeForBuiltinFnName(writer, inst_ty); + try writer.writeAll("(("); + try f.renderTypecast(writer, inst_ty); + try writer.writeByte(')'); + try f.writeCValue(writer, try f.resolveInst(element), .Other); + try writer.writeAll(", "); + try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); + try f.renderBuiltinInfo(writer, inst_ty, .Bits); + try writer.writeByte(')'); + if (!empty) try writer.writeByte(')'); + + bit_offset_val_pl.data += field_ty.bitSize(target); + empty = false; + } + if (empty) try f.writeCValue(writer, .{ .undef = inst_ty }, .Initializer); try writer.writeAll(";\n"); - try writer.writeAll("memcpy("); - try f.writeCValue(writer, local, .Other); - try writer.print(".{ }, ", .{fmtIdent(field_name)}); - try f.writeCValue(writer, try f.resolveInst(element), .FunctionArgument); - try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, element_ty); - try writer.writeAll("));\n"); - } + }, }, .Vector => return f.fail("TODO: C backend: implement airAggregateInit for vectors", .{}), else => unreachable, @@ -4947,7 +5312,7 @@ fn formatIntLiteral( const int_info = data.ty.intInfo(target); const Limb = std.math.big.Limb; - const expected_contents = struct { + const ExpectedContents = struct { const base = 10; const limbs_count_128 = BigInt.calcTwosCompLimbCount(128); const expected_needed_limbs_count = BigInt.calcToStringLimbsBufferLen(limbs_count_128, base); @@ -4959,8 +5324,8 @@ fn formatIntLiteral( undef_limbs: [limbs_count_128]Limb, wrap_limbs: [limbs_count_128]Limb, }; - var stack align(@alignOf(expected_contents)) = - std.heap.stackFallback(@sizeOf(expected_contents), data.mod.gpa); + var stack align(@alignOf(ExpectedContents)) = + std.heap.stackFallback(@sizeOf(ExpectedContents), data.mod.gpa); const allocator = stack.get(); var undef_limbs: []Limb = &.{}; @@ -5092,18 +5457,60 @@ fn formatIntLiteral( } } -fn loweredFnRetTyHasBits(fn_ty: Type) bool { - const ret_ty = fn_ty.fnReturnType(); - if (ret_ty.hasRuntimeBitsIgnoreComptime()) { - return true; - } - if (ret_ty.isError()) { - return true; - } - return false; -} - fn isByRef(ty: Type) bool { _ = ty; return false; } + +const LowerFnRetTyBuffer = struct { + const names = [1][]const u8{"array"}; + types: [1]Type, + values: [1]Value, + payload: Type.Payload.AnonStruct, +}; +fn lowerFnRetTy(ret_ty: Type, buffer: *LowerFnRetTyBuffer, target: std.Target) Type { + if (ret_ty.zigTypeTag() == .NoReturn) return Type.initTag(.noreturn); + + if (lowersToArray(ret_ty, target)) { + 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, + } }; + return Type.initPayload(&buffer.payload.base); + } + + return if (ret_ty.hasRuntimeBitsIgnoreComptime()) ret_ty else Type.void; +} + +fn lowersToArray(ty: Type, target: std.Target) bool { + return switch (ty.zigTypeTag()) { + .Array => return true, + else => return ty.isAbiInt() and toCIntBits(@intCast(u32, ty.bitSize(target))) == null, + }; +} + +fn loweredArrayInfo(ty: Type, target: std.Target) ?Type.ArrayInfo { + if (!lowersToArray(ty, target)) return null; + + switch (ty.zigTypeTag()) { + .Array => return ty.arrayInfo(), + else => { + const abi_size = ty.abiSize(target); + const abi_align = ty.abiAlignment(target); + return Type.ArrayInfo{ + .elem_type = switch (abi_align) { + 1 => Type.u8, + 2 => Type.u16, + 4 => Type.u32, + 8 => Type.u64, + 16 => Type.initTag(.u128), + else => unreachable, + }, + .len = @divExact(abi_size, abi_align), + }; + }, + } +} diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig index e35b037749..f5db19789d 100644 --- a/test/behavior/bitcast.zig +++ b/test/behavior/bitcast.zig @@ -108,7 +108,6 @@ test "@bitCast packed structs at runtime and comptime" { return error.SkipZigTest; } if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; @@ -167,7 +166,6 @@ test "@bitCast extern structs at runtime and comptime" { test "bitcast packed struct to integer and back" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 82bfee7e1c..7e46633172 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -718,7 +718,6 @@ test "*align(1) u16 is the same as *align(1:0:2) u16" { } test "array concatenation of function calls" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; @@ -727,7 +726,6 @@ test "array concatenation of function calls" { } test "array multiplication of function calls" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index dd6aa7549d..552a5fedf3 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -414,7 +414,6 @@ test "load pointer from packed struct" { } test "@ptrToInt on a packed struct field" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; @@ -448,6 +447,7 @@ test "optional pointer in packed struct" { } test "nested packed struct field access test" { + 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/struct.zig b/test/behavior/struct.zig index a6faee9858..66bffdacff 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -505,7 +505,6 @@ test "packed struct fields are ordered from LSB to MSB" { } if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO 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_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1301,7 +1300,6 @@ test "packed struct aggregate init" { } 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_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/undefined.zig b/test/behavior/undefined.zig index 52cde0057e..945d820a6b 100644 --- a/test/behavior/undefined.zig +++ b/test/behavior/undefined.zig @@ -15,7 +15,6 @@ const static_array = initStaticArray(); test "init static array to undefined" { // This test causes `initStaticArray()` to be codegen'd, and the // C backend does not yet support returning arrays, so it fails - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; From e470cf361fb190863341c8859eccf9edda6d122c Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 24 Oct 2022 11:32:31 -0400 Subject: [PATCH 40/47] cbe: update test cases --- lib/include/zig.h | 1 + lib/std/start.zig | 2 +- src/test.zig | 1 + test/stage2/cbe.zig | 22 +++++++++++----------- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/include/zig.h b/lib/include/zig.h index 4ac6537188..0d9e1ab3c2 100644 --- a/lib/include/zig.h +++ b/lib/include/zig.h @@ -3,6 +3,7 @@ #define __STDC_WANT_IEC_60559_TYPES_EXT__ #include #include +#include #include #if defined(__has_builtin) diff --git a/lib/std/start.zig b/lib/std/start.zig index cc8a92d7b6..14903b0bc9 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -506,7 +506,7 @@ fn main(c_argc: c_int, c_argv: [*c][*c]u8, c_envp: [*c][*c]u8) callconv(.C) c_in } fn mainWithoutEnv(c_argc: c_int, c_argv: [*c][*c]u8) callconv(.C) c_int { - std.os.argv = c_argv[0..@intCast(usize, c_argc)]; + std.os.argv = @ptrCast([*][*:0]u8, c_argv)[0..@intCast(usize, c_argc)]; return @call(.{ .modifier = .always_inline }, callMain, .{}); } diff --git a/src/test.zig b/src/test.zig index 0c1ee4c9ec..41e5f23025 100644 --- a/src/test.zig +++ b/src/test.zig @@ -791,6 +791,7 @@ pub const TestContext = struct { .updates = std.ArrayList(Update).init(ctx.cases.allocator), .output_mode = .Exe, .files = std.ArrayList(File).init(ctx.arena), + .link_libc = true, }) catch @panic("out of memory"); return &ctx.cases.items[ctx.cases.items.len - 1]; } diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index d22fdea866..479f3f781b 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -951,7 +951,7 @@ pub fn addCases(ctx: *TestContext) !void { ctx.h("simple header", linux_x64, \\export fn start() void{} , - \\ZIG_EXTERN_C void start(void); + \\zig_extern_c zig_void start(zig_void); \\ ); ctx.h("header with single param function", linux_x64, @@ -959,7 +959,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ _ = a; \\} , - \\ZIG_EXTERN_C void start(uint8_t a0); + \\zig_extern_c zig_void start(zig_u8 const a0); \\ ); ctx.h("header with multiple param function", linux_x64, @@ -967,25 +967,25 @@ pub fn addCases(ctx: *TestContext) !void { \\ _ = a; _ = b; _ = c; \\} , - \\ZIG_EXTERN_C void start(uint8_t a0, uint8_t a1, uint8_t a2); + \\zig_extern_c zig_void start(zig_u8 const a0, zig_u8 const a1, zig_u8 const a2); \\ ); ctx.h("header with u32 param function", linux_x64, \\export fn start(a: u32) void{ _ = a; } , - \\ZIG_EXTERN_C void start(uint32_t a0); + \\zig_extern_c zig_void start(zig_u32 const a0); \\ ); ctx.h("header with usize param function", linux_x64, \\export fn start(a: usize) void{ _ = a; } , - \\ZIG_EXTERN_C void start(uintptr_t a0); + \\zig_extern_c zig_void start(zig_usize const a0); \\ ); ctx.h("header with bool param function", linux_x64, \\export fn start(a: bool) void{_ = a;} , - \\ZIG_EXTERN_C void start(bool a0); + \\zig_extern_c zig_void start(zig_bool const a0); \\ ); ctx.h("header with noreturn function", linux_x64, @@ -993,7 +993,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ unreachable; \\} , - \\ZIG_EXTERN_C zig_noreturn void start(void); + \\zig_extern_c zig_noreturn start(zig_void); \\ ); ctx.h("header with multiple functions", linux_x64, @@ -1001,15 +1001,15 @@ pub fn addCases(ctx: *TestContext) !void { \\export fn b() void{} \\export fn c() void{} , - \\ZIG_EXTERN_C void a(void); - \\ZIG_EXTERN_C void b(void); - \\ZIG_EXTERN_C void c(void); + \\zig_extern_c zig_void a(zig_void); + \\zig_extern_c zig_void b(zig_void); + \\zig_extern_c zig_void c(zig_void); \\ ); ctx.h("header with multiple includes", linux_x64, \\export fn start(a: u32, b: usize) void{ _ = a; _ = b; } , - \\ZIG_EXTERN_C void start(uint32_t a0, uintptr_t a1); + \\zig_extern_c zig_void start(zig_u32 const a0, zig_usize const a1); \\ ); } From 361035fe7a74fed71303a92d15465daa647ac83e Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 24 Oct 2022 20:53:55 -0400 Subject: [PATCH 41/47] cbe: implement cmp_lt_errors_len --- src/codegen/c.zig | 18 ++++++++++++++++-- test/behavior/cast.zig | 1 - 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 8052c8116b..897af5ef65 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -2078,7 +2078,6 @@ pub fn genGlobalAsm(mod: *Module, code: *std.ArrayList(u8)) !void { } pub fn genErrDecls(o: *Object) !void { - if (o.dg.module.global_error_set.size == 0) return; const writer = o.writer(); try writer.writeAll("enum {\n"); @@ -2373,7 +2372,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .cmp_neq => try airEquality(f, inst, "!((", "!="), .cmp_vector => return f.fail("TODO: C backend: implement cmp_vector", .{}), - .cmp_lt_errors_len => return f.fail("TODO: C backend: implement cmp_lt_errors_len", .{}), + .cmp_lt_errors_len => try airCmpLtErrorsLen(f, inst), // bool_and and bool_or are non-short-circuit operations .bool_and, .bit_and => try airBinOp(f, inst, "&", "and", .None), @@ -3176,6 +3175,21 @@ fn airEquality( return local; } +fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue { + if (f.liveness.isUnused(inst)) return CValue.none; + + const un_op = f.air.instructions.items(.data)[inst].un_op; + const inst_ty = f.air.typeOfIndex(inst); + const operand = try f.resolveInst(un_op); + + const writer = f.object.writer(); + const local = try f.allocLocal(inst_ty, .Const); + try writer.writeAll(" = "); + try f.writeCValue(writer, operand, .Other); + try writer.print(" < sizeof({ }) / sizeof(*{0 });\n", .{fmtIdent("zig_errorName")}); + return local; +} + fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 0b8787e8b6..8a0001297c 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -394,7 +394,6 @@ test "expected [*c]const u8, found [*:0]const u8" { test "explicit cast from integer to error type" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; 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_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO From 54326cc55485fa71ce729cd553cb9af9ea816faa Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 24 Oct 2022 21:22:28 -0400 Subject: [PATCH 42/47] cbe: implement field_parent_ptr --- src/codegen/c.zig | 51 ++++++++++++++++++++++++------ test/behavior/field_parent_ptr.zig | 2 -- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 897af5ef65..46215039c4 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -583,12 +583,12 @@ pub const DeclGen = struct { }; const byte_offset_val = Value.initPayload(&byte_offset_pl.base); - var ptr_u8_pl = ptr_info; - ptr_u8_pl.data.pointee_type = Type.u8; - const ptr_u8_ty = Type.initPayload(&ptr_u8_pl.base); + var u8_ptr_pl = ptr_info; + u8_ptr_pl.data.pointee_type = Type.u8; + const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); try writer.writeAll("&(("); - try dg.renderTypecast(writer, ptr_u8_ty); + try dg.renderTypecast(writer, u8_ptr_ty); try writer.writeByte(')'); try dg.renderParentPtr(writer, field_ptr.container_ptr, container_ptr_ty); return writer.print(")[{}]", .{try dg.fmtIntLiteral(Type.usize, byte_offset_val)}); @@ -4050,8 +4050,39 @@ fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue } fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { - _ = inst; - return f.fail("TODO: C backend: implement airFieldParentPtr", .{}); + if (f.liveness.isUnused(inst)) return CValue.none; + + const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; + const extra = f.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; + + const struct_ptr_ty = f.air.typeOfIndex(inst); + const field_ptr_ty = f.air.typeOf(extra.field_ptr); + const field_ptr_val = try f.resolveInst(extra.field_ptr); + + const target = f.object.dg.module.getTarget(); + const struct_ty = struct_ptr_ty.childType(); + const field_offset = struct_ty.structFieldOffset(extra.field_index, target); + + var field_offset_pl = Value.Payload.I64{ + .base = .{ .tag = .int_i64 }, + .data = -@intCast(i64, field_offset), + }; + const field_offset_val = Value.initPayload(&field_offset_pl.base); + + var u8_ptr_pl = field_ptr_ty.ptrInfo(); + u8_ptr_pl.data.pointee_type = Type.u8; + const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); + + const writer = f.object.writer(); + const local = try f.allocLocal(struct_ptr_ty, .Const); + try writer.writeAll(" = ("); + try f.renderTypecast(writer, struct_ptr_ty); + try writer.writeAll(")&(("); + try f.renderTypecast(writer, u8_ptr_ty); + try writer.writeByte(')'); + try f.writeCValue(writer, field_ptr_val, .Other); + try writer.print(")[{}];\n", .{try f.fmtIntLiteral(Type.isize, field_offset_val)}); + return local; } fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struct_ptr: CValue, index: u32) !CValue { @@ -4089,12 +4120,12 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc }; const byte_offset_val = Value.initPayload(&byte_offset_pl.base); - var ptr_u8_pl = field_ptr_info; - ptr_u8_pl.data.pointee_type = Type.u8; - const ptr_u8_ty = Type.initPayload(&ptr_u8_pl.base); + var u8_ptr_pl = field_ptr_info; + u8_ptr_pl.data.pointee_type = Type.u8; + const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); try writer.writeAll("&(("); - try f.renderTypecast(writer, ptr_u8_ty); + try f.renderTypecast(writer, u8_ptr_ty); try writer.writeByte(')'); try f.writeCValue(writer, struct_ptr, .Other); try writer.print(")[{}];\n", .{try f.fmtIntLiteral(Type.usize, byte_offset_val)}); diff --git a/test/behavior/field_parent_ptr.zig b/test/behavior/field_parent_ptr.zig index 570a1f9522..e8ce1867cc 100644 --- a/test/behavior/field_parent_ptr.zig +++ b/test/behavior/field_parent_ptr.zig @@ -4,7 +4,6 @@ const builtin = @import("builtin"); test "@fieldParentPtr non-first field" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; try testParentFieldPtr(&foo.c); comptime try testParentFieldPtr(&foo.c); } @@ -12,7 +11,6 @@ test "@fieldParentPtr non-first field" { test "@fieldParentPtr first field" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; try testParentFieldPtrFirst(&foo.a); comptime try testParentFieldPtrFirst(&foo.a); } From 6021edd7cee104f69c9ceb1af27e46637cab79e3 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 24 Oct 2022 23:05:25 -0400 Subject: [PATCH 43/47] cbe: add support for all float literals types --- lib/include/zig.h | 4 +- src/codegen/c.zig | 138 +++++++++++++++++++---------------- test/behavior/bugs/12891.zig | 1 - test/behavior/cast.zig | 2 - test/behavior/floatop.zig | 6 -- test/behavior/math.zig | 2 - test/behavior/muladd.zig | 1 - test/behavior/pointers.zig | 1 - test/behavior/tuple.zig | 2 - 9 files changed, 76 insertions(+), 81 deletions(-) diff --git a/lib/include/zig.h b/lib/include/zig.h index 0d9e1ab3c2..5327c59b8f 100644 --- a/lib/include/zig.h +++ b/lib/include/zig.h @@ -340,7 +340,7 @@ zig_int_operators(64) #define zig_int_helpers(w) \ static inline zig_i##w zig_shr_i##w(zig_i##w lhs, zig_u8 rhs) { \ - zig_i##w sign_mask = lhs < zig_as_i##w(0) ? zig_as_i##w(-1) : zig_as_i##w(0); \ + zig_i##w sign_mask = lhs < zig_as_i##w(0) ? -zig_as_i##w(1) : zig_as_i##w(0); \ return ((lhs ^ sign_mask) >> rhs) ^ sign_mask; \ } \ \ @@ -1166,7 +1166,7 @@ static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { #define zig_mod_u128 zig_rem_u128 static inline zig_i128 zig_shr_i128(zig_i128 lhs, zig_u8 rhs) { - zig_i128 sign_mask = zig_cmp_i128(lhs, zig_as_i128(0, 0)) < zig_as_i8(0) ? zig_as_i128(-1, UINT64_MAX) : zig_as_i128(0, 0); + zig_i128 sign_mask = zig_cmp_i128(lhs, zig_as_i128(0, 0)) < zig_as_i8(0) ? -zig_as_i128(0, 1) : zig_as_i128(0, 0); return zig_xor_i128(zig_bitcast_i128(zig_shr_u128(zig_bitcast_u128(zig_xor_i128(lhs, sign_mask)), rhs)), sign_mask); } diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 46215039c4..6db5f5df5a 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -415,26 +415,6 @@ pub const Function = struct { }, } } - - fn renderFloatFnName(f: *Function, writer: anytype, operation: []const u8, float_ty: Type) !void { - const target = f.object.dg.module.getTarget(); - const float_bits = float_ty.floatBits(target); - const is_longdouble = float_bits == CType.longdouble.sizeInBits(target); - try writer.writeAll("__"); - if (is_longdouble or float_bits != 80) { - try writer.writeAll("builtin_"); - } - try writer.writeAll(operation); - if (is_longdouble) { - try writer.writeByte('l'); - } else switch (float_bits) { - 16, 32 => try writer.writeByte('f'), - 64 => {}, - 80 => try writer.writeByte('x'), - 128 => try writer.writeByte('q'), - else => unreachable, - } - } }; /// This data is available when outputting .c code for a `Module`. @@ -674,14 +654,18 @@ pub const DeclGen = struct { // bool b = 0xaa; evals to true, but memcpy(&b, 0xaa, 1); evals to false. .Bool => return dg.renderValue(writer, ty, Value.@"false", location), .Int, .Enum, .ErrorSet => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, val)}), - .Float => switch (ty.tag()) { - .f32 => return writer.print("zig_bitcast_f32_u32({x})", .{ - try dg.fmtIntLiteral(Type.u32, val), - }), - .f64 => return writer.print("zig_bitcast_f64_u64({x})", .{ - try dg.fmtIntLiteral(Type.u64, val), - }), - else => return dg.fail("TODO float types > 64 bits are not support in renderValue() as of now", .{}), + .Float => { + try writer.writeByte('('); + try dg.renderTypecast(writer, ty); + try writer.writeByte(')'); + switch (ty.floatBits(target)) { + 16 => return writer.print("{x}f", .{@bitCast(f16, undefPattern(u16))}), + 32 => return writer.print("{x}f", .{@bitCast(f32, undefPattern(u32))}), + 64 => return writer.print("{x}", .{@bitCast(f64, undefPattern(u64))}), + 80 => return writer.print("{x}l", .{@bitCast(f80, undefPattern(u80))}), + 128 => return writer.print("{x}l", .{@bitCast(f128, undefPattern(u128))}), + else => unreachable, + } }, .Pointer => switch (ty.ptrSize()) { .Slice => { @@ -833,37 +817,41 @@ pub const DeclGen = struct { else => try writer.print("{}", .{try dg.fmtIntLiteral(ty, val)}), }, .Float => { - if (ty.floatBits(target) <= 64) { - if (std.math.isNan(val.toFloat(f64)) or std.math.isInf(val.toFloat(f64))) { - // just generate a bit cast (exactly like we do in airBitcast) - switch (ty.tag()) { - .f32 => { - var bitcast_pl = Value.Payload.U64{ - .base = .{ .tag = .int_u64 }, - .data = @bitCast(u32, val.toFloat(f32)), - }; - const bitcast_val = Value.initPayload(&bitcast_pl.base); - return writer.print("zig_bitcast_f32_u32({x})", .{ - try dg.fmtIntLiteral(Type.u32, bitcast_val), - }); - }, - .f64 => { - var bitcast_pl = Value.Payload.U64{ - .base = .{ .tag = .int_u64 }, - .data = @bitCast(u64, val.toFloat(f64)), - }; - const bitcast_val = Value.initPayload(&bitcast_pl.base); - return writer.print("zig_bitcast_f64_u64({x})", .{ - try dg.fmtIntLiteral(Type.u64, bitcast_val), - }); - }, - else => return dg.fail("TODO float types > 64 bits are not support in renderValue() as of now", .{}), - } - } else { - return writer.print("{x}", .{val.toFloat(f64)}); - } + try writer.writeByte('('); + try dg.renderTypecast(writer, ty); + try writer.writeByte(')'); + const f128_val = val.toFloat(f128); + if (!std.math.isFinite(f128_val)) { + if (std.math.signbit(f128_val)) try writer.writeByte('-'); + const fn_name = if (std.math.isSignalNan(f128_val)) + "nans" + else if (std.math.isNan(f128_val)) + "nan" + else if (std.math.isInf(f128_val)) + "inf" + else + unreachable; + try dg.renderFloatFnName(writer, fn_name, ty); + try writer.writeByte('('); + if (std.math.isNan(f128_val)) switch (ty.floatBits(target)) { + // We only actually need to pass the significant, but it will get + // properly masked anyway, so just pass the whole value. + 16 => try writer.print("\"0x{x}\"", .{@bitCast(u16, val.toFloat(f16))}), + 32 => try writer.print("\"0x{x}\"", .{@bitCast(u32, val.toFloat(f32))}), + 64 => try writer.print("\"0x{x}\"", .{@bitCast(u64, val.toFloat(f64))}), + 80 => try writer.print("\"0x{x}\"", .{@bitCast(u80, val.toFloat(f80))}), + 128 => try writer.print("\"0x{x}\"", .{@bitCast(u128, f128_val)}), + else => unreachable, + }; + return writer.writeByte(')'); + } else switch (ty.floatBits(target)) { + 16 => return writer.print("{x}f", .{val.toFloat(f16)}), + 32 => return writer.print("{x}f", .{val.toFloat(f32)}), + 64 => return writer.print("{x}", .{val.toFloat(f64)}), + 80 => return writer.print("{x}l", .{val.toFloat(f80)}), + 128 => return writer.print("{x}l", .{f128_val}), + else => unreachable, } - return dg.fail("TODO: C backend: implement lowering large float values", .{}); }, .Pointer => switch (val.tag()) { .null_value, .zero => { @@ -2053,6 +2041,26 @@ pub const DeclGen = struct { } } + fn renderFloatFnName(dg: *DeclGen, writer: anytype, operation: []const u8, float_ty: Type) !void { + const target = dg.module.getTarget(); + const float_bits = float_ty.floatBits(target); + const is_longdouble = float_bits == CType.longdouble.sizeInBits(target); + try writer.writeAll("__"); + if (is_longdouble or float_bits != 80) { + try writer.writeAll("builtin_"); + } + try writer.writeAll(operation); + if (is_longdouble) { + try writer.writeByte('l'); + } else switch (float_bits) { + 16, 32 => try writer.writeByte('f'), + 64 => {}, + 80 => try writer.writeByte('x'), + 128 => try writer.writeByte('q'), + else => unreachable, + } + } + fn fmtIntLiteral( dg: *DeclGen, ty: Type, @@ -5169,7 +5177,7 @@ fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVal const operand = try f.resolveInst(un_op); const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = "); - try f.renderFloatFnName(writer, operation, inst_ty); + try f.object.dg.renderFloatFnName(writer, operation, inst_ty); try writer.writeByte('('); try f.writeCValue(writer, operand, .FunctionArgument); try writer.writeAll(");\n"); @@ -5185,7 +5193,7 @@ fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVa const rhs = try f.resolveInst(bin_op.rhs); const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = "); - try f.renderFloatFnName(writer, operation, inst_ty); + try f.object.dg.renderFloatFnName(writer, operation, inst_ty); try writer.writeByte('('); try f.writeCValue(writer, lhs, .FunctionArgument); try writer.writeAll(", "); @@ -5205,7 +5213,7 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = "); - try f.renderFloatFnName(writer, "fma", inst_ty); + try f.object.dg.renderFloatFnName(writer, "fma", inst_ty); try writer.writeByte('('); try f.writeCValue(writer, mulend1, .FunctionArgument); try writer.writeAll(", "); @@ -5342,6 +5350,10 @@ fn fmtStringLiteral(str: []const u8) std.fmt.Formatter(formatStringLiteral) { return .{ .data = str }; } +fn undefPattern(comptime T: type) T { + return (1 << (@bitSizeOf(T) | 1)) / 3; +} + const FormatIntLiteralContext = struct { ty: Type, val: Value, @@ -5379,9 +5391,7 @@ fn formatIntLiteral( var int_buf: Value.BigIntSpace = undefined; const int = if (data.val.isUndefDeep()) blk: { undef_limbs = try allocator.alloc(Limb, BigInt.calcTwosCompLimbCount(int_info.bits)); - - const undef_pattern: Limb = (1 << (@bitSizeOf(Limb) | 1)) / 3; - std.mem.set(Limb, undef_limbs, undef_pattern); + std.mem.set(Limb, undef_limbs, undefPattern(Limb)); var undef_int = BigInt.Mutable{ .limbs = undef_limbs, diff --git a/test/behavior/bugs/12891.zig b/test/behavior/bugs/12891.zig index 78947d1776..14eea5cf7f 100644 --- a/test/behavior/bugs/12891.zig +++ b/test/behavior/bugs/12891.zig @@ -8,7 +8,6 @@ test "issue12891" { } test "nan" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const f = comptime std.math.nan(f64); var i: usize = 0; diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 8a0001297c..4e49935f7c 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -1283,7 +1283,6 @@ fn boolToStr(b: bool) []const u8 { test "cast f16 to wider types" { 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 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO @@ -1302,7 +1301,6 @@ test "cast f16 to wider types" { test "cast f128 to narrower types" { 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 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index 8524a11b6a..fa2eb2f8f0 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -147,7 +147,6 @@ fn testSqrt() !void { test "more @sqrt f16 tests" { 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 @@ -608,7 +607,6 @@ test "negation f80" { 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 - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const S = struct { @@ -629,7 +627,6 @@ test "negation f128" { 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 - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const S = struct { @@ -709,7 +706,6 @@ test "nan negation f16" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const nan_comptime = comptime math.nan(f16); const neg_nan_comptime = -nan_comptime; @@ -729,7 +725,6 @@ test "nan negation f32" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const nan_comptime = comptime math.nan(f32); const neg_nan_comptime = -nan_comptime; @@ -749,7 +744,6 @@ test "nan negation f64" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const nan_comptime = comptime math.nan(f64); const neg_nan_comptime = -nan_comptime; diff --git a/test/behavior/math.zig b/test/behavior/math.zig index ca7c3b044a..b80a14fb57 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -1316,7 +1316,6 @@ test "@fabs f80" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) 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 try testFabs(f80, 12.0); comptime try testFabs(f80, 12.0); @@ -1625,7 +1624,6 @@ test "compare undefined literal with comptime_int" { } test "signed zeros are represented properly" { - 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_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO diff --git a/test/behavior/muladd.zig b/test/behavior/muladd.zig index c688f27593..bff9bf3e32 100644 --- a/test/behavior/muladd.zig +++ b/test/behavior/muladd.zig @@ -44,7 +44,6 @@ fn testMulAdd16() !void { test "@mulAdd f80" { 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/behavior/pointers.zig b/test/behavior/pointers.zig index 05d2c7784c..2e09aa2802 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -335,7 +335,6 @@ test "pointer sentinel with optional element" { test "pointer sentinel with +inf" { 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 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig index 4b36ded087..f9a1603a5f 100644 --- a/test/behavior/tuple.zig +++ b/test/behavior/tuple.zig @@ -205,7 +205,6 @@ test "initializing anon struct with explicit type" { } test "fieldParentPtr of tuple" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; @@ -216,7 +215,6 @@ test "fieldParentPtr of tuple" { } test "fieldParentPtr of anon struct" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; From 94425fe46edb326c38ccf87872b2167bc8f24643 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 24 Oct 2022 23:52:13 -0400 Subject: [PATCH 44/47] cbe: improve floating point type support --- lib/include/zig.h | 139 +++++++++++++----------- src/codegen/c.zig | 223 +++++++++++++++++++------------------- test/behavior/floatop.zig | 2 - test/behavior/math.zig | 2 - test/behavior/muladd.zig | 1 - 5 files changed, 186 insertions(+), 181 deletions(-) diff --git a/lib/include/zig.h b/lib/include/zig.h index 5327c59b8f..b64e157b98 100644 --- a/lib/include/zig.h +++ b/lib/include/zig.h @@ -257,56 +257,116 @@ typedef int64_t zig_i64; #if FLT_MANT_DIG == 11 typedef float zig_f16; +#define zig_suffix_f16(x) x##f +#define zig_builtin_f16(name) __builtin_##name##f #elif DBL_MANT_DIG == 11 typedef double zig_f16; +#define zig_suffix_f16(x) x +#define zig_builtin_f16(name) __builtin_##name #elif LDBL_MANT_DIG == 11 typedef long double zig_f16; +#define zig_suffix_f16(x) x##l +#define zig_builtin_f16(name) __builtin_##name##l #elif FLT16_MANT_DIG == 11 typedef _Float16 zig_f16; +#define zig_suffix_f16(x) x##f16 +#define zig_builtin_f16(name) __builtin_##name +#elif defined(__SIZEOF_FP16__) +typedef __fp16 zig_f16; +#define zig_suffix_f16(x) x +#define zig_builtin_f16(name) __builtin_##name #endif #if FLT_MANT_DIG == 24 typedef float zig_f32; +#define zig_suffix_f32(x) x##f +#define zig_builtin_f32(name) __builtin_##name##f #elif DBL_MANT_DIG == 24 typedef double zig_f32; +#define zig_suffix_f32(x) x +#define zig_builtin_f32(name) __builtin_##name #elif LDBL_MANT_DIG == 24 typedef long double zig_f32; +#define zig_suffix_f32(x) x##l +#define zig_builtin_f32(name) __builtin_##name##l #elif FLT32_MANT_DIG == 24 typedef _Float32 zig_f32; +#define zig_suffix_f32(x) x##f32 +#define zig_builtin_f32(name) __builtin_##name #endif #if FLT_MANT_DIG == 53 typedef float zig_f64; +#define zig_suffix_f64(x) x##f +#define zig_builtin_f64(name) __builtin_##name##f #elif DBL_MANT_DIG == 53 typedef double zig_f64; +#define zig_suffix_f64(x) x +#define zig_builtin_f64(name) __builtin_##name #elif LDBL_MANT_DIG == 53 typedef long double zig_f64; +#define zig_suffix_f64(x) x##l +#define zig_builtin_f64(name) __builtin_##name##l #elif FLT64_MANT_DIG == 53 typedef _Float64 zig_f64; +#define zig_suffix_f64(x) x##f64 +#define zig_builtin_f64(name) __builtin_##name##l +#elif FLT32X_MANT_DIG == 53 +typedef _Float32x zig_f64; +#define zig_suffix_f64(x) x##f32x +#define zig_builtin_f64(name) __builtin_##name##l #endif #if FLT_MANT_DIG == 64 typedef float zig_f80; +#define zig_suffix_f80(x) x##f +#define zig_builtin_f80(name) __builtin_##name##f #elif DBL_MANT_DIG == 64 typedef double zig_f80; +#define zig_suffix_f80(x) x +#define zig_builtin_f80(name) __builtin_##name #elif LDBL_MANT_DIG == 64 typedef long double zig_f80; +#define zig_suffix_f80(x) x##l +#define zig_builtin_f80(name) __builtin_##name##l #elif FLT80_MANT_DIG == 64 typedef _Float80 zig_f80; +#define zig_suffix_f80(x) x##f80 +#define zig_builtin_f80(name) __builtin_##name##l +#elif FLT64X_MANT_DIG == 64 +typedef _Float64x zig_f80; +#define zig_suffix_f80(x) x##f64x +#define zig_builtin_f80(name) __builtin_##name##l #elif defined(__SIZEOF_FLOAT80__) typedef __float80 zig_f80; +#define zig_suffix_f80(x) x##l +#define zig_builtin_f80(name) __builtin_##name##l #endif #if FLT_MANT_DIG == 113 typedef float zig_f128; +#define zig_suffix_f128(x) x##f +#define zig_builtin_f128(name) __builtin_##name##f #elif DBL_MANT_DIG == 113 typedef double zig_f128; +#define zig_suffix_f128(x) x +#define zig_builtin_f128(name) __builtin_##name #elif LDBL_MANT_DIG == 113 typedef long double zig_f128; +#define zig_suffix_f128(x) x##l +#define zig_builtin_f128(name) __builtin_##name##l #elif FLT128_MANT_DIG == 113 typedef _Float128 zig_f128; +#define zig_suffix_f128(x) x##f128 +#define zig_builtin_f128(name) __builtin_##name##l +#elif FLT64X_MANT_DIG == 113 +typedef _Float64x zig_f128; +#define zig_suffix_f128(x) x##f64x +#define zig_builtin_f128(name) __builtin_##name##l #elif defined(__SIZEOF_FLOAT128__) typedef __float128 zig_f128; +#define zig_suffix_f128(x) x##l +#define zig_builtin_f128(name) __builtin_##name##l #endif zig_extern_c void *memcpy (void *zig_restrict, void const *zig_restrict, zig_usize); @@ -1416,65 +1476,20 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, zig_u8 bits) { /* ========================== Float Point Routines ========================== */ -static inline zig_f32 zig_bitcast_f32_u32(zig_u32 arg) { - zig_f32 dest; - memcpy(&dest, &arg, sizeof dest); - return dest; -} - -static inline zig_f64 zig_bitcast_f64_u64(zig_u64 arg) { - zig_f64 dest; - memcpy(&dest, &arg, sizeof dest); - return dest; -} - -static inline float zig_div_truncf(float numerator, float denominator) { - return __builtin_truncf(numerator / denominator); -} - -static inline double zig_div_trunc(double numerator, double denominator) { - return __builtin_trunc(numerator / denominator); -} - -static inline long double zig_div_truncl(long double numerator, long double denominator) { - return __builtin_truncf(numerator / denominator); -} - -#define zig_div_trunc_f16 zig_div_truncf -#define zig_div_trunc_f32 zig_div_truncf -#define zig_div_trunc_f64 zig_div_trunc -#define zig_div_trunc_f80 zig_div_truncl -#define zig_div_trunc_f128 zig_div_truncl - -#define zig_div_floorf(numerator, denominator) \ - __builtin_floorf((float)(numerator) / (float)(denominator)) - -#define zig_div_floor(numerator, denominator) \ - __builtin_floor((double)(numerator) / (double)(denominator)) - -#define zig_div_floorl(numerator, denominator) \ - __builtin_floorl((long double)(numerator) / (long double)(denominator)) - -#define zig_div_floor_f16 zig_div_floorf -#define zig_div_floor_f32 zig_div_floorf -#define zig_div_floor_f64 zig_div_floor -#define zig_div_floor_f80 zig_div_floorl -#define zig_div_floor_f128 zig_div_floorl - -static inline float zig_modf(float numerator, float denominator) { - return (numerator - (zig_div_floorf(numerator, denominator) * denominator)); -} - -static inline double zig_mod(double numerator, double denominator) { - return (numerator - (zig_div_floor(numerator, denominator) * denominator)); -} - -static inline long double zig_modl(long double numerator, long double denominator) { - return (numerator - (zig_div_floorl(numerator, denominator) * denominator)); -} - -#define zig_mod_f16 zig_modf -#define zig_mod_f32 zig_modf -#define zig_mod_f64 zig_mod -#define zig_mod_f80 zig_modl -#define zig_mod_f128 zig_modl +#define zig_float_builtins(w) \ + static inline zig_f##w zig_div_trunc_f##w(zig_f##w lhs, zig_f##w rhs) { \ + return zig_builtin_f##w(trunc)(lhs / rhs); \ + } \ +\ + static inline zig_f##w zig_div_floor_f##w(zig_f##w lhs, zig_f##w rhs) { \ + return zig_builtin_f##w(floor)(lhs / rhs); \ + } \ +\ + static inline zig_f##w zig_mod_f##w(zig_f##w lhs, zig_f##w rhs) { \ + return lhs - zig_div_floor_f##w(lhs, rhs) * rhs; \ + } +zig_float_builtins(16) +zig_float_builtins(32) +zig_float_builtins(64) +zig_float_builtins(80) +zig_float_builtins(128) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 6db5f5df5a..74a4fb178a 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -367,54 +367,6 @@ pub const Function = struct { fn fmtIntLiteral(f: *Function, ty: Type, val: Value) !std.fmt.Formatter(formatIntLiteral) { return f.object.dg.fmtIntLiteral(ty, val); } - - fn renderTypeForBuiltinFnName(f: *Function, writer: anytype, ty: Type) !void { - const target = f.object.dg.module.getTarget(); - const c_bits = if (ty.isAbiInt()) c_bits: { - const int_info = ty.intInfo(target); - try writer.writeByte(signAbbrev(int_info.signedness)); - break :c_bits toCIntBits(int_info.bits) orelse - return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); - } else if (ty.isRuntimeFloat()) c_bits: { - try writer.writeByte('f'); - break :c_bits ty.floatBits(target); - } else return f.fail("TODO: CBE: implement renderTypeForBuiltinFnName for type {}", .{ - ty.fmt(f.object.dg.module), - }); - try writer.print("{d}", .{c_bits}); - } - - fn renderBuiltinInfo(f: *Function, writer: anytype, ty: Type, info: BuiltinInfo) !void { - const target = f.object.dg.module.getTarget(); - switch (info) { - .None => {}, - .Range => { - var arena = std.heap.ArenaAllocator.init(f.object.dg.gpa); - defer arena.deinit(); - - const ExpectedContents = union { u: Value.Payload.U64, i: Value.Payload.I64 }; - var stack align(@alignOf(ExpectedContents)) = - std.heap.stackFallback(@sizeOf(ExpectedContents), arena.allocator()); - - const int_info = ty.intInfo(target); - if (int_info.signedness == .signed) { - const min_val = try ty.minInt(stack.get(), target); - try writer.print(", {x}", .{try f.object.dg.fmtIntLiteral(ty, min_val)}); - } - - const max_val = try ty.maxInt(stack.get(), target); - try writer.print(", {x}", .{try f.object.dg.fmtIntLiteral(ty, max_val)}); - }, - .Bits => { - var bits_pl = Value.Payload.U64{ - .base = .{ .tag = .int_u64 }, - .data = ty.bitSize(target), - }; - const bits_val = Value.initPayload(&bits_pl.base); - try writer.print(", {}", .{try f.object.dg.fmtIntLiteral(Type.u8, bits_val)}); - }, - } - } }; /// This data is available when outputting .c code for a `Module`. @@ -657,15 +609,18 @@ pub const DeclGen = struct { .Float => { try writer.writeByte('('); try dg.renderTypecast(writer, ty); - try writer.writeByte(')'); + try writer.writeAll(")zig_suffix_"); + try dg.renderTypeForBuiltinFnName(writer, ty); + try writer.writeByte('('); switch (ty.floatBits(target)) { - 16 => return writer.print("{x}f", .{@bitCast(f16, undefPattern(u16))}), - 32 => return writer.print("{x}f", .{@bitCast(f32, undefPattern(u32))}), - 64 => return writer.print("{x}", .{@bitCast(f64, undefPattern(u64))}), - 80 => return writer.print("{x}l", .{@bitCast(f80, undefPattern(u80))}), - 128 => return writer.print("{x}l", .{@bitCast(f128, undefPattern(u128))}), + 16 => try writer.print("{x}", .{@bitCast(f16, undefPattern(u16))}), + 32 => try writer.print("{x}", .{@bitCast(f32, undefPattern(u32))}), + 64 => try writer.print("{x}", .{@bitCast(f64, undefPattern(u64))}), + 80 => try writer.print("{x}", .{@bitCast(f80, undefPattern(u80))}), + 128 => try writer.print("{x}", .{@bitCast(f128, undefPattern(u128))}), else => unreachable, } + return writer.writeByte(')'); }, .Pointer => switch (ty.ptrSize()) { .Slice => { @@ -821,9 +776,21 @@ pub const DeclGen = struct { try dg.renderTypecast(writer, ty); try writer.writeByte(')'); const f128_val = val.toFloat(f128); - if (!std.math.isFinite(f128_val)) { - if (std.math.signbit(f128_val)) try writer.writeByte('-'); - const fn_name = if (std.math.isSignalNan(f128_val)) + if (std.math.signbit(f128_val)) try writer.writeByte('-'); + if (std.math.isFinite(f128_val)) { + try writer.writeAll("zig_suffix_"); + try dg.renderTypeForBuiltinFnName(writer, ty); + try writer.writeByte('('); + switch (ty.floatBits(target)) { + 16 => try writer.print("{x}", .{@fabs(val.toFloat(f16))}), + 32 => try writer.print("{x}", .{@fabs(val.toFloat(f32))}), + 64 => try writer.print("{x}", .{@fabs(val.toFloat(f64))}), + 80 => try writer.print("{x}", .{@fabs(val.toFloat(f80))}), + 128 => try writer.print("{x}", .{@fabs(f128_val)}), + else => unreachable, + } + } else { + const operation = if (std.math.isSignalNan(f128_val)) "nans" else if (std.math.isNan(f128_val)) "nan" @@ -831,27 +798,23 @@ pub const DeclGen = struct { "inf" else unreachable; - try dg.renderFloatFnName(writer, fn_name, ty); + try writer.writeAll("zig_builtin_"); + try dg.renderTypeForBuiltinFnName(writer, ty); try writer.writeByte('('); + try writer.writeAll(operation); + try writer.writeAll(")("); if (std.math.isNan(f128_val)) switch (ty.floatBits(target)) { - // We only actually need to pass the significant, but it will get + // We only actually need to pass the significand, but it will get // properly masked anyway, so just pass the whole value. - 16 => try writer.print("\"0x{x}\"", .{@bitCast(u16, val.toFloat(f16))}), - 32 => try writer.print("\"0x{x}\"", .{@bitCast(u32, val.toFloat(f32))}), - 64 => try writer.print("\"0x{x}\"", .{@bitCast(u64, val.toFloat(f64))}), - 80 => try writer.print("\"0x{x}\"", .{@bitCast(u80, val.toFloat(f80))}), - 128 => try writer.print("\"0x{x}\"", .{@bitCast(u128, f128_val)}), + 16 => try writer.print("\"0x{x}\"", .{@bitCast(u16, @fabs(val.toFloat(f16)))}), + 32 => try writer.print("\"0x{x}\"", .{@bitCast(u32, @fabs(val.toFloat(f32)))}), + 64 => try writer.print("\"0x{x}\"", .{@bitCast(u64, @fabs(val.toFloat(f64)))}), + 80 => try writer.print("\"0x{x}\"", .{@bitCast(u80, @fabs(val.toFloat(f80)))}), + 128 => try writer.print("\"0x{x}\"", .{@bitCast(u128, @fabs(f128_val))}), else => unreachable, }; - return writer.writeByte(')'); - } else switch (ty.floatBits(target)) { - 16 => return writer.print("{x}f", .{val.toFloat(f16)}), - 32 => return writer.print("{x}f", .{val.toFloat(f32)}), - 64 => return writer.print("{x}", .{val.toFloat(f64)}), - 80 => return writer.print("{x}l", .{val.toFloat(f80)}), - 128 => return writer.print("{x}l", .{f128_val}), - else => unreachable, } + return writer.writeByte(')'); }, .Pointer => switch (val.tag()) { .null_value, .zero => { @@ -2041,23 +2004,51 @@ pub const DeclGen = struct { } } - fn renderFloatFnName(dg: *DeclGen, writer: anytype, operation: []const u8, float_ty: Type) !void { + fn renderTypeForBuiltinFnName(dg: *DeclGen, writer: anytype, ty: Type) !void { const target = dg.module.getTarget(); - const float_bits = float_ty.floatBits(target); - const is_longdouble = float_bits == CType.longdouble.sizeInBits(target); - try writer.writeAll("__"); - if (is_longdouble or float_bits != 80) { - try writer.writeAll("builtin_"); - } - try writer.writeAll(operation); - if (is_longdouble) { - try writer.writeByte('l'); - } else switch (float_bits) { - 16, 32 => try writer.writeByte('f'), - 64 => {}, - 80 => try writer.writeByte('x'), - 128 => try writer.writeByte('q'), - else => unreachable, + const c_bits = if (ty.isAbiInt()) c_bits: { + const int_info = ty.intInfo(target); + try writer.writeByte(signAbbrev(int_info.signedness)); + break :c_bits toCIntBits(int_info.bits) orelse + return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); + } else if (ty.isRuntimeFloat()) c_bits: { + try writer.writeByte('f'); + break :c_bits ty.floatBits(target); + } else return dg.fail("TODO: CBE: implement renderTypeForBuiltinFnName for type {}", .{ + ty.fmt(dg.module), + }); + try writer.print("{d}", .{c_bits}); + } + + fn renderBuiltinInfo(dg: *DeclGen, writer: anytype, ty: Type, info: BuiltinInfo) !void { + const target = dg.module.getTarget(); + switch (info) { + .None => {}, + .Range => { + var arena = std.heap.ArenaAllocator.init(dg.gpa); + defer arena.deinit(); + + const ExpectedContents = union { u: Value.Payload.U64, i: Value.Payload.I64 }; + var stack align(@alignOf(ExpectedContents)) = + std.heap.stackFallback(@sizeOf(ExpectedContents), arena.allocator()); + + const int_info = ty.intInfo(target); + if (int_info.signedness == .signed) { + const min_val = try ty.minInt(stack.get(), target); + try writer.print(", {x}", .{try dg.fmtIntLiteral(ty, min_val)}); + } + + const max_val = try ty.maxInt(stack.get(), target); + try writer.print(", {x}", .{try dg.fmtIntLiteral(ty, max_val)}); + }, + .Bits => { + var bits_pl = Value.Payload.U64{ + .base = .{ .tag = .int_u64 }, + .data = ty.bitSize(target), + }; + const bits_val = Value.initPayload(&bits_pl.base); + try writer.print(", {}", .{try dg.fmtIntLiteral(Type.u8, bits_val)}); + }, } } @@ -2760,15 +2751,15 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(" = ("); try f.renderTypecast(writer, inst_ty); try writer.writeAll(")zig_wrap_"); - try f.renderTypeForBuiltinFnName(writer, field_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, field_ty); try writer.writeAll("(("); try f.renderTypecast(writer, field_ty); try writer.writeAll(")zig_shr_"); - try f.renderTypeForBuiltinFnName(writer, host_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); try writer.writeByte('('); try f.writeCValueDeref(writer, operand); try writer.print(", {})", .{try f.fmtIntLiteral(bit_offset_ty, bit_offset_val)}); - try f.renderBuiltinInfo(writer, field_ty, .Bits); + try f.object.dg.renderBuiltinInfo(writer, field_ty, .Bits); try writer.writeByte(')'); } else { try writer.writeAll(" = "); @@ -2998,13 +2989,13 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValueDeref(writer, ptr_val); try writer.writeAll(" = zig_or_"); - try f.renderTypeForBuiltinFnName(writer, host_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); try writer.writeAll("(zig_and_"); - try f.renderTypeForBuiltinFnName(writer, host_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); try writer.writeByte('('); try f.writeCValueDeref(writer, ptr_val); try writer.print(", {x}), zig_shl_", .{try f.fmtIntLiteral(host_ty, mask_val)}); - try f.renderTypeForBuiltinFnName(writer, host_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); try writer.writeAll("(("); try f.renderTypecast(writer, host_ty); try writer.writeByte(')'); @@ -3040,14 +3031,14 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: try w.writeAll(".field_1 = zig_"); try w.writeAll(operation); try w.writeAll("o_"); - try f.renderTypeForBuiltinFnName(w, scalar_ty); + try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty); try w.writeAll("(&"); try f.writeCValueMember(w, local, .{ .identifier = "field_0" }); try w.writeAll(", "); try f.writeCValue(w, lhs, .FunctionArgument); try w.writeAll(", "); try f.writeCValue(w, rhs, .FunctionArgument); - try f.renderBuiltinInfo(w, scalar_ty, info); + try f.object.dg.renderBuiltinInfo(w, scalar_ty, info); try w.writeAll(");\n"); return local; } @@ -4217,17 +4208,17 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { const temp_local = try f.allocLocal(field_int_ty, .Const); try writer.writeAll(" = zig_wrap_"); - try f.renderTypeForBuiltinFnName(writer, field_int_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, field_int_ty); try writer.writeAll("(("); try f.renderTypecast(writer, field_int_ty); try writer.writeAll(")zig_shr_"); - try f.renderTypeForBuiltinFnName(writer, struct_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, struct_ty); try writer.writeByte('('); try f.writeCValue(writer, struct_byval, .Other); try writer.writeAll(", "); try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); try writer.writeByte(')'); - try f.renderBuiltinInfo(writer, field_int_ty, .Bits); + try f.object.dg.renderBuiltinInfo(writer, field_int_ty, .Bits); try writer.writeAll(");\n"); if (inst_ty.eql(field_int_ty, f.object.dg.module)) return temp_local; @@ -4567,10 +4558,10 @@ fn airUnBuiltinCall( try writer.writeAll(" = zig_"); try writer.writeAll(operation); try writer.writeByte('_'); - try f.renderTypeForBuiltinFnName(writer, operand_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); try writer.writeByte('('); try f.writeCValue(writer, try f.resolveInst(operand), .FunctionArgument); - try f.renderBuiltinInfo(writer, operand_ty, info); + try f.object.dg.renderBuiltinInfo(writer, operand_ty, info); try writer.writeAll(");\n"); return local; } @@ -4592,12 +4583,12 @@ fn airBinBuiltinCall( try writer.writeAll(" = zig_"); try writer.writeAll(operation); try writer.writeByte('_'); - try f.renderTypeForBuiltinFnName(writer, operand_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); try writer.writeByte('('); try f.writeCValue(writer, try f.resolveInst(bin_op.lhs), .FunctionArgument); try writer.writeAll(", "); try f.writeCValue(writer, try f.resolveInst(bin_op.rhs), .FunctionArgument); - try f.renderBuiltinInfo(writer, operand_ty, info); + try f.object.dg.renderBuiltinInfo(writer, operand_ty, info); try writer.writeAll(");\n"); return local; } @@ -4612,7 +4603,7 @@ fn airCmpBuiltinCall(f: *Function, inst: Air.Inst.Index, operator: []const u8) ! const local = try f.allocLocal(inst_ty, .Const); const writer = f.object.writer(); try writer.writeAll(" = zig_cmp_"); - try f.renderTypeForBuiltinFnName(writer, operand_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); try writer.writeByte('('); try f.writeCValue(writer, try f.resolveInst(bin_op.lhs), .FunctionArgument); try writer.writeAll(", "); @@ -5027,7 +5018,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { if (!empty) { try writer.writeAll("zig_or_"); - try f.renderTypeForBuiltinFnName(writer, inst_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty); try writer.writeByte('('); } empty = false; @@ -5039,14 +5030,14 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { if (!empty) try writer.writeAll(", "); try writer.writeAll("zig_shlw_"); - try f.renderTypeForBuiltinFnName(writer, inst_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty); try writer.writeAll("(("); try f.renderTypecast(writer, inst_ty); try writer.writeByte(')'); try f.writeCValue(writer, try f.resolveInst(element), .Other); try writer.writeAll(", "); try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); - try f.renderBuiltinInfo(writer, inst_ty, .Bits); + try f.object.dg.renderBuiltinInfo(writer, inst_ty, .Bits); try writer.writeByte(')'); if (!empty) try writer.writeByte(')'); @@ -5176,9 +5167,11 @@ fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVal const inst_ty = f.air.typeOfIndex(inst); const operand = try f.resolveInst(un_op); const local = try f.allocLocal(inst_ty, .Const); - try writer.writeAll(" = "); - try f.object.dg.renderFloatFnName(writer, operation, inst_ty); + try writer.writeAll(" = zig_builtin_"); + try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty); try writer.writeByte('('); + try writer.writeAll(operation); + try writer.writeAll(")("); try f.writeCValue(writer, operand, .FunctionArgument); try writer.writeAll(");\n"); return local; @@ -5192,9 +5185,11 @@ fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVa const lhs = try f.resolveInst(bin_op.lhs); const rhs = try f.resolveInst(bin_op.rhs); const local = try f.allocLocal(inst_ty, .Const); - try writer.writeAll(" = "); - try f.object.dg.renderFloatFnName(writer, operation, inst_ty); + try writer.writeAll(" = zig_builtin_"); + try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty); try writer.writeByte('('); + try writer.writeAll(operation); + try writer.writeAll(")("); try f.writeCValue(writer, lhs, .FunctionArgument); try writer.writeAll(", "); try f.writeCValue(writer, rhs, .FunctionArgument); @@ -5212,9 +5207,9 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { const addend = try f.resolveInst(pl_op.operand); const writer = f.object.writer(); const local = try f.allocLocal(inst_ty, .Const); - try writer.writeAll(" = "); - try f.object.dg.renderFloatFnName(writer, "fma", inst_ty); - try writer.writeByte('('); + try writer.writeAll(" = zig_builtin_"); + try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty); + try writer.writeAll("(fma)("); try f.writeCValue(writer, mulend1, .FunctionArgument); try writer.writeAll(", "); try f.writeCValue(writer, mulend2, .FunctionArgument); diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index fa2eb2f8f0..bdfdaacf01 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -670,7 +670,6 @@ test "comptime fixed-width float zero divided by zero produces NaN" { 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 - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO inline for (.{ f16, f32, f64, f80, f128 }) |F| { try expect(math.isNan(@as(F, 0) / @as(F, 0))); @@ -763,7 +762,6 @@ test "nan negation f128" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const nan_comptime = comptime math.nan(f128); const neg_nan_comptime = -nan_comptime; diff --git a/test/behavior/math.zig b/test/behavior/math.zig index b80a14fb57..96683c1816 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -1146,7 +1146,6 @@ test "comptime float rem int" { test "remainder division" { 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_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1294,7 +1293,6 @@ test "@fabs" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) 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 try testFabs(f128, 12.0); comptime try testFabs(f128, 12.0); diff --git a/test/behavior/muladd.zig b/test/behavior/muladd.zig index bff9bf3e32..9dfab2ad85 100644 --- a/test/behavior/muladd.zig +++ b/test/behavior/muladd.zig @@ -62,7 +62,6 @@ fn testMulAdd80() !void { } test "@mulAdd f128" { - 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_arm) return error.SkipZigTest; // TODO From 3d22327b2392050e088c8b6d5c21ae31bbd0cd90 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 25 Oct 2022 05:22:34 -0400 Subject: [PATCH 45/47] cbe: enable test fixed by #13296 --- test/behavior/math.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 96683c1816..b3558094f9 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -1662,7 +1662,6 @@ test "fabs" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) 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 inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| { // normals From e20d2b3151607fe078b43331ea27d5b34f95360b Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 25 Oct 2022 07:00:17 -0400 Subject: [PATCH 46/47] cbe: fix floating point builtins --- lib/include/zig.h | 90 +++++++++++++++++++++++------------------- src/codegen/c.zig | 20 +++++----- test/behavior/cast.zig | 1 - test/behavior/math.zig | 13 ------ 4 files changed, 59 insertions(+), 65 deletions(-) diff --git a/lib/include/zig.h b/lib/include/zig.h index b64e157b98..95c7b8b4b3 100644 --- a/lib/include/zig.h +++ b/lib/include/zig.h @@ -216,7 +216,6 @@ typedef signed long int zig_c_long; typedef unsigned long int zig_c_ulong; typedef signed long long int zig_c_longlong; typedef unsigned long long int zig_c_ulonglong; -typedef long double zig_c_longdouble; typedef uint8_t zig_u8; typedef int8_t zig_i8; @@ -255,120 +254,111 @@ typedef int64_t zig_i64; #define zig_minInt_i64 INT64_MIN #define zig_maxInt_i64 INT64_MAX +#define zig_builtin_f16(name) __##name##h +#define zig_builtin_constant_f16(name) zig_suffix_f16(__builtin_##name) #if FLT_MANT_DIG == 11 typedef float zig_f16; #define zig_suffix_f16(x) x##f -#define zig_builtin_f16(name) __builtin_##name##f #elif DBL_MANT_DIG == 11 typedef double zig_f16; #define zig_suffix_f16(x) x -#define zig_builtin_f16(name) __builtin_##name #elif LDBL_MANT_DIG == 11 typedef long double zig_f16; #define zig_suffix_f16(x) x##l -#define zig_builtin_f16(name) __builtin_##name##l #elif FLT16_MANT_DIG == 11 typedef _Float16 zig_f16; #define zig_suffix_f16(x) x##f16 -#define zig_builtin_f16(name) __builtin_##name #elif defined(__SIZEOF_FP16__) typedef __fp16 zig_f16; -#define zig_suffix_f16(x) x -#define zig_builtin_f16(name) __builtin_##name +#define zig_suffix_f16(x) x##f16 #endif +#define zig_builtin_f32(name) name##f +#define zig_builtin_constant_f32(name) zig_suffix_f32(__builtin_##name) #if FLT_MANT_DIG == 24 typedef float zig_f32; #define zig_suffix_f32(x) x##f -#define zig_builtin_f32(name) __builtin_##name##f #elif DBL_MANT_DIG == 24 typedef double zig_f32; #define zig_suffix_f32(x) x -#define zig_builtin_f32(name) __builtin_##name #elif LDBL_MANT_DIG == 24 typedef long double zig_f32; #define zig_suffix_f32(x) x##l -#define zig_builtin_f32(name) __builtin_##name##l #elif FLT32_MANT_DIG == 24 typedef _Float32 zig_f32; #define zig_suffix_f32(x) x##f32 -#define zig_builtin_f32(name) __builtin_##name #endif +#define zig_builtin_f64(name) name +#define zig_builtin_constant_f64(name) zig_suffix_f64(__builtin_##name) #if FLT_MANT_DIG == 53 typedef float zig_f64; #define zig_suffix_f64(x) x##f -#define zig_builtin_f64(name) __builtin_##name##f #elif DBL_MANT_DIG == 53 typedef double zig_f64; #define zig_suffix_f64(x) x -#define zig_builtin_f64(name) __builtin_##name #elif LDBL_MANT_DIG == 53 typedef long double zig_f64; #define zig_suffix_f64(x) x##l -#define zig_builtin_f64(name) __builtin_##name##l #elif FLT64_MANT_DIG == 53 typedef _Float64 zig_f64; #define zig_suffix_f64(x) x##f64 -#define zig_builtin_f64(name) __builtin_##name##l #elif FLT32X_MANT_DIG == 53 typedef _Float32x zig_f64; #define zig_suffix_f64(x) x##f32x -#define zig_builtin_f64(name) __builtin_##name##l #endif +#define zig_builtin_f80(name) __##name##x +#define zig_builtin_constant_f80(name) zig_suffix_f80(__builtin_##name) #if FLT_MANT_DIG == 64 typedef float zig_f80; #define zig_suffix_f80(x) x##f -#define zig_builtin_f80(name) __builtin_##name##f #elif DBL_MANT_DIG == 64 typedef double zig_f80; #define zig_suffix_f80(x) x -#define zig_builtin_f80(name) __builtin_##name #elif LDBL_MANT_DIG == 64 typedef long double zig_f80; #define zig_suffix_f80(x) x##l -#define zig_builtin_f80(name) __builtin_##name##l #elif FLT80_MANT_DIG == 64 typedef _Float80 zig_f80; #define zig_suffix_f80(x) x##f80 -#define zig_builtin_f80(name) __builtin_##name##l #elif FLT64X_MANT_DIG == 64 typedef _Float64x zig_f80; #define zig_suffix_f80(x) x##f64x -#define zig_builtin_f80(name) __builtin_##name##l #elif defined(__SIZEOF_FLOAT80__) typedef __float80 zig_f80; #define zig_suffix_f80(x) x##l -#define zig_builtin_f80(name) __builtin_##name##l #endif +#define zig_builtin_f128(name) name##q +#define zig_builtin_constant_f128(name) zig_suffix_f80(__builtin_##name) #if FLT_MANT_DIG == 113 typedef float zig_f128; #define zig_suffix_f128(x) x##f -#define zig_builtin_f128(name) __builtin_##name##f #elif DBL_MANT_DIG == 113 typedef double zig_f128; #define zig_suffix_f128(x) x -#define zig_builtin_f128(name) __builtin_##name #elif LDBL_MANT_DIG == 113 typedef long double zig_f128; #define zig_suffix_f128(x) x##l -#define zig_builtin_f128(name) __builtin_##name##l #elif FLT128_MANT_DIG == 113 typedef _Float128 zig_f128; #define zig_suffix_f128(x) x##f128 -#define zig_builtin_f128(name) __builtin_##name##l #elif FLT64X_MANT_DIG == 113 typedef _Float64x zig_f128; #define zig_suffix_f128(x) x##f64x -#define zig_builtin_f128(name) __builtin_##name##l #elif defined(__SIZEOF_FLOAT128__) typedef __float128 zig_f128; -#define zig_suffix_f128(x) x##l -#define zig_builtin_f128(name) __builtin_##name##l +#define zig_suffix_f128(x) x##q +#undef zig_builtin_constant_f128 +#define zig_builtin_constant_f128(name) __builtin_##name##f128 #endif +typedef long double zig_c_longdouble; +#define zig_suffix_c_longdouble(x) x##l +#define zig_builtin_c_longdouble(name) zig_suffix_c_longdouble(name) +#define zig_builtin_constant_c_longdouble(name) zig_suffix_c_longdouble(__builtin_##name) + zig_extern_c void *memcpy (void *zig_restrict, void const *zig_restrict, zig_usize); zig_extern_c void *memset (void *, int, zig_usize); @@ -1476,20 +1466,38 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, zig_u8 bits) { /* ========================== Float Point Routines ========================== */ -#define zig_float_builtins(w) \ - static inline zig_f##w zig_div_trunc_f##w(zig_f##w lhs, zig_f##w rhs) { \ - return zig_builtin_f##w(trunc)(lhs / rhs); \ +#define zig_float_builtins(Type) \ + zig_extern_c zig_##Type zig_builtin_##Type(sqrt)(zig_##Type); \ + zig_extern_c zig_##Type zig_builtin_##Type(sin)(zig_##Type); \ + zig_extern_c zig_##Type zig_builtin_##Type(cos)(zig_##Type); \ + zig_extern_c zig_##Type zig_builtin_##Type(tan)(zig_##Type); \ + zig_extern_c zig_##Type zig_builtin_##Type(exp)(zig_##Type); \ + zig_extern_c zig_##Type zig_builtin_##Type(exp2)(zig_##Type); \ + zig_extern_c zig_##Type zig_builtin_##Type(log)(zig_##Type); \ + zig_extern_c zig_##Type zig_builtin_##Type(log2)(zig_##Type); \ + zig_extern_c zig_##Type zig_builtin_##Type(log10)(zig_##Type); \ + zig_extern_c zig_##Type zig_builtin_##Type(fabs)(zig_##Type); \ + zig_extern_c zig_##Type zig_builtin_##Type(floor)(zig_##Type); \ + zig_extern_c zig_##Type zig_builtin_##Type(ceil)(zig_##Type); \ + zig_extern_c zig_##Type zig_builtin_##Type(round)(zig_##Type); \ + zig_extern_c zig_##Type zig_builtin_##Type(trunc)(zig_##Type); \ + zig_extern_c zig_##Type zig_builtin_##Type(fmod)(zig_##Type, zig_##Type); \ + zig_extern_c zig_##Type zig_builtin_##Type(fma)(zig_##Type, zig_##Type, zig_##Type); \ +\ + static inline zig_##Type zig_div_trunc_##Type(zig_##Type lhs, zig_##Type rhs) { \ + return zig_builtin_##Type(trunc)(lhs / rhs); \ } \ \ - static inline zig_f##w zig_div_floor_f##w(zig_f##w lhs, zig_f##w rhs) { \ - return zig_builtin_f##w(floor)(lhs / rhs); \ + static inline zig_##Type zig_div_floor_##Type(zig_##Type lhs, zig_##Type rhs) { \ + return zig_builtin_##Type(floor)(lhs / rhs); \ } \ \ - static inline zig_f##w zig_mod_f##w(zig_f##w lhs, zig_f##w rhs) { \ - return lhs - zig_div_floor_f##w(lhs, rhs) * rhs; \ + static inline zig_##Type zig_mod_##Type(zig_##Type lhs, zig_##Type rhs) { \ + return lhs - zig_div_floor_##Type(lhs, rhs) * rhs; \ } -zig_float_builtins(16) -zig_float_builtins(32) -zig_float_builtins(64) -zig_float_builtins(80) -zig_float_builtins(128) +zig_float_builtins(f16) +zig_float_builtins(f32) +zig_float_builtins(f64) +zig_float_builtins(f80) +zig_float_builtins(f128) +zig_float_builtins(c_longdouble) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 74a4fb178a..098424524f 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -798,7 +798,7 @@ pub const DeclGen = struct { "inf" else unreachable; - try writer.writeAll("zig_builtin_"); + try writer.writeAll("zig_builtin_constant_"); try dg.renderTypeForBuiltinFnName(writer, ty); try writer.writeByte('('); try writer.writeAll(operation); @@ -2006,18 +2006,16 @@ pub const DeclGen = struct { fn renderTypeForBuiltinFnName(dg: *DeclGen, writer: anytype, ty: Type) !void { const target = dg.module.getTarget(); - const c_bits = if (ty.isAbiInt()) c_bits: { + if (ty.isAbiInt()) { const int_info = ty.intInfo(target); - try writer.writeByte(signAbbrev(int_info.signedness)); - break :c_bits toCIntBits(int_info.bits) orelse + const c_bits = toCIntBits(int_info.bits) orelse return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); - } else if (ty.isRuntimeFloat()) c_bits: { - try writer.writeByte('f'); - break :c_bits ty.floatBits(target); + try writer.print("{c}{d}", .{ signAbbrev(int_info.signedness), c_bits }); + } else if (ty.isRuntimeFloat()) { + try ty.print(writer, dg.module); } else return dg.fail("TODO: CBE: implement renderTypeForBuiltinFnName for type {}", .{ ty.fmt(dg.module), }); - try writer.print("{d}", .{c_bits}); } fn renderBuiltinInfo(dg: *DeclGen, writer: anytype, ty: Type, info: BuiltinInfo) !void { @@ -3077,7 +3075,8 @@ fn airBinOp( const operand_ty = f.air.typeOf(bin_op.lhs); const target = f.object.dg.module.getTarget(); - if (operand_ty.bitSize(target) > 64) return try airBinBuiltinCall(f, inst, operation, info); + if (operand_ty.isInt() and operand_ty.bitSize(target) > 64) + return try airBinBuiltinCall(f, inst, operation, info); const inst_ty = f.air.typeOfIndex(inst); const lhs = try f.resolveInst(bin_op.lhs); @@ -3104,7 +3103,8 @@ fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: []const u8) !CValue { const operand_ty = f.air.typeOf(bin_op.lhs); const target = f.object.dg.module.getTarget(); - if (operand_ty.bitSize(target) > 64) return try airCmpBuiltinCall(f, inst, operator); + if (operand_ty.isInt() and operand_ty.bitSize(target) > 64) + return try airCmpBuiltinCall(f, inst, operator); const inst_ty = f.air.typeOfIndex(inst); const lhs = try f.resolveInst(bin_op.lhs); diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 4e49935f7c..2cbfdd1d4a 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -1379,7 +1379,6 @@ test "pointer to empty struct literal to mutable slice" { } test "coerce between pointers of compatible differently-named floats" { - 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 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO diff --git a/test/behavior/math.zig b/test/behavior/math.zig index b3558094f9..17a4c5e64b 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -990,7 +990,6 @@ test "allow signed integer division/remainder when values are comptime-known and } test "quad hex float literal parsing accurate" { - 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 @@ -1178,7 +1177,6 @@ fn remdivOne(comptime T: type, a: T, b: T, c: T) !void { test "float remainder division using @rem" { 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_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1222,7 +1220,6 @@ fn fremOne(comptime T: type, a: T, b: T, c: T, epsilon: T) !void { test "float modulo division using @mod" { 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_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -1349,7 +1346,6 @@ test "@floor f80" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) 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_llvm and builtin.os.tag == .windows) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/12602 try testFloor(f80, 12.0); @@ -1361,7 +1357,6 @@ test "@floor f128" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) 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 try testFloor(f128, 12.0); comptime try testFloor(f128, 12.0); @@ -1397,7 +1392,6 @@ test "@ceil f80" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) 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_llvm and builtin.os.tag == .windows) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/12602 try testCeil(f80, 12.0); @@ -1409,7 +1403,6 @@ test "@ceil f128" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) 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 try testCeil(f128, 12.0); comptime try testCeil(f128, 12.0); @@ -1445,7 +1438,6 @@ test "@trunc f80" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) 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_llvm and builtin.os.tag == .windows) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/12602 try testTrunc(f80, 12.0); @@ -1463,7 +1455,6 @@ test "@trunc f128" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) 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 try testTrunc(f128, 12.0); comptime try testTrunc(f128, 12.0); @@ -1507,7 +1498,6 @@ test "@round f80" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) 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_llvm and builtin.os.tag == .windows) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/12602 try testRound(f80, 12.0); @@ -1519,7 +1509,6 @@ test "@round f128" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) 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 try testRound(f128, 12.0); comptime try testRound(f128, 12.0); @@ -1553,7 +1542,6 @@ test "vector integer addition" { } test "NaN comparison" { - 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_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -1570,7 +1558,6 @@ test "NaN comparison" { } test "NaN comparison f80" { - 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_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO From 4364f5147697f3c6a9147efd74eec82586cf568c Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 29 Oct 2022 16:53:47 -0400 Subject: [PATCH 47/47] cbe: finish partial zig_noreturn rewrite --- lib/include/zig.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/include/zig.h b/lib/include/zig.h index 95c7b8b4b3..ea2bbca132 100644 --- a/lib/include/zig.h +++ b/lib/include/zig.h @@ -176,16 +176,12 @@ #if __STDC_VERSION__ >= 201112L #define zig_noreturn _Noreturn void -#define zig_threadlocal thread_local -#elif __GNUC__ -#define zig_noreturn __attribute__ ((noreturn)) void -#define zig_threadlocal __thread +#elif zig_has_attribute(noreturn) +#define zig_noreturn __attribute__((noreturn)) void #elif _MSC_VER #define zig_noreturn __declspec(noreturn) void -#define zig_threadlocal __declspec(thread) #else #define zig_noreturn void -#define zig_threadlocal zig_threadlocal_unavailable #endif #define zig_bitSizeOf(T) (CHAR_BIT * sizeof(T))