Skip to content

Commit

Permalink
C backend: better f16 support
Browse files Browse the repository at this point in the history
  • Loading branch information
jean-dao committed Apr 1, 2022
1 parent d8ae041 commit 5358aed
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 11 deletions.
38 changes: 27 additions & 11 deletions src/codegen/c.zig
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ const reserved_idents = std.ComptimeStringMap(void, .{
.{ "extern ", {} },
.{ "float", {} },
.{ "float128_t", {} },
.{ "float16_t", {} },
.{ "for", {} },
.{ "fortran", {} },
.{ "goto", {} },
Expand Down Expand Up @@ -556,22 +557,26 @@ 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)
// Special case: C doesn't support f16 as a return type, so do a 32-bit bit cast,
// and let the C compiler do the conversion from f32 to f16.
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}),
f16 => return writer.print(
"(float16_t)zig_bitcast_f32_u32(0x{x})",
.{@bitCast(u32, val.toFloat(f32))},
),
f32 => return writer.print("zig_bitcast_f32_u32(0x{x})", .{@bitCast(u32, v)}),
f64 => return writer.print("zig_bitcast_f64_u64(0x{x})", .{@bitCast(u64, v)}),
else => unreachable,
}
} else {
return writer.print("{x}", .{v});
return writer.print("{}", .{v});
}
}

Expand Down Expand Up @@ -651,9 +656,9 @@ pub const DeclGen = struct {
},
.Float => {
switch (ty.tag()) {
.f16 => return dg.renderSmallFloat(f16, writer, val),
.f32 => return dg.renderSmallFloat(f32, writer, val),
.f64 => return dg.renderSmallFloat(f64, writer, val),
.f16 => return renderSmallFloat(f16, writer, val),
.f32 => return renderSmallFloat(f32, writer, val),
.f64 => return renderSmallFloat(f64, writer, val),
.f128 => return renderFloat128(writer, val.toFloat(f128)),
else => unreachable,
}
Expand Down Expand Up @@ -921,7 +926,12 @@ pub const DeclGen = struct {
}
const return_ty = dg.decl.ty.fnReturnType();
if (return_ty.hasRuntimeBits()) {
try dg.renderType(w, return_ty);
// Special case: C does not support f16 as a return type
if (return_ty.tag() == .f16) {
try w.writeAll("float");
} else {
try dg.renderType(w, return_ty);
}
} else if (return_ty.zigTypeTag() == .NoReturn) {
try w.writeAll("zig_noreturn void");
} else {
Expand Down Expand Up @@ -1334,7 +1344,7 @@ pub const DeclGen = struct {
},
.Float => {
switch (t.tag()) {
.f16 => return dg.fail("TODO: C backend: implement float type f16", .{}),
.f16 => try w.writeAll("float16_t"),
.f32 => try w.writeAll("float"),
.f64 => try w.writeAll("double"),
.f128 => try w.writeAll("float128_t"),
Expand Down Expand Up @@ -1500,7 +1510,13 @@ pub const DeclGen = struct {

if (alignment != 0)
try w.print("ZIG_ALIGN({}) ", .{alignment});
try dg.renderType(w, render_ty);

// Special case: C does not support f16 as a fn parameter type
if (render_ty.tag() == .f16 and name == .arg) {
try w.writeAll("float");
} else {
try dg.renderType(w, render_ty);
}

const const_prefix = switch (mutability) {
.Const => "const ",
Expand Down
10 changes: 10 additions & 0 deletions src/link/C/zig.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,16 @@
#define float128_t __float128
#endif

#if defined(__clang__)
#define float16_t __fp16
#elif defined(__GNUC__) && (defined(__arm__) || defined(__aarch64__))
#define float16_t __fp16
#elif defined(__GNUC__) && defined(__i386__) && defined(__SSE2__)
#define float16_t _Float16
#else
#define float16_t ZIG_UNSUPPORTED_SYMBOL_ON_TARGET_float16_t
#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
39 changes: 39 additions & 0 deletions test/behavior/floatop.zig
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,45 @@ test "f128 at compile time is lossy" {
try expect(@as(f128, 10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0);
}

test "f16 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

const f16_inf = @bitCast(f16, @as(u16, 0x7c00));
const f16_nan = @bitCast(f16, @as(u16, 0x7c01));

var buf: f16 = math.f16_true_min;
try expect(math.f16_true_min == buf);

buf = math.f16_min;
try expect(math.f16_min == buf);

buf = math.f16_max;
try expect(math.f16_max == buf);

buf = -math.f16_max;
try expect(-math.f16_max == buf);

buf = 0;
try expect(@as(f16, 0) == buf);

buf = -0;
try expect(@as(f16, -0) == buf);

buf = -1;
try expect(@as(f16, -1) == buf);

buf = f16_inf;
try expect(f16_inf == buf);

buf = -f16_inf;
try expect(-f16_inf == buf);

buf = f16_nan;
try expect(math.isNan(buf));
}

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
Expand Down

0 comments on commit 5358aed

Please sign in to comment.