-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
C Backend: f16 and f128 support #10771
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some things need clarification. Better split this in 2 PRs, as this also includes 128bit integer support. Tests for the c codegen them are also missing.
A few more tests would be great, both for floatop.zig and the C side.
floatop.zig has very few test cases, which do not involve negative numbers or the floating point number edge cases.
src/codegen/c.zig
Outdated
assert(bits <= 128); | ||
|
||
// Clang and GCC don't support 128-bit integer constants but will | ||
// hopefully unfold them if we construct one manually. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be helpful, if we could test this in CI. Do you have an idea on that?
const magnitude = if (val.positive) | ||
val.to(u128) catch unreachable | ||
else | ||
std.math.absCast(val.to(i128) catch unreachable); | ||
|
||
const high = @truncate(u64, magnitude >> 64); | ||
const low = @truncate(u64, magnitude); | ||
|
||
// (int128_t)/<->( ( (uint128_t)( val_high << 64 )u ) + (uint128_t)val_low/u ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This reads like it is little endian only.
Make a comptime error for big-endian or clarify.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that I didn't write the int128 handling code, just modifying the function to also accept smaller values. I can put a TODO comment, but I think it should be part of another PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better would be a compilation error with comptime check of the endianess.
src/codegen/c.zig
Outdated
@@ -386,52 +386,97 @@ pub const DeclGen = struct { | |||
try dg.renderDeclName(decl, writer); | |||
} | |||
|
|||
fn renderInt128( | |||
fn renderBigIntConst( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the reason to rename it like it would accept any integer sizes and then do assert(bits <= 128);
in the fn body?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason I did those changes (renderInt128
/renderBigIntConst
refactoring/renaming) is not obvious anymore, I'll remove unneeded changes.
src/codegen/c.zig
Outdated
|
||
return writer.writeByte(')'); | ||
} | ||
|
||
fn renderBigIntConst( | ||
fn renderInt128( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dont understand why this fn is necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason I did those changes (renderInt128/renderBigIntConst refactoring/renaming) is not obvious anymore, I'll remove unneeded changes.
I didn't write the 128-bit integer support, this was already here. I realize there are a bunch of unneeded changes in this PR, so I'll clean it up. I did have to change a bit the 128-bit int function to handle smaller values as well.
Agreed, will write more tests. Thanks for the review! |
50665a9
to
10c47c5
Compare
I updated my branch with the following changes:
|
Hi, I wrote the original renderInt128(). The endianness check should not be needed because you're just taking integer values and shifting them; the underlying representation only matters when you put it into memory, although given that there's doubt about it it would be worth it if someone could test this with a big endian arch. |
@jean-dao Can you rebase to resolve the conflicts + rerun CI, which hopefully passes this time? |
096ca20
to
c796979
Compare
test/behavior/floatop.zig
Outdated
@@ -697,3 +697,38 @@ test "f128 at compile time is lossy" { | |||
|
|||
try expect(@as(f128, 10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); | |||
} | |||
|
|||
test "f16 literals" { | |||
if (builtin.zig_backend != .stage2_c) return error.SkipZigTest; // TODO |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are this test and the next being skipped for the C backend? I would assume them to be passing after your changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, only the f128 tests are not passing, I'll update again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
stage2/bin/zig test test/behavior.zig -I test -target aarch64-linux --test-cmd qemu-aarch64 --test-cmd-bin
test/behavior/floatop.zig:701:1: error: TODO implement const of type 'f16'
Actually it's not passing, although for a different reason than f128. So I think it should still be skipped for backend != c. It's hard to tell for me which backend is actually supported apart from C.
8492f51
to
e03aabf
Compare
src/codegen/c.zig
Outdated
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 writer.print("zig_bitcast_f16_u16(0x{x})", .{v}), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is a bitCast of v
missing here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think @topolarity is right, without the @bitCast()
this will print the float to hex as is, resulting in something like 0x0x1.ecp6
: https://zig.godbolt.org/z/9ehccKWW3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add a few examples to test the relevant cases.
src/link/C/zig.h
Outdated
#define ZIG_HAS_FLOAT128 | ||
#define float128_t long double | ||
#elif defined(__FreeBSD__) || defined(__APPLE__) | ||
#define float128_t float128_t_is_not_supported_on_this_target |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just curious: Why not #error float128_t_is_not_supported_on_this_target
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 to @matu3ba's point, please use:
#error "float128_t is not supported on this target"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From my understanding, using #error
will make all compilations fail on unsupported targets, even if float128_t
is not used. I agree this is less clear though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you attach a TODO and make this something along ZIG_UNSUPPORTED_SYMBOL_ON_TARGET_float128_t
?
It would be nice to enable a central example header for the user to invoke such that the user can check these kind of things and also includes stuff like abi checks as macros: marler8997/ziglibc#1
Getting a convention for what stuff is unsupported + make it easily checkable and the checks editable sounds very relevant to me.
src/link/C/zig.h
Outdated
#define ZIG_HAS_FLOAT16 | ||
#define float16_t _Float16 | ||
#else | ||
#define float16_t float16_t_is_not_supported_on_this_target |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same question: Why not #error float128_t_is_not_supported_on_this_target
?
|
||
var buf: f16 = math.f16_min; | ||
try expect(math.f16_min == buf); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing test cases +-inf, -0 with at least 1 example with negative sign.
Not sure, if you also want to check f16_true_min
or what the expected behavior for denormals is.
test/behavior/floatop.zig
Outdated
if (builtin.zig_backend != .stage2_c) return error.SkipZigTest; // TODO | ||
|
||
const f128_min = @bitCast(f128, @as(u128, 0x00010000000000000000000000000000)); | ||
const f128_max = @bitCast(f128, @as(u128, 0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same story: at least 1 example with negative sign.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your patch. @topolarity's review comment looks like a merge blocker to me and I found a few other issues as well-
test/behavior/floatop.zig
Outdated
try expect(@as(f16, 0) == buf); | ||
} | ||
|
||
test "f128 tests" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please be more specific? What exactly is this testing?
src/codegen/c.zig
Outdated
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 writer.print("zig_bitcast_f16_u16(0x{x})", .{v}), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think @topolarity is right, without the @bitCast()
this will print the float to hex as is, resulting in something like 0x0x1.ecp6
: https://zig.godbolt.org/z/9ehccKWW3
src/link/C/zig.h
Outdated
#define ZIG_HAS_FLOAT128 | ||
#define float128_t long double | ||
#elif defined(__FreeBSD__) || defined(__APPLE__) | ||
#define float128_t float128_t_is_not_supported_on_this_target |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 to @matu3ba's point, please use:
#error "float128_t is not supported on this target"
98e008b
to
34ecec5
Compare
The new update changelog:
|
34ecec5
to
5358aed
Compare
The CI run crashed. |
@jmc-88 I cannot reproduce it locally. I thought maybe it could be related to musl libc vs glibc, but I couldn't yet reproduce the ci setup on my local setup. Since there is no output from the |
I was able to reproduce the failure locally. This is the failing test case from const builtin = @import("builtin");
const expect = @import("std").testing.expect;
test "take address of parameter" {
try testTakeAddressOfParameter(12.34);
}
fn testTakeAddressOfParameter(f: f32) !void {
const f_ptr = &f;
try expect(f_ptr.* == 12.34);
} Looks like the problem is that 12.34 is rendered in decimal fp |
2b9091c
to
1d7af0c
Compare
1d7af0c
to
243e160
Compare
@topolarity thank you for your help! On another topic, it seems the f128 tests are segfaulting on i386. I'm not quite sure how it passed before, I couldn't reproduce it (= passing test), even on an older master branch. I made the f128 test skipped on i386, not sure if that is acceptable. |
Good news, it doesn't like the segfault is related to these changes! 👍 Bug filed here, with root cause: #11383 |
comptime assert(int_info.bits > 64 and int_info.bits <= 128); | ||
|
||
// TODO support big endian arch | ||
comptime assert(builtin.cpu.arch.endian() == .Little); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is checking the endianness of the compiler, not the target. Also can you just fix the bug, if any, rather than asserting? It's not that hard to solve.
#define ZIG_HAS_FLOAT128 | ||
#define float128_t long double | ||
#elif defined(__FreeBSD__) || defined(__APPLE__) | ||
#define float128_t ZIG_UNSUPPORTED_SYMBOL_ON_TARGET_float128_t |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case make it a 128 bit integer and emit instructions to perform softfloat on it. All the softfloat functions are available in compiler-rt.
This PR seems to be abandoned. Please open a fresh one if you want to continue the effort. |
__float128
type, re-use 128-bit int literal trick for floats__fp16
or_Float16
when supportedCompiler support is not super robust, but since
__int128
is also used without any#if defined
guard, I did the same for__float128
.Support for 16-bit floats is a bit more documented from what I found and I tried to make the
#if defined
guards match actual compiler support. See https://gcc.gnu.org/onlinedocs/gcc/Half-Precision.html and https://clang.llvm.org/docs/LanguageExtensions.html#id12. (I didn't found any doc for the Microsoft compiler.)Note/Question: The
TODO: f128 ?
comment inValue.floatToInt()
leads me to think there was maybe a reason (performance related?) not to directly use a f128 instead of a f64, so it may require some rework.