From ddbdb83c865b1124487b3a00747fc5c1a67e5770 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Fri, 21 Oct 2022 18:56:18 -0700 Subject: stage 2: Update C types' size/alignment These updates were made by testing against the `sizeof/_Alignof` reported by Clang for all supported arch-OS-ABI combinations and correcting any discrepancies. This is bound to have a few errors (the recent long double fix for i386 Android is one example), but Clang is certainly not a bad place to start, especially for our most popular targets. --- src/type.zig | 363 +++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 276 insertions(+), 87 deletions(-) (limited to 'src/type.zig') diff --git a/src/type.zig b/src/type.zig index a2f0bb9e8f..bcb8969484 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2892,41 +2892,24 @@ pub const Type = extern union { .anyframe_T, => return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }, - .c_short => return AbiAlignmentAdvanced{ .scalar = @divExact(CType.short.sizeInBits(target), 8) }, - .c_ushort => return AbiAlignmentAdvanced{ .scalar = @divExact(CType.ushort.sizeInBits(target), 8) }, - .c_int => return AbiAlignmentAdvanced{ .scalar = @divExact(CType.int.sizeInBits(target), 8) }, - .c_uint => return AbiAlignmentAdvanced{ .scalar = @divExact(CType.uint.sizeInBits(target), 8) }, - .c_long => return AbiAlignmentAdvanced{ .scalar = @divExact(CType.long.sizeInBits(target), 8) }, - .c_ulong => return AbiAlignmentAdvanced{ .scalar = @divExact(CType.ulong.sizeInBits(target), 8) }, - .c_longlong => switch (target.cpu.arch) { - .i386 => switch (target.os.tag) { - .windows, .uefi => return AbiAlignmentAdvanced{ .scalar = 8 }, - else => return AbiAlignmentAdvanced{ .scalar = 4 }, - }, - else => return AbiAlignmentAdvanced{ .scalar = @divExact(CType.longlong.sizeInBits(target), 8) }, - }, - .c_ulonglong => switch (target.cpu.arch) { - .i386 => switch (target.os.tag) { - .windows, .uefi => return AbiAlignmentAdvanced{ .scalar = 8 }, - else => return AbiAlignmentAdvanced{ .scalar = 4 }, - }, - else => return AbiAlignmentAdvanced{ .scalar = @divExact(CType.ulonglong.sizeInBits(target), 8) }, - }, + .c_short => return AbiAlignmentAdvanced{ .scalar = CType.short.alignment(target) }, + .c_ushort => return AbiAlignmentAdvanced{ .scalar = CType.ushort.alignment(target) }, + .c_int => return AbiAlignmentAdvanced{ .scalar = CType.int.alignment(target) }, + .c_uint => return AbiAlignmentAdvanced{ .scalar = CType.uint.alignment(target) }, + .c_long => return AbiAlignmentAdvanced{ .scalar = CType.long.alignment(target) }, + .c_ulong => return AbiAlignmentAdvanced{ .scalar = CType.ulong.alignment(target) }, + .c_longlong => return AbiAlignmentAdvanced{ .scalar = CType.longlong.alignment(target) }, + .c_ulonglong => return AbiAlignmentAdvanced{ .scalar = CType.ulonglong.alignment(target) }, + .c_longdouble => return AbiAlignmentAdvanced{ .scalar = CType.longdouble.alignment(target) }, .f16 => return AbiAlignmentAdvanced{ .scalar = 2 }, - .f32 => return AbiAlignmentAdvanced{ .scalar = 4 }, - .f64 => switch (target.cpu.arch) { - .i386 => switch (target.os.tag) { - .windows, .uefi => return AbiAlignmentAdvanced{ .scalar = 8 }, - else => return AbiAlignmentAdvanced{ .scalar = 4 }, - }, + .f32 => return AbiAlignmentAdvanced{ .scalar = CType.float.alignment(target) }, + .f64 => switch (CType.double.sizeInBits(target)) { + 64 => return AbiAlignmentAdvanced{ .scalar = CType.double.alignment(target) }, else => return AbiAlignmentAdvanced{ .scalar = 8 }, }, - .f128 => return AbiAlignmentAdvanced{ .scalar = 16 }, - - .f80 => switch (target.cpu.arch) { - .i386 => return AbiAlignmentAdvanced{ .scalar = 4 }, - .x86_64 => return AbiAlignmentAdvanced{ .scalar = 16 }, + .f80 => switch (CType.longdouble.sizeInBits(target)) { + 80 => return AbiAlignmentAdvanced{ .scalar = CType.longdouble.alignment(target) }, else => { var payload: Payload.Bits = .{ .base = .{ .tag = .int_unsigned }, @@ -2936,17 +2919,7 @@ pub const Type = extern union { return AbiAlignmentAdvanced{ .scalar = abiAlignment(u80_ty, target) }; }, }, - .c_longdouble => switch (CType.longdouble.sizeInBits(target)) { - 16 => return AbiAlignmentAdvanced{ .scalar = abiAlignment(Type.f16, target) }, - 32 => return AbiAlignmentAdvanced{ .scalar = abiAlignment(Type.f32, target) }, - 64 => return AbiAlignmentAdvanced{ .scalar = abiAlignment(Type.f64, target) }, - 80 => if (target.cpu.arch == .i386 and target.isMinGW()) - return AbiAlignmentAdvanced{ .scalar = 4 } - else - return AbiAlignmentAdvanced{ .scalar = abiAlignment(Type.f80, target) }, - 128 => return AbiAlignmentAdvanced{ .scalar = abiAlignment(Type.f128, target) }, - else => unreachable, - }, + .f128 => return AbiAlignmentAdvanced{ .scalar = 16 }, // TODO revisit this when we have the concept of the error tag type .anyerror_void_error_union, @@ -3411,16 +3384,8 @@ pub const Type = extern union { .f32 => return AbiSizeAdvanced{ .scalar = 4 }, .f64 => return AbiSizeAdvanced{ .scalar = 8 }, .f128 => return AbiSizeAdvanced{ .scalar = 16 }, - - .f80 => switch (target.cpu.arch) { - .i386 => switch (target.os.tag) { - .windows => switch (target.abi) { - .msvc => return AbiSizeAdvanced{ .scalar = 16 }, - else => return AbiSizeAdvanced{ .scalar = 12 }, - }, - else => return AbiSizeAdvanced{ .scalar = 12 }, - }, - .x86_64 => return AbiSizeAdvanced{ .scalar = 16 }, + .f80 => switch (CType.longdouble.sizeInBits(target)) { + 80 => return AbiSizeAdvanced{ .scalar = std.mem.alignForward(10, CType.longdouble.alignment(target)) }, else => { var payload: Payload.Bits = .{ .base = .{ .tag = .int_unsigned }, @@ -6654,45 +6619,80 @@ pub const CType = enum { ulonglong, longdouble, + // We don't have a `c_float`/`c_double` type in Zig, but these + // are useful for querying target-correct alignment and checking + // whether C's double is f64 or f32 + float, + double, + pub fn sizeInBits(self: CType, target: Target) u16 { switch (target.os.tag) { .freestanding, .other => switch (target.cpu.arch) { .msp430 => switch (self) { .short, .ushort, .int, .uint => return 16, - .long, .ulong => return 32, - .longlong, .ulonglong, .longdouble => return 64, + .float, .long, .ulong => return 32, + .longlong, .ulonglong, .double, .longdouble => return 64, }, .avr => switch (self) { .short, .ushort, .int, .uint => return 16, - .long, .ulong, .longdouble => return 32, + .long, .ulong, .float, .double, .longdouble => return 32, .longlong, .ulonglong => return 64, }, + .tce, .tcele => switch (self) { + .short, .ushort => return 16, + .int, .uint, .long, .ulong, .longlong, .ulonglong => return 32, + .float, .double, .longdouble => return 32, + }, + .mips64, .mips64el => switch (self) { + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => return if (target.abi != .gnuabin32) 64 else 32, + .longlong, .ulonglong, .double => return 64, + .longdouble => return 128, + }, + .x86_64 => switch (self) { + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => switch (target.abi) { + .gnux32, .muslx32 => return 32, + else => return 64, + }, + .longlong, .ulonglong, .double => return 64, + .longdouble => return 80, + }, else => switch (self) { .short, .ushort => return 16, - .int, .uint => return 32, + .int, .uint, .float => return 32, .long, .ulong => return target.cpu.arch.ptrBitWidth(), - .longlong, .ulonglong => return 64, + .longlong, .ulonglong, .double => return 64, .longdouble => switch (target.cpu.arch) { .i386 => switch (target.abi) { .android => return 64, else => return 80, }, - .x86_64 => return 80, + .powerpc, + .powerpcle, + .powerpc64, + .powerpc64le, + => switch (target.abi) { + .musl, + .musleabi, + .musleabihf, + .muslx32, + => return 64, + else => return 128, + }, + + .riscv32, .riscv64, .aarch64, .aarch64_be, .aarch64_32, .s390x, - .mips64, - .mips64el, .sparc, .sparc64, .sparcel, - .powerpc, - .powerpcle, - .powerpc64, - .powerpc64le, .wasm32, .wasm64, => return 128, @@ -6716,23 +6716,78 @@ pub const CType = enum { .fuchsia, .minix, => switch (target.cpu.arch) { + .msp430 => switch (self) { + .short, .ushort, .int, .uint => return 16, + .long, .ulong, .float => return 32, + .longlong, .ulonglong, .double, .longdouble => return 64, + }, .avr => switch (self) { .short, .ushort, .int, .uint => return 16, - .long, .ulong, .longdouble => return 32, + .long, .ulong, .float, .double, .longdouble => return 32, .longlong, .ulonglong => return 64, }, + .tce, .tcele => switch (self) { + .short, .ushort => return 16, + .int, .uint, .long, .ulong, .longlong, .ulonglong => return 32, + .float, .double, .longdouble => return 32, + }, + .mips64, .mips64el => switch (self) { + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => return if (target.abi != .gnuabin32) 64 else 32, + .longlong, .ulonglong, .double => return 64, + .longdouble => if (target.os.tag == .freebsd) return 64 else return 128, + }, + .x86_64 => switch (self) { + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => switch (target.abi) { + .gnux32, .muslx32 => return 32, + else => return 64, + }, + .longlong, .ulonglong, .double => return 64, + .longdouble => return 80, + }, else => switch (self) { .short, .ushort => return 16, - .int, .uint => return 32, + .int, .uint, .float => return 32, .long, .ulong => return target.cpu.arch.ptrBitWidth(), - .longlong, .ulonglong => return 64, + .longlong, .ulonglong, .double => return 64, .longdouble => switch (target.cpu.arch) { .i386 => switch (target.abi) { .android => return 64, else => return 80, }, - .x86_64 => return 80, + .powerpc, + .powerpcle, + => switch (target.abi) { + .musl, + .musleabi, + .musleabihf, + .muslx32, + => return 64, + else => switch (target.os.tag) { + .freebsd, .netbsd, .openbsd => return 64, + else => return 128, + }, + }, + + .powerpc64, + .powerpc64le, + => switch (target.abi) { + .musl, + .musleabi, + .musleabihf, + .muslx32, + => return 64, + else => switch (target.os.tag) { + .freebsd, .openbsd => return 64, + else => return 128, + }, + }, + + .riscv32, .riscv64, .aarch64, .aarch64_be, @@ -6743,10 +6798,6 @@ pub const CType = enum { .sparc, .sparc64, .sparcel, - .powerpc, - .powerpcle, - .powerpc64, - .powerpc64le, .wasm32, .wasm64, => return 128, @@ -6756,37 +6807,65 @@ pub const CType = enum { }, }, - .windows, .uefi => switch (self) { - .short, .ushort => return 16, - .int, .uint, .long, .ulong => return 32, - .longlong, .ulonglong => return 64, - .longdouble => switch (target.cpu.arch) { - .i386 => switch (target.abi) { - .gnu => return 80, + .windows, .uefi => switch (target.cpu.arch) { + .i386 => switch (self) { + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => return 32, + .longlong, .ulonglong, .double => return 64, + .longdouble => switch (target.abi) { + .gnu, .gnuilp32, .cygnus => return 80, else => return 64, }, - .x86_64 => switch (target.abi) { - .gnu => return 80, + }, + .x86_64 => switch (self) { + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => switch (target.abi) { + .cygnus => return 64, + else => return 32, + }, + .longlong, .ulonglong, .double => return 64, + .longdouble => switch (target.abi) { + .gnu, .gnuilp32, .cygnus => return 128, else => return 64, }, - else => return 64, + }, + else => switch (self) { + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => return 32, + .longlong, .ulonglong, .double => return 64, + .longdouble => return 64, }, }, .macos, .ios, .tvos, .watchos => switch (self) { .short, .ushort => return 16, - .int, .uint => return 32, - .long, .ulong, .longlong, .ulonglong => return 64, + .int, .uint, .float => return 32, + .long, .ulong => switch (target.cpu.arch) { + .i386, .arm, .aarch64_32 => return 32, + .x86_64 => switch (target.abi) { + .gnux32, .muslx32 => return 32, + else => return 64, + }, + else => return 64, + }, + .longlong, .ulonglong, .double => return 64, .longdouble => switch (target.cpu.arch) { - .i386, .x86_64 => return 80, + .i386 => switch (target.abi) { + .android => return 64, + else => return 80, + }, + .x86_64 => return 80, else => return 64, }, }, .amdhsa, .amdpal => switch (self) { .short, .ushort => return 16, - .int, .uint => return 32, - .long, .ulong, .longlong, .ulonglong => return 64, + .int, .uint, .float => return 32, + .long, .ulong, .longlong, .ulonglong, .double => return 64, .longdouble => return 128, }, @@ -6814,4 +6893,114 @@ pub const CType = enum { => @panic("TODO specify the C integer and float type sizes for this OS"), } } + + pub fn alignment(self: CType, target: Target) u16 { + + // Overrides for unusual alignments + switch (target.cpu.arch) { + .avr => switch (self) { + .short, .ushort => return 2, + else => return 1, + }, + .i386 => switch (target.os.tag) { + .windows, .uefi => switch (self) { + .longlong, .ulonglong, .double => return 8, + .longdouble => switch (target.abi) { + .gnu, .gnuilp32, .cygnus => return 4, + else => return 8, + }, + else => {}, + }, + else => {}, + }, + else => {}, + } + + // Self-aligned, up to a maximum. + return @min( + std.math.ceilPowerOfTwoAssert(u16, (self.sizeInBits(target) + 7) / 8), + switch (target.cpu.arch) { + .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) { + .netbsd => switch (target.abi) { + .gnueabi, + .gnueabihf, + .eabi, + .eabihf, + .android, + .musleabi, + .musleabihf, + => 8, + + else => @as(u16, 4), + }, + .ios, .tvos, .watchos => 4, + else => 8, + }, + + .msp430, + .avr, + => 2, + + .arc, + .csky, + .i386, + .xcore, + .dxil, + .loongarch32, + .tce, + .tcele, + .le32, + .amdil, + .hsail, + .spir, + .spirv32, + .kalimba, + .shave, + .renderscript32, + .ve, + .spu_2, + => 4, + + .aarch64_32, + .amdgcn, + .amdil64, + .bpfel, + .bpfeb, + .hexagon, + .hsail64, + .loongarch64, + .m68k, + .mips, + .mipsel, + .sparc, + .sparcel, + .sparc64, + .lanai, + .le64, + .nvptx, + .nvptx64, + .r600, + .s390x, + .spir64, + .spirv64, + .renderscript64, + => 8, + + .aarch64, + .aarch64_be, + .mips64, + .mips64el, + .powerpc, + .powerpcle, + .powerpc64, + .powerpc64le, + .riscv32, + .riscv64, + .x86_64, + .wasm32, + .wasm64, + => 16, + }, + ); + } }; -- cgit v1.2.3 From 94945864b9d8ffa7b707432fb877ae42e383db68 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Fri, 21 Oct 2022 20:16:00 -0700 Subject: Type.zig: Add `nvcl`/`cuda` CType definitions --- src/type.zig | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'src/type.zig') diff --git a/src/type.zig b/src/type.zig index bcb8969484..51b326e18e 100644 --- a/src/type.zig +++ b/src/type.zig @@ -6862,6 +6862,18 @@ pub const CType = enum { }, }, + .nvcl, .cuda => switch (self) { + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => switch (target.cpu.arch) { + .nvptx => return 32, + .nvptx64 => return 64, + else => return 64, + }, + .longlong, .ulonglong, .double => return 64, + .longdouble => return 64, + }, + .amdhsa, .amdpal => switch (self) { .short, .ushort => return 16, .int, .uint, .float => return 32, @@ -6876,8 +6888,6 @@ pub const CType = enum { .rtems, .nacl, .aix, - .cuda, - .nvcl, .ps4, .ps5, .elfiamcu, -- cgit v1.2.3 From c96f85852ed2e1d5b2ecb43770a3c41d7f38f284 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Sat, 22 Oct 2022 17:25:02 -0700 Subject: CType: Add `preferredAlignment` This value corresponds to clang/gcc's `__alignof` (rather than `_Alignof` which reports the minimum alignment). We don't use this information yet, but it might be useful for implementing ABIs so it is included here. --- src/type.zig | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 127 insertions(+), 1 deletion(-) (limited to 'src/type.zig') diff --git a/src/type.zig b/src/type.zig index 51b326e18e..d78e2909b5 100644 --- a/src/type.zig +++ b/src/type.zig @@ -6926,7 +6926,7 @@ pub const CType = enum { else => {}, } - // Self-aligned, up to a maximum. + // Next-power-of-two-aligned, up to a maximum. return @min( std.math.ceilPowerOfTwoAssert(u16, (self.sizeInBits(target) + 7) / 8), switch (target.cpu.arch) { @@ -7013,4 +7013,130 @@ pub const CType = enum { }, ); } + + pub fn preferredAlignment(self: CType, target: Target) u16 { + + // Overrides for unusual alignments + switch (target.cpu.arch) { + .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) { + .netbsd => switch (target.abi) { + .gnueabi, + .gnueabihf, + .eabi, + .eabihf, + .android, + .musleabi, + .musleabihf, + => {}, + + else => switch (self) { + .longdouble => return 4, + else => {}, + }, + }, + .ios, .tvos, .watchos => switch (self) { + .longdouble => return 4, + else => {}, + }, + else => {}, + }, + .arc => switch (self) { + .longdouble => return 4, + else => {}, + }, + .avr => switch (self) { + .int, .uint, .long, .ulong, .float, .longdouble => return 1, + .short, .ushort => return 2, + .double => return 4, + .longlong, .ulonglong => return 8, + }, + .i386 => switch (target.os.tag) { + .windows, .uefi => switch (self) { + .longdouble => switch (target.abi) { + .gnu, .gnuilp32, .cygnus => return 4, + else => return 8, + }, + else => {}, + }, + else => switch (self) { + .longdouble => return 4, + else => {}, + }, + }, + else => {}, + } + + // Next-power-of-two-aligned, up to a maximum. + return @min( + std.math.ceilPowerOfTwoAssert(u16, (self.sizeInBits(target) + 7) / 8), + switch (target.cpu.arch) { + .msp430 => @as(u16, 2), + + .csky, + .xcore, + .dxil, + .loongarch32, + .tce, + .tcele, + .le32, + .amdil, + .hsail, + .spir, + .spirv32, + .kalimba, + .shave, + .renderscript32, + .ve, + .spu_2, + => 4, + + .arc, + .arm, + .armeb, + .avr, + .thumb, + .thumbeb, + .aarch64_32, + .amdgcn, + .amdil64, + .bpfel, + .bpfeb, + .hexagon, + .hsail64, + .i386, + .loongarch64, + .m68k, + .mips, + .mipsel, + .sparc, + .sparcel, + .sparc64, + .lanai, + .le64, + .nvptx, + .nvptx64, + .r600, + .s390x, + .spir64, + .spirv64, + .renderscript64, + => 8, + + .aarch64, + .aarch64_be, + .mips64, + .mips64el, + .powerpc, + .powerpcle, + .powerpc64, + .powerpc64le, + .riscv32, + .riscv64, + .x86_64, + .wasm32, + .wasm64, + => 16, + }, + ); + } }; -- cgit v1.2.3 From 4ecc384f99e5f4c5a320714484866fb48699245f Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Sun, 23 Oct 2022 23:17:23 -0700 Subject: Fix long double on x86_64-windows The larger alignment on this platform means that long double reports a sizeof 16 bytes, but it's underlying size is really just the 10 bytes of `f80` C doesn't give us a way to see the "underlying" size of a type, so this has to be caught by hand or by monitoring runtime memory. Luckily, x86 and x86-64 are the only platforms that seem to use a non-power-of-two type like this. --- src/type.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/type.zig') diff --git a/src/type.zig b/src/type.zig index d78e2909b5..8f2cd7c54d 100644 --- a/src/type.zig +++ b/src/type.zig @@ -6827,7 +6827,7 @@ pub const CType = enum { }, .longlong, .ulonglong, .double => return 64, .longdouble => switch (target.abi) { - .gnu, .gnuilp32, .cygnus => return 128, + .gnu, .gnuilp32, .cygnus => return 80, else => return 64, }, }, -- cgit v1.2.3 From dd437ae39948031dc04836f245c8b77d459a428a Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 24 Oct 2022 14:41:22 +0300 Subject: stage2: optimize size of optional slices --- src/codegen/c.zig | 14 +++++++++----- src/codegen/llvm.zig | 10 ++++++++-- src/type.zig | 46 ++++++++-------------------------------------- test/behavior/cast.zig | 4 ++++ test/behavior/optional.zig | 16 ++++++++++++++++ 5 files changed, 45 insertions(+), 45 deletions(-) (limited to 'src/type.zig') diff --git a/src/codegen/c.zig b/src/codegen/c.zig index d6584d75ae..5f6f2fd6d5 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -726,7 +726,11 @@ pub const DeclGen = struct { } if (ty.optionalReprIsPayload()) { - return dg.renderValue(writer, payload_ty, val, location); + if (val.castTag(.opt_payload)) |payload| { + return dg.renderValue(writer, payload_ty, payload.data, location); + } else { + return dg.renderValue(writer, payload_ty, val, location); + } } try writer.writeByte('('); @@ -3263,11 +3267,9 @@ fn airIsNull( try f.writeCValue(writer, operand); const ty = f.air.typeOf(un_op); + const opt_ty = if (deref_suffix[0] != 0) ty.childType() else ty; 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 payload_ty = opt_ty.optionalChild(&opt_buf); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { try writer.print("){s} {s} true;\n", .{ deref_suffix, operator }); @@ -3276,6 +3278,8 @@ fn airIsNull( try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator }); } else if (payload_ty.zigTypeTag() == .ErrorSet) { try writer.print("){s} {s} 0;\n", .{ deref_suffix, operator }); + } else if (payload_ty.isSlice() and opt_ty.optionalReprIsPayload()) { + try writer.print("){s}.ptr {s} NULL;\n", .{ deref_suffix, operator }); } else { try writer.print("){s}.is_null {s} true;\n", .{ deref_suffix, operator }); } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index ffc19cb6f6..d4a94d1308 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -6316,18 +6316,24 @@ pub const FuncGen = struct { const operand_ty = self.air.typeOf(un_op); const optional_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; const optional_llvm_ty = try self.dg.lowerType(optional_ty); + var buf: Type.Payload.ElemType = undefined; + const payload_ty = optional_ty.optionalChild(&buf); if (optional_ty.optionalReprIsPayload()) { const loaded = if (operand_is_ptr) self.builder.buildLoad(optional_llvm_ty, operand, "") else operand; + if (payload_ty.isSlice()) { + const slice_ptr = self.builder.buildExtractValue(loaded, 0, ""); + var slice_buf: Type.SlicePtrFieldTypeBuffer = undefined; + const ptr_ty = try self.dg.lowerType(payload_ty.slicePtrFieldType(&slice_buf)); + return self.builder.buildICmp(pred, slice_ptr, ptr_ty.constNull(), ""); + } return self.builder.buildICmp(pred, loaded, optional_llvm_ty.constNull(), ""); } comptime assert(optional_layout_version == 3); - var buf: Type.Payload.ElemType = undefined; - const payload_ty = optional_ty.optionalChild(&buf); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { const loaded = if (operand_is_ptr) self.builder.buildLoad(optional_llvm_ty, operand, "") diff --git a/src/type.zig b/src/type.zig index a2f0bb9e8f..8904b3178d 100644 --- a/src/type.zig +++ b/src/type.zig @@ -3469,20 +3469,8 @@ pub const Type = extern union { if (!child_type.hasRuntimeBits()) return AbiSizeAdvanced{ .scalar = 1 }; - switch (child_type.zigTypeTag()) { - .Pointer => { - const ptr_info = child_type.ptrInfo().data; - const has_null = switch (ptr_info.size) { - .Slice, .C => true, - else => ptr_info.@"allowzero", - }; - if (!has_null) { - const ptr_size_bytes = @divExact(target.cpu.arch.ptrBitWidth(), 8); - return AbiSizeAdvanced{ .scalar = ptr_size_bytes }; - } - }, - .ErrorSet => return abiSizeAdvanced(Type.anyerror, target, strat), - else => {}, + if (ty.optionalReprIsPayload()) { + return abiSizeAdvanced(child_type, target, strat); } const payload_size = switch (try child_type.abiSizeAdvanced(target, strat)) { @@ -3747,28 +3735,10 @@ pub const Type = extern union { .int_signed, .int_unsigned => return ty.cast(Payload.Bits).?.data, - .optional => { - var buf: Payload.ElemType = undefined; - const child_type = ty.optionalChild(&buf); - if (!child_type.hasRuntimeBits()) return 8; - - if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr() and !child_type.isSlice()) - return target.cpu.arch.ptrBitWidth(); - - // Optional types are represented as a struct with the child type as the first - // field and a boolean as the second. Since the child type's abi alignment is - // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal - // to the child type's ABI alignment. - const child_bit_size = try bitSizeAdvanced(child_type, target, sema_kit); - return child_bit_size + 1; - }, - - .error_union => { - const payload = ty.castTag(.error_union).?.data; - if (!payload.payload.hasRuntimeBits()) { - return payload.error_set.bitSizeAdvanced(target, sema_kit); - } - @panic("TODO bitSize error union"); + .optional, .error_union => { + // Optionals and error unions are not packed so their bitsize + // includes padding bits. + return (try abiSizeAdvanced(ty, target, if (sema_kit) |sk| .{ .sema_kit = sk } else .eager)).scalar * 8; }, .atomic_order, @@ -4045,8 +4015,8 @@ pub const Type = extern union { .Pointer => { const info = child_ty.ptrInfo().data; switch (info.size) { - .Slice, .C => return false, - .Many, .One => return !info.@"allowzero", + .C => return false, + .Slice, .Many, .One => return !info.@"allowzero", } }, .ErrorSet => return true, diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 9a02e74853..cb76f86820 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -1170,6 +1170,7 @@ 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 + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO try expect(mem.eql(u8, castToOptionalSlice().?, "hi")); comptime try expect(mem.eql(u8, castToOptionalSlice().?, "hi")); @@ -1256,6 +1257,7 @@ 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 + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -1394,6 +1396,8 @@ test "cast i8 fn call peers to i32 result" { test "cast compatible optional types" { 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 var a: ?[:0]const u8 = null; var b: ?[]const u8 = a; diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index eb693147e6..9c9211f777 100644 --- a/test/behavior/optional.zig +++ b/test/behavior/optional.zig @@ -3,6 +3,7 @@ const std = @import("std"); const testing = std.testing; const expect = testing.expect; const expectEqual = testing.expectEqual; +const expectEqualStrings = std.testing.expectEqualStrings; test "passing an optional integer as a parameter" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; @@ -428,3 +429,18 @@ test "alignment of wrapping an optional payload" { }; try expect(S.foo().?.x == 1234); } + +test "Optional slice size is optimized" { + 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; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + + try expect(@sizeOf(?[]u8) == @sizeOf([]u8)); + var a: ?[]const u8 = null; + try expect(a == null); + a = "hello"; + try expectEqualStrings(a.?, "hello"); +} -- cgit v1.2.3