diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 780bfbb5561a..fc4ff6fe815b 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -154,6 +154,7 @@ const reserved_idents = std.ComptimeStringMap(void, .{ .{ "enum", {} }, .{ "extern ", {} }, .{ "float", {} }, + .{ "float128_t", {} }, .{ "for", {} }, .{ "fortran", {} }, .{ "goto", {} }, @@ -420,6 +421,7 @@ pub const DeclGen = struct { const int_info = @typeInfo(@TypeOf(int_val)).Int; const is_signed = int_info.signedness == .signed; const is_neg = int_val < 0; + comptime assert(int_info.bits > 64 and int_info.bits <= 128); // TODO support big endian arch @@ -433,13 +435,27 @@ pub const DeclGen = struct { const low = @truncate(u64, magnitude); // (int128_t)/<->( ( (uint128_t)( val_high << 64 )u ) + (uint128_t)val_low/u ) - if (is_signed) try writer.writeAll("(int128_t)"); - if (is_neg) try writer.writeByte('-'); + if (is_signed) { + try writer.writeAll("(int128_t)"); + if (is_neg) + try writer.writeByte('-'); + } else { + try writer.writeAll("(uint128_t)"); + } + + if (magnitude == 0) + return writer.print("0", .{}); - try writer.print("(((uint128_t)0x{x}u<<64)", .{high}); + try writer.print("(", .{}); + + if (high > 0) + try writer.print("((uint128_t)0x{x}u << 64)", .{high}); + + if (high > 0 and low > 0) + try writer.print(" + ", .{}); if (low > 0) - try writer.print("+(uint128_t)0x{x}u", .{low}); + try writer.print("(uint128_t)0x{x}u", .{low}); return writer.writeByte(')'); } @@ -539,6 +555,35 @@ pub const DeclGen = struct { } } + fn renderSmallFloat( + dg: *DeclGen, + comptime T: type, + writer: anytype, + val: Value, + ) error{ OutOfMemory, AnalysisFail }!void { + const v = val.toFloat(T); + if (std.math.isNan(v) or std.math.isInf(v)) { + // just generate a bit cast (exactly like we do in airBitcast) + switch (T) { + f16 => return dg.fail("TODO: C backend: implement 16-bit float lowering", .{}), + f32 => return writer.print("zig_bitcast_f32_u32(0x{x})", .{v}), + f64 => return writer.print("zig_bitcast_f64_u64(0x{x})", .{v}), + else => unreachable, + } + } else { + return writer.print("{x}", .{v}); + } + } + + fn renderFloat128( + writer: anytype, + val: f128, + ) error{ OutOfMemory, AnalysisFail }!void { + try writer.print("zig_bitcast_f128_u128(", .{}); + try renderInt128(writer, @bitCast(u128, val)); + try writer.print(")", .{}); + } + fn renderValue( dg: *DeclGen, writer: anytype, @@ -605,19 +650,13 @@ pub const DeclGen = struct { }, }, .Float => { - if (ty.floatBits(dg.module.getTarget()) <= 64) { - if (std.math.isNan(val.toFloat(f64)) or std.math.isInf(val.toFloat(f64))) { - // just generate a bit cast (exactly like we do in airBitcast) - switch (ty.tag()) { - .f32 => return writer.print("zig_bitcast_f32_u32(0x{x})", .{@bitCast(u32, val.toFloat(f32))}), - .f64 => return writer.print("zig_bitcast_f64_u64(0x{x})", .{@bitCast(u64, val.toFloat(f64))}), - else => return dg.fail("TODO float types > 64 bits are not support in renderValue() as of now", .{}), - } - } else { - return writer.print("{x}", .{val.toFloat(f64)}); - } + switch (ty.tag()) { + .f16 => return dg.renderSmallFloat(f16, writer, val), + .f32 => return dg.renderSmallFloat(f32, writer, val), + .f64 => return dg.renderSmallFloat(f64, writer, val), + .f128 => return renderFloat128(writer, val.toFloat(f128)), + else => unreachable, } - return dg.fail("TODO: C backend: implement lowering large float values", .{}); }, .Pointer => switch (val.tag()) { .null_value => try writer.writeAll("NULL"), @@ -1295,11 +1334,11 @@ pub const DeclGen = struct { }, .Float => { switch (t.tag()) { + .f16 => return dg.fail("TODO: C backend: implement float type f16", .{}), .f32 => try w.writeAll("float"), .f64 => try w.writeAll("double"), + .f128 => try w.writeAll("float128_t"), .c_longdouble => try w.writeAll("long double"), - .f16 => return dg.fail("TODO: C backend: implement float type f16", .{}), - .f128 => return dg.fail("TODO: C backend: implement float type f128", .{}), else => unreachable, } }, diff --git a/src/link/C/zig.h b/src/link/C/zig.h index ce547abd0a68..85f2244d539d 100644 --- a/src/link/C/zig.h +++ b/src/link/C/zig.h @@ -165,6 +165,17 @@ #define int128_t __int128 #define uint128_t unsigned __int128 + +#if defined(__aarch64__) +#define ZIG_HAS_FLOAT128 +#define float128_t long double +#elif defined(__FreeBSD__) || defined(__APPLE__) +#define float128_t ZIG_UNSUPPORTED_SYMBOL_ON_TARGET_float128_t +#else +#define ZIG_HAS_FLOAT128 +#define float128_t __float128 +#endif + ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t); ZIG_EXTERN_C void *memset (void *, int, size_t); @@ -408,6 +419,14 @@ static inline double zig_bitcast_f64_u64(uint64_t arg) { return dest; } +#if defined(ZIG_HAS_FLOAT128) +static inline float128_t zig_bitcast_f128_u128(uint128_t arg) { + float128_t dest; + memcpy(&dest, &arg, sizeof dest); + return dest; +} +#endif + #define zig_add_sat_u(ZT, T) static inline T zig_adds_##ZT(T x, T y, T max) { \ return (x > max - y) ? max : x + y; \ } diff --git a/src/value.zig b/src/value.zig index c7960741f6ed..20b2d4d184da 100644 --- a/src/value.zig +++ b/src/value.zig @@ -2897,7 +2897,7 @@ pub const Value = extern union { pub fn floatToIntScalar(val: Value, arena: Allocator, int_ty: Type, target: Target) error{ FloatCannotFit, OutOfMemory }!Value { const Limb = std.math.big.Limb; - var value = val.toFloat(f64); // TODO: f128 ? + var value = val.toFloat(f128); if (std.math.isNan(value) or std.math.isInf(value)) { return error.FloatCannotFit; } @@ -2909,7 +2909,7 @@ pub const Value = extern union { var rational = try std.math.big.Rational.init(arena); defer rational.deinit(); - rational.setFloat(f64, floored) catch |err| switch (err) { + rational.setFloat(f128, floored) catch |err| switch (err) { error.NonFiniteFloat => unreachable, error.OutOfMemory => return error.OutOfMemory, }; diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index 6d8c33efa248..681de9558cae 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -696,3 +696,53 @@ test "f128 at compile time is lossy" { try expect(@as(f128, 10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); } + +test "f128 special values" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.cpu.arch == .i386) return error.SkipZigTest; // TODO + + const f128_true_min = @bitCast(f128, @as(u128, 0x00000000000000000000000000000001)); + const f128_min = @bitCast(f128, @as(u128, 0x00010000000000000000000000000000)); + const f128_max = @bitCast(f128, @as(u128, 0x7ffeffffffffffffffffffffffffffff)); + const f128_inf = @bitCast(f128, @as(u128, 0x7fff0000000000000000000000000000)); + const f128_nan = @bitCast(f128, @as(u128, 0x7fff0000000000000000000000000001)); + + var buf = f128_true_min; + try expect(f128_true_min == buf); + + buf = f128_min; + try expect(f128_min == buf); + + buf = f128_max; + try expect(f128_max == buf); + + buf = -f128_max; + try expect(-f128_max == buf); + + buf = 0; + try expect(@as(f128, 0) == buf); + + buf = -0; + try expect(@as(f128, -0) == buf); + + buf = -1; + try expect(@as(f128, -1) == buf); + + buf = @as(f128, math.f64_max) + 42.0; + try expect(@floatCast(f64, buf - 42.0) == math.f64_max); + + buf = @as(f128, math.f64_min) - f128_min; + try expect(@floatCast(f64, buf + f128_min) == math.f64_min); + + buf = f128_inf; + try expect(f128_inf == buf); + + buf = -f128_inf; + try expect(-f128_inf == buf); + + buf = f128_nan; + try expect(math.isNan(buf)); +}