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.
This commit is contained in:
Cody Tapscott 2022-10-21 18:56:18 -07:00
parent 8d4778b4f9
commit ddbdb83c86
2 changed files with 355 additions and 145 deletions

View File

@ -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,
},
.riscv64,
.aarch64_be,
.aarch64_32,
.s390x,
.mips64,
.mips64el,
.sparc,
.sparc64,
.sparcel,
.powerpc,
.powerpcle,
.powerpc64,
.powerpc64le,
.wasm32,
.wasm64,
=> true,
else => false,
else => return F == f64,
},
f80 => switch (target.cpu.arch) {
.x86_64, .i386 => true,
else => false,
},
f64 => switch (target.cpu.arch) {
.aarch64 => target.isDarwin(),
else => {},
}
.x86_64,
.i386,
.riscv64,
.aarch64_be,
.aarch64_32,
.s390x,
.mips64,
.mips64el,
.sparc,
.sparc64,
.sparcel,
.powerpc,
.powerpcle,
.powerpc64,
.powerpc64le,
=> false,
if (target.abi == .android and target.cpu.arch == .i386)
return F == f64;
else => true,
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,
},
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,
},
.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,
},
},
.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,
},
},
.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,
},

View File

@ -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,
},
);
}
};