| author | |
| committer | |
| log | a024aff9324e827d6595e44f922d87f8ed2dbd0d |
| tree | 2c0557a4e57acd198ca57deb7cc387109d96c26d |
| parent | 1c23321d03a48558b02c2faf818b82811b8ab11d |
Get rid of `std.math.F80Repr`. Instead of trying to match the memory
layout of f80, we treat it as a value, same as the other floating point
types. The functions `make_f80` and `break_f80` are introduced to
compose an f80 value out of its parts, and the inverse operation.
stage2 LLVM backend: fix pointer to zero length array tripping LLVM
assertion. It now checks for when the element type is a zero-bit type
and lowers such thing the same way that pointers to other zero-bit types
are lowered.
Both stage1 and stage2 LLVM backends are adjusted so that f80 is lowered
as x86_fp80 on x86_64 and i386 architectures, and identical to a u80 on
others. LLVM constants are lowered in a less hacky way now that #10860
is fixed, by using the expression `(exp << 64) | fraction` using llvm
constants.
Sema is improved to handle c_longdouble by recursively handling it
correctly for whatever the float bit width is. In both stage1 and
stage2.10 files changed, 201 insertions(+), 111 deletions(-)
lib/std/math.zig+25-15| ... | @@ -42,19 +42,11 @@ pub const f128_max = @bitCast(f128, @as(u128, 0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF | ... | @@ -42,19 +42,11 @@ pub const f128_max = @bitCast(f128, @as(u128, 0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF |
| 42 | pub const f128_epsilon = @bitCast(f128, @as(u128, 0x3F8F0000000000000000000000000000)); | 42 | pub const f128_epsilon = @bitCast(f128, @as(u128, 0x3F8F0000000000000000000000000000)); |
| 43 | pub const f128_toint = 1.0 / f128_epsilon; | 43 | pub const f128_toint = 1.0 / f128_epsilon; |
| 44 | 44 | ||
| 45 | pub const F80Repr = if (@import("builtin").cpu.arch.endian() == .Little) extern struct { | ||
| 46 | fraction: u64 align(@alignOf(f80)), | ||
| 47 | exp: u16, | ||
| 48 | } else extern struct { | ||
| 49 | exp: u16 align(@alignOf(f80)), | ||
| 50 | fraction: u64, | ||
| 51 | }; | ||
| 52 | |||
| 53 | // float.h details | 45 | // float.h details |
| 54 | pub const f80_true_min = @ptrCast(*const f80, &F80Repr{ .fraction = 1, .exp = 0 }).*; | 46 | pub const f80_true_min = make_f80(.{ .fraction = 1, .exp = 0 }); |
| 55 | pub const f80_min = @ptrCast(*const f80, &F80Repr{ .fraction = 0x8000000000000000, .exp = 1 }).*; | 47 | pub const f80_min = make_f80(.{ .fraction = 0x8000000000000000, .exp = 1 }); |
| 56 | pub const f80_max = @ptrCast(*const f80, &F80Repr{ .fraction = 0xFFFFFFFFFFFFFFFF, .exp = 0x7FFE }).*; | 48 | pub const f80_max = make_f80(.{ .fraction = 0xFFFFFFFFFFFFFFFF, .exp = 0x7FFE }); |
| 57 | pub const f80_epsilon = @ptrCast(*const f80, &F80Repr{ .fraction = 0x8000000000000000, .exp = 0x3FC0 }).*; | 49 | pub const f80_epsilon = make_f80(.{ .fraction = 0x8000000000000000, .exp = 0x3FC0 }); |
| 58 | pub const f80_toint = 1.0 / f80_epsilon; | 50 | pub const f80_toint = 1.0 / f80_epsilon; |
| 59 | 51 | ||
| 60 | pub const f64_true_min = 4.94065645841246544177e-324; | 52 | pub const f64_true_min = 4.94065645841246544177e-324; |
| ... | @@ -104,9 +96,9 @@ pub const qnan_f64 = @bitCast(f64, qnan_u64); | ... | @@ -104,9 +96,9 @@ pub const qnan_f64 = @bitCast(f64, qnan_u64); |
| 104 | pub const inf_u64 = @as(u64, 0x7FF << 52); | 96 | pub const inf_u64 = @as(u64, 0x7FF << 52); |
| 105 | pub const inf_f64 = @bitCast(f64, inf_u64); | 97 | pub const inf_f64 = @bitCast(f64, inf_u64); |
| 106 | 98 | ||
| 107 | pub const inf_f80 = @ptrCast(*const f80, &F80Repr{ .fraction = 0x8000000000000000, .exp = 0x7fff }).*; | 99 | pub const inf_f80 = make_f80(F80{ .fraction = 0x8000000000000000, .exp = 0x7fff }); |
| 108 | pub const nan_f80 = @ptrCast(*const f80, &F80Repr{ .fraction = 0xA000000000000000, .exp = 0x7fff }).*; | 100 | pub const nan_f80 = make_f80(F80{ .fraction = 0xA000000000000000, .exp = 0x7fff }); |
| 109 | pub const qnan_f80 = @ptrCast(*const f80, &F80Repr{ .fraction = 0xC000000000000000, .exp = 0x7fff }).*; | 101 | pub const qnan_f80 = make_f80(F80{ .fraction = 0xC000000000000000, .exp = 0x7fff }); |
| 110 | 102 | ||
| 111 | pub const nan_u128 = @as(u128, 0x7fff0000000000000000000000000001); | 103 | pub const nan_u128 = @as(u128, 0x7fff0000000000000000000000000001); |
| 112 | pub const nan_f128 = @bitCast(f128, nan_u128); | 104 | pub const nan_f128 = @bitCast(f128, nan_u128); |
| ... | @@ -1501,3 +1493,21 @@ test "boolMask" { | ... | @@ -1501,3 +1493,21 @@ test "boolMask" { |
| 1501 | pub fn comptimeMod(num: anytype, denom: comptime_int) IntFittingRange(0, denom - 1) { | 1493 | pub fn comptimeMod(num: anytype, denom: comptime_int) IntFittingRange(0, denom - 1) { |
| 1502 | return @intCast(IntFittingRange(0, denom - 1), @mod(num, denom)); | 1494 | return @intCast(IntFittingRange(0, denom - 1), @mod(num, denom)); |
| 1503 | } | 1495 | } |
| 1496 | |||
| 1497 | pub const F80 = struct { | ||
| 1498 | fraction: u64, | ||
| 1499 | exp: u16, | ||
| 1500 | }; | ||
| 1501 | |||
| 1502 | pub fn make_f80(repr: F80) f80 { | ||
| 1503 | const int = (@as(u80, repr.exp) << 64) | repr.fraction; | ||
| 1504 | return @bitCast(f80, int); | ||
| 1505 | } | ||
| 1506 | |||
| 1507 | pub fn break_f80(x: f80) F80 { | ||
| 1508 | const int = @bitCast(u80, x); | ||
| 1509 | return .{ | ||
| 1510 | .fraction = @truncate(u64, int), | ||
| 1511 | .exp = @truncate(u16, int >> 64), | ||
| 1512 | }; | ||
| 1513 | } |
lib/std/special/compiler_rt/addXf3.zig+9-9| ... | @@ -232,8 +232,8 @@ fn normalize_f80(exp: *i32, significand: *u80) void { | ... | @@ -232,8 +232,8 @@ fn normalize_f80(exp: *i32, significand: *u80) void { |
| 232 | } | 232 | } |
| 233 | 233 | ||
| 234 | pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { | 234 | pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { |
| 235 | var a_rep align(16) = @ptrCast(*const std.math.F80Repr, &a).*; | 235 | var a_rep = std.math.break_f80(a); |
| 236 | var b_rep align(16) = @ptrCast(*const std.math.F80Repr, &b).*; | 236 | var b_rep = std.math.break_f80(b); |
| 237 | var a_exp: i32 = a_rep.exp & 0x7FFF; | 237 | var a_exp: i32 = a_rep.exp & 0x7FFF; |
| 238 | var b_exp: i32 = b_rep.exp & 0x7FFF; | 238 | var b_exp: i32 = b_rep.exp & 0x7FFF; |
| 239 | 239 | ||
| ... | @@ -257,7 +257,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { | ... | @@ -257,7 +257,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { |
| 257 | std.debug.assert(a_rep.fraction & significand_mask != 0); | 257 | std.debug.assert(a_rep.fraction & significand_mask != 0); |
| 258 | // NaN + anything = qNaN | 258 | // NaN + anything = qNaN |
| 259 | a_rep.fraction |= qnan_bit; | 259 | a_rep.fraction |= qnan_bit; |
| 260 | return @ptrCast(*const f80, &a_rep).*; | 260 | return std.math.make_f80(a_rep); |
| 261 | } | 261 | } |
| 262 | } | 262 | } |
| 263 | if (b_exp == max_exp) { | 263 | if (b_exp == max_exp) { |
| ... | @@ -268,7 +268,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { | ... | @@ -268,7 +268,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { |
| 268 | std.debug.assert(b_rep.fraction & significand_mask != 0); | 268 | std.debug.assert(b_rep.fraction & significand_mask != 0); |
| 269 | // anything + NaN = qNaN | 269 | // anything + NaN = qNaN |
| 270 | b_rep.fraction |= qnan_bit; | 270 | b_rep.fraction |= qnan_bit; |
| 271 | return @ptrCast(*const f80, &b_rep).*; | 271 | return std.math.make_f80(b_rep); |
| 272 | } | 272 | } |
| 273 | } | 273 | } |
| 274 | 274 | ||
| ... | @@ -279,7 +279,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { | ... | @@ -279,7 +279,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { |
| 279 | if (b_zero) { | 279 | if (b_zero) { |
| 280 | // but we need to get the sign right for zero + zero | 280 | // but we need to get the sign right for zero + zero |
| 281 | a_rep.exp &= b_rep.exp; | 281 | a_rep.exp &= b_rep.exp; |
| 282 | return @ptrCast(*const f80, &a_rep).*; | 282 | return std.math.make_f80(a_rep); |
| 283 | } else { | 283 | } else { |
| 284 | return b; | 284 | return b; |
| 285 | } | 285 | } |
| ... | @@ -359,7 +359,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { | ... | @@ -359,7 +359,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { |
| 359 | if (a_exp >= max_exp) { | 359 | if (a_exp >= max_exp) { |
| 360 | a_rep.exp = max_exp | result_sign; | 360 | a_rep.exp = max_exp | result_sign; |
| 361 | a_rep.fraction = int_bit; // integer bit is set for +/-inf | 361 | a_rep.fraction = int_bit; // integer bit is set for +/-inf |
| 362 | return @ptrCast(*const f80, &a_rep).*; | 362 | return std.math.make_f80(a_rep); |
| 363 | } | 363 | } |
| 364 | 364 | ||
| 365 | if (a_exp <= 0) { | 365 | if (a_exp <= 0) { |
| ... | @@ -387,13 +387,13 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { | ... | @@ -387,13 +387,13 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { |
| 387 | 387 | ||
| 388 | a_rep.fraction = @truncate(u64, a_int); | 388 | a_rep.fraction = @truncate(u64, a_int); |
| 389 | a_rep.exp = @truncate(u16, a_int >> significand_bits); | 389 | a_rep.exp = @truncate(u16, a_int >> significand_bits); |
| 390 | return @ptrCast(*const f80, &a_rep).*; | 390 | return std.math.make_f80(a_rep); |
| 391 | } | 391 | } |
| 392 | 392 | ||
| 393 | pub fn __subxf3(a: f80, b: f80) callconv(.C) f80 { | 393 | pub fn __subxf3(a: f80, b: f80) callconv(.C) f80 { |
| 394 | var b_rep align(16) = @ptrCast(*const std.math.F80Repr, &b).*; | 394 | var b_rep = std.math.break_f80(b); |
| 395 | b_rep.exp ^= 0x8000; | 395 | b_rep.exp ^= 0x8000; |
| 396 | return __addxf3(a, @ptrCast(*const f80, &b_rep).*); | 396 | return __addxf3(a, std.math.make_f80(b_rep)); |
| 397 | } | 397 | } |
| 398 | 398 | ||
| 399 | test { | 399 | test { |
lib/std/special/compiler_rt/compareXf2.zig+2-2| ... | @@ -147,8 +147,8 @@ pub fn __gtdf2(a: f64, b: f64) callconv(.C) i32 { | ... | @@ -147,8 +147,8 @@ pub fn __gtdf2(a: f64, b: f64) callconv(.C) i32 { |
| 147 | // Comparison between f80 | 147 | // Comparison between f80 |
| 148 | 148 | ||
| 149 | pub inline fn cmp_f80(comptime RT: type, a: f80, b: f80) RT { | 149 | pub inline fn cmp_f80(comptime RT: type, a: f80, b: f80) RT { |
| 150 | const a_rep = @ptrCast(*const std.math.F80Repr, &a).*; | 150 | const a_rep = std.math.break_f80(a); |
| 151 | const b_rep = @ptrCast(*const std.math.F80Repr, &b).*; | 151 | const b_rep = std.math.break_f80(b); |
| 152 | const sig_bits = std.math.floatMantissaBits(f80); | 152 | const sig_bits = std.math.floatMantissaBits(f80); |
| 153 | const int_bit = 0x8000000000000000; | 153 | const int_bit = 0x8000000000000000; |
| 154 | const sign_bit = 0x8000; | 154 | const sign_bit = 0x8000; |
lib/std/special/compiler_rt/extend_f80.zig+3-3| ... | @@ -41,7 +41,7 @@ inline fn extendF80(comptime src_t: type, a: std.meta.Int(.unsigned, @typeInfo(s | ... | @@ -41,7 +41,7 @@ inline fn extendF80(comptime src_t: type, a: std.meta.Int(.unsigned, @typeInfo(s |
| 41 | const src_qnan = 1 << (src_sig_bits - 1); | 41 | const src_qnan = 1 << (src_sig_bits - 1); |
| 42 | const src_nan_code = src_qnan - 1; | 42 | const src_nan_code = src_qnan - 1; |
| 43 | 43 | ||
| 44 | var dst: std.math.F80Repr align(16) = undefined; | 44 | var dst: std.math.F80 = undefined; |
| 45 | 45 | ||
| 46 | // Break a into a sign and representation of the absolute value | 46 | // Break a into a sign and representation of the absolute value |
| 47 | const a_abs = a & src_abs_mask; | 47 | const a_abs = a & src_abs_mask; |
| ... | @@ -83,7 +83,7 @@ inline fn extendF80(comptime src_t: type, a: std.meta.Int(.unsigned, @typeInfo(s | ... | @@ -83,7 +83,7 @@ inline fn extendF80(comptime src_t: type, a: std.meta.Int(.unsigned, @typeInfo(s |
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | dst.exp |= sign; | 85 | dst.exp |= sign; |
| 86 | return @ptrCast(*const f80, &dst).*; | 86 | return std.math.make_f80(dst); |
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | pub fn __extendxftf2(a: f80) callconv(.C) f128 { | 89 | pub fn __extendxftf2(a: f80) callconv(.C) f128 { |
| ... | @@ -99,7 +99,7 @@ pub fn __extendxftf2(a: f80) callconv(.C) f128 { | ... | @@ -99,7 +99,7 @@ pub fn __extendxftf2(a: f80) callconv(.C) f128 { |
| 99 | const dst_min_normal = @as(u128, 1) << dst_sig_bits; | 99 | const dst_min_normal = @as(u128, 1) << dst_sig_bits; |
| 100 | 100 | ||
| 101 | // Break a into a sign and representation of the absolute value | 101 | // Break a into a sign and representation of the absolute value |
| 102 | var a_rep = @ptrCast(*const std.math.F80Repr, &a).*; | 102 | var a_rep = std.math.break_f80(a); |
| 103 | const sign = a_rep.exp & 0x8000; | 103 | const sign = a_rep.exp & 0x8000; |
| 104 | a_rep.exp &= 0x7FFF; | 104 | a_rep.exp &= 0x7FFF; |
| 105 | var abs_result: u128 = undefined; | 105 | var abs_result: u128 = undefined; |
lib/std/special/compiler_rt/trunc_f80.zig+3-3| ... | @@ -42,7 +42,7 @@ inline fn trunc(comptime dst_t: type, a: f80) dst_t { | ... | @@ -42,7 +42,7 @@ inline fn trunc(comptime dst_t: type, a: f80) dst_t { |
| 42 | const dst_nan_mask = dst_qnan - 1; | 42 | const dst_nan_mask = dst_qnan - 1; |
| 43 | 43 | ||
| 44 | // Break a into a sign and representation of the absolute value | 44 | // Break a into a sign and representation of the absolute value |
| 45 | var a_rep = @ptrCast(*const std.math.F80Repr, &a).*; | 45 | var a_rep = std.math.break_f80(a); |
| 46 | const sign = a_rep.exp & 0x8000; | 46 | const sign = a_rep.exp & 0x8000; |
| 47 | a_rep.exp &= 0x7FFF; | 47 | a_rep.exp &= 0x7FFF; |
| 48 | a_rep.fraction &= 0x7FFFFFFFFFFFFFFF; | 48 | a_rep.fraction &= 0x7FFFFFFFFFFFFFFF; |
| ... | @@ -125,7 +125,7 @@ pub fn __trunctfxf2(a: f128) callconv(.C) f80 { | ... | @@ -125,7 +125,7 @@ pub fn __trunctfxf2(a: f128) callconv(.C) f80 { |
| 125 | const a_abs = a_rep & src_abs_mask; | 125 | const a_abs = a_rep & src_abs_mask; |
| 126 | const sign: u16 = if (a_rep & src_sign_mask != 0) 0x8000 else 0; | 126 | const sign: u16 = if (a_rep & src_sign_mask != 0) 0x8000 else 0; |
| 127 | 127 | ||
| 128 | var res: std.math.F80Repr align(16) = undefined; | 128 | var res: std.math.F80 = undefined; |
| 129 | 129 | ||
| 130 | if (a_abs > src_inf) { | 130 | if (a_abs > src_inf) { |
| 131 | // a is NaN. | 131 | // a is NaN. |
| ... | @@ -155,5 +155,5 @@ pub fn __trunctfxf2(a: f128) callconv(.C) f80 { | ... | @@ -155,5 +155,5 @@ pub fn __trunctfxf2(a: f128) callconv(.C) f80 { |
| 155 | } | 155 | } |
| 156 | 156 | ||
| 157 | res.exp |= sign; | 157 | res.exp |= sign; |
| 158 | return @ptrCast(*const f80, &res).*; | 158 | return std.math.make_f80(res); |
| 159 | } | 159 | } |
src/codegen/llvm.zig+28-13| ... | @@ -824,23 +824,24 @@ pub const DeclGen = struct { | ... | @@ -824,23 +824,24 @@ pub const DeclGen = struct { |
| 824 | 824 | ||
| 825 | fn llvmType(dg: *DeclGen, t: Type) Allocator.Error!*const llvm.Type { | 825 | fn llvmType(dg: *DeclGen, t: Type) Allocator.Error!*const llvm.Type { |
| 826 | const gpa = dg.gpa; | 826 | const gpa = dg.gpa; |
| 827 | const target = dg.module.getTarget(); | ||
| 827 | switch (t.zigTypeTag()) { | 828 | switch (t.zigTypeTag()) { |
| 828 | .Void, .NoReturn => return dg.context.voidType(), | 829 | .Void, .NoReturn => return dg.context.voidType(), |
| 829 | .Int => { | 830 | .Int => { |
| 830 | const info = t.intInfo(dg.module.getTarget()); | 831 | const info = t.intInfo(target); |
| 831 | return dg.context.intType(info.bits); | 832 | return dg.context.intType(info.bits); |
| 832 | }, | 833 | }, |
| 833 | .Enum => { | 834 | .Enum => { |
| 834 | var buffer: Type.Payload.Bits = undefined; | 835 | var buffer: Type.Payload.Bits = undefined; |
| 835 | const int_ty = t.intTagType(&buffer); | 836 | const int_ty = t.intTagType(&buffer); |
| 836 | const bit_count = int_ty.intInfo(dg.module.getTarget()).bits; | 837 | const bit_count = int_ty.intInfo(target).bits; |
| 837 | return dg.context.intType(bit_count); | 838 | return dg.context.intType(bit_count); |
| 838 | }, | 839 | }, |
| 839 | .Float => switch (t.floatBits(dg.module.getTarget())) { | 840 | .Float => switch (t.floatBits(target)) { |
| 840 | 16 => return dg.context.halfType(), | 841 | 16 => return dg.context.halfType(), |
| 841 | 32 => return dg.context.floatType(), | 842 | 32 => return dg.context.floatType(), |
| 842 | 64 => return dg.context.doubleType(), | 843 | 64 => return dg.context.doubleType(), |
| 843 | 80 => return dg.context.x86FP80Type(), | 844 | 80 => return if (backendSupportsF80(target)) dg.context.x86FP80Type() else dg.context.intType(80), |
| 844 | 128 => return dg.context.fp128Type(), | 845 | 128 => return dg.context.fp128Type(), |
| 845 | else => unreachable, | 846 | else => unreachable, |
| 846 | }, | 847 | }, |
| ... | @@ -859,7 +860,8 @@ pub const DeclGen = struct { | ... | @@ -859,7 +860,8 @@ pub const DeclGen = struct { |
| 859 | const llvm_addrspace = dg.llvmAddressSpace(t.ptrAddressSpace()); | 860 | const llvm_addrspace = dg.llvmAddressSpace(t.ptrAddressSpace()); |
| 860 | const elem_ty = t.childType(); | 861 | const elem_ty = t.childType(); |
| 861 | const lower_elem_ty = switch (elem_ty.zigTypeTag()) { | 862 | const lower_elem_ty = switch (elem_ty.zigTypeTag()) { |
| 862 | .Opaque, .Array, .Fn => true, | 863 | .Opaque, .Fn => true, |
| 864 | .Array => elem_ty.childType().hasRuntimeBits(), | ||
| 863 | else => elem_ty.hasRuntimeBits(), | 865 | else => elem_ty.hasRuntimeBits(), |
| 864 | }; | 866 | }; |
| 865 | const llvm_elem_ty = if (lower_elem_ty) | 867 | const llvm_elem_ty = if (lower_elem_ty) |
| ... | @@ -889,9 +891,11 @@ pub const DeclGen = struct { | ... | @@ -889,9 +891,11 @@ pub const DeclGen = struct { |
| 889 | else => unreachable, | 891 | else => unreachable, |
| 890 | }, | 892 | }, |
| 891 | .Array => { | 893 | .Array => { |
| 892 | const elem_type = try dg.llvmType(t.childType()); | 894 | const elem_ty = t.childType(); |
| 895 | assert(elem_ty.onePossibleValue() == null); | ||
| 896 | const elem_llvm_ty = try dg.llvmType(elem_ty); | ||
| 893 | const total_len = t.arrayLen() + @boolToInt(t.sentinel() != null); | 897 | const total_len = t.arrayLen() + @boolToInt(t.sentinel() != null); |
| 894 | return elem_type.arrayType(@intCast(c_uint, total_len)); | 898 | return elem_llvm_ty.arrayType(@intCast(c_uint, total_len)); |
| 895 | }, | 899 | }, |
| 896 | .Vector => { | 900 | .Vector => { |
| 897 | const elem_type = try dg.llvmType(t.childType()); | 901 | const elem_type = try dg.llvmType(t.childType()); |
| ... | @@ -978,7 +982,6 @@ pub const DeclGen = struct { | ... | @@ -978,7 +982,6 @@ pub const DeclGen = struct { |
| 978 | 982 | ||
| 979 | if (struct_obj.layout == .Packed) { | 983 | if (struct_obj.layout == .Packed) { |
| 980 | try llvm_field_types.ensureUnusedCapacity(gpa, struct_obj.fields.count() * 2); | 984 | try llvm_field_types.ensureUnusedCapacity(gpa, struct_obj.fields.count() * 2); |
| 981 | const target = dg.module.getTarget(); | ||
| 982 | comptime assert(Type.packed_struct_layout_version == 1); | 985 | comptime assert(Type.packed_struct_layout_version == 1); |
| 983 | var offset: u64 = 0; | 986 | var offset: u64 = 0; |
| 984 | var big_align: u32 = 0; | 987 | var big_align: u32 = 0; |
| ... | @@ -1073,7 +1076,6 @@ pub const DeclGen = struct { | ... | @@ -1073,7 +1076,6 @@ pub const DeclGen = struct { |
| 1073 | gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator()); | 1076 | gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator()); |
| 1074 | 1077 | ||
| 1075 | const union_obj = t.cast(Type.Payload.Union).?.data; | 1078 | const union_obj = t.cast(Type.Payload.Union).?.data; |
| 1076 | const target = dg.module.getTarget(); | ||
| 1077 | if (t.unionTagType()) |enum_tag_ty| { | 1079 | if (t.unionTagType()) |enum_tag_ty| { |
| 1078 | const enum_tag_llvm_ty = try dg.llvmType(enum_tag_ty); | 1080 | const enum_tag_llvm_ty = try dg.llvmType(enum_tag_ty); |
| 1079 | const layout = union_obj.getLayout(target, true); | 1081 | const layout = union_obj.getLayout(target, true); |
| ... | @@ -1141,7 +1143,6 @@ pub const DeclGen = struct { | ... | @@ -1141,7 +1143,6 @@ pub const DeclGen = struct { |
| 1141 | }, | 1143 | }, |
| 1142 | .Fn => { | 1144 | .Fn => { |
| 1143 | const fn_info = t.fnInfo(); | 1145 | const fn_info = t.fnInfo(); |
| 1144 | const target = dg.module.getTarget(); | ||
| 1145 | const sret = firstParamSRet(fn_info, target); | 1146 | const sret = firstParamSRet(fn_info, target); |
| 1146 | const return_type = fn_info.return_type; | 1147 | const return_type = fn_info.return_type; |
| 1147 | const raw_llvm_ret_ty = try dg.llvmType(return_type); | 1148 | const raw_llvm_ret_ty = try dg.llvmType(return_type); |
| ... | @@ -1257,16 +1258,21 @@ pub const DeclGen = struct { | ... | @@ -1257,16 +1258,21 @@ pub const DeclGen = struct { |
| 1257 | }, | 1258 | }, |
| 1258 | .Float => { | 1259 | .Float => { |
| 1259 | const llvm_ty = try dg.llvmType(tv.ty); | 1260 | const llvm_ty = try dg.llvmType(tv.ty); |
| 1260 | switch (tv.ty.floatBits(dg.module.getTarget())) { | 1261 | const target = dg.module.getTarget(); |
| 1262 | switch (tv.ty.floatBits(target)) { | ||
| 1261 | 16, 32, 64 => return llvm_ty.constReal(tv.val.toFloat(f64)), | 1263 | 16, 32, 64 => return llvm_ty.constReal(tv.val.toFloat(f64)), |
| 1262 | 80 => { | 1264 | 80 => { |
| 1263 | const float = tv.val.toFloat(f80); | 1265 | const float = tv.val.toFloat(f80); |
| 1264 | const repr = @ptrCast(*const std.math.F80Repr, &float); | 1266 | const repr = std.math.break_f80(float); |
| 1265 | const llvm_i80 = dg.context.intType(80); | 1267 | const llvm_i80 = dg.context.intType(80); |
| 1266 | var x = llvm_i80.constInt(repr.exp, .False); | 1268 | var x = llvm_i80.constInt(repr.exp, .False); |
| 1267 | x = x.constShl(llvm_i80.constInt(64, .False)); | 1269 | x = x.constShl(llvm_i80.constInt(64, .False)); |
| 1268 | x = x.constOr(llvm_i80.constInt(repr.fraction, .False)); | 1270 | x = x.constOr(llvm_i80.constInt(repr.fraction, .False)); |
| 1269 | return x.constBitCast(llvm_ty); | 1271 | if (backendSupportsF80(target)) { |
| 1272 | return x.constBitCast(llvm_ty); | ||
| 1273 | } else { | ||
| 1274 | return x; | ||
| 1275 | } | ||
| 1270 | }, | 1276 | }, |
| 1271 | 128 => { | 1277 | 128 => { |
| 1272 | var buf: [2]u64 = @bitCast([2]u64, tv.val.toFloat(f128)); | 1278 | var buf: [2]u64 = @bitCast([2]u64, tv.val.toFloat(f128)); |
| ... | @@ -5353,3 +5359,12 @@ fn isByRef(ty: Type) bool { | ... | @@ -5353,3 +5359,12 @@ fn isByRef(ty: Type) bool { |
| 5353 | }, | 5359 | }, |
| 5354 | } | 5360 | } |
| 5355 | } | 5361 | } |
| 5362 | |||
| 5363 | /// This function returns true if we expect LLVM to lower x86_fp80 correctly | ||
| 5364 | /// and false if we expect LLVM to crash if it counters an x86_fp80 type. | ||
| 5365 | fn backendSupportsF80(target: std.Target) bool { | ||
| 5366 | return switch (target.cpu.arch) { | ||
| 5367 | .x86_64, .i386 => true, | ||
| 5368 | else => false, | ||
| 5369 | }; | ||
| 5370 | } |
src/stage1/codegen.cpp+36-34| ... | @@ -8195,17 +8195,15 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n | ... | @@ -8195,17 +8195,15 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n |
| 8195 | case 64: | 8195 | case 64: |
| 8196 | return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f64); | 8196 | return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f64); |
| 8197 | case 80: { | 8197 | case 80: { |
| 8198 | uint64_t buf[2]; | 8198 | LLVMTypeRef llvm_i80 = LLVMIntType(80); |
| 8199 | memcpy(&buf, &const_val->data.x_f80, 16); | 8199 | LLVMValueRef x = LLVMConstInt(llvm_i80, const_val->data.x_f80.signExp, false); |
| 8200 | #if ZIG_BYTE_ORDER == ZIG_BIG_ENDIAN | 8200 | x = LLVMConstShl(x, LLVMConstInt(llvm_i80, 64, false)); |
| 8201 | uint64_t tmp = buf[0]; | 8201 | x = LLVMConstOr(x, LLVMConstInt(llvm_i80, const_val->data.x_f80.signif, false)); |
| 8202 | buf[0] = buf[1]; | 8202 | if (target_has_f80(g->zig_target)) { |
| 8203 | buf[1] = tmp; | 8203 | return LLVMConstBitCast(x, LLVMX86FP80Type()); |
| 8204 | #endif | 8204 | } else { |
| 8205 | LLVMValueRef as_i128 = LLVMConstIntOfArbitraryPrecision(LLVMInt128Type(), 2, buf); | 8205 | return x; |
| 8206 | if (!target_has_f80(g->zig_target)) return as_i128; | 8206 | } |
| 8207 | LLVMValueRef as_int = LLVMConstTrunc(as_i128, LLVMIntType(80)); | ||
| 8208 | return LLVMConstBitCast(as_int, get_llvm_type(g, type_entry)); | ||
| 8209 | } | 8207 | } |
| 8210 | case 128: | 8208 | case 128: |
| 8211 | { | 8209 | { |
| ... | @@ -9429,32 +9427,36 @@ static void define_builtin_types(CodeGen *g) { | ... | @@ -9429,32 +9427,36 @@ static void define_builtin_types(CodeGen *g) { |
| 9429 | 9427 | ||
| 9430 | { | 9428 | { |
| 9431 | ZigType *entry = new_type_table_entry(ZigTypeIdFloat); | 9429 | ZigType *entry = new_type_table_entry(ZigTypeIdFloat); |
| 9432 | unsigned u64_alignment = LLVMABIAlignmentOfType(g->target_data_ref, LLVMInt64Type()); | 9430 | entry->size_in_bits = 80; |
| 9433 | 9431 | ||
| 9434 | if (u64_alignment >= 8) { | ||
| 9435 | entry->size_in_bits = 128; | ||
| 9436 | entry->abi_size = 16; | ||
| 9437 | entry->abi_align = 16; | ||
| 9438 | } else if (u64_alignment >= 4) { | ||
| 9439 | entry->size_in_bits = 96; | ||
| 9440 | entry->abi_size = 12; | ||
| 9441 | entry->abi_align = 4; | ||
| 9442 | } else { | ||
| 9443 | entry->size_in_bits = 80; | ||
| 9444 | entry->abi_size = 10; | ||
| 9445 | entry->abi_align = 2; | ||
| 9446 | } | ||
| 9447 | if (target_has_f80(g->zig_target)) { | ||
| 9448 | entry->llvm_type = LLVMX86FP80Type(); | ||
| 9449 | } else { | ||
| 9450 | // We use an int here instead of x86_fp80 because on targets such as arm, | ||
| 9451 | // LLVM will give "ERROR: Cannot select" for any instructions involving | ||
| 9452 | // the x86_fp80 type. | ||
| 9453 | entry->llvm_type = get_int_type(g, false, entry->size_in_bits)->llvm_type; | ||
| 9454 | } | ||
| 9455 | buf_init_from_str(&entry->name, "f80"); | 9432 | buf_init_from_str(&entry->name, "f80"); |
| 9456 | entry->data.floating.bit_count = 80; | 9433 | entry->data.floating.bit_count = 80; |
| 9457 | 9434 | ||
| 9435 | switch (g->zig_target->arch) { | ||
| 9436 | case ZigLLVM_x86_64: | ||
| 9437 | entry->llvm_type = LLVMX86FP80Type(); | ||
| 9438 | entry->abi_size = 16; | ||
| 9439 | entry->abi_align = 16; | ||
| 9440 | break; | ||
| 9441 | case ZigLLVM_x86: | ||
| 9442 | entry->llvm_type = LLVMX86FP80Type(); | ||
| 9443 | entry->abi_size = 12; | ||
| 9444 | entry->abi_align = 4; | ||
| 9445 | break; | ||
| 9446 | default: { | ||
| 9447 | // We use an int here instead of x86_fp80 because on targets such as arm, | ||
| 9448 | // LLVM will give "ERROR: Cannot select" for any instructions involving | ||
| 9449 | // the x86_fp80 type. | ||
| 9450 | ZigType *u80_ty = get_int_type(g, false, 80); | ||
| 9451 | assert(!target_has_f80(g->zig_target)); | ||
| 9452 | assert(u80_ty->size_in_bits == entry->size_in_bits); | ||
| 9453 | entry->llvm_type = get_llvm_type(g, u80_ty); | ||
| 9454 | entry->abi_size = u80_ty->abi_size; | ||
| 9455 | entry->abi_align = u80_ty->abi_align; | ||
| 9456 | break; | ||
| 9457 | } | ||
| 9458 | } | ||
| 9459 | |||
| 9458 | entry->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), | 9460 | entry->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), |
| 9459 | entry->size_in_bits, ZigLLVMEncoding_DW_ATE_unsigned()); | 9461 | entry->size_in_bits, ZigLLVMEncoding_DW_ATE_unsigned()); |
| 9460 | 9462 |
src/type.zig+50-5| ... | @@ -1877,9 +1877,28 @@ pub const Type = extern union { | ... | @@ -1877,9 +1877,28 @@ pub const Type = extern union { |
| 1877 | .f16 => return 2, | 1877 | .f16 => return 2, |
| 1878 | .f32 => return 4, | 1878 | .f32 => return 4, |
| 1879 | .f64 => return 8, | 1879 | .f64 => return 8, |
| 1880 | .f80 => return 16, | ||
| 1881 | .f128 => return 16, | 1880 | .f128 => return 16, |
| 1882 | .c_longdouble => return 16, | 1881 | |
| 1882 | .f80 => switch (target.cpu.arch) { | ||
| 1883 | .i386 => return 4, | ||
| 1884 | .x86_64 => return 16, | ||
| 1885 | else => { | ||
| 1886 | var payload: Payload.Bits = .{ | ||
| 1887 | .base = .{ .tag = .int_unsigned }, | ||
| 1888 | .data = 80, | ||
| 1889 | }; | ||
| 1890 | const u80_ty = initPayload(&payload.base); | ||
| 1891 | return abiAlignment(u80_ty, target); | ||
| 1892 | }, | ||
| 1893 | }, | ||
| 1894 | .c_longdouble => switch (CType.longdouble.sizeInBits(target)) { | ||
| 1895 | 16 => return abiAlignment(Type.f16, target), | ||
| 1896 | 32 => return abiAlignment(Type.f32, target), | ||
| 1897 | 64 => return abiAlignment(Type.f64, target), | ||
| 1898 | 80 => return abiAlignment(Type.f80, target), | ||
| 1899 | 128 => return abiAlignment(Type.f128, target), | ||
| 1900 | else => unreachable, | ||
| 1901 | }, | ||
| 1883 | 1902 | ||
| 1884 | .error_set, | 1903 | .error_set, |
| 1885 | .error_set_single, | 1904 | .error_set_single, |
| ... | @@ -2158,9 +2177,28 @@ pub const Type = extern union { | ... | @@ -2158,9 +2177,28 @@ pub const Type = extern union { |
| 2158 | .f16 => return 2, | 2177 | .f16 => return 2, |
| 2159 | .f32 => return 4, | 2178 | .f32 => return 4, |
| 2160 | .f64 => return 8, | 2179 | .f64 => return 8, |
| 2161 | .f80 => return 16, | ||
| 2162 | .f128 => return 16, | 2180 | .f128 => return 16, |
| 2163 | .c_longdouble => return 16, | 2181 | |
| 2182 | .f80 => switch (target.cpu.arch) { | ||
| 2183 | .i386 => return 12, | ||
| 2184 | .x86_64 => return 16, | ||
| 2185 | else => { | ||
| 2186 | var payload: Payload.Bits = .{ | ||
| 2187 | .base = .{ .tag = .int_unsigned }, | ||
| 2188 | .data = 80, | ||
| 2189 | }; | ||
| 2190 | const u80_ty = initPayload(&payload.base); | ||
| 2191 | return abiSize(u80_ty, target); | ||
| 2192 | }, | ||
| 2193 | }, | ||
| 2194 | .c_longdouble => switch (CType.longdouble.sizeInBits(target)) { | ||
| 2195 | 16 => return abiSize(Type.f16, target), | ||
| 2196 | 32 => return abiSize(Type.f32, target), | ||
| 2197 | 64 => return abiSize(Type.f64, target), | ||
| 2198 | 80 => return abiSize(Type.f80, target), | ||
| 2199 | 128 => return abiSize(Type.f128, target), | ||
| 2200 | else => unreachable, | ||
| 2201 | }, | ||
| 2164 | 2202 | ||
| 2165 | .error_set, | 2203 | .error_set, |
| 2166 | .error_set_single, | 2204 | .error_set_single, |
| ... | @@ -2349,7 +2387,7 @@ pub const Type = extern union { | ... | @@ -2349,7 +2387,7 @@ pub const Type = extern union { |
| 2349 | .c_ulong => return CType.ulong.sizeInBits(target), | 2387 | .c_ulong => return CType.ulong.sizeInBits(target), |
| 2350 | .c_longlong => return CType.longlong.sizeInBits(target), | 2388 | .c_longlong => return CType.longlong.sizeInBits(target), |
| 2351 | .c_ulonglong => return CType.ulonglong.sizeInBits(target), | 2389 | .c_ulonglong => return CType.ulonglong.sizeInBits(target), |
| 2352 | .c_longdouble => 128, | 2390 | .c_longdouble => return CType.longdouble.sizeInBits(target), |
| 2353 | 2391 | ||
| 2354 | .error_set, | 2392 | .error_set, |
| 2355 | .error_set_single, | 2393 | .error_set_single, |
| ... | @@ -4772,6 +4810,13 @@ pub const Type = extern union { | ... | @@ -4772,6 +4810,13 @@ pub const Type = extern union { |
| 4772 | pub const @"u8" = initTag(.u8); | 4810 | pub const @"u8" = initTag(.u8); |
| 4773 | pub const @"u32" = initTag(.u32); | 4811 | pub const @"u32" = initTag(.u32); |
| 4774 | pub const @"u64" = initTag(.u64); | 4812 | pub const @"u64" = initTag(.u64); |
| 4813 | |||
| 4814 | pub const @"f16" = initTag(.f16); | ||
| 4815 | pub const @"f32" = initTag(.f32); | ||
| 4816 | pub const @"f64" = initTag(.f64); | ||
| 4817 | pub const @"f80" = initTag(.f80); | ||
| 4818 | pub const @"f128" = initTag(.f128); | ||
| 4819 | |||
| 4775 | pub const @"bool" = initTag(.bool); | 4820 | pub const @"bool" = initTag(.bool); |
| 4776 | pub const @"usize" = initTag(.usize); | 4821 | pub const @"usize" = initTag(.usize); |
| 4777 | pub const @"isize" = initTag(.isize); | 4822 | pub const @"isize" = initTag(.isize); |
src/value.zig+41-26| ... | @@ -1112,6 +1112,19 @@ pub const Value = extern union { | ... | @@ -1112,6 +1112,19 @@ pub const Value = extern union { |
| 1112 | } | 1112 | } |
| 1113 | 1113 | ||
| 1114 | fn floatWriteToMemory(comptime F: type, f: F, target: Target, buffer: []u8) void { | 1114 | fn floatWriteToMemory(comptime F: type, f: F, target: Target, buffer: []u8) void { |
| 1115 | if (F == f80) { | ||
| 1116 | switch (target.cpu.arch) { | ||
| 1117 | .i386, .x86_64 => { | ||
| 1118 | const repr = std.math.break_f80(f); | ||
| 1119 | std.mem.writeIntLittle(u64, buffer[0..8], repr.fraction); | ||
| 1120 | std.mem.writeIntLittle(u16, buffer[8..10], repr.exp); | ||
| 1121 | // TODO set the rest of the bytes to undefined. should we use 0xaa | ||
| 1122 | // or is there a different way? | ||
| 1123 | return; | ||
| 1124 | }, | ||
| 1125 | else => {}, | ||
| 1126 | } | ||
| 1127 | } | ||
| 1115 | const Int = @Type(.{ .Int = .{ | 1128 | const Int = @Type(.{ .Int = .{ |
| 1116 | .signedness = .unsigned, | 1129 | .signedness = .unsigned, |
| 1117 | .bits = @typeInfo(F).Float.bits, | 1130 | .bits = @typeInfo(F).Float.bits, |
| ... | @@ -1122,41 +1135,43 @@ pub const Value = extern union { | ... | @@ -1122,41 +1135,43 @@ pub const Value = extern union { |
| 1122 | 1135 | ||
| 1123 | fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F { | 1136 | fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F { |
| 1124 | if (F == f80) { | 1137 | if (F == f80) { |
| 1125 | switch (target.cpu.arch.endian()) { | 1138 | switch (target.cpu.arch) { |
| 1126 | .Little => { | 1139 | .i386, .x86_64 => return std.math.make_f80(.{ |
| 1127 | const TargetF80Repr = extern struct { | 1140 | .fraction = std.mem.readIntLittle(u64, buffer[0..8]), |
| 1128 | fraction: u64, | 1141 | .exp = std.mem.readIntLittle(u16, buffer[8..10]), |
| 1129 | exp: u16, | 1142 | }), |
| 1130 | }; | 1143 | else => {}, |
| 1131 | const target_repr = @ptrCast(*align(1) const TargetF80Repr, buffer.ptr); | ||
| 1132 | const real_repr: std.math.F80Repr = .{ | ||
| 1133 | .fraction = target_repr.fraction, | ||
| 1134 | .exp = target_repr.exp, | ||
| 1135 | }; | ||
| 1136 | return @ptrCast(*const f80, &real_repr).*; | ||
| 1137 | }, | ||
| 1138 | .Big => { | ||
| 1139 | const TargetF80Repr = extern struct { | ||
| 1140 | exp: u16, | ||
| 1141 | fraction: u64, | ||
| 1142 | }; | ||
| 1143 | const target_repr = @ptrCast(*align(1) const TargetF80Repr, buffer.ptr); | ||
| 1144 | const real_repr: std.math.F80Repr = .{ | ||
| 1145 | .fraction = target_repr.fraction, | ||
| 1146 | .exp = target_repr.exp, | ||
| 1147 | }; | ||
| 1148 | return @ptrCast(*const f80, &real_repr).*; | ||
| 1149 | }, | ||
| 1150 | } | 1144 | } |
| 1151 | } | 1145 | } |
| 1152 | const Int = @Type(.{ .Int = .{ | 1146 | const Int = @Type(.{ .Int = .{ |
| 1153 | .signedness = .unsigned, | 1147 | .signedness = .unsigned, |
| 1154 | .bits = @typeInfo(F).Float.bits, | 1148 | .bits = @typeInfo(F).Float.bits, |
| 1155 | } }); | 1149 | } }); |
| 1156 | const int = std.mem.readInt(Int, buffer[0..@sizeOf(Int)], target.cpu.arch.endian()); | 1150 | const int = readInt(Int, buffer[0..@sizeOf(Int)], target.cpu.arch.endian()); |
| 1157 | return @bitCast(F, int); | 1151 | return @bitCast(F, int); |
| 1158 | } | 1152 | } |
| 1159 | 1153 | ||
| 1154 | fn readInt(comptime Int: type, buffer: *const [@sizeOf(Int)]u8, endian: std.builtin.Endian) Int { | ||
| 1155 | var result: Int = 0; | ||
| 1156 | switch (endian) { | ||
| 1157 | .Big => { | ||
| 1158 | for (buffer) |byte| { | ||
| 1159 | result <<= 8; | ||
| 1160 | result |= byte; | ||
| 1161 | } | ||
| 1162 | }, | ||
| 1163 | .Little => { | ||
| 1164 | var i: usize = buffer.len; | ||
| 1165 | while (i != 0) { | ||
| 1166 | i -= 1; | ||
| 1167 | result <<= 8; | ||
| 1168 | result |= buffer[i]; | ||
| 1169 | } | ||
| 1170 | }, | ||
| 1171 | } | ||
| 1172 | return result; | ||
| 1173 | } | ||
| 1174 | |||
| 1160 | /// Asserts that the value is a float or an integer. | 1175 | /// Asserts that the value is a float or an integer. |
| 1161 | pub fn toFloat(val: Value, comptime T: type) T { | 1176 | pub fn toFloat(val: Value, comptime T: type) T { |
| 1162 | return switch (val.tag()) { | 1177 | return switch (val.tag()) { |
test/behavior/floatop.zig+4-1| ... | @@ -5,7 +5,10 @@ const math = std.math; | ... | @@ -5,7 +5,10 @@ const math = std.math; |
| 5 | const pi = std.math.pi; | 5 | const pi = std.math.pi; |
| 6 | const e = std.math.e; | 6 | const e = std.math.e; |
| 7 | const Vector = std.meta.Vector; | 7 | const Vector = std.meta.Vector; |
| 8 | const has_f80_rt = @import("builtin").cpu.arch == .x86_64; | 8 | const has_f80_rt = switch (builtin.cpu.arch) { |
| 9 | .x86_64, .i386 => true, | ||
| 10 | else => false, | ||
| 11 | }; | ||
| 9 | 12 | ||
| 10 | const epsilon_16 = 0.001; | 13 | const epsilon_16 = 0.001; |
| 11 | const epsilon = 0.000001; | 14 | const epsilon = 0.000001; |