From a024aff9324e827d6595e44f922d87f8ed2dbd0d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 10 Feb 2022 22:06:43 -0700 Subject: [PATCH] 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. --- lib/std/math.zig | 40 ++++++++----- lib/std/special/compiler_rt/addXf3.zig | 18 +++--- lib/std/special/compiler_rt/compareXf2.zig | 4 +- lib/std/special/compiler_rt/extend_f80.zig | 6 +- lib/std/special/compiler_rt/trunc_f80.zig | 6 +- src/codegen/llvm.zig | 41 ++++++++----- src/stage1/codegen.cpp | 68 +++++++++++----------- src/type.zig | 55 +++++++++++++++-- src/value.zig | 67 ++++++++++++--------- test/behavior/floatop.zig | 5 +- 10 files changed, 200 insertions(+), 110 deletions(-) diff --git a/lib/std/math.zig b/lib/std/math.zig index 8398842e28..4b8bcf2287 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -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_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 -pub const f80_true_min = @ptrCast(*const f80, &F80Repr{ .fraction = 1, .exp = 0 }).*; -pub const f80_min = @ptrCast(*const f80, &F80Repr{ .fraction = 0x8000000000000000, .exp = 1 }).*; -pub const f80_max = @ptrCast(*const f80, &F80Repr{ .fraction = 0xFFFFFFFFFFFFFFFF, .exp = 0x7FFE }).*; -pub const f80_epsilon = @ptrCast(*const f80, &F80Repr{ .fraction = 0x8000000000000000, .exp = 0x3FC0 }).*; +pub const f80_true_min = make_f80(.{ .fraction = 1, .exp = 0 }); +pub const f80_min = make_f80(.{ .fraction = 0x8000000000000000, .exp = 1 }); +pub const f80_max = make_f80(.{ .fraction = 0xFFFFFFFFFFFFFFFF, .exp = 0x7FFE }); +pub const f80_epsilon = make_f80(.{ .fraction = 0x8000000000000000, .exp = 0x3FC0 }); pub const f80_toint = 1.0 / f80_epsilon; 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_f64 = @bitCast(f64, inf_u64); -pub const inf_f80 = @ptrCast(*const f80, &F80Repr{ .fraction = 0x8000000000000000, .exp = 0x7fff }).*; -pub const nan_f80 = @ptrCast(*const f80, &F80Repr{ .fraction = 0xA000000000000000, .exp = 0x7fff }).*; -pub const qnan_f80 = @ptrCast(*const f80, &F80Repr{ .fraction = 0xC000000000000000, .exp = 0x7fff }).*; +pub const inf_f80 = make_f80(F80{ .fraction = 0x8000000000000000, .exp = 0x7fff }); +pub const nan_f80 = make_f80(F80{ .fraction = 0xA000000000000000, .exp = 0x7fff }); +pub const qnan_f80 = make_f80(F80{ .fraction = 0xC000000000000000, .exp = 0x7fff }); pub const nan_u128 = @as(u128, 0x7fff0000000000000000000000000001); 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) { 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), + }; +} diff --git a/lib/std/special/compiler_rt/addXf3.zig b/lib/std/special/compiler_rt/addXf3.zig index 1339cc340d..13758afce7 100644 --- a/lib/std/special/compiler_rt/addXf3.zig +++ b/lib/std/special/compiler_rt/addXf3.zig @@ -232,8 +232,8 @@ fn normalize_f80(exp: *i32, significand: *u80) void { } pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { - var a_rep align(16) = @ptrCast(*const std.math.F80Repr, &a).*; - var b_rep align(16) = @ptrCast(*const std.math.F80Repr, &b).*; + var a_rep = std.math.break_f80(a); + var b_rep = std.math.break_f80(b); var a_exp: i32 = a_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); // NaN + anything = qNaN a_rep.fraction |= qnan_bit; - return @ptrCast(*const f80, &a_rep).*; + return std.math.make_f80(a_rep); } } 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); // anything + NaN = qNaN 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) { // but we need to get the sign right for zero + zero a_rep.exp &= b_rep.exp; - return @ptrCast(*const f80, &a_rep).*; + return std.math.make_f80(a_rep); } else { return b; } @@ -359,7 +359,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { if (a_exp >= max_exp) { a_rep.exp = max_exp | result_sign; 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) { @@ -387,13 +387,13 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { a_rep.fraction = @truncate(u64, a_int); 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 { - var b_rep align(16) = @ptrCast(*const std.math.F80Repr, &b).*; + var b_rep = std.math.break_f80(b); b_rep.exp ^= 0x8000; - return __addxf3(a, @ptrCast(*const f80, &b_rep).*); + return __addxf3(a, std.math.make_f80(b_rep)); } test { diff --git a/lib/std/special/compiler_rt/compareXf2.zig b/lib/std/special/compiler_rt/compareXf2.zig index 36f6f5f1c1..9640298f8f 100644 --- a/lib/std/special/compiler_rt/compareXf2.zig +++ b/lib/std/special/compiler_rt/compareXf2.zig @@ -147,8 +147,8 @@ pub fn __gtdf2(a: f64, b: f64) callconv(.C) i32 { // Comparison between f80 pub inline fn cmp_f80(comptime RT: type, a: f80, b: f80) RT { - const a_rep = @ptrCast(*const std.math.F80Repr, &a).*; - const b_rep = @ptrCast(*const std.math.F80Repr, &b).*; + const a_rep = std.math.break_f80(a); + const b_rep = std.math.break_f80(b); const sig_bits = std.math.floatMantissaBits(f80); const int_bit = 0x8000000000000000; const sign_bit = 0x8000; diff --git a/lib/std/special/compiler_rt/extend_f80.zig b/lib/std/special/compiler_rt/extend_f80.zig index 29ba8560ce..4686421db0 100644 --- a/lib/std/special/compiler_rt/extend_f80.zig +++ b/lib/std/special/compiler_rt/extend_f80.zig @@ -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_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 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; - return @ptrCast(*const f80, &dst).*; + return std.math.make_f80(dst); } 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; // 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; a_rep.exp &= 0x7FFF; var abs_result: u128 = undefined; diff --git a/lib/std/special/compiler_rt/trunc_f80.zig b/lib/std/special/compiler_rt/trunc_f80.zig index 567d03be63..19e8d44b86 100644 --- a/lib/std/special/compiler_rt/trunc_f80.zig +++ b/lib/std/special/compiler_rt/trunc_f80.zig @@ -42,7 +42,7 @@ inline fn trunc(comptime dst_t: type, a: f80) dst_t { const dst_nan_mask = dst_qnan - 1; // 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; a_rep.exp &= 0x7FFF; 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 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) { // a is NaN. @@ -155,5 +155,5 @@ pub fn __trunctfxf2(a: f128) callconv(.C) f80 { } res.exp |= sign; - return @ptrCast(*const f80, &res).*; + return std.math.make_f80(res); } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 54468162ad..b2adb898d3 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -824,23 +824,24 @@ pub const DeclGen = struct { fn llvmType(dg: *DeclGen, t: Type) Allocator.Error!*const llvm.Type { const gpa = dg.gpa; + const target = dg.module.getTarget(); switch (t.zigTypeTag()) { .Void, .NoReturn => return dg.context.voidType(), .Int => { - const info = t.intInfo(dg.module.getTarget()); + const info = t.intInfo(target); return dg.context.intType(info.bits); }, .Enum => { var buffer: Type.Payload.Bits = undefined; 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); }, - .Float => switch (t.floatBits(dg.module.getTarget())) { + .Float => switch (t.floatBits(target)) { 16 => return dg.context.halfType(), 32 => return dg.context.floatType(), 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(), else => unreachable, }, @@ -859,7 +860,8 @@ pub const DeclGen = struct { const llvm_addrspace = dg.llvmAddressSpace(t.ptrAddressSpace()); const elem_ty = t.childType(); const lower_elem_ty = switch (elem_ty.zigTypeTag()) { - .Opaque, .Array, .Fn => true, + .Opaque, .Fn => true, + .Array => elem_ty.childType().hasRuntimeBits(), else => elem_ty.hasRuntimeBits(), }; const llvm_elem_ty = if (lower_elem_ty) @@ -889,9 +891,11 @@ pub const DeclGen = struct { else => unreachable, }, .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); - return elem_type.arrayType(@intCast(c_uint, total_len)); + return elem_llvm_ty.arrayType(@intCast(c_uint, total_len)); }, .Vector => { const elem_type = try dg.llvmType(t.childType()); @@ -978,7 +982,6 @@ pub const DeclGen = struct { if (struct_obj.layout == .Packed) { try llvm_field_types.ensureUnusedCapacity(gpa, struct_obj.fields.count() * 2); - const target = dg.module.getTarget(); comptime assert(Type.packed_struct_layout_version == 1); var offset: u64 = 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()); const union_obj = t.cast(Type.Payload.Union).?.data; - const target = dg.module.getTarget(); if (t.unionTagType()) |enum_tag_ty| { const enum_tag_llvm_ty = try dg.llvmType(enum_tag_ty); const layout = union_obj.getLayout(target, true); @@ -1141,7 +1143,6 @@ pub const DeclGen = struct { }, .Fn => { const fn_info = t.fnInfo(); - const target = dg.module.getTarget(); const sret = firstParamSRet(fn_info, target); const return_type = fn_info.return_type; const raw_llvm_ret_ty = try dg.llvmType(return_type); @@ -1257,16 +1258,21 @@ pub const DeclGen = struct { }, .Float => { 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)), 80 => { 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); var x = llvm_i80.constInt(repr.exp, .False); x = x.constShl(llvm_i80.constInt(64, .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 => { 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, + }; +} diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 7b0bcbe2f5..f1a94b9bb9 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -8195,17 +8195,15 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n case 64: return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f64); case 80: { - uint64_t buf[2]; - memcpy(&buf, &const_val->data.x_f80, 16); -#if ZIG_BYTE_ORDER == ZIG_BIG_ENDIAN - uint64_t tmp = buf[0]; - buf[0] = buf[1]; - buf[1] = tmp; -#endif - LLVMValueRef as_i128 = LLVMConstIntOfArbitraryPrecision(LLVMInt128Type(), 2, buf); - 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)); + LLVMTypeRef llvm_i80 = LLVMIntType(80); + LLVMValueRef x = LLVMConstInt(llvm_i80, const_val->data.x_f80.signExp, false); + x = LLVMConstShl(x, LLVMConstInt(llvm_i80, 64, false)); + x = LLVMConstOr(x, LLVMConstInt(llvm_i80, const_val->data.x_f80.signif, false)); + if (target_has_f80(g->zig_target)) { + return LLVMConstBitCast(x, LLVMX86FP80Type()); + } else { + return x; + } } case 128: { @@ -9429,32 +9427,36 @@ static void define_builtin_types(CodeGen *g) { { 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"); 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->size_in_bits, ZigLLVMEncoding_DW_ATE_unsigned()); diff --git a/src/type.zig b/src/type.zig index 0827b2e2d7..27fdb0abc8 100644 --- a/src/type.zig +++ b/src/type.zig @@ -1877,9 +1877,28 @@ pub const Type = extern union { .f16 => return 2, .f32 => return 4, .f64 => return 8, - .f80 => 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_single, @@ -2158,9 +2177,28 @@ pub const Type = extern union { .f16 => return 2, .f32 => return 4, .f64 => return 8, - .f80 => 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_single, @@ -2349,7 +2387,7 @@ pub const Type = extern union { .c_ulong => return CType.ulong.sizeInBits(target), .c_longlong => return CType.longlong.sizeInBits(target), .c_ulonglong => return CType.ulonglong.sizeInBits(target), - .c_longdouble => 128, + .c_longdouble => return CType.longdouble.sizeInBits(target), .error_set, .error_set_single, @@ -4772,6 +4810,13 @@ pub const Type = extern union { pub const @"u8" = initTag(.u8); pub const @"u32" = initTag(.u32); 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 @"usize" = initTag(.usize); pub const @"isize" = initTag(.isize); diff --git a/src/value.zig b/src/value.zig index aefb0a3e20..3479819160 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1112,6 +1112,19 @@ pub const Value = extern union { } 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 = .{ .signedness = .unsigned, .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 { if (F == f80) { - switch (target.cpu.arch.endian()) { - .Little => { - const TargetF80Repr = extern struct { - fraction: u64, - exp: u16, - }; - 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).*; - }, + switch (target.cpu.arch) { + .i386, .x86_64 => return std.math.make_f80(.{ + .fraction = std.mem.readIntLittle(u64, buffer[0..8]), + .exp = std.mem.readIntLittle(u16, buffer[8..10]), + }), + else => {}, } } const Int = @Type(.{ .Int = .{ .signedness = .unsigned, .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); } + 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. pub fn toFloat(val: Value, comptime T: type) T { return switch (val.tag()) { diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index ed632c26c5..00f4cff6e5 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -5,7 +5,10 @@ const math = std.math; const pi = std.math.pi; const e = std.math.e; 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 = 0.000001;