mirror of
https://github.com/ziglang/zig.git
synced 2025-12-13 01:33:09 +00:00
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.
This commit is contained in:
parent
1c23321d03
commit
a024aff932
@ -42,19 +42,11 @@ pub const f128_max = @bitCast(f128, @as(u128, 0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
|||||||
pub const f128_epsilon = @bitCast(f128, @as(u128, 0x3F8F0000000000000000000000000000));
|
pub const f128_epsilon = @bitCast(f128, @as(u128, 0x3F8F0000000000000000000000000000));
|
||||||
pub const f128_toint = 1.0 / f128_epsilon;
|
pub const f128_toint = 1.0 / f128_epsilon;
|
||||||
|
|
||||||
pub const F80Repr = if (@import("builtin").cpu.arch.endian() == .Little) extern struct {
|
|
||||||
fraction: u64 align(@alignOf(f80)),
|
|
||||||
exp: u16,
|
|
||||||
} else extern struct {
|
|
||||||
exp: u16 align(@alignOf(f80)),
|
|
||||||
fraction: u64,
|
|
||||||
};
|
|
||||||
|
|
||||||
// float.h details
|
// float.h details
|
||||||
pub const f80_true_min = @ptrCast(*const f80, &F80Repr{ .fraction = 1, .exp = 0 }).*;
|
pub const f80_true_min = make_f80(.{ .fraction = 1, .exp = 0 });
|
||||||
pub const f80_min = @ptrCast(*const f80, &F80Repr{ .fraction = 0x8000000000000000, .exp = 1 }).*;
|
pub const f80_min = make_f80(.{ .fraction = 0x8000000000000000, .exp = 1 });
|
||||||
pub const f80_max = @ptrCast(*const f80, &F80Repr{ .fraction = 0xFFFFFFFFFFFFFFFF, .exp = 0x7FFE }).*;
|
pub const f80_max = make_f80(.{ .fraction = 0xFFFFFFFFFFFFFFFF, .exp = 0x7FFE });
|
||||||
pub const f80_epsilon = @ptrCast(*const f80, &F80Repr{ .fraction = 0x8000000000000000, .exp = 0x3FC0 }).*;
|
pub const f80_epsilon = make_f80(.{ .fraction = 0x8000000000000000, .exp = 0x3FC0 });
|
||||||
pub const f80_toint = 1.0 / f80_epsilon;
|
pub const f80_toint = 1.0 / f80_epsilon;
|
||||||
|
|
||||||
pub const f64_true_min = 4.94065645841246544177e-324;
|
pub const f64_true_min = 4.94065645841246544177e-324;
|
||||||
@ -104,9 +96,9 @@ pub const qnan_f64 = @bitCast(f64, qnan_u64);
|
|||||||
pub const inf_u64 = @as(u64, 0x7FF << 52);
|
pub const inf_u64 = @as(u64, 0x7FF << 52);
|
||||||
pub const inf_f64 = @bitCast(f64, inf_u64);
|
pub const inf_f64 = @bitCast(f64, inf_u64);
|
||||||
|
|
||||||
pub const inf_f80 = @ptrCast(*const f80, &F80Repr{ .fraction = 0x8000000000000000, .exp = 0x7fff }).*;
|
pub const inf_f80 = make_f80(F80{ .fraction = 0x8000000000000000, .exp = 0x7fff });
|
||||||
pub const nan_f80 = @ptrCast(*const f80, &F80Repr{ .fraction = 0xA000000000000000, .exp = 0x7fff }).*;
|
pub const nan_f80 = make_f80(F80{ .fraction = 0xA000000000000000, .exp = 0x7fff });
|
||||||
pub const qnan_f80 = @ptrCast(*const f80, &F80Repr{ .fraction = 0xC000000000000000, .exp = 0x7fff }).*;
|
pub const qnan_f80 = make_f80(F80{ .fraction = 0xC000000000000000, .exp = 0x7fff });
|
||||||
|
|
||||||
pub const nan_u128 = @as(u128, 0x7fff0000000000000000000000000001);
|
pub const nan_u128 = @as(u128, 0x7fff0000000000000000000000000001);
|
||||||
pub const nan_f128 = @bitCast(f128, nan_u128);
|
pub const nan_f128 = @bitCast(f128, nan_u128);
|
||||||
@ -1501,3 +1493,21 @@ test "boolMask" {
|
|||||||
pub fn comptimeMod(num: anytype, denom: comptime_int) IntFittingRange(0, denom - 1) {
|
pub fn comptimeMod(num: anytype, denom: comptime_int) IntFittingRange(0, denom - 1) {
|
||||||
return @intCast(IntFittingRange(0, denom - 1), @mod(num, denom));
|
return @intCast(IntFittingRange(0, denom - 1), @mod(num, denom));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const F80 = struct {
|
||||||
|
fraction: u64,
|
||||||
|
exp: u16,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn make_f80(repr: F80) f80 {
|
||||||
|
const int = (@as(u80, repr.exp) << 64) | repr.fraction;
|
||||||
|
return @bitCast(f80, int);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn break_f80(x: f80) F80 {
|
||||||
|
const int = @bitCast(u80, x);
|
||||||
|
return .{
|
||||||
|
.fraction = @truncate(u64, int),
|
||||||
|
.exp = @truncate(u16, int >> 64),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@ -232,8 +232,8 @@ fn normalize_f80(exp: *i32, significand: *u80) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 {
|
pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 {
|
||||||
var a_rep align(16) = @ptrCast(*const std.math.F80Repr, &a).*;
|
var a_rep = std.math.break_f80(a);
|
||||||
var b_rep align(16) = @ptrCast(*const std.math.F80Repr, &b).*;
|
var b_rep = std.math.break_f80(b);
|
||||||
var a_exp: i32 = a_rep.exp & 0x7FFF;
|
var a_exp: i32 = a_rep.exp & 0x7FFF;
|
||||||
var b_exp: i32 = b_rep.exp & 0x7FFF;
|
var b_exp: i32 = b_rep.exp & 0x7FFF;
|
||||||
|
|
||||||
@ -257,7 +257,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 {
|
|||||||
std.debug.assert(a_rep.fraction & significand_mask != 0);
|
std.debug.assert(a_rep.fraction & significand_mask != 0);
|
||||||
// NaN + anything = qNaN
|
// NaN + anything = qNaN
|
||||||
a_rep.fraction |= qnan_bit;
|
a_rep.fraction |= qnan_bit;
|
||||||
return @ptrCast(*const f80, &a_rep).*;
|
return std.math.make_f80(a_rep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (b_exp == max_exp) {
|
if (b_exp == max_exp) {
|
||||||
@ -268,7 +268,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 {
|
|||||||
std.debug.assert(b_rep.fraction & significand_mask != 0);
|
std.debug.assert(b_rep.fraction & significand_mask != 0);
|
||||||
// anything + NaN = qNaN
|
// anything + NaN = qNaN
|
||||||
b_rep.fraction |= qnan_bit;
|
b_rep.fraction |= qnan_bit;
|
||||||
return @ptrCast(*const f80, &b_rep).*;
|
return std.math.make_f80(b_rep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,7 +279,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 {
|
|||||||
if (b_zero) {
|
if (b_zero) {
|
||||||
// but we need to get the sign right for zero + zero
|
// but we need to get the sign right for zero + zero
|
||||||
a_rep.exp &= b_rep.exp;
|
a_rep.exp &= b_rep.exp;
|
||||||
return @ptrCast(*const f80, &a_rep).*;
|
return std.math.make_f80(a_rep);
|
||||||
} else {
|
} else {
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
@ -359,7 +359,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 {
|
|||||||
if (a_exp >= max_exp) {
|
if (a_exp >= max_exp) {
|
||||||
a_rep.exp = max_exp | result_sign;
|
a_rep.exp = max_exp | result_sign;
|
||||||
a_rep.fraction = int_bit; // integer bit is set for +/-inf
|
a_rep.fraction = int_bit; // integer bit is set for +/-inf
|
||||||
return @ptrCast(*const f80, &a_rep).*;
|
return std.math.make_f80(a_rep);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a_exp <= 0) {
|
if (a_exp <= 0) {
|
||||||
@ -387,13 +387,13 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 {
|
|||||||
|
|
||||||
a_rep.fraction = @truncate(u64, a_int);
|
a_rep.fraction = @truncate(u64, a_int);
|
||||||
a_rep.exp = @truncate(u16, a_int >> significand_bits);
|
a_rep.exp = @truncate(u16, a_int >> significand_bits);
|
||||||
return @ptrCast(*const f80, &a_rep).*;
|
return std.math.make_f80(a_rep);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn __subxf3(a: f80, b: f80) callconv(.C) f80 {
|
pub fn __subxf3(a: f80, b: f80) callconv(.C) f80 {
|
||||||
var b_rep align(16) = @ptrCast(*const std.math.F80Repr, &b).*;
|
var b_rep = std.math.break_f80(b);
|
||||||
b_rep.exp ^= 0x8000;
|
b_rep.exp ^= 0x8000;
|
||||||
return __addxf3(a, @ptrCast(*const f80, &b_rep).*);
|
return __addxf3(a, std.math.make_f80(b_rep));
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
|
|||||||
@ -147,8 +147,8 @@ pub fn __gtdf2(a: f64, b: f64) callconv(.C) i32 {
|
|||||||
// Comparison between f80
|
// Comparison between f80
|
||||||
|
|
||||||
pub inline fn cmp_f80(comptime RT: type, a: f80, b: f80) RT {
|
pub inline fn cmp_f80(comptime RT: type, a: f80, b: f80) RT {
|
||||||
const a_rep = @ptrCast(*const std.math.F80Repr, &a).*;
|
const a_rep = std.math.break_f80(a);
|
||||||
const b_rep = @ptrCast(*const std.math.F80Repr, &b).*;
|
const b_rep = std.math.break_f80(b);
|
||||||
const sig_bits = std.math.floatMantissaBits(f80);
|
const sig_bits = std.math.floatMantissaBits(f80);
|
||||||
const int_bit = 0x8000000000000000;
|
const int_bit = 0x8000000000000000;
|
||||||
const sign_bit = 0x8000;
|
const sign_bit = 0x8000;
|
||||||
|
|||||||
@ -41,7 +41,7 @@ inline fn extendF80(comptime src_t: type, a: std.meta.Int(.unsigned, @typeInfo(s
|
|||||||
const src_qnan = 1 << (src_sig_bits - 1);
|
const src_qnan = 1 << (src_sig_bits - 1);
|
||||||
const src_nan_code = src_qnan - 1;
|
const src_nan_code = src_qnan - 1;
|
||||||
|
|
||||||
var dst: std.math.F80Repr align(16) = undefined;
|
var dst: std.math.F80 = undefined;
|
||||||
|
|
||||||
// Break a into a sign and representation of the absolute value
|
// Break a into a sign and representation of the absolute value
|
||||||
const a_abs = a & src_abs_mask;
|
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
|
|||||||
}
|
}
|
||||||
|
|
||||||
dst.exp |= sign;
|
dst.exp |= sign;
|
||||||
return @ptrCast(*const f80, &dst).*;
|
return std.math.make_f80(dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn __extendxftf2(a: f80) callconv(.C) f128 {
|
pub fn __extendxftf2(a: f80) callconv(.C) f128 {
|
||||||
@ -99,7 +99,7 @@ pub fn __extendxftf2(a: f80) callconv(.C) f128 {
|
|||||||
const dst_min_normal = @as(u128, 1) << dst_sig_bits;
|
const dst_min_normal = @as(u128, 1) << dst_sig_bits;
|
||||||
|
|
||||||
// Break a into a sign and representation of the absolute value
|
// Break a into a sign and representation of the absolute value
|
||||||
var a_rep = @ptrCast(*const std.math.F80Repr, &a).*;
|
var a_rep = std.math.break_f80(a);
|
||||||
const sign = a_rep.exp & 0x8000;
|
const sign = a_rep.exp & 0x8000;
|
||||||
a_rep.exp &= 0x7FFF;
|
a_rep.exp &= 0x7FFF;
|
||||||
var abs_result: u128 = undefined;
|
var abs_result: u128 = undefined;
|
||||||
|
|||||||
@ -42,7 +42,7 @@ inline fn trunc(comptime dst_t: type, a: f80) dst_t {
|
|||||||
const dst_nan_mask = dst_qnan - 1;
|
const dst_nan_mask = dst_qnan - 1;
|
||||||
|
|
||||||
// Break a into a sign and representation of the absolute value
|
// Break a into a sign and representation of the absolute value
|
||||||
var a_rep = @ptrCast(*const std.math.F80Repr, &a).*;
|
var a_rep = std.math.break_f80(a);
|
||||||
const sign = a_rep.exp & 0x8000;
|
const sign = a_rep.exp & 0x8000;
|
||||||
a_rep.exp &= 0x7FFF;
|
a_rep.exp &= 0x7FFF;
|
||||||
a_rep.fraction &= 0x7FFFFFFFFFFFFFFF;
|
a_rep.fraction &= 0x7FFFFFFFFFFFFFFF;
|
||||||
@ -125,7 +125,7 @@ pub fn __trunctfxf2(a: f128) callconv(.C) f80 {
|
|||||||
const a_abs = a_rep & src_abs_mask;
|
const a_abs = a_rep & src_abs_mask;
|
||||||
const sign: u16 = if (a_rep & src_sign_mask != 0) 0x8000 else 0;
|
const sign: u16 = if (a_rep & src_sign_mask != 0) 0x8000 else 0;
|
||||||
|
|
||||||
var res: std.math.F80Repr align(16) = undefined;
|
var res: std.math.F80 = undefined;
|
||||||
|
|
||||||
if (a_abs > src_inf) {
|
if (a_abs > src_inf) {
|
||||||
// a is NaN.
|
// a is NaN.
|
||||||
@ -155,5 +155,5 @@ pub fn __trunctfxf2(a: f128) callconv(.C) f80 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
res.exp |= sign;
|
res.exp |= sign;
|
||||||
return @ptrCast(*const f80, &res).*;
|
return std.math.make_f80(res);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -824,23 +824,24 @@ pub const DeclGen = struct {
|
|||||||
|
|
||||||
fn llvmType(dg: *DeclGen, t: Type) Allocator.Error!*const llvm.Type {
|
fn llvmType(dg: *DeclGen, t: Type) Allocator.Error!*const llvm.Type {
|
||||||
const gpa = dg.gpa;
|
const gpa = dg.gpa;
|
||||||
|
const target = dg.module.getTarget();
|
||||||
switch (t.zigTypeTag()) {
|
switch (t.zigTypeTag()) {
|
||||||
.Void, .NoReturn => return dg.context.voidType(),
|
.Void, .NoReturn => return dg.context.voidType(),
|
||||||
.Int => {
|
.Int => {
|
||||||
const info = t.intInfo(dg.module.getTarget());
|
const info = t.intInfo(target);
|
||||||
return dg.context.intType(info.bits);
|
return dg.context.intType(info.bits);
|
||||||
},
|
},
|
||||||
.Enum => {
|
.Enum => {
|
||||||
var buffer: Type.Payload.Bits = undefined;
|
var buffer: Type.Payload.Bits = undefined;
|
||||||
const int_ty = t.intTagType(&buffer);
|
const int_ty = t.intTagType(&buffer);
|
||||||
const bit_count = int_ty.intInfo(dg.module.getTarget()).bits;
|
const bit_count = int_ty.intInfo(target).bits;
|
||||||
return dg.context.intType(bit_count);
|
return dg.context.intType(bit_count);
|
||||||
},
|
},
|
||||||
.Float => switch (t.floatBits(dg.module.getTarget())) {
|
.Float => switch (t.floatBits(target)) {
|
||||||
16 => return dg.context.halfType(),
|
16 => return dg.context.halfType(),
|
||||||
32 => return dg.context.floatType(),
|
32 => return dg.context.floatType(),
|
||||||
64 => return dg.context.doubleType(),
|
64 => return dg.context.doubleType(),
|
||||||
80 => return dg.context.x86FP80Type(),
|
80 => return if (backendSupportsF80(target)) dg.context.x86FP80Type() else dg.context.intType(80),
|
||||||
128 => return dg.context.fp128Type(),
|
128 => return dg.context.fp128Type(),
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
},
|
},
|
||||||
@ -859,7 +860,8 @@ pub const DeclGen = struct {
|
|||||||
const llvm_addrspace = dg.llvmAddressSpace(t.ptrAddressSpace());
|
const llvm_addrspace = dg.llvmAddressSpace(t.ptrAddressSpace());
|
||||||
const elem_ty = t.childType();
|
const elem_ty = t.childType();
|
||||||
const lower_elem_ty = switch (elem_ty.zigTypeTag()) {
|
const lower_elem_ty = switch (elem_ty.zigTypeTag()) {
|
||||||
.Opaque, .Array, .Fn => true,
|
.Opaque, .Fn => true,
|
||||||
|
.Array => elem_ty.childType().hasRuntimeBits(),
|
||||||
else => elem_ty.hasRuntimeBits(),
|
else => elem_ty.hasRuntimeBits(),
|
||||||
};
|
};
|
||||||
const llvm_elem_ty = if (lower_elem_ty)
|
const llvm_elem_ty = if (lower_elem_ty)
|
||||||
@ -889,9 +891,11 @@ pub const DeclGen = struct {
|
|||||||
else => unreachable,
|
else => unreachable,
|
||||||
},
|
},
|
||||||
.Array => {
|
.Array => {
|
||||||
const elem_type = try dg.llvmType(t.childType());
|
const elem_ty = t.childType();
|
||||||
|
assert(elem_ty.onePossibleValue() == null);
|
||||||
|
const elem_llvm_ty = try dg.llvmType(elem_ty);
|
||||||
const total_len = t.arrayLen() + @boolToInt(t.sentinel() != null);
|
const total_len = t.arrayLen() + @boolToInt(t.sentinel() != null);
|
||||||
return elem_type.arrayType(@intCast(c_uint, total_len));
|
return elem_llvm_ty.arrayType(@intCast(c_uint, total_len));
|
||||||
},
|
},
|
||||||
.Vector => {
|
.Vector => {
|
||||||
const elem_type = try dg.llvmType(t.childType());
|
const elem_type = try dg.llvmType(t.childType());
|
||||||
@ -978,7 +982,6 @@ pub const DeclGen = struct {
|
|||||||
|
|
||||||
if (struct_obj.layout == .Packed) {
|
if (struct_obj.layout == .Packed) {
|
||||||
try llvm_field_types.ensureUnusedCapacity(gpa, struct_obj.fields.count() * 2);
|
try llvm_field_types.ensureUnusedCapacity(gpa, struct_obj.fields.count() * 2);
|
||||||
const target = dg.module.getTarget();
|
|
||||||
comptime assert(Type.packed_struct_layout_version == 1);
|
comptime assert(Type.packed_struct_layout_version == 1);
|
||||||
var offset: u64 = 0;
|
var offset: u64 = 0;
|
||||||
var big_align: u32 = 0;
|
var big_align: u32 = 0;
|
||||||
@ -1073,7 +1076,6 @@ pub const DeclGen = struct {
|
|||||||
gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator());
|
gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator());
|
||||||
|
|
||||||
const union_obj = t.cast(Type.Payload.Union).?.data;
|
const union_obj = t.cast(Type.Payload.Union).?.data;
|
||||||
const target = dg.module.getTarget();
|
|
||||||
if (t.unionTagType()) |enum_tag_ty| {
|
if (t.unionTagType()) |enum_tag_ty| {
|
||||||
const enum_tag_llvm_ty = try dg.llvmType(enum_tag_ty);
|
const enum_tag_llvm_ty = try dg.llvmType(enum_tag_ty);
|
||||||
const layout = union_obj.getLayout(target, true);
|
const layout = union_obj.getLayout(target, true);
|
||||||
@ -1141,7 +1143,6 @@ pub const DeclGen = struct {
|
|||||||
},
|
},
|
||||||
.Fn => {
|
.Fn => {
|
||||||
const fn_info = t.fnInfo();
|
const fn_info = t.fnInfo();
|
||||||
const target = dg.module.getTarget();
|
|
||||||
const sret = firstParamSRet(fn_info, target);
|
const sret = firstParamSRet(fn_info, target);
|
||||||
const return_type = fn_info.return_type;
|
const return_type = fn_info.return_type;
|
||||||
const raw_llvm_ret_ty = try dg.llvmType(return_type);
|
const raw_llvm_ret_ty = try dg.llvmType(return_type);
|
||||||
@ -1257,16 +1258,21 @@ pub const DeclGen = struct {
|
|||||||
},
|
},
|
||||||
.Float => {
|
.Float => {
|
||||||
const llvm_ty = try dg.llvmType(tv.ty);
|
const llvm_ty = try dg.llvmType(tv.ty);
|
||||||
switch (tv.ty.floatBits(dg.module.getTarget())) {
|
const target = dg.module.getTarget();
|
||||||
|
switch (tv.ty.floatBits(target)) {
|
||||||
16, 32, 64 => return llvm_ty.constReal(tv.val.toFloat(f64)),
|
16, 32, 64 => return llvm_ty.constReal(tv.val.toFloat(f64)),
|
||||||
80 => {
|
80 => {
|
||||||
const float = tv.val.toFloat(f80);
|
const float = tv.val.toFloat(f80);
|
||||||
const repr = @ptrCast(*const std.math.F80Repr, &float);
|
const repr = std.math.break_f80(float);
|
||||||
const llvm_i80 = dg.context.intType(80);
|
const llvm_i80 = dg.context.intType(80);
|
||||||
var x = llvm_i80.constInt(repr.exp, .False);
|
var x = llvm_i80.constInt(repr.exp, .False);
|
||||||
x = x.constShl(llvm_i80.constInt(64, .False));
|
x = x.constShl(llvm_i80.constInt(64, .False));
|
||||||
x = x.constOr(llvm_i80.constInt(repr.fraction, .False));
|
x = x.constOr(llvm_i80.constInt(repr.fraction, .False));
|
||||||
return x.constBitCast(llvm_ty);
|
if (backendSupportsF80(target)) {
|
||||||
|
return x.constBitCast(llvm_ty);
|
||||||
|
} else {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
128 => {
|
128 => {
|
||||||
var buf: [2]u64 = @bitCast([2]u64, tv.val.toFloat(f128));
|
var buf: [2]u64 = @bitCast([2]u64, tv.val.toFloat(f128));
|
||||||
@ -5353,3 +5359,12 @@ fn isByRef(ty: Type) bool {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This function returns true if we expect LLVM to lower x86_fp80 correctly
|
||||||
|
/// and false if we expect LLVM to crash if it counters an x86_fp80 type.
|
||||||
|
fn backendSupportsF80(target: std.Target) bool {
|
||||||
|
return switch (target.cpu.arch) {
|
||||||
|
.x86_64, .i386 => true,
|
||||||
|
else => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@ -8195,17 +8195,15 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n
|
|||||||
case 64:
|
case 64:
|
||||||
return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f64);
|
return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f64);
|
||||||
case 80: {
|
case 80: {
|
||||||
uint64_t buf[2];
|
LLVMTypeRef llvm_i80 = LLVMIntType(80);
|
||||||
memcpy(&buf, &const_val->data.x_f80, 16);
|
LLVMValueRef x = LLVMConstInt(llvm_i80, const_val->data.x_f80.signExp, false);
|
||||||
#if ZIG_BYTE_ORDER == ZIG_BIG_ENDIAN
|
x = LLVMConstShl(x, LLVMConstInt(llvm_i80, 64, false));
|
||||||
uint64_t tmp = buf[0];
|
x = LLVMConstOr(x, LLVMConstInt(llvm_i80, const_val->data.x_f80.signif, false));
|
||||||
buf[0] = buf[1];
|
if (target_has_f80(g->zig_target)) {
|
||||||
buf[1] = tmp;
|
return LLVMConstBitCast(x, LLVMX86FP80Type());
|
||||||
#endif
|
} else {
|
||||||
LLVMValueRef as_i128 = LLVMConstIntOfArbitraryPrecision(LLVMInt128Type(), 2, buf);
|
return x;
|
||||||
if (!target_has_f80(g->zig_target)) return as_i128;
|
}
|
||||||
LLVMValueRef as_int = LLVMConstTrunc(as_i128, LLVMIntType(80));
|
|
||||||
return LLVMConstBitCast(as_int, get_llvm_type(g, type_entry));
|
|
||||||
}
|
}
|
||||||
case 128:
|
case 128:
|
||||||
{
|
{
|
||||||
@ -9429,32 +9427,36 @@ static void define_builtin_types(CodeGen *g) {
|
|||||||
|
|
||||||
{
|
{
|
||||||
ZigType *entry = new_type_table_entry(ZigTypeIdFloat);
|
ZigType *entry = new_type_table_entry(ZigTypeIdFloat);
|
||||||
unsigned u64_alignment = LLVMABIAlignmentOfType(g->target_data_ref, LLVMInt64Type());
|
entry->size_in_bits = 80;
|
||||||
|
|
||||||
if (u64_alignment >= 8) {
|
|
||||||
entry->size_in_bits = 128;
|
|
||||||
entry->abi_size = 16;
|
|
||||||
entry->abi_align = 16;
|
|
||||||
} else if (u64_alignment >= 4) {
|
|
||||||
entry->size_in_bits = 96;
|
|
||||||
entry->abi_size = 12;
|
|
||||||
entry->abi_align = 4;
|
|
||||||
} else {
|
|
||||||
entry->size_in_bits = 80;
|
|
||||||
entry->abi_size = 10;
|
|
||||||
entry->abi_align = 2;
|
|
||||||
}
|
|
||||||
if (target_has_f80(g->zig_target)) {
|
|
||||||
entry->llvm_type = LLVMX86FP80Type();
|
|
||||||
} else {
|
|
||||||
// We use an int here instead of x86_fp80 because on targets such as arm,
|
|
||||||
// LLVM will give "ERROR: Cannot select" for any instructions involving
|
|
||||||
// the x86_fp80 type.
|
|
||||||
entry->llvm_type = get_int_type(g, false, entry->size_in_bits)->llvm_type;
|
|
||||||
}
|
|
||||||
buf_init_from_str(&entry->name, "f80");
|
buf_init_from_str(&entry->name, "f80");
|
||||||
entry->data.floating.bit_count = 80;
|
entry->data.floating.bit_count = 80;
|
||||||
|
|
||||||
|
switch (g->zig_target->arch) {
|
||||||
|
case ZigLLVM_x86_64:
|
||||||
|
entry->llvm_type = LLVMX86FP80Type();
|
||||||
|
entry->abi_size = 16;
|
||||||
|
entry->abi_align = 16;
|
||||||
|
break;
|
||||||
|
case ZigLLVM_x86:
|
||||||
|
entry->llvm_type = LLVMX86FP80Type();
|
||||||
|
entry->abi_size = 12;
|
||||||
|
entry->abi_align = 4;
|
||||||
|
break;
|
||||||
|
default: {
|
||||||
|
// We use an int here instead of x86_fp80 because on targets such as arm,
|
||||||
|
// LLVM will give "ERROR: Cannot select" for any instructions involving
|
||||||
|
// the x86_fp80 type.
|
||||||
|
ZigType *u80_ty = get_int_type(g, false, 80);
|
||||||
|
assert(!target_has_f80(g->zig_target));
|
||||||
|
assert(u80_ty->size_in_bits == entry->size_in_bits);
|
||||||
|
entry->llvm_type = get_llvm_type(g, u80_ty);
|
||||||
|
entry->abi_size = u80_ty->abi_size;
|
||||||
|
entry->abi_align = u80_ty->abi_align;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
entry->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
|
entry->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
|
||||||
entry->size_in_bits, ZigLLVMEncoding_DW_ATE_unsigned());
|
entry->size_in_bits, ZigLLVMEncoding_DW_ATE_unsigned());
|
||||||
|
|
||||||
|
|||||||
55
src/type.zig
55
src/type.zig
@ -1877,9 +1877,28 @@ pub const Type = extern union {
|
|||||||
.f16 => return 2,
|
.f16 => return 2,
|
||||||
.f32 => return 4,
|
.f32 => return 4,
|
||||||
.f64 => return 8,
|
.f64 => return 8,
|
||||||
.f80 => return 16,
|
|
||||||
.f128 => return 16,
|
.f128 => return 16,
|
||||||
.c_longdouble => return 16,
|
|
||||||
|
.f80 => switch (target.cpu.arch) {
|
||||||
|
.i386 => return 4,
|
||||||
|
.x86_64 => return 16,
|
||||||
|
else => {
|
||||||
|
var payload: Payload.Bits = .{
|
||||||
|
.base = .{ .tag = .int_unsigned },
|
||||||
|
.data = 80,
|
||||||
|
};
|
||||||
|
const u80_ty = initPayload(&payload.base);
|
||||||
|
return abiAlignment(u80_ty, target);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.c_longdouble => switch (CType.longdouble.sizeInBits(target)) {
|
||||||
|
16 => return abiAlignment(Type.f16, target),
|
||||||
|
32 => return abiAlignment(Type.f32, target),
|
||||||
|
64 => return abiAlignment(Type.f64, target),
|
||||||
|
80 => return abiAlignment(Type.f80, target),
|
||||||
|
128 => return abiAlignment(Type.f128, target),
|
||||||
|
else => unreachable,
|
||||||
|
},
|
||||||
|
|
||||||
.error_set,
|
.error_set,
|
||||||
.error_set_single,
|
.error_set_single,
|
||||||
@ -2158,9 +2177,28 @@ pub const Type = extern union {
|
|||||||
.f16 => return 2,
|
.f16 => return 2,
|
||||||
.f32 => return 4,
|
.f32 => return 4,
|
||||||
.f64 => return 8,
|
.f64 => return 8,
|
||||||
.f80 => return 16,
|
|
||||||
.f128 => return 16,
|
.f128 => return 16,
|
||||||
.c_longdouble => return 16,
|
|
||||||
|
.f80 => switch (target.cpu.arch) {
|
||||||
|
.i386 => return 12,
|
||||||
|
.x86_64 => return 16,
|
||||||
|
else => {
|
||||||
|
var payload: Payload.Bits = .{
|
||||||
|
.base = .{ .tag = .int_unsigned },
|
||||||
|
.data = 80,
|
||||||
|
};
|
||||||
|
const u80_ty = initPayload(&payload.base);
|
||||||
|
return abiSize(u80_ty, target);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.c_longdouble => switch (CType.longdouble.sizeInBits(target)) {
|
||||||
|
16 => return abiSize(Type.f16, target),
|
||||||
|
32 => return abiSize(Type.f32, target),
|
||||||
|
64 => return abiSize(Type.f64, target),
|
||||||
|
80 => return abiSize(Type.f80, target),
|
||||||
|
128 => return abiSize(Type.f128, target),
|
||||||
|
else => unreachable,
|
||||||
|
},
|
||||||
|
|
||||||
.error_set,
|
.error_set,
|
||||||
.error_set_single,
|
.error_set_single,
|
||||||
@ -2349,7 +2387,7 @@ pub const Type = extern union {
|
|||||||
.c_ulong => return CType.ulong.sizeInBits(target),
|
.c_ulong => return CType.ulong.sizeInBits(target),
|
||||||
.c_longlong => return CType.longlong.sizeInBits(target),
|
.c_longlong => return CType.longlong.sizeInBits(target),
|
||||||
.c_ulonglong => return CType.ulonglong.sizeInBits(target),
|
.c_ulonglong => return CType.ulonglong.sizeInBits(target),
|
||||||
.c_longdouble => 128,
|
.c_longdouble => return CType.longdouble.sizeInBits(target),
|
||||||
|
|
||||||
.error_set,
|
.error_set,
|
||||||
.error_set_single,
|
.error_set_single,
|
||||||
@ -4772,6 +4810,13 @@ pub const Type = extern union {
|
|||||||
pub const @"u8" = initTag(.u8);
|
pub const @"u8" = initTag(.u8);
|
||||||
pub const @"u32" = initTag(.u32);
|
pub const @"u32" = initTag(.u32);
|
||||||
pub const @"u64" = initTag(.u64);
|
pub const @"u64" = initTag(.u64);
|
||||||
|
|
||||||
|
pub const @"f16" = initTag(.f16);
|
||||||
|
pub const @"f32" = initTag(.f32);
|
||||||
|
pub const @"f64" = initTag(.f64);
|
||||||
|
pub const @"f80" = initTag(.f80);
|
||||||
|
pub const @"f128" = initTag(.f128);
|
||||||
|
|
||||||
pub const @"bool" = initTag(.bool);
|
pub const @"bool" = initTag(.bool);
|
||||||
pub const @"usize" = initTag(.usize);
|
pub const @"usize" = initTag(.usize);
|
||||||
pub const @"isize" = initTag(.isize);
|
pub const @"isize" = initTag(.isize);
|
||||||
|
|||||||
@ -1112,6 +1112,19 @@ pub const Value = extern union {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn floatWriteToMemory(comptime F: type, f: F, target: Target, buffer: []u8) void {
|
fn floatWriteToMemory(comptime F: type, f: F, target: Target, buffer: []u8) void {
|
||||||
|
if (F == f80) {
|
||||||
|
switch (target.cpu.arch) {
|
||||||
|
.i386, .x86_64 => {
|
||||||
|
const repr = std.math.break_f80(f);
|
||||||
|
std.mem.writeIntLittle(u64, buffer[0..8], repr.fraction);
|
||||||
|
std.mem.writeIntLittle(u16, buffer[8..10], repr.exp);
|
||||||
|
// TODO set the rest of the bytes to undefined. should we use 0xaa
|
||||||
|
// or is there a different way?
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
const Int = @Type(.{ .Int = .{
|
const Int = @Type(.{ .Int = .{
|
||||||
.signedness = .unsigned,
|
.signedness = .unsigned,
|
||||||
.bits = @typeInfo(F).Float.bits,
|
.bits = @typeInfo(F).Float.bits,
|
||||||
@ -1122,41 +1135,43 @@ pub const Value = extern union {
|
|||||||
|
|
||||||
fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F {
|
fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F {
|
||||||
if (F == f80) {
|
if (F == f80) {
|
||||||
switch (target.cpu.arch.endian()) {
|
switch (target.cpu.arch) {
|
||||||
.Little => {
|
.i386, .x86_64 => return std.math.make_f80(.{
|
||||||
const TargetF80Repr = extern struct {
|
.fraction = std.mem.readIntLittle(u64, buffer[0..8]),
|
||||||
fraction: u64,
|
.exp = std.mem.readIntLittle(u16, buffer[8..10]),
|
||||||
exp: u16,
|
}),
|
||||||
};
|
else => {},
|
||||||
const target_repr = @ptrCast(*align(1) const TargetF80Repr, buffer.ptr);
|
|
||||||
const real_repr: std.math.F80Repr = .{
|
|
||||||
.fraction = target_repr.fraction,
|
|
||||||
.exp = target_repr.exp,
|
|
||||||
};
|
|
||||||
return @ptrCast(*const f80, &real_repr).*;
|
|
||||||
},
|
|
||||||
.Big => {
|
|
||||||
const TargetF80Repr = extern struct {
|
|
||||||
exp: u16,
|
|
||||||
fraction: u64,
|
|
||||||
};
|
|
||||||
const target_repr = @ptrCast(*align(1) const TargetF80Repr, buffer.ptr);
|
|
||||||
const real_repr: std.math.F80Repr = .{
|
|
||||||
.fraction = target_repr.fraction,
|
|
||||||
.exp = target_repr.exp,
|
|
||||||
};
|
|
||||||
return @ptrCast(*const f80, &real_repr).*;
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const Int = @Type(.{ .Int = .{
|
const Int = @Type(.{ .Int = .{
|
||||||
.signedness = .unsigned,
|
.signedness = .unsigned,
|
||||||
.bits = @typeInfo(F).Float.bits,
|
.bits = @typeInfo(F).Float.bits,
|
||||||
} });
|
} });
|
||||||
const int = std.mem.readInt(Int, buffer[0..@sizeOf(Int)], target.cpu.arch.endian());
|
const int = readInt(Int, buffer[0..@sizeOf(Int)], target.cpu.arch.endian());
|
||||||
return @bitCast(F, int);
|
return @bitCast(F, int);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn readInt(comptime Int: type, buffer: *const [@sizeOf(Int)]u8, endian: std.builtin.Endian) Int {
|
||||||
|
var result: Int = 0;
|
||||||
|
switch (endian) {
|
||||||
|
.Big => {
|
||||||
|
for (buffer) |byte| {
|
||||||
|
result <<= 8;
|
||||||
|
result |= byte;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Little => {
|
||||||
|
var i: usize = buffer.len;
|
||||||
|
while (i != 0) {
|
||||||
|
i -= 1;
|
||||||
|
result <<= 8;
|
||||||
|
result |= buffer[i];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/// Asserts that the value is a float or an integer.
|
/// Asserts that the value is a float or an integer.
|
||||||
pub fn toFloat(val: Value, comptime T: type) T {
|
pub fn toFloat(val: Value, comptime T: type) T {
|
||||||
return switch (val.tag()) {
|
return switch (val.tag()) {
|
||||||
|
|||||||
@ -5,7 +5,10 @@ const math = std.math;
|
|||||||
const pi = std.math.pi;
|
const pi = std.math.pi;
|
||||||
const e = std.math.e;
|
const e = std.math.e;
|
||||||
const Vector = std.meta.Vector;
|
const Vector = std.meta.Vector;
|
||||||
const has_f80_rt = @import("builtin").cpu.arch == .x86_64;
|
const has_f80_rt = switch (builtin.cpu.arch) {
|
||||||
|
.x86_64, .i386 => true,
|
||||||
|
else => false,
|
||||||
|
};
|
||||||
|
|
||||||
const epsilon_16 = 0.001;
|
const epsilon_16 = 0.001;
|
||||||
const epsilon = 0.000001;
|
const epsilon = 0.000001;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user