| 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 | 42 | pub const f128_epsilon = @bitCast(f128, @as(u128, 0x3F8F0000000000000000000000000000)); |
| 43 | 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 | 45 | // float.h details |
| 54 | pub const f80_true_min = @ptrCast(*const f80, &F80Repr{ .fraction = 1, .exp = 0 }).*; | |
| 55 | pub const f80_min = @ptrCast(*const f80, &F80Repr{ .fraction = 0x8000000000000000, .exp = 1 }).*; | |
| 56 | pub const f80_max = @ptrCast(*const f80, &F80Repr{ .fraction = 0xFFFFFFFFFFFFFFFF, .exp = 0x7FFE }).*; | |
| 57 | pub const f80_epsilon = @ptrCast(*const f80, &F80Repr{ .fraction = 0x8000000000000000, .exp = 0x3FC0 }).*; | |
| 46 | pub const f80_true_min = make_f80(.{ .fraction = 1, .exp = 0 }); | |
| 47 | pub const f80_min = make_f80(.{ .fraction = 0x8000000000000000, .exp = 1 }); | |
| 48 | pub const f80_max = make_f80(.{ .fraction = 0xFFFFFFFFFFFFFFFF, .exp = 0x7FFE }); | |
| 49 | pub const f80_epsilon = make_f80(.{ .fraction = 0x8000000000000000, .exp = 0x3FC0 }); | |
| 58 | 50 | pub const f80_toint = 1.0 / f80_epsilon; |
| 59 | 51 | |
| 60 | 52 | pub const f64_true_min = 4.94065645841246544177e-324; |
| ... | ... | @@ -104,9 +96,9 @@ pub const qnan_f64 = @bitCast(f64, qnan_u64); |
| 104 | 96 | pub const inf_u64 = @as(u64, 0x7FF << 52); |
| 105 | 97 | pub const inf_f64 = @bitCast(f64, inf_u64); |
| 106 | 98 | |
| 107 | pub const inf_f80 = @ptrCast(*const f80, &F80Repr{ .fraction = 0x8000000000000000, .exp = 0x7fff }).*; | |
| 108 | pub const nan_f80 = @ptrCast(*const f80, &F80Repr{ .fraction = 0xA000000000000000, .exp = 0x7fff }).*; | |
| 109 | pub const qnan_f80 = @ptrCast(*const f80, &F80Repr{ .fraction = 0xC000000000000000, .exp = 0x7fff }).*; | |
| 99 | pub const inf_f80 = make_f80(F80{ .fraction = 0x8000000000000000, .exp = 0x7fff }); | |
| 100 | pub const nan_f80 = make_f80(F80{ .fraction = 0xA000000000000000, .exp = 0x7fff }); | |
| 101 | pub const qnan_f80 = make_f80(F80{ .fraction = 0xC000000000000000, .exp = 0x7fff }); | |
| 110 | 102 | |
| 111 | 103 | pub const nan_u128 = @as(u128, 0x7fff0000000000000000000000000001); |
| 112 | 104 | pub const nan_f128 = @bitCast(f128, nan_u128); |
| ... | ... | @@ -1501,3 +1493,21 @@ test "boolMask" { |
| 1501 | 1493 | pub fn comptimeMod(num: anytype, denom: comptime_int) IntFittingRange(0, denom - 1) { |
| 1502 | 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 | 232 | } |
| 233 | 233 | |
| 234 | 234 | pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { |
| 235 | var a_rep align(16) = @ptrCast(*const std.math.F80Repr, &a).*; | |
| 236 | var b_rep align(16) = @ptrCast(*const std.math.F80Repr, &b).*; | |
| 235 | var a_rep = std.math.break_f80(a); | |
| 236 | var b_rep = std.math.break_f80(b); | |
| 237 | 237 | var a_exp: i32 = a_rep.exp & 0x7FFF; |
| 238 | 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 | 257 | std.debug.assert(a_rep.fraction & significand_mask != 0); |
| 258 | 258 | // NaN + anything = qNaN |
| 259 | 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 | 263 | if (b_exp == max_exp) { |
| ... | ... | @@ -268,7 +268,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { |
| 268 | 268 | std.debug.assert(b_rep.fraction & significand_mask != 0); |
| 269 | 269 | // anything + NaN = qNaN |
| 270 | 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 | 279 | if (b_zero) { |
| 280 | 280 | // but we need to get the sign right for zero + zero |
| 281 | 281 | a_rep.exp &= b_rep.exp; |
| 282 | return @ptrCast(*const f80, &a_rep).*; | |
| 282 | return std.math.make_f80(a_rep); | |
| 283 | 283 | } else { |
| 284 | 284 | return b; |
| 285 | 285 | } |
| ... | ... | @@ -359,7 +359,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { |
| 359 | 359 | if (a_exp >= max_exp) { |
| 360 | 360 | a_rep.exp = max_exp | result_sign; |
| 361 | 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 | 365 | if (a_exp <= 0) { |
| ... | ... | @@ -387,13 +387,13 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { |
| 387 | 387 | |
| 388 | 388 | a_rep.fraction = @truncate(u64, a_int); |
| 389 | 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 | 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 | 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 | 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 | 147 | // Comparison between f80 |
| 148 | 148 | |
| 149 | 149 | pub inline fn cmp_f80(comptime RT: type, a: f80, b: f80) RT { |
| 150 | const a_rep = @ptrCast(*const std.math.F80Repr, &a).*; | |
| 151 | const b_rep = @ptrCast(*const std.math.F80Repr, &b).*; | |
| 150 | const a_rep = std.math.break_f80(a); | |
| 151 | const b_rep = std.math.break_f80(b); | |
| 152 | 152 | const sig_bits = std.math.floatMantissaBits(f80); |
| 153 | 153 | const int_bit = 0x8000000000000000; |
| 154 | 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 | 41 | const src_qnan = 1 << (src_sig_bits - 1); |
| 42 | 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 | 46 | // Break a into a sign and representation of the absolute value |
| 47 | 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 | 83 | } |
| 84 | 84 | |
| 85 | 85 | dst.exp |= sign; |
| 86 | return @ptrCast(*const f80, &dst).*; | |
| 86 | return std.math.make_f80(dst); | |
| 87 | 87 | } |
| 88 | 88 | |
| 89 | 89 | pub fn __extendxftf2(a: f80) callconv(.C) f128 { |
| ... | ... | @@ -99,7 +99,7 @@ pub fn __extendxftf2(a: f80) callconv(.C) f128 { |
| 99 | 99 | const dst_min_normal = @as(u128, 1) << dst_sig_bits; |
| 100 | 100 | |
| 101 | 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 | 103 | const sign = a_rep.exp & 0x8000; |
| 104 | 104 | a_rep.exp &= 0x7FFF; |
| 105 | 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 | 42 | const dst_nan_mask = dst_qnan - 1; |
| 43 | 43 | |
| 44 | 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 | 46 | const sign = a_rep.exp & 0x8000; |
| 47 | 47 | a_rep.exp &= 0x7FFF; |
| 48 | 48 | a_rep.fraction &= 0x7FFFFFFFFFFFFFFF; |
| ... | ... | @@ -125,7 +125,7 @@ pub fn __trunctfxf2(a: f128) callconv(.C) f80 { |
| 125 | 125 | const a_abs = a_rep & src_abs_mask; |
| 126 | 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 | 130 | if (a_abs > src_inf) { |
| 131 | 131 | // a is NaN. |
| ... | ... | @@ -155,5 +155,5 @@ pub fn __trunctfxf2(a: f128) callconv(.C) f80 { |
| 155 | 155 | } |
| 156 | 156 | |
| 157 | 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 | 824 | |
| 825 | 825 | fn llvmType(dg: *DeclGen, t: Type) Allocator.Error!*const llvm.Type { |
| 826 | 826 | const gpa = dg.gpa; |
| 827 | const target = dg.module.getTarget(); | |
| 827 | 828 | switch (t.zigTypeTag()) { |
| 828 | 829 | .Void, .NoReturn => return dg.context.voidType(), |
| 829 | 830 | .Int => { |
| 830 | const info = t.intInfo(dg.module.getTarget()); | |
| 831 | const info = t.intInfo(target); | |
| 831 | 832 | return dg.context.intType(info.bits); |
| 832 | 833 | }, |
| 833 | 834 | .Enum => { |
| 834 | 835 | var buffer: Type.Payload.Bits = undefined; |
| 835 | 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 | 838 | return dg.context.intType(bit_count); |
| 838 | 839 | }, |
| 839 | .Float => switch (t.floatBits(dg.module.getTarget())) { | |
| 840 | .Float => switch (t.floatBits(target)) { | |
| 840 | 841 | 16 => return dg.context.halfType(), |
| 841 | 842 | 32 => return dg.context.floatType(), |
| 842 | 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 | 845 | 128 => return dg.context.fp128Type(), |
| 845 | 846 | else => unreachable, |
| 846 | 847 | }, |
| ... | ... | @@ -859,7 +860,8 @@ pub const DeclGen = struct { |
| 859 | 860 | const llvm_addrspace = dg.llvmAddressSpace(t.ptrAddressSpace()); |
| 860 | 861 | const elem_ty = t.childType(); |
| 861 | 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 | 865 | else => elem_ty.hasRuntimeBits(), |
| 864 | 866 | }; |
| 865 | 867 | const llvm_elem_ty = if (lower_elem_ty) |
| ... | ... | @@ -889,9 +891,11 @@ pub const DeclGen = struct { |
| 889 | 891 | else => unreachable, |
| 890 | 892 | }, |
| 891 | 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 | 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 | 900 | .Vector => { |
| 897 | 901 | const elem_type = try dg.llvmType(t.childType()); |
| ... | ... | @@ -978,7 +982,6 @@ pub const DeclGen = struct { |
| 978 | 982 | |
| 979 | 983 | if (struct_obj.layout == .Packed) { |
| 980 | 984 | try llvm_field_types.ensureUnusedCapacity(gpa, struct_obj.fields.count() * 2); |
| 981 | const target = dg.module.getTarget(); | |
| 982 | 985 | comptime assert(Type.packed_struct_layout_version == 1); |
| 983 | 986 | var offset: u64 = 0; |
| 984 | 987 | var big_align: u32 = 0; |
| ... | ... | @@ -1073,7 +1076,6 @@ pub const DeclGen = struct { |
| 1073 | 1076 | gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator()); |
| 1074 | 1077 | |
| 1075 | 1078 | const union_obj = t.cast(Type.Payload.Union).?.data; |
| 1076 | const target = dg.module.getTarget(); | |
| 1077 | 1079 | if (t.unionTagType()) |enum_tag_ty| { |
| 1078 | 1080 | const enum_tag_llvm_ty = try dg.llvmType(enum_tag_ty); |
| 1079 | 1081 | const layout = union_obj.getLayout(target, true); |
| ... | ... | @@ -1141,7 +1143,6 @@ pub const DeclGen = struct { |
| 1141 | 1143 | }, |
| 1142 | 1144 | .Fn => { |
| 1143 | 1145 | const fn_info = t.fnInfo(); |
| 1144 | const target = dg.module.getTarget(); | |
| 1145 | 1146 | const sret = firstParamSRet(fn_info, target); |
| 1146 | 1147 | const return_type = fn_info.return_type; |
| 1147 | 1148 | const raw_llvm_ret_ty = try dg.llvmType(return_type); |
| ... | ... | @@ -1257,16 +1258,21 @@ pub const DeclGen = struct { |
| 1257 | 1258 | }, |
| 1258 | 1259 | .Float => { |
| 1259 | 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 | 1263 | 16, 32, 64 => return llvm_ty.constReal(tv.val.toFloat(f64)), |
| 1262 | 1264 | 80 => { |
| 1263 | 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 | 1267 | const llvm_i80 = dg.context.intType(80); |
| 1266 | 1268 | var x = llvm_i80.constInt(repr.exp, .False); |
| 1267 | 1269 | x = x.constShl(llvm_i80.constInt(64, .False)); |
| 1268 | 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 | 1277 | 128 => { |
| 1272 | 1278 | var buf: [2]u64 = @bitCast([2]u64, tv.val.toFloat(f128)); |
| ... | ... | @@ -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 | 8195 | case 64: |
| 8196 | 8196 | return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f64); |
| 8197 | 8197 | case 80: { |
| 8198 | uint64_t buf[2]; | |
| 8199 | memcpy(&buf, &const_val->data.x_f80, 16); | |
| 8200 | #if ZIG_BYTE_ORDER == ZIG_BIG_ENDIAN | |
| 8201 | uint64_t tmp = buf[0]; | |
| 8202 | buf[0] = buf[1]; | |
| 8203 | buf[1] = tmp; | |
| 8204 | #endif | |
| 8205 | LLVMValueRef as_i128 = LLVMConstIntOfArbitraryPrecision(LLVMInt128Type(), 2, buf); | |
| 8206 | if (!target_has_f80(g->zig_target)) return as_i128; | |
| 8207 | LLVMValueRef as_int = LLVMConstTrunc(as_i128, LLVMIntType(80)); | |
| 8208 | return LLVMConstBitCast(as_int, get_llvm_type(g, type_entry)); | |
| 8198 | LLVMTypeRef llvm_i80 = LLVMIntType(80); | |
| 8199 | LLVMValueRef x = LLVMConstInt(llvm_i80, const_val->data.x_f80.signExp, false); | |
| 8200 | x = LLVMConstShl(x, LLVMConstInt(llvm_i80, 64, false)); | |
| 8201 | x = LLVMConstOr(x, LLVMConstInt(llvm_i80, const_val->data.x_f80.signif, false)); | |
| 8202 | if (target_has_f80(g->zig_target)) { | |
| 8203 | return LLVMConstBitCast(x, LLVMX86FP80Type()); | |
| 8204 | } else { | |
| 8205 | return x; | |
| 8206 | } | |
| 8209 | 8207 | } |
| 8210 | 8208 | case 128: |
| 8211 | 8209 | { |
| ... | ... | @@ -9429,32 +9427,36 @@ static void define_builtin_types(CodeGen *g) { |
| 9429 | 9427 | |
| 9430 | 9428 | { |
| 9431 | 9429 | ZigType *entry = new_type_table_entry(ZigTypeIdFloat); |
| 9432 | unsigned u64_alignment = LLVMABIAlignmentOfType(g->target_data_ref, LLVMInt64Type()); | |
| 9433 | ||
| 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 | } | |
| 9430 | entry->size_in_bits = 80; | |
| 9431 | ||
| 9455 | 9432 | buf_init_from_str(&entry->name, "f80"); |
| 9456 | 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 | 9460 | entry->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), |
| 9459 | 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 | 1877 | .f16 => return 2, |
| 1878 | 1878 | .f32 => return 4, |
| 1879 | 1879 | .f64 => return 8, |
| 1880 | .f80 => return 16, | |
| 1881 | 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 | 1903 | .error_set, |
| 1885 | 1904 | .error_set_single, |
| ... | ... | @@ -2158,9 +2177,28 @@ pub const Type = extern union { |
| 2158 | 2177 | .f16 => return 2, |
| 2159 | 2178 | .f32 => return 4, |
| 2160 | 2179 | .f64 => return 8, |
| 2161 | .f80 => return 16, | |
| 2162 | 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 | 2203 | .error_set, |
| 2166 | 2204 | .error_set_single, |
| ... | ... | @@ -2349,7 +2387,7 @@ pub const Type = extern union { |
| 2349 | 2387 | .c_ulong => return CType.ulong.sizeInBits(target), |
| 2350 | 2388 | .c_longlong => return CType.longlong.sizeInBits(target), |
| 2351 | 2389 | .c_ulonglong => return CType.ulonglong.sizeInBits(target), |
| 2352 | .c_longdouble => 128, | |
| 2390 | .c_longdouble => return CType.longdouble.sizeInBits(target), | |
| 2353 | 2391 | |
| 2354 | 2392 | .error_set, |
| 2355 | 2393 | .error_set_single, |
| ... | ... | @@ -4772,6 +4810,13 @@ pub const Type = extern union { |
| 4772 | 4810 | pub const @"u8" = initTag(.u8); |
| 4773 | 4811 | pub const @"u32" = initTag(.u32); |
| 4774 | 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 | 4820 | pub const @"bool" = initTag(.bool); |
| 4776 | 4821 | pub const @"usize" = initTag(.usize); |
| 4777 | 4822 | pub const @"isize" = initTag(.isize); |
src/value.zig+41-26| ... | ... | @@ -1112,6 +1112,19 @@ pub const Value = extern union { |
| 1112 | 1112 | } |
| 1113 | 1113 | |
| 1114 | 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 | 1128 | const Int = @Type(.{ .Int = .{ |
| 1116 | 1129 | .signedness = .unsigned, |
| 1117 | 1130 | .bits = @typeInfo(F).Float.bits, |
| ... | ... | @@ -1122,41 +1135,43 @@ pub const Value = extern union { |
| 1122 | 1135 | |
| 1123 | 1136 | fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F { |
| 1124 | 1137 | if (F == f80) { |
| 1125 | switch (target.cpu.arch.endian()) { | |
| 1126 | .Little => { | |
| 1127 | const TargetF80Repr = extern struct { | |
| 1128 | fraction: u64, | |
| 1129 | exp: u16, | |
| 1130 | }; | |
| 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 | }, | |
| 1138 | switch (target.cpu.arch) { | |
| 1139 | .i386, .x86_64 => return std.math.make_f80(.{ | |
| 1140 | .fraction = std.mem.readIntLittle(u64, buffer[0..8]), | |
| 1141 | .exp = std.mem.readIntLittle(u16, buffer[8..10]), | |
| 1142 | }), | |
| 1143 | else => {}, | |
| 1150 | 1144 | } |
| 1151 | 1145 | } |
| 1152 | 1146 | const Int = @Type(.{ .Int = .{ |
| 1153 | 1147 | .signedness = .unsigned, |
| 1154 | 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 | 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 | 1175 | /// Asserts that the value is a float or an integer. |
| 1161 | 1176 | pub fn toFloat(val: Value, comptime T: type) T { |
| 1162 | 1177 | return switch (val.tag()) { |
test/behavior/floatop.zig+4-1| ... | ... | @@ -5,7 +5,10 @@ const math = std.math; |
| 5 | 5 | const pi = std.math.pi; |
| 6 | 6 | const e = std.math.e; |
| 7 | 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 | 13 | const epsilon_16 = 0.001; |
| 11 | 14 | const epsilon = 0.000001; |