authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-10 22:06:43-07:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-12 11:18:23+01:00
loga024aff9324e827d6595e44f922d87f8ed2dbd0d
tree2c0557a4e57acd198ca57deb7cc387109d96c26d
parent1c23321d03a48558b02c2faf818b82811b8ab11d

make f80 less hacky; lower as u80 on non-x86

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
42pub const f128_epsilon = @bitCast(f128, @as(u128, 0x3F8F0000000000000000000000000000));42pub const f128_epsilon = @bitCast(f128, @as(u128, 0x3F8F0000000000000000000000000000));
43pub const f128_toint = 1.0 / f128_epsilon;43pub const f128_toint = 1.0 / f128_epsilon;
4444
45pub 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 details45// float.h details
54pub const f80_true_min = @ptrCast(*const f80, &F80Repr{ .fraction = 1, .exp = 0 }).*;46pub const f80_true_min = make_f80(.{ .fraction = 1, .exp = 0 });
55pub const f80_min = @ptrCast(*const f80, &F80Repr{ .fraction = 0x8000000000000000, .exp = 1 }).*;47pub const f80_min = make_f80(.{ .fraction = 0x8000000000000000, .exp = 1 });
56pub const f80_max = @ptrCast(*const f80, &F80Repr{ .fraction = 0xFFFFFFFFFFFFFFFF, .exp = 0x7FFE }).*;48pub const f80_max = make_f80(.{ .fraction = 0xFFFFFFFFFFFFFFFF, .exp = 0x7FFE });
57pub const f80_epsilon = @ptrCast(*const f80, &F80Repr{ .fraction = 0x8000000000000000, .exp = 0x3FC0 }).*;49pub const f80_epsilon = make_f80(.{ .fraction = 0x8000000000000000, .exp = 0x3FC0 });
58pub const f80_toint = 1.0 / f80_epsilon;50pub const f80_toint = 1.0 / f80_epsilon;
5951
60pub const f64_true_min = 4.94065645841246544177e-324;52pub 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);
104pub const inf_u64 = @as(u64, 0x7FF << 52);96pub const inf_u64 = @as(u64, 0x7FF << 52);
105pub const inf_f64 = @bitCast(f64, inf_u64);97pub const inf_f64 = @bitCast(f64, inf_u64);
10698
107pub const inf_f80 = @ptrCast(*const f80, &F80Repr{ .fraction = 0x8000000000000000, .exp = 0x7fff }).*;99pub const inf_f80 = make_f80(F80{ .fraction = 0x8000000000000000, .exp = 0x7fff });
108pub const nan_f80 = @ptrCast(*const f80, &F80Repr{ .fraction = 0xA000000000000000, .exp = 0x7fff }).*;100pub const nan_f80 = make_f80(F80{ .fraction = 0xA000000000000000, .exp = 0x7fff });
109pub const qnan_f80 = @ptrCast(*const f80, &F80Repr{ .fraction = 0xC000000000000000, .exp = 0x7fff }).*;101pub const qnan_f80 = make_f80(F80{ .fraction = 0xC000000000000000, .exp = 0x7fff });
110102
111pub const nan_u128 = @as(u128, 0x7fff0000000000000000000000000001);103pub const nan_u128 = @as(u128, 0x7fff0000000000000000000000000001);
112pub const nan_f128 = @bitCast(f128, nan_u128);104pub const nan_f128 = @bitCast(f128, nan_u128);
...@@ -1501,3 +1493,21 @@ test "boolMask" {...@@ -1501,3 +1493,21 @@ test "boolMask" {
1501pub fn comptimeMod(num: anytype, denom: comptime_int) IntFittingRange(0, denom - 1) {1493pub 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
1497pub const F80 = struct {
1498 fraction: u64,
1499 exp: u16,
1500};
1501
1502pub fn make_f80(repr: F80) f80 {
1503 const int = (@as(u80, repr.exp) << 64) | repr.fraction;
1504 return @bitCast(f80, int);
1505}
1506
1507pub 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}
233233
234pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 {234pub 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;
239239
...@@ -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 = qNaN258 // 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 = qNaN269 // 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 }
274274
...@@ -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 + zero280 // 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 +/-inf361 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 }
364364
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 {
387387
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}
392392
393pub fn __subxf3(a: f80, b: f80) callconv(.C) f80 {393pub 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}
398398
399test {399test {
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 f80147// Comparison between f80
148148
149pub inline fn cmp_f80(comptime RT: type, a: f80, b: f80) RT {149pub 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;
4343
44 var dst: std.math.F80Repr align(16) = undefined;44 var dst: std.math.F80 = undefined;
4545
46 // Break a into a sign and representation of the absolute value46 // 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 }
8484
85 dst.exp |= sign;85 dst.exp |= sign;
86 return @ptrCast(*const f80, &dst).*;86 return std.math.make_f80(dst);
87}87}
8888
89pub fn __extendxftf2(a: f80) callconv(.C) f128 {89pub 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;
100100
101 // Break a into a sign and representation of the absolute value101 // 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;
4343
44 // Break a into a sign and representation of the absolute value44 // 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;
127127
128 var res: std.math.F80Repr align(16) = undefined;128 var res: std.math.F80 = undefined;
129129
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 }
156156
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 {
824824
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 {
978982
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());
10741077
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.
5365fn 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_ENDIAN8200 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#endif8204 } 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) {
94299427
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;
94339431
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;
94579434
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());
94609462
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 },
18831902
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 },
21642202
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),
23532391
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 }
11131113
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 {
11221135
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 }
11591153
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;
5const pi = std.math.pi;5const pi = std.math.pi;
6const e = std.math.e;6const e = std.math.e;
7const Vector = std.meta.Vector;7const Vector = std.meta.Vector;
8const has_f80_rt = @import("builtin").cpu.arch == .x86_64;8const has_f80_rt = switch (builtin.cpu.arch) {
9 .x86_64, .i386 => true,
10 else => false,
11};
912
10const epsilon_16 = 0.001;13const epsilon_16 = 0.001;
11const epsilon = 0.000001;14const epsilon = 0.000001;