From 166db1a3ed7eca9b04b0626eaea8de0634ab9667 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 10 Feb 2022 17:36:31 -0700 Subject: [PATCH] stage1: fix f80 size and alignment on x86 and arm * F80Repr extern struct needs no explicit padding; let's match the target padding. * stage2: fix lowering of f80 constants. * stage1: decide ABI size and alignment of f80 based on alignment of u64. x86 has alignof u64 equal to 4 but arm has it as 8. * stage2: fix Value.floatReadFromMemory to use F80Repr --- lib/std/math.zig | 7 ++----- src/codegen/llvm.zig | 34 ++++++++++++++++++++++------------ src/stage1/codegen.cpp | 22 +++++++++++++++++----- src/value.zig | 30 ++++++++++++++++++++++++++---- 4 files changed, 67 insertions(+), 26 deletions(-) diff --git a/lib/std/math.zig b/lib/std/math.zig index 6802d420fd..8398842e28 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -36,7 +36,6 @@ pub const sqrt2 = 1.414213562373095048801688724209698079; /// 1/sqrt(2) pub const sqrt1_2 = 0.707106781186547524400844362104849039; -// From a small c++ [program using boost float128](https://github.com/winksaville/cpp_boost_float128) pub const f128_true_min = @bitCast(f128, @as(u128, 0x00000000000000000000000000000001)); pub const f128_min = @bitCast(f128, @as(u128, 0x00010000000000000000000000000000)); pub const f128_max = @bitCast(f128, @as(u128, 0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF)); @@ -44,12 +43,10 @@ pub const f128_epsilon = @bitCast(f128, @as(u128, 0x3F8F000000000000000000000000 pub const f128_toint = 1.0 / f128_epsilon; pub const F80Repr = if (@import("builtin").cpu.arch.endian() == .Little) extern struct { - fraction: u64, + fraction: u64 align(@alignOf(f80)), exp: u16, - _pad: u32 = undefined, } else extern struct { - exp: u16, - _pad: u32 = undefined, // TODO verify compatibility with hardware + exp: u16 align(@alignOf(f80)), fraction: u64, }; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 425808efb1..54468162ad 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1257,19 +1257,29 @@ pub const DeclGen = struct { }, .Float => { const llvm_ty = try dg.llvmType(tv.ty); - if (tv.ty.floatBits(dg.module.getTarget()) <= 64) { - return llvm_ty.constReal(tv.val.toFloat(f64)); + switch (tv.ty.floatBits(dg.module.getTarget())) { + 16, 32, 64 => return llvm_ty.constReal(tv.val.toFloat(f64)), + 80 => { + const float = tv.val.toFloat(f80); + const repr = @ptrCast(*const std.math.F80Repr, &float); + const llvm_i80 = dg.context.intType(80); + var x = llvm_i80.constInt(repr.exp, .False); + x = x.constShl(llvm_i80.constInt(64, .False)); + x = x.constOr(llvm_i80.constInt(repr.fraction, .False)); + return x.constBitCast(llvm_ty); + }, + 128 => { + var buf: [2]u64 = @bitCast([2]u64, tv.val.toFloat(f128)); + // LLVM seems to require that the lower half of the f128 be placed first + // in the buffer. + if (native_endian == .Big) { + std.mem.swap(u64, &buf[0], &buf[1]); + } + const int = dg.context.intType(128).constIntOfArbitraryPrecision(buf.len, &buf); + return int.constBitCast(llvm_ty); + }, + else => unreachable, } - - var buf: [2]u64 = @bitCast([2]u64, tv.val.toFloat(f128)); - // LLVM seems to require that the lower half of the f128 be placed first - // in the buffer. - if (native_endian == .Big) { - std.mem.swap(u64, &buf[0], &buf[1]); - } - - const int = dg.context.intType(128).constIntOfArbitraryPrecision(buf.len, &buf); - return int.constBitCast(llvm_ty); }, .Pointer => switch (tv.val.tag()) { .decl_ref_mut => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref_mut).?.data.decl), diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index c06f71e834..7b0bcbe2f5 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -9429,17 +9429,29 @@ static void define_builtin_types(CodeGen *g) { { ZigType *entry = new_type_table_entry(ZigTypeIdFloat); + unsigned u64_alignment = LLVMABIAlignmentOfType(g->target_data_ref, LLVMInt64Type()); + + if (u64_alignment >= 8) { + entry->size_in_bits = 128; + entry->abi_size = 16; + entry->abi_align = 16; + } else if (u64_alignment >= 4) { + entry->size_in_bits = 96; + entry->abi_size = 12; + entry->abi_align = 4; + } else { + entry->size_in_bits = 80; + entry->abi_size = 10; + entry->abi_align = 2; + } if (target_has_f80(g->zig_target)) { entry->llvm_type = LLVMX86FP80Type(); } else { - // We use i128 here instead of x86_fp80 because on targets such as arm, + // We use an int here instead of x86_fp80 because on targets such as arm, // LLVM will give "ERROR: Cannot select" for any instructions involving // the x86_fp80 type. - entry->llvm_type = get_int_type(g, false, 128)->llvm_type; + entry->llvm_type = get_int_type(g, false, entry->size_in_bits)->llvm_type; } - entry->size_in_bits = 8 * 16; - entry->abi_size = 16; // matches LLVMABISizeOfType(LLVMX86FP80Type()) - entry->abi_align = 16; // matches LLVMABIAlignmentOfType(LLVMX86FP80Type()) buf_init_from_str(&entry->name, "f80"); entry->data.floating.bit_count = 80; diff --git a/src/value.zig b/src/value.zig index 33a75e08bb..aefb0a3e20 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1122,10 +1122,32 @@ pub const Value = extern union { fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F { if (F == f80) { - // TODO: use std.math.F80Repr? - const int = std.mem.readInt(u128, buffer[0..16], target.cpu.arch.endian()); - // TODO shouldn't this be a bitcast from u80 to f80 instead of u128 to f80? - return @bitCast(F, int); + switch (target.cpu.arch.endian()) { + .Little => { + const TargetF80Repr = extern struct { + fraction: u64, + exp: u16, + }; + const target_repr = @ptrCast(*align(1) const TargetF80Repr, buffer.ptr); + const real_repr: std.math.F80Repr = .{ + .fraction = target_repr.fraction, + .exp = target_repr.exp, + }; + return @ptrCast(*const f80, &real_repr).*; + }, + .Big => { + const TargetF80Repr = extern struct { + exp: u16, + fraction: u64, + }; + const target_repr = @ptrCast(*align(1) const TargetF80Repr, buffer.ptr); + const real_repr: std.math.F80Repr = .{ + .fraction = target_repr.fraction, + .exp = target_repr.exp, + }; + return @ptrCast(*const f80, &real_repr).*; + }, + } } const Int = @Type(.{ .Int = .{ .signedness = .unsigned,