Commit ddbdb83c86

Cody Tapscott <topolarity@tapscott.me>
2022-10-22 03:56:18
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.
1 parent 8d4778b
Changed files (2)
lib
src
lib/std/target.zig
@@ -1781,68 +1781,89 @@ pub const Target = struct {
     }
 
     pub inline fn longDoubleIs(target: Target, comptime F: type) bool {
-        if (target.abi == .msvc or (target.abi == .android and target.cpu.arch == .i386)) {
-            return F == f64;
-        }
-        return switch (F) {
-            f128 => switch (target.cpu.arch) {
-                .aarch64 => {
-                    // According to Apple's official guide:
-                    // > The long double type is a double precision IEEE754 binary floating-point type,
-                    // > which makes it identical to the double type. This behavior contrasts to the
-                    // > standard specification, in which a long double is a quad-precision, IEEE754
-                    // > binary, floating-point type.
-                    // https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms
-                    return !target.isDarwin();
+        switch (target.os.tag) {
+            .windows, .uefi => switch (target.abi) {
+                .gnu, .gnuilp32, .cygnus => switch (target.cpu.arch) {
+                    .i386 => return F == f80,
+                    .x86_64 => return F == f128,
+                    else => return F == f64,
                 },
+                else => return F == f64,
+            },
+            else => {},
+        }
 
-                .riscv64,
-                .aarch64_be,
-                .aarch64_32,
-                .s390x,
-                .mips64,
-                .mips64el,
-                .sparc,
-                .sparc64,
-                .sparcel,
-                .powerpc,
-                .powerpcle,
-                .powerpc64,
-                .powerpc64le,
-                .wasm32,
-                .wasm64,
-                => true,
+        if (target.abi == .android and target.cpu.arch == .i386)
+            return F == f64;
 
-                else => false,
+        switch (target.cpu.arch) {
+            .aarch64,
+            .aarch64_be,
+            .aarch64_32,
+            => switch (target.os.tag) {
+                // According to Apple's official guide:
+                // > The long double type is a double precision IEEE754 binary floating-point type,
+                // > which makes it identical to the double type. This behavior contrasts to the
+                // > standard specification, in which a long double is a quad-precision, IEEE754
+                // > binary, floating-point type.
+                // https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms
+                .ios, .macos, .watchos, .tvos => return F == f64,
+                .windows, .uefi => return F == f64,
+                else => return F == f128,
             },
-            f80 => switch (target.cpu.arch) {
-                .x86_64, .i386 => true,
-                else => false,
+
+            .i386 => return F == f80,
+            .x86_64 => return F == f80,
+
+            .mips64,
+            .mips64el,
+            => switch (target.os.tag) {
+                .freebsd => return F == f64,
+                else => return F == f128,
             },
-            f64 => switch (target.cpu.arch) {
-                .aarch64 => target.isDarwin(),
 
-                .x86_64,
-                .i386,
-                .riscv64,
-                .aarch64_be,
-                .aarch64_32,
-                .s390x,
-                .mips64,
-                .mips64el,
-                .sparc,
-                .sparc64,
-                .sparcel,
-                .powerpc,
-                .powerpcle,
-                .powerpc64,
-                .powerpc64le,
-                => false,
+            .powerpc,
+            .powerpcle,
+            => switch (target.abi) {
+                .musl,
+                .musleabi,
+                .musleabihf,
+                .muslx32,
+                => return F == f64,
+                else => switch (target.os.tag) {
+                    .freebsd, .netbsd, .openbsd => return F == f64,
+                    else => return F == f128,
+                },
+            },
 
-                else => true,
+            .powerpc64,
+            .powerpc64le,
+            => switch (target.abi) {
+                .musl,
+                .musleabi,
+                .musleabihf,
+                .muslx32,
+                => return F == f64,
+                else => switch (target.os.tag) {
+                    .freebsd, .openbsd => return F == f64,
+                    else => return F == f128,
+                },
             },
-            else => false,
-        };
+
+            .riscv32,
+            .riscv64,
+            .s390x,
+            .sparc,
+            .sparc64,
+            .sparcel,
+            .wasm32,
+            .wasm64,
+            => return F == f128,
+
+            .avr, .tce, .tcele => return F == f32,
+
+            else => return F == f64,
+        }
     }
 
     pub inline fn maxIntAlignment(target: Target) u16 {
@@ -1872,7 +1893,7 @@ pub const Target = struct {
             => 8,
 
             .i386 => return switch (target.os.tag) {
-                .windows => 8,
+                .windows, .uefi => 8,
                 else => 4,
             },
 
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,
+            },
+        );
+    }
 };