Skip to content

Commit

Permalink
C backend: better f128 support
Browse files Browse the repository at this point in the history
  • Loading branch information
jean-dao committed Apr 3, 2022
1 parent 385307e commit de241bb
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 20 deletions.
75 changes: 57 additions & 18 deletions src/codegen/c.zig
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ const reserved_idents = std.ComptimeStringMap(void, .{
.{ "enum", {} },
.{ "extern ", {} },
.{ "float", {} },
.{ "float128_t", {} },
.{ "for", {} },
.{ "fortran", {} },
.{ "goto", {} },
Expand Down Expand Up @@ -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
Expand All @@ -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(')');
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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"),
Expand Down Expand Up @@ -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,
}
},
Expand Down
19 changes: 19 additions & 0 deletions src/link/C/zig.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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; \
}
Expand Down
4 changes: 2 additions & 2 deletions src/value.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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,
};
Expand Down
50 changes: 50 additions & 0 deletions test/behavior/floatop.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}

0 comments on commit de241bb

Please sign in to comment.