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 0c9ecbc08c62307aaf58855ca17f3817726c7e25..0fcb2ae5b19c5f585ea11fbcdccdf4a3d6a9f17c 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)}); } -- 2.54.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 d6584d75aed0969a9501a2b8788959a219e9f8fb..704e8a1391989c57891e975e9fe20933aca974ab 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; } -- 2.54.0 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 704e8a1391989c57891e975e9fe20933aca974ab..4bc573ba6cc4a67d01f4e6d4f4b5a7f9dae01753 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 955044f90d4616175a92b2014a132f5e1ecc27c1..ebe37a819238ad9bf29ef9dac5f6c9d43d9b33ce 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 6b64a0dc014226b090549a07e58ba46bb5bd003a..7fb125ab2a2b47ba6c443e21b36fcf427032aab8 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))); + } +} -- 2.54.0 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 4bc573ba6cc4a67d01f4e6d4f4b5a7f9dae01753..183baca48d8edf89936efd74b178e6c5c531f3c9 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 ebe37a819238ad9bf29ef9dac5f6c9d43d9b33ce..ad64f764664554798867e1bba819c06e6d703d49 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 7fb125ab2a2b47ba6c443e21b36fcf427032aab8..8a78e6eaa0a7ede4c208d0b7e9a530ab0e056e26 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; -- 2.54.0 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 | 94 +++++++++++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 36 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 183baca48d8edf89936efd74b178e6c5c531f3c9..33339f589d7bd8cbc5cd1e4b981a4a27d6aafa2b 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; + + empty = false; } + if (empty) try writer.writeByte('0'); - try writer.writeAll("}"); + 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; -- 2.54.0 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 33339f589d7bd8cbc5cd1e4b981a4a27d6aafa2b..2ae0421008b770a3e7da293628e93252b67fd09d 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(); -- 2.54.0 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 2ae0421008b770a3e7da293628e93252b67fd09d..2684ebf3413cfddd630ef411c8b3bef8b7564d84 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); - }; - return std.fmt.bufPrint(buf, "{d}", .{val}) catch |err| switch (err) { - error.NoSpaceLeft => unreachable, +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, }; - }, - } + 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 { -- 2.54.0 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 2684ebf3413cfddd630ef411c8b3bef8b7564d84..645bc7074f8c253585a4f31a30f7136f0e082692 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, + }; } }; -- 2.54.0 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 645bc7074f8c253585a4f31a30f7136f0e082692..be69321ec635a778824f479b8194fa3110337e18 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()) { -- 2.54.0 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 be69321ec635a778824f479b8194fa3110337e18..f52d2e83cb8726683caee8a8c4fea6a1419b3a4f 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); -- 2.54.0 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 f52d2e83cb8726683caee8a8c4fea6a1419b3a4f..c11187e6dab5273448edc24eaee77e208ff7bb36 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); -- 2.54.0 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 c11187e6dab5273448edc24eaee77e208ff7bb36..b884e2fb0f57776ce59a41629a6201b5fb4b053e 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 5cacd2904498b1ccd81ee53a743385745430d630..704dd829fb32010cf2268a1b741d472b271f767f 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; } -- 2.54.0 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 b884e2fb0f57776ce59a41629a6201b5fb4b053e..fddf33defdb51f51bfea3867093588ae53bc900e 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); -- 2.54.0 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 fddf33defdb51f51bfea3867093588ae53bc900e..3e8398bb83293e0ec516f6c94f9f5fabfc151124 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()) { -- 2.54.0 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 2f5d752087a9fa0ced185277b8495f8b74054903..3e4a45c9926cb260bcdaa34e420dfe9f6486f694 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 f822a81a138f86c311238abd2353d4440adf0481..0982985cee1432114a34b7ce107c6c11cb2ad249 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 4a483ec0d5da42ba715ffe786f8853dfa1c53b78..a0c21a75d8d9614415e67741e8e1c11856f83fa7 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 b85d39d7bbdd369af4a5fe9c97446cae9b0a207b..48f2e3e22db8284b2aada2ee008c3d701c6e19a5 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; -- 2.54.0 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 3e8398bb83293e0ec516f6c94f9f5fabfc151124..a973dd1fc52479e211ffc8cf86f694601e0c9f91 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 2ebdda341ac08f4b3eb0124289e2391c46a34252..3f7b7184c29c67bf356c36b62acc900a4978cadd 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 d6491ff22e9cec6859b6422724cabb578ce4f590..4dd62906c41e72b6a0d65c7cef75cd83146ed7ae 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 ab084ff30a75f9f059e03ecd32a26b5174be90f4..911fe28cd87ba4f43fe7a95e203c9e4a1f8340b3 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 f82e6eef4b9c5f536a87b9a51c40cd3a4c4fd839..e2df3d6aed026a78f1925c242274dec66a7df8a0 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 c629a1a34bd4d81dc7cfd47a0584da2f55a01f49..e35b037749983fc96f28c0254d4575f9ccf09321 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 72084188d72f164be30228a720f7b6a5d6ddc9e3..e781b51fe9ce8c2e1fb73d873d3424d550eaa673 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 02cccf3e0aa41d80447985b8084e5093deecd6df..035ea103d91c5a43b03b48c24d63cddbcf1c9d8a 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 67d26d6b4e55bbfbb8946087c6703af7fe6d8b27..8e45c6612e15f838c5f740d6e6f4f3f1c59283a9 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 4699bda33335ce57ad00fdda58788d2a4c98f168..7f13b6efd80f98a5c606a29bddc927a253208325 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 e8fe106ac7c7c2d2839d7ea2b727a71378e36172..3c89163f27202621d5542fc2c62b3df01b5e83cd 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 d6ea8a2bad45f329bc4ad47bf59fbd66d66d1e6a..fbd05fb73ca44580c291a1c720311bb646ae9e1a 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 1933c8022e62f61b87fe27e36fd51673cc1b2ac5..95eafbeeaae985628efac1eaebc39b5726f3da01 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 dfcea537b530b6df4fc578c83dbc5f2a5048dbc6..5eff7f81b9536cc0e83f67d47f4e75e0cdbb3ebe 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 36fd763ebd367dfc10f11057d164432e8cb011b7..63fc43b85bb502d18df0df0723abcf157d0efd1d 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 c93bbf8d20eb82aba8a33cb3bff089deddbf0037..20c9a8c9137f5aba40fda8a08b973eef3b4feaae 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 1122ab4fe9695bad98579ef1f7de3abfead3fdc8..3cf42a2ce74ac4a86f8415d14c1a64b2e0de1c9f 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 bd289af2b477dd8e238ed7dee65ebe0b514e7f56..68b7d90ae8f05dbd21e2b3f4d4d256d67884640f 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 aa339de3265e4a60c61c661929fe354c46b24910..737333a3d7a4bfc457b5e025420c5395713128fa 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 caf36390ecf9d3ba84a6fffd6ef66d924b569d4a..d7d01a46c3d69a46c62d6fce5f598965d43c3728 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 9955d1f2f58abb490a4857ffcd8e40bf4a892a3d..04d5f0e5b48b22f834e04311aed0e3af39828bd3 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 03e010e4f128f0990d81ab586819058a136dfa9a..165bf899ca12a8783cda0ef9de0c9e7fd87be2fb 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 503a402fed347c110d1cd2add0b97de9968a1da7..48475cd96b55e4e4474eed856f78b86375c8754d 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 37c6ce369184357a95b37f3b0880a2d7e48da980..a16c7af5a5906b8db4ed000f46c7c4ec04f52bf9 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 9a02e7485339ba2e83cf2152e4c9c40bff3e6bc5..cf086328a4bfba5a3f92e6332bd035cceccf314a 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 c30039eb524f79770d878782cd78d75fad472da6..c6df9d32572d6beb375a605baf09fee851ba4507 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 051e464b72a1f1b09135108b718680b374b5d621..3f79df0446955bde2facb47dcb4f8b50de69dc44 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 8a78e6eaa0a7ede4c208d0b7e9a530ab0e056e26..25ac894c6c44b1629fd372886529e81c4ae53aaf 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 2fa07d0de7971b9178470736d51cb946315022a3..8ae10fa1ad4820f4d43baeee1bec27a45152cc7c 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 47ba63c429582d41df0d1949c09ad3c2c0e4face..e46a91022704f8981e72173bfc13ad66d02a5151 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 d70b7a8f7e880e54db104b59b3aebf97a78041af..744f4adb9edc8f3c3dc1a145f0ab9db6fc694b6e 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 eb693147e6b5a18110077a55020a123da99d44cb..67b29c68853ec87cd6aac8de449da0507e95ad05 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 b0fb0f3a4239e91d57cd2144e59b9ba798d3154b..d3fb839baac4347b4a6b0f39052e00b1f8e1caf7 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 25527ed742b7f294a8142142eb01c902a6971d4a..1b6fc3634fccd7ece5b9be6a31f82365347a2704 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 2b9df0652775d0af296e04be405040679bb5fd18..acd28426abf473bc5e858084dd5ec58db5a666e9 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 6abae1a7b6cf7c30d10980c681055210b6687bf4..36076df9cd626f49e115ea4915ce11812275e559 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 04d217f4889a8384712632655fba4b216bb10ee6..3a4de7b6f6686d243637a9dd8dc06ec0f97bfc7d 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 4b70f502c7cfc543cc78772cbf519ea26167a6bb..1b71d67df3b0fb3746bce4eb6fc85a537c9649fd 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 48f2e3e22db8284b2aada2ee008c3d701c6e19a5..80a3c7210a5792b8e35be36e94d9a96d68c9de6f 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 ddad27e15058e1709afb923825b9f35a38c30a52..cfb522b74ec49c3f18373f111ae4cd97540d7e68 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 72e86ca7ac3c8fe358561e6157e3c944a5a726b0..0309ed801e1f485dda7a66e8afac854efe11808f 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 691755ac2af55c6f647fe949e538fa561d1eaa1e..801c7d6a9bed693505681bbd4ffd2def51e2a5ef 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; -- 2.54.0 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 43d9913039c25657214fa6b83086127802e7d66e..9568753345fd0ca8ca0ca302825d80e88571b0dc 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 a973dd1fc52479e211ffc8cf86f694601e0c9f91..e006c9c795276479e526c0a4ff065a5f6772880c 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 FormatIntLiteralContext = struct { + ty: Type, + val: Value, + mod: *Module, + location: ValueRenderLocation, }; -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 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, - }; - 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); +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); - 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 }); - } - } + 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, + }; - 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 ad64f764664554798867e1bba819c06e6d703d49..bb321c1e831ac3602dc67fb92bb6f5696265eafc 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 a2f0bb9e8f2f96b3249e0500c0dc4a43de37a1a3..5c74f39290d8b79be90f215710d12e111a8d327d 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); }, }; -- 2.54.0 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 9568753345fd0ca8ca0ca302825d80e88571b0dc..e4a0c4cc59a403dff9def61f1301c66acbc4db31 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 fc71da56f3f0707af6eb731a3b988737b8b97cfe..99332e5d2bb662740779db4d0cf5ed5e4caa8d10 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 e006c9c795276479e526c0a4ff065a5f6772880c..beafafc94fd175efaca2645cd9d04440ae17fcac 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"); + if (empty) { + try buffer.appendSlice(indent); + try buffer.appendSlice("char empty_union;\n"); + } } - try buffer.appendSlice("} "); - if (t.unionTagTypeSafety()) |_| { - try buffer.appendSlice("payload;\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 bb321c1e831ac3602dc67fb92bb6f5696265eafc..d7432eb015cea9098a8afb2baff06eda72ac76ad 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 3f7b7184c29c67bf356c36b62acc900a4978cadd..3bed9cbbb696fb3ac2334614fad0a727d4937b75 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 1495ac75a4fe59a691fd2ee1a67a75302284cb19..50bf65a8a458a0cf91167a648fe43dbdb4a2788f 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 e2df3d6aed026a78f1925c242274dec66a7df8a0..78cc68331015f068c8543037161d6f66c87dc8a0 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 0f574942d329ef64f5b951ef4b29f78f1120d786..9d93dacca269f75c38116fa35d4b533e53cffd48 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 6a41617d2bd320b4a37ec5d8f7a92ca1908e030c..cc12c5d0be9aedd06911a4b86c98c2733c2e740a 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 4d76230c885ca1c96c65f3593475051ec3f521c7..ff759f2846d93399347306df03d4f7ef36c04754 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 cf086328a4bfba5a3f92e6332bd035cceccf314a..6445bbf3e3b87ea0620737d2608067e1517d1116 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 a5eb25d4f5b96bd201208d60fcc1b2ff5d642284..3bb4ba9d0a3d2bd843fd772062b6ac39675b2892 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 a0c21a75d8d9614415e67741e8e1c11856f83fa7..b7904fb7f998bc14a2e18ba548bf57b87eff2e0a 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 744f4adb9edc8f3c3dc1a145f0ab9db6fc694b6e..193efd3e7a452e343c35b4642bd8435bb27b265e 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 685f65b2b9d20a59d26b474f7469ff4a7599b869..977d06204e43de5d2c323508db3de579d563e446 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 1ce5ffb1e7ec4dc44bd0f90a10f909466dfb778d..814e00795056c43ae13b03c703db5e7742b28ce1 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 5a878112b552905d7e2110c2b5bc37156332197d..f9571e9590e650f5eed44d29c87ebfca7e202e04 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 1790fe45053819908a52ce4995fae9eb5ee9c575..16bc32a5f1350c26262c90890264726e051eb8c7 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 acd28426abf473bc5e858084dd5ec58db5a666e9..cd9572ae3a09b4924bdfc13dda0b2d6698059972 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 7a2e32b2c2a4c0b7b4d02e58c9d8553db31f49fd..0e015aaf58a21b37e61bbaa8f1d9740cd2ed2369 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 716afc3ace72c059b918b0c3f20084d32ba88198..f58853adb102d8506f03e2c91a6a6c094a80c6a3 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 cfb522b74ec49c3f18373f111ae4cd97540d7e68..0160c21761a15f765be295205bd3660b45223f8a 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 80fa2021d8a6a11b1c416c14df268e487c77e6a6..581d68dcb6c2d0ff8508a124012450cd9935bdb3 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 -- 2.54.0 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 beafafc94fd175efaca2645cd9d04440ae17fcac..39ed7cf4779c471c3137ef91c24bd4241abc7baa 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 3a4de7b6f6686d243637a9dd8dc06ec0f97bfc7d..94590d31590966accc5e2a545787ef0769f27765 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 0160c21761a15f765be295205bd3660b45223f8a..91dea304c7872bc40486ee985913861c91bd947c 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 -- 2.54.0 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 39ed7cf4779c471c3137ef91c24bd4241abc7baa..f00bd2877365f1311784e0ff307d5f7fe8769ffc 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 3f79df0446955bde2facb47dcb4f8b50de69dc44..55dac727e8cfb3bfbbcc46f58a861b96e9c5bc12 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 -- 2.54.0 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 f00bd2877365f1311784e0ff307d5f7fe8769ffc..6b25cf83604dc4d464938f0185018c5f45b58029 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 acd8d1730241eabfb94ff78af1483720fe38a473..b00c7dbca27125f7c4c6e195a301bdf47e35db5b 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; -- 2.54.0 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 6b25cf83604dc4d464938f0185018c5f45b58029..a60e6986a40810e39375f47ca07e1c546bba4707 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 78cc68331015f068c8543037161d6f66c87dc8a0..d47138010d0c13a27db15ffce6e9f1a584328552 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 cd9572ae3a09b4924bdfc13dda0b2d6698059972..6f5a4d93f40476bc2fac23982aff27f9e419b092 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); } -- 2.54.0 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 | 234 +++++++++++++++++++++++++++--------------- test/behavior/asm.zig | 1 - 2 files changed, 152 insertions(+), 83 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index a60e6986a40810e39375f47ca07e1c546bba4707..d479f30e604c9ee5e5d9f64ee348a7e9f376550a 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(); + 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; + try writer.writeAll("{\n"); + f.object.indent_writer.pushIndent(); - const inputs_extra_begin = extra_i; - for (inputs) |input, i| { + 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,44 @@ 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)); - - 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 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; + 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) { @@ -3518,53 +3534,79 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { // 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; - - // TODO honor these } } - 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", .{}); + 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(')'); } - 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); + 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) { + 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 += (constraint.len + name.len + (2 + 3)) / 4; + extra_i += clobber.len / 4 + 1; - 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.len == 0) continue; + + 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 8f235a384b9ab6a5f70da93a5b88a07dec48aaa5..c1542719645307fc07e13fe5f6c5294126a847ee 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 -- 2.54.0 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 d479f30e604c9ee5e5d9f64ee348a7e9f376550a..e3391c450af7ce1c77f19c46922ac27b11dd1d18 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 a538b62e8d569e34249a2a0a0f727ee8abf735d5..fec32947c9ea1acfcffd462ebc4439d657f9d7a2 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 4ac2b929a2751aa014fd54f2f7c72cb3d07cd31b..67b2e9093003a26856333236d1bfa6b16707d3b0 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 6445bbf3e3b87ea0620737d2608067e1517d1116..b9317d96adac92132d5bb258eb887dc0c2b06076 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 e46a91022704f8981e72173bfc13ad66d02a5151..64112d0d087562e338c855a78ad3d979dfbfa788 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 1b6fc3634fccd7ece5b9be6a31f82365347a2704..3b79f83d8fc4ec884d6e16087dfc440e9b1ace01 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 6f5a4d93f40476bc2fac23982aff27f9e419b092..a6b9a76ba6b134bf5697424ab995129e0e260668 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 feb382ed3ece692113da3b8d250d35793925aae1..22e5327b3878ae55fa05ee255f88c6c5a47376bc 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 2b715c3b23c1848febb2389f56b3bcb6b4025d40..4b36ded0874498a77e0bd41e1c25b35fbddd9f7a 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 -- 2.54.0 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 e3391c450af7ce1c77f19c46922ac27b11dd1d18..cecc2f8dbed008253537125df750f39a2dd2e53a 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 3bed9cbbb696fb3ac2334614fad0a727d4937b75..4188104459c3a6ae516fc14f9109787b9d580f4c 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 7f2cd2ab8d83562997d9ee3a8974e0e804b017a0..b88df909cdbff3fdb51032d5c764cd30fe6f58c4 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 3b79f83d8fc4ec884d6e16087dfc440e9b1ace01..8965c667c1b255de2b0b464d9f295e258a5d16a1 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 -- 2.54.0 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 | 113 ++++++++++++++++++--------------------- test/behavior/call.zig | 1 - test/behavior/struct.zig | 2 - 3 files changed, 53 insertions(+), 63 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index cecc2f8dbed008253537125df750f39a2dd2e53a..1ccb03a5542f1d2c8bd85b8a5b368b83e557c885 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()) { + 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)}); + }, .Struct => { - const tuple = vector_ty.tupleFields(); - var i: usize = 0; - for (elements) |elem, elem_index| { - if (tuple.values[elem_index].tag() != .unreachable_value) continue; + var empty = true; + for (elements) |element, index| { + if (inst_ty.structFieldValueComptime(index)) |_| continue; - const value = try f.resolveInst(elem); - if (i != 0) try writer.writeAll(", "); - try f.writeCValue(writer, value); - i += 1; + 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)}); }, - else => |tag| return f.fail("TODO: C backend: implement airAggregateInit for type {s}", .{@tagName(tag)}), + .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 a16c7af5a5906b8db4ed000f46c7c4ec04f52bf9..a8d59bddff89f3da1e282a40d8971aa615c79c71 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 a6b9a76ba6b134bf5697424ab995129e0e260668..f29df6becf7fa04f21a3be346e3c746ccfd833b4 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 -- 2.54.0 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 1ccb03a5542f1d2c8bd85b8a5b368b83e557c885..37baad3e9501b5c8e3110699ca6f4e9e2ca0ea2f 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 90db29615870ae6f4646d66b6135ee0363c0a7dd..91bf28386afb2f43093f1a868fd4716ef2a7ea85 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 b9317d96adac92132d5bb258eb887dc0c2b06076..3a7dda642c6f3fc4c39e55bc1a73ce72384f1cc6 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 64112d0d087562e338c855a78ad3d979dfbfa788..22935feaa1cdfcc0040e25f1ff7c13e204322037 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; -- 2.54.0 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 37baad3e9501b5c8e3110699ca6f4e9e2ca0ea2f..1e3669849be637ebcc5d39465f6980b8c092d0f8 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 d7432eb015cea9098a8afb2baff06eda72ac76ad..8509e4ae8a378e837635758439b0c791aed99862 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 911fe28cd87ba4f43fe7a95e203c9e4a1f8340b3..6311c4cd301b0cd0be7b3f77a47e2070aef432cd 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 c1542719645307fc07e13fe5f6c5294126a847ee..636647947919117a4a1e5ea6fbbf938337838a0b 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 8ae10fa1ad4820f4d43baeee1bec27a45152cc7c..438f05b5639e8b687f9a9c074570c830b67591fa 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 8965c667c1b255de2b0b464d9f295e258a5d16a1..4fe61f521d74430562e4de355962b2bc710d9513 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 -- 2.54.0 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 1e3669849be637ebcc5d39465f6980b8c092d0f8..942c2e28aca27068b75305c206cffb9e935effac 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 636647947919117a4a1e5ea6fbbf938337838a0b..fd1601bf064f10b242f18e632449b8b8a0e05054 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 0e017827051d8a2c98257f4070e33520771d303c..8ec2b42692c955368cb7c80d9f4daee6a6e7a055 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 057270a96ff4a55acd3806231d20d6ec4aa8aedb..9e92e66dafd7b4397a444edd319a42a5494fef75 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 -- 2.54.0 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 942c2e28aca27068b75305c206cffb9e935effac..26b177c2b788130469eb6a3217b87f7bde726d4f 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 3bb4ba9d0a3d2bd843fd772062b6ac39675b2892..4abd09b9c6b670696704bc12b323c421a91718af 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 193efd3e7a452e343c35b4642bd8435bb27b265e..e8cecf803a5e31362011b5a29e2b7cee0e9b292c 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(); -- 2.54.0 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 | 111 ++++++++++++++---- 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, 123 insertions(+), 88 deletions(-) diff --git a/lib/include/zig.h b/lib/include/zig.h index e4a0c4cc59a403dff9def61f1301c66acbc4db31..ee9e31496dbb7fecdc55b59880c420fc5ff3ac10 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 26b177c2b788130469eb6a3217b87f7bde726d4f..c9e77d0df4d278fd9d823f876932536458e9e984 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}); - try f.writeCValue(writer, ptr); - try writer.writeAll(", "); + 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, 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 50bf65a8a458a0cf91167a648fe43dbdb4a2788f..365df0992852db4eaf79cbf8adbf50b55b48889b 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 bfc05452a4990213da37564186678f6a493bf916..414217b7ea5ad0fe95b56f31b55ce4bdcaa2b697 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 7bf66496a4646875282a530d0240a6ab1b3e2c27..fdf43750be3f2eb29bd132f3e8bad600885c6238 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 438f05b5639e8b687f9a9c074570c830b67591fa..82bfee7e1c4a23d7bc3220ab4d190c38406e0c44 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 f9571e9590e650f5eed44d29c87ebfca7e202e04..1b5ee24936fb576ae260e003fce650cd66fa97ca 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 165e94fd4e731c476264d22b01cfbccd415640f2..daed41a626d51634f46760ade7e7da92a54e8cc4 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 d3fb839baac4347b4a6b0f39052e00b1f8e1caf7..05d2c7784c47522ac45362c042a547fb7753c72e 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 f29df6becf7fa04f21a3be346e3c746ccfd833b4..f9c1aec1e4a39937a412184af93788fad8018d91 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 9552ea5008207f8f2b2d94bcb9ab0636bff2d115..b8529742405a86bddb015ffd8ca6581aa2da2aff 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 94590d31590966accc5e2a545787ef0769f27765..6f5ccc737fd2dd0edd8d7821269d655631b0e620 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 1b71d67df3b0fb3746bce4eb6fc85a537c9649fd..42cb67ba2fd4dc6fbd3ea5ed80b2b03bf4f5259b 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 91dea304c7872bc40486ee985913861c91bd947c..2540d780a6c3ce1d9896f8bf7576afc9525e01e8 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 -- 2.54.0 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 ee9e31496dbb7fecdc55b59880c420fc5ff3ac10..41b7406fa2aff50ee55f236368407697ecd52cf3 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 21b05249a1f2c901324e8df516f2326ef8432bb8..de7aa07b8b8f3fd8e6c997eda6972a45940e69dd 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 010ecce0543458e5a746d0de3aadd61ad27d8213..7f320c010a8ae86de1e2662cae841ea2d281109c 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 9f70cce1eaee71110320e84820cea7715477a831..0563e2adad39326b3aa9d00fbaa868dd24469d8e 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 aafaf1b07389f7c3c898b86b0a003124af54722a..079d9c813a4784efe735878f90d8cc53931a4ca7 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 c9e77d0df4d278fd9d823f876932536458e9e984..6b89d37dd91adc1247818c91c68bd531ddf4689a 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 2885cdba95034aa5c527feb97bda21d8f7a6bb14..6bde852eeedb5cc0e7b8544f6a7c5f57a2272d3a 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 4ade6895f8d187af1a4d845bc4aab40f0912dd9f..2d28e59f4db199a4fe9ec9f211e036da1cd70bd1 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 22935feaa1cdfcc0040e25f1ff7c13e204322037..ad92cb8962f85fcbf18d75438f345c8c37568e08 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 f9c1aec1e4a39937a412184af93788fad8018d91..a6faee9858b01b46114ba4bd2b4ba4bd942c2510 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 6f5ccc737fd2dd0edd8d7821269d655631b0e620..07d8442844a8a2aa24ef33338438991c4da8536f 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 -- 2.54.0 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 6b89d37dd91adc1247818c91c68bd531ddf4689a..b5705ad6da624345436ddb9fd64705c9e085c915 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, -- 2.54.0 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 b5705ad6da624345436ddb9fd64705c9e085c915..d0bcba4ab2275e43ea930a60a2a1b3008d718601 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,29 +3598,26 @@ 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"); + 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("\")"); } - } else { - return f.fail("CBE: constraint not supported: '{s}'", .{constraint}); + try writer.writeAll(" = "); + try f.writeCValue(writer, input_val, .Initializer); + try writer.writeAll(";\n"); } } { @@ -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"); } } -- 2.54.0 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 | 321 ++++++++++++------------ 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, 164 insertions(+), 161 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index d0bcba4ab2275e43ea930a60a2a1b3008d718601..c12b028d0c9059c6d61f04011a0ecd91b0b863bd 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,49 +1155,67 @@ pub const DeclGen = struct { return name; } + 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 = 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 name = rendered[name_begin..name_end]; + + try dg.typedefs.ensureUnusedCapacity(1); + dg.typedefs.putAssumeCapacityNoClobber( + try t.copy(dg.typedefs_arena), + .{ .name = name, .rendered = rendered }, + ); + + 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); + 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 = "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("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 b94b4257b67762f2009222bcea292002bc71aa2f..5deca6bfa5bf6d3fb6e8a3d54ff1b4dd9212f54a 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 67b2e9093003a26856333236d1bfa6b16707d3b0..bd3f830e6024fa6e95fab6daa75364227ccb58f4 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 25ac894c6c44b1629fd372886529e81c4ae53aaf..2013cbcfa3d5d39dcaa16c4a79829403cd182b98 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 16bc32a5f1350c26262c90890264726e051eb8c7..4ab01ecddcb7a1fa9903971643877b6f48baf9ba 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 { -- 2.54.0 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 c12b028d0c9059c6d61f04011a0ecd91b0b863bd..5f62b93922d9e5a451b412427c9f3e0c93edf529 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 8509e4ae8a378e837635758439b0c791aed99862..a52ac63df5c5e084ea6ebd8791db733f3029960b 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 { -- 2.54.0 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 | 2352 +++++++++++------------ src/codegen/c.zig | 1115 +++++------ 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, 1636 insertions(+), 1861 deletions(-) diff --git a/lib/include/zig.h b/lib/include/zig.h index 41b7406fa2aff50ee55f236368407697ecd52cf3..45e573d0924e36f37d5967f4646c9aa033dfe76b 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,1366 +173,1258 @@ #define zig_fence(order) zig_unimplemented() #endif -#include -#include -#include - -#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); - - -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 __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 -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; - } - } - return lhs + rhs; -} +#define zig_bitSizeOf(T) (CHAR_BIT * sizeof(T)) -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; - } -} +typedef void zig_void; -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; - } - } - 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 -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; - } -} +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 -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; -} +#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 -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; - } -} +#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 -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; -} +#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 -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)); -} +#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 -static inline short zig_addw_short(short lhs, short rhs, short min, short max) { - return (short)(((unsigned short)lhs) + ((unsigned short)rhs)); +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 int zig_addw_int(int lhs, int rhs, int min, int max) { - return (int)(((unsigned)lhs) + ((unsigned)rhs)); +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 long zig_addw_long(long lhs, long rhs, long min, long max) { - return (long)(((unsigned long)lhs) + ((unsigned long)rhs)); +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 } -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)); +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 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 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 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); - } - } - return lhs - rhs; +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 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 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 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); - } - } - return lhs - rhs; +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 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_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 } -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; +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 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_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 } -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; +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 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_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 short zig_subw_short(short lhs, short rhs, short min, short max) { - return (short)(((unsigned short)lhs) - ((unsigned short)rhs)); +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 int zig_subw_int(int lhs, int rhs, int min, int max) { - return (int)(((unsigned)lhs) - ((unsigned)rhs)); +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 long zig_subw_long(long lhs, long rhs, long min, long max) { - return (long)(((unsigned long)lhs) - ((unsigned long)rhs)); +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 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)); +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 } -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); - } +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 - 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; + *res = zig_wrap_i32(full_res, bits); + return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); } -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); - } +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 - 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_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 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); +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 - int int_overflow; - *res = __addodi4(lhs, rhs, &int_overflow); - overflow = int_overflow != 0; + zig_c_int overflow_int; + zig_u64 full_res = __mulodi4(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_i64(full_res, bits); + return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); } -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); +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 - int int_overflow; - *res = __addoti4(lhs, rhs, &int_overflow); - overflow = int_overflow != 0; + return zig_mulo_u32(res, lhs, rhs, bits); #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_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); - } +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 - 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); - } +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 - 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_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 - int int_overflow; - *res = __uaddodi4(lhs, rhs, &int_overflow); - overflow = int_overflow != 0; + return zig_mulo_i32(res, lhs, rhs, bits); #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; -} +#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 -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); - } +#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 - 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); - } +#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 - 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); - } +#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 - 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); -#else - int int_overflow; - *res = __subodi4(lhs, rhs, &int_overflow); - overflow = int_overflow != 0; + +#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 - 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_u8 zig_byte_swap_u8(zig_u8 val, zig_u8 bits) { + return zig_wrap_u8(val >> (8 - bits), 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_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 - int int_overflow; - *res = __suboti4(lhs, rhs, &int_overflow); - overflow = int_overflow != 0; + 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 - 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_wrap_u16(full_res >> (16 - bits), bits); } -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 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 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); +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 - 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; - } + return zig_wrap_u32(full_res >> (32 - bits), bits); } -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); +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 - int int_overflow; - *res = __usubodi4(lhs, rhs, &int_overflow); - return int_overflow != 0; + 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 - } else { - if (rhs > lhs) { - *res = max - (rhs - lhs - 1); - return true; - } - *res = lhs - rhs; - return false; - } + return zig_wrap_u64(full_res >> (64 - bits), 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; - } +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 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); - } +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 - 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; + return zig_wrap_u8(full_res >> (8 - bits), bits); } -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; +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 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 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_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 - int int_overflow; - *res = __mulodi4(lhs, rhs, &int_overflow); - overflow = int_overflow != 0; + 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 - 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_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 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); +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 - int int_overflow; - *res = __muloti4(lhs, rhs, &int_overflow); - overflow = int_overflow != 0; + 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 - 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_wrap_u32(full_res >> (32 - bits), 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_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 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); - } +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 - 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; + return zig_wrap_u64(full_res >> (64 - bits), 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); - } +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 - uint64_t big_result = (uint64_t)lhs * (uint64_t)rhs; - if (big_result > max) { - *res = big_result - max - 1; - return true; + +#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; \ } - *res = big_result; - return false; -} - -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 /* 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 - int int_overflow; - *res = __umulodi4(lhs, rhs, &int_overflow); - overflow = int_overflow != 0; +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 - if (*res > max && !overflow) { - *res -= max - 1; - return true; + +#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 }; \ } - return overflow; + +#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 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 zig_i128 zig_not_i128(zig_i128 val, zig_u8 bits) { + (void)bits; + return ~val; } -static inline float zig_bitcast_f32_u32(uint32_t arg) { - float dest; - memcpy(&dest, &arg, sizeof dest); - return dest; +static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs) { + return lhs >> rhs; } -static inline float zig_bitcast_f64_u64(uint64_t arg) { - double dest; - memcpy(&dest, &arg, sizeof dest); - return dest; +static inline zig_u128 zig_shl_u128(zig_u128 lhs, zig_u8 rhs) { + return lhs << rhs; } -#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; \ +static inline zig_i128 zig_shl_i128(zig_i128 lhs, zig_u8 rhs) { + return lhs << rhs; } -#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) - +static inline zig_u128 zig_add_u128(zig_u128 lhs, zig_u128 rhs) { + return lhs + rhs; +} -#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; \ +static inline zig_i128 zig_add_i128(zig_i128 lhs, zig_i128 rhs) { + return lhs + rhs; } -#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; \ +static inline zig_u128 zig_sub_u128(zig_u128 lhs, zig_u128 rhs) { + return lhs - rhs; } -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) +static inline zig_i128 zig_sub_i128(zig_i128 lhs, zig_i128 rhs) { + return lhs - rhs; +} -#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; \ +static inline zig_u128 zig_mul_u128(zig_u128 lhs, zig_u128 rhs) { + return lhs * rhs; } -#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; \ +static inline zig_i128 zig_mul_i128(zig_i128 lhs, zig_i128 rhs) { + return lhs * rhs; } -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)) +static inline zig_u128 zig_div_trunc_u128(zig_u128 lhs, zig_u128 rhs) { + return lhs / rhs; +} -#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 zig_i128 zig_div_trunc_i128(zig_i128 lhs, zig_i128 rhs) { + return lhs / rhs; +} -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 zig_u128 zig_rem_u128(zig_u128 lhs, zig_u128 rhs) { + return lhs % rhs; } -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 zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) { + return lhs % rhs; } -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; +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)); } -#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 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)); +} + +#else /* zig_has_int128 */ -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; +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)) }; } -#define zig_clz_i128 zig_clz_u128 +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 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 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 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 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 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)); +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 }; } -#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 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 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)); +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; } -#define zig_ctz_i128 zig_ctz_u128 +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 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 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 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 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 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)); +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)); } -#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 +#endif /* zig_has_int128 */ -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_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); } -#define zig_popcount_i128 zig_popcount_u128 +static inline zig_u128 zig_wrap_u128(zig_u128 val, zig_u8 bits) { + return zig_and_u128(val, zig_maxInt(u128, bits)); +} -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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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); } -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; +#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); } -#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; \ - } +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; +} -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) +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 + *res = zig_addw_u128(lhs, rhs, bits); + return *res < lhs; +#endif +} -#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); \ - } +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 + zig_c_int overflow_int; + zig_i128 full_res = __addoti4(lhs, rhs, &overflow_int); + zig_bool overflow = overflow_int != 0; +#endif + *res = zig_wrap_i128(full_res, bits); + return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits); +} -#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); \ - } +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 + *res = zig_subw_u128(lhs, rhs, bits); + return *res > lhs; +#endif +} + +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 + zig_c_int overflow_int; + zig_i128 full_res = __suboti4(lhs, rhs, &overflow_int); + zig_bool overflow = overflow_int != 0; +#endif + *res = zig_wrap_i128(full_res, bits); + return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits); +} + +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 + *res = zig_mulw_u128(lhs, rhs, bits); + return rhs != zig_as_u128(0, 0) && lhs > zig_maxInt(u128, bits) / rhs; +#endif +} + +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 + zig_c_int overflow_int; + zig_i128 full_res = __muloti4(lhs, rhs, &overflow); + zig_bool overflow = overflow_int != 0; +#endif + *res = zig_wrap_i128(full_res, bits); + return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits); +} + +#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 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)); +} + +#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 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 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); +} -#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 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 + 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 + return zig_shr_u128(full_res, zig_as_u8(128) - bits); +} + +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 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 zig_i128 zig_bit_reverse_i128(zig_i128 val, zig_u8 bits) { + return zig_bit_reverse_u128(zig_bitcast_u128(val), 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); @@ -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 5f62b93922d9e5a451b412427c9f3e0c93edf529..00fc70ff2a44660234e46a4c8c6b903b6cd4dec0 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,55 +4184,74 @@ 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(); + 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; +} - // 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())}); - } +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.writeAll(");\n"); + try writer.print(") {s} {};\n", .{ operator, try f.fmtIntLiteral(Type.initTag(.i8), Value.zero) }); return local; } @@ -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 4188104459c3a6ae516fc14f9109787b9d580f4c..ca2aee52dd4063c107ad9fb554d8ad333e54e8b4 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 a013688f4e6d8cb0817bb7c2b11e216349712a3e..fdfb8884a57424d711d4e3887adcf5e0f6aa0547 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 3a7dda642c6f3fc4c39e55bc1a73ce72384f1cc6..0b8787e8b60a90b0d2b88800b2673f47774b5f1b 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 4abd09b9c6b670696704bc12b323c421a91718af..8524a11b6a97ed12ef12d3b6b4b088c46429152c 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 3a1b198aa1d06c02cfb069bc7e045b7c1057fd7f..ceb63290cd5133940ff754ed27a215e193a1cad6 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 e8cecf803a5e31362011b5a29e2b7cee0e9b292c..ca7c3b044a99298f3e2e1bb11b5d3d9fa6cdb389 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 814e00795056c43ae13b03c703db5e7742b28ce1..c688f27593fbf8060ed2c0e6682685879fac170a 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 1b5ee24936fb576ae260e003fce650cd66fa97ca..dd6aa7549d57a04133dd04e1869243ffaaa9b3fe 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 21e8b544a8cbad14c62af4b9827468b363221268..79f75e0131b6faeb36a6a71dba8e3896d5800faa 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 4ab01ecddcb7a1fa9903971643877b6f48baf9ba..6dff9a9112faa8fbf6e90bf78e03c0adf1c61d09 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 0c0069b28f0b5d4554066325a5a9a8c578e84801..6f27d414d2e8a8928eb808f34cf34e5c2e34c863 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 801c7d6a9bed693505681bbd4ffd2def51e2a5ef..b916e9ca872581c95a65fdcab359bbe96e0e1f76 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; -- 2.54.0 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 8571b260713ab628dff00b10df1b99ea37ca7be7..c3b9f09028e8a5bed25a08bf71a6a4e6dbc9b560 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 2766393487b66b379d7041554ac210eec3f1065e..93570025191aecfecc5989b4a094e008963af259 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 7f320c010a8ae86de1e2662cae841ea2d281109c..90aae99082fa7f208bc8343657731a3f8d33e736 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 0563e2adad39326b3aa9d00fbaa868dd24469d8e..cc8a92d7b6444e1a4cbfee934ce6fa852d929578 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 9f516ff1390f02aafc6203c8625793f8e58acdaf..a3bcb8527f7d022a89339467a8c0bdb398d73ba3 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 40abdd6c1fa4217b99f077793ecbb56bfa3f89a0..70cec703dad782900b6f9b84809b6b525344173d 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 e0680c81d7420441fabd29498bf9258a5c16f291..3b8758a0e5e43f0e48d2b9a51415e560ee076571 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 04e1d4cfad0ac9045fb033b9f550e2d5fb1e64c8..e9a55f60610e7d3e4609914ca65340e5bc0916f6 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' -- 2.54.0 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 | 953 +++++++++++++++++++++++--------- 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, 729 insertions(+), 328 deletions(-) diff --git a/lib/include/zig.h b/lib/include/zig.h index 45e573d0924e36f37d5967f4646c9aa033dfe76b..4ac6537188c43c3c43e4407b7c3c83710aa2f476 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 00fc70ff2a44660234e46a4c8c6b903b6cd4dec0..8052c8116b3c168bc9f2d29f5f540238d0aed870 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(); + 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, fn_info.return_type, .Forward); + 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; + const field_ty = struct_ty.structFieldType(index); - var field_name_buf: []const u8 = ""; + // Ensure complete type definition is visible before accessing fields. + try f.renderType(std.io.null_writer, struct_ty); + + 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); - switch (struct_ty.tag()) { - .@"struct" => { - const fields = struct_ty.structFields(); - field_name = fields.keys()[index]; - field_val_ty = fields.values()[index].ty; + 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 => { - const fields = struct_ty.unionFields(); - field_name = fields.keys()[index]; - field_val_ty = fields.values()[index].ty; - }, - .tuple, .anon_struct => { + .@"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}); - field_name = field_name_buf; - field_val_ty = tuple.types[index]; + break :field_name field_name_buf; }, else => unreachable, - } - 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); - 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"); - } + 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; - - if (!empty) try writer.writeAll(", "); - if (!inst_ty.isTupleOrAnonStruct()) { - try writer.print(".{ } = ", .{fmtIdent(inst_ty.structFieldName(index))}); + .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))}); + } + + 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; + + 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); + + 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); - 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; - - 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); + 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 e35b037749983fc96f28c0254d4575f9ccf09321..f5db19789d280a3492937f2b2ff73f6b6bff05a7 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 82bfee7e1c4a23d7bc3220ab4d190c38406e0c44..7e46633172a20ef59ba06b7539253ef49d6182aa 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 dd6aa7549d57a04133dd04e1869243ffaaa9b3fe..552a5fedf32983a289e3f6b7ca845ce1b5596615 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 a6faee9858b01b46114ba4bd2b4ba4bd942c2510..66bffdacffbd081d62b5b59b51001b4eb5ac7ba7 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 52cde0057efa18a508012829e19fc33e91054b83..945d820a6bd2c6271308c61d6d50e42556d28f6b 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; -- 2.54.0 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 4ac6537188c43c3c43e4407b7c3c83710aa2f476..0d9e1ab3c2087d7710b830fbdca2915e8e19c48d 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 cc8a92d7b6444e1a4cbfee934ce6fa852d929578..14903b0bc904525a6bf12d29114a4fae483e9240 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 0c1ee4c9ec5d7479a5091f398fe3f0534cba1e9d..41e5f23025fb38c062536cee8c32991196e6c3db 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 d22fdea86682d53a964a1bc3cc59f49bd891c9c3..479f3f781b024f1bbd030cc796b2cab4b3127f32 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); \\ ); } -- 2.54.0 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 8052c8116b3c168bc9f2d29f5f540238d0aed870..897af5ef65b4fb0439972f9675a0656e56786694 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 0b8787e8b60a90b0d2b88800b2673f47774b5f1b..8a0001297c520c7cd8f579bd233436fc96329c1a 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 -- 2.54.0 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 897af5ef65b4fb0439972f9675a0656e56786694..46215039c436666d924b34827ec9770aeabaaa7a 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 570a1f9522232ebd33d8d4f55956487b1c32284d..e8ce1867cc0a5c26da05342e46ee843aace94ab9 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); } -- 2.54.0 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 0d9e1ab3c2087d7710b830fbdca2915e8e19c48d..5327c59b8f239ebc827d98b0cd709d1fdb8378f6 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 46215039c436666d924b34827ec9770aeabaaa7a..6db5f5df5a9d5aa52765561f0ad5d2d90b6ddf41 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 78947d1776b8f7c83502ffa1ac317c4d70169bc6..14eea5cf7fc55a8c88de57f8ad3443befd33065e 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 8a0001297c520c7cd8f579bd233436fc96329c1a..4e49935f7c92509039cd75d003266fcaec22339f 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 8524a11b6a97ed12ef12d3b6b4b088c46429152c..fa2eb2f8f0d3566b2705425df5274ed18ce4f8fc 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 ca7c3b044a99298f3e2e1bb11b5d3d9fa6cdb389..b80a14fb57f1bbeadc7825d965b384b601e3adf2 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 c688f27593fbf8060ed2c0e6682685879fac170a..bff9bf3e32f65b5c693bfa57fbfc73d25f8cc8ba 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 05d2c7784c47522ac45362c042a547fb7753c72e..2e09aa280227693a24f08d4cdf50831388f18671 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 4b36ded0874498a77e0bd41e1c25b35fbddd9f7a..f9a1603a5f445a8aaccb3f5800bc2438531d89e4 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; -- 2.54.0 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 5327c59b8f239ebc827d98b0cd709d1fdb8378f6..b64e157b987edb2b7313a2c0be97ca082c90f7cf 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 6db5f5df5a9d5aa52765561f0ad5d2d90b6ddf41..74a4fb178ad6742bf826d9f53ecb13c4cfa554b3 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 fa2eb2f8f0d3566b2705425df5274ed18ce4f8fc..bdfdaacf010c7b49e48f03f6a5f01b97de7ea71f 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 b80a14fb57f1bbeadc7825d965b384b601e3adf2..96683c1816f6fe590a8238f667f0d7a84a90ac57 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 bff9bf3e32f65b5c693bfa57fbfc73d25f8cc8ba..9dfab2ad855e967b0bf6b6ed0b41fbc98a155132 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 -- 2.54.0 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 96683c1816f6fe590a8238f667f0d7a84a90ac57..b3558094f9bde75744589a9da527cf5b93f24841 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 -- 2.54.0 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 b64e157b987edb2b7313a2c0be97ca082c90f7cf..95c7b8b4b3aeea5e1e3640f81b53d5312128194f 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 74a4fb178ad6742bf826d9f53ecb13c4cfa554b3..098424524f678070e27e1c216f3ea7d90a799488 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 4e49935f7c92509039cd75d003266fcaec22339f..2cbfdd1d4a2335482131fc305de476f3c74bd10f 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 b3558094f9bde75744589a9da527cf5b93f24841..17a4c5e64b2001a0332032dfa19b6e742c8f28c1 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 -- 2.54.0 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 95c7b8b4b3aeea5e1e3640f81b53d5312128194f..ea2bbca1321af16938721729f52b3ae0767ae12f 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)) -- 2.54.0