authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-10 17:36:31-07:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-12 11:18:23+01:00
log166db1a3ed7eca9b04b0626eaea8de0634ab9667
tree958a97baa486eda299dc268a83f3e89fd9a81f63
parentf293fbbeaf6c48e6ce1410743181f89f359eb697

stage1: fix f80 size and alignment on x86 and arm

* F80Repr extern struct needs no explicit padding; let's match the target padding. * stage2: fix lowering of f80 constants. * stage1: decide ABI size and alignment of f80 based on alignment of u64. x86 has alignof u64 equal to 4 but arm has it as 8. * stage2: fix Value.floatReadFromMemory to use F80Repr

4 files changed, 67 insertions(+), 26 deletions(-)

lib/std/math.zig+2-5
...@@ -36,7 +36,6 @@ pub const sqrt2 = 1.414213562373095048801688724209698079;...@@ -36,7 +36,6 @@ pub const sqrt2 = 1.414213562373095048801688724209698079;
36/// 1/sqrt(2)36/// 1/sqrt(2)
37pub const sqrt1_2 = 0.707106781186547524400844362104849039;37pub const sqrt1_2 = 0.707106781186547524400844362104849039;
3838
39// From a small c++ [program using boost float128](https://github.com/winksaville/cpp_boost_float128)
40pub const f128_true_min = @bitCast(f128, @as(u128, 0x00000000000000000000000000000001));39pub const f128_true_min = @bitCast(f128, @as(u128, 0x00000000000000000000000000000001));
41pub const f128_min = @bitCast(f128, @as(u128, 0x00010000000000000000000000000000));40pub const f128_min = @bitCast(f128, @as(u128, 0x00010000000000000000000000000000));
42pub const f128_max = @bitCast(f128, @as(u128, 0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF));41pub const f128_max = @bitCast(f128, @as(u128, 0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF));
...@@ -44,12 +43,10 @@ pub const f128_epsilon = @bitCast(f128, @as(u128, 0x3F8F000000000000000000000000...@@ -44,12 +43,10 @@ pub const f128_epsilon = @bitCast(f128, @as(u128, 0x3F8F000000000000000000000000
44pub const f128_toint = 1.0 / f128_epsilon;43pub const f128_toint = 1.0 / f128_epsilon;
4544
46pub const F80Repr = if (@import("builtin").cpu.arch.endian() == .Little) extern struct {45pub const F80Repr = if (@import("builtin").cpu.arch.endian() == .Little) extern struct {
47 fraction: u64,46 fraction: u64 align(@alignOf(f80)),
48 exp: u16,47 exp: u16,
49 _pad: u32 = undefined,
50} else extern struct {48} else extern struct {
51 exp: u16,49 exp: u16 align(@alignOf(f80)),
52 _pad: u32 = undefined, // TODO verify compatibility with hardware
53 fraction: u64,50 fraction: u64,
54};51};
5552
src/codegen/llvm.zig+22-12
...@@ -1257,19 +1257,29 @@ pub const DeclGen = struct {...@@ -1257,19 +1257,29 @@ pub const DeclGen = struct {
1257 },1257 },
1258 .Float => {1258 .Float => {
1259 const llvm_ty = try dg.llvmType(tv.ty);1259 const llvm_ty = try dg.llvmType(tv.ty);
1260 if (tv.ty.floatBits(dg.module.getTarget()) <= 64) {1260 switch (tv.ty.floatBits(dg.module.getTarget())) {
1261 return llvm_ty.constReal(tv.val.toFloat(f64));1261 16, 32, 64 => return llvm_ty.constReal(tv.val.toFloat(f64)),
1262 }1262 80 => {
12631263 const float = tv.val.toFloat(f80);
1264 var buf: [2]u64 = @bitCast([2]u64, tv.val.toFloat(f128));1264 const repr = @ptrCast(*const std.math.F80Repr, &float);
1265 // LLVM seems to require that the lower half of the f128 be placed first1265 const llvm_i80 = dg.context.intType(80);
1266 // in the buffer.1266 var x = llvm_i80.constInt(repr.exp, .False);
1267 if (native_endian == .Big) {1267 x = x.constShl(llvm_i80.constInt(64, .False));
1268 std.mem.swap(u64, &buf[0], &buf[1]);1268 x = x.constOr(llvm_i80.constInt(repr.fraction, .False));
1269 return x.constBitCast(llvm_ty);
1270 },
1271 128 => {
1272 var buf: [2]u64 = @bitCast([2]u64, tv.val.toFloat(f128));
1273 // LLVM seems to require that the lower half of the f128 be placed first
1274 // in the buffer.
1275 if (native_endian == .Big) {
1276 std.mem.swap(u64, &buf[0], &buf[1]);
1277 }
1278 const int = dg.context.intType(128).constIntOfArbitraryPrecision(buf.len, &buf);
1279 return int.constBitCast(llvm_ty);
1280 },
1281 else => unreachable,
1269 }1282 }
1270
1271 const int = dg.context.intType(128).constIntOfArbitraryPrecision(buf.len, &buf);
1272 return int.constBitCast(llvm_ty);
1273 },1283 },
1274 .Pointer => switch (tv.val.tag()) {1284 .Pointer => switch (tv.val.tag()) {
1275 .decl_ref_mut => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref_mut).?.data.decl),1285 .decl_ref_mut => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref_mut).?.data.decl),
src/stage1/codegen.cpp+17-5
...@@ -9429,17 +9429,29 @@ static void define_builtin_types(CodeGen *g) {...@@ -9429,17 +9429,29 @@ static void define_builtin_types(CodeGen *g) {
94299429
9430 {9430 {
9431 ZigType *entry = new_type_table_entry(ZigTypeIdFloat);9431 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 }
9432 if (target_has_f80(g->zig_target)) {9447 if (target_has_f80(g->zig_target)) {
9433 entry->llvm_type = LLVMX86FP80Type();9448 entry->llvm_type = LLVMX86FP80Type();
9434 } else {9449 } else {
9435 // We use i128 here instead of x86_fp80 because on targets such as arm,9450 // We use an int here instead of x86_fp80 because on targets such as arm,
9436 // LLVM will give "ERROR: Cannot select" for any instructions involving9451 // LLVM will give "ERROR: Cannot select" for any instructions involving
9437 // the x86_fp80 type.9452 // the x86_fp80 type.
9438 entry->llvm_type = get_int_type(g, false, 128)->llvm_type;9453 entry->llvm_type = get_int_type(g, false, entry->size_in_bits)->llvm_type;
9439 }9454 }
9440 entry->size_in_bits = 8 * 16;
9441 entry->abi_size = 16; // matches LLVMABISizeOfType(LLVMX86FP80Type())
9442 entry->abi_align = 16; // matches LLVMABIAlignmentOfType(LLVMX86FP80Type())
9443 buf_init_from_str(&entry->name, "f80");9455 buf_init_from_str(&entry->name, "f80");
9444 entry->data.floating.bit_count = 80;9456 entry->data.floating.bit_count = 80;
94459457
src/value.zig+26-4
...@@ -1122,10 +1122,32 @@ pub const Value = extern union {...@@ -1122,10 +1122,32 @@ pub const Value = extern union {
11221122
1123 fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F {1123 fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F {
1124 if (F == f80) {1124 if (F == f80) {
1125 // TODO: use std.math.F80Repr?1125 switch (target.cpu.arch.endian()) {
1126 const int = std.mem.readInt(u128, buffer[0..16], target.cpu.arch.endian());1126 .Little => {
1127 // TODO shouldn't this be a bitcast from u80 to f80 instead of u128 to f80?1127 const TargetF80Repr = extern struct {
1128 return @bitCast(F, int);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 },
1150 }
1129 }1151 }
1130 const Int = @Type(.{ .Int = .{1152 const Int = @Type(.{ .Int = .{
1131 .signedness = .unsigned,1153 .signedness = .unsigned,