authorgravatar for carl@astholm.seCarl Åstholm <carl@astholm.se> 2023-11-01 19:12:54+01:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2023-11-21 14:14:01+02:00
log8bf4b3c611b39fc39bad2a6c657541fa13973dea
tree663c8227fe167c7ad189162d7337e49bf22a747f
parent40bd93e2a24e3bdebd4f6454cfcffc51e5aaeeae

translate-c: translate 80/128-bit long double literals


5 files changed, 77 insertions(+), 15 deletions(-)

src/clang.zig+3
......@@ -506,6 +506,9 @@ pub const FloatingLiteral = opaque {
506506 pub const getValueAsApproximateDouble = ZigClangFloatingLiteral_getValueAsApproximateDouble;
507507 extern fn ZigClangFloatingLiteral_getValueAsApproximateDouble(*const FloatingLiteral) f64;
508508
509 pub const getValueAsApproximateQuadBits = ZigClangFloatingLiteral_getValueAsApproximateQuadBits;
510 extern fn ZigClangFloatingLiteral_getValueAsApproximateQuadBits(*const FloatingLiteral, low: *u64, high: *u64) void;
511
509512 pub const getBeginLoc = ZigClangFloatingLiteral_getBeginLoc;
510513 extern fn ZigClangFloatingLiteral_getBeginLoc(*const FloatingLiteral) SourceLocation;
511514
src/translate_c.zig+54-9
......@@ -3936,11 +3936,26 @@ fn transCPtrCast(
39363936}
39373937
39383938fn transFloatingLiteral(c: *Context, expr: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node {
3939 // TODO use something more accurate than widening to a larger float type and printing that result
39393940 switch (expr.getRawSemantics()) {
39403941 .IEEEhalf, // f16
39413942 .IEEEsingle, // f32
39423943 .IEEEdouble, // f64
3943 => {},
3944 => {
3945 var dbl = expr.getValueAsApproximateDouble();
3946 const is_negative = dbl < 0; // -0.0 is considered non-negative
3947 if (is_negative) dbl = -dbl;
3948 const str = if (dbl == @floor(dbl))
3949 try std.fmt.allocPrint(c.arena, "{d}.0", .{dbl})
3950 else
3951 try std.fmt.allocPrint(c.arena, "{d}", .{dbl});
3952 var node = try Tag.float_literal.create(c.arena, str);
3953 if (is_negative) node = try Tag.negate.create(c.arena, node);
3954 return maybeSuppressResult(c, used, node);
3955 },
3956 .x87DoubleExtended, // f80
3957 .IEEEquad, // f128
3958 => return transFloatingLiteralQuad(c, expr, used),
39443959 else => |format| return fail(
39453960 c,
39463961 error.UnsupportedTranslation,
......@@ -3949,14 +3964,44 @@ fn transFloatingLiteral(c: *Context, expr: *const clang.FloatingLiteral, used: R
39493964 .{format},
39503965 ),
39513966 }
3952 // TODO use something more accurate
3953 var dbl = expr.getValueAsApproximateDouble();
3954 const is_negative = dbl < 0;
3955 if (is_negative) dbl = -dbl;
3956 const str = if (dbl == @floor(dbl))
3957 try std.fmt.allocPrint(c.arena, "{d}.0", .{dbl})
3958 else
3959 try std.fmt.allocPrint(c.arena, "{d}", .{dbl});
3967}
3968
3969fn transFloatingLiteralQuad(c: *Context, expr: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node {
3970 assert(switch (expr.getRawSemantics()) {
3971 .x87DoubleExtended, .IEEEquad => true,
3972 else => false,
3973 });
3974
3975 var low: u64 = undefined;
3976 var high: u64 = undefined;
3977 expr.getValueAsApproximateQuadBits(&low, &high);
3978 var quad: f128 = @bitCast(low | @as(u128, high) << 64);
3979 const is_negative = quad < 0; // -0.0 is considered non-negative
3980 if (is_negative) quad = -quad;
3981
3982 // TODO implement decimal format for f128 <https://github.com/ziglang/zig/issues/1181>
3983 // in the meantime, if the value can be roundtripped by casting it to f64, serializing it to
3984 // the decimal format and parsing it back as the exact same f128 value, then use that serialized form
3985 const str = fmt_decimal: {
3986 var buf: [512]u8 = undefined; // should be large enough to print any f64 in decimal form
3987 const dbl: f64 = @floatCast(quad);
3988 const temp_str = if (dbl == @floor(dbl))
3989 std.fmt.bufPrint(&buf, "{d}.0", .{dbl}) catch |err| switch (err) {
3990 error.NoSpaceLeft => unreachable,
3991 }
3992 else
3993 std.fmt.bufPrint(&buf, "{d}", .{dbl}) catch |err| switch (err) {
3994 error.NoSpaceLeft => unreachable,
3995 };
3996 const could_roundtrip = if (std.fmt.parseFloat(f128, temp_str)) |parsed_quad|
3997 quad == parsed_quad
3998 else |_|
3999 false;
4000 break :fmt_decimal if (could_roundtrip) try c.arena.dupe(u8, temp_str) else null;
4001 }
4002 // otherwise, fall back to the hexadecimal format
4003 orelse try std.fmt.allocPrint(c.arena, "{x}", .{quad});
4004
39604005 var node = try Tag.float_literal.create(c.arena, str);
39614006 if (is_negative) node = try Tag.negate.create(c.arena, node);
39624007 return maybeSuppressResult(c, used, node);
src/zig_clang.cpp+11
......@@ -3245,6 +3245,17 @@ double ZigClangFloatingLiteral_getValueAsApproximateDouble(const ZigClangFloatin
32453245 return casted->getValueAsApproximateDouble();
32463246}
32473247
3248void ZigClangFloatingLiteral_getValueAsApproximateQuadBits(const ZigClangFloatingLiteral *self, uint64_t *low, uint64_t *high) {
3249 auto casted = reinterpret_cast<const clang::FloatingLiteral *>(self);
3250 llvm::APFloat apf = casted->getValue();
3251 bool ignored;
3252 apf.convert(llvm::APFloat::IEEEquad(), llvm::APFloat::rmNearestTiesToEven, &ignored);
3253 const llvm::APInt api = apf.bitcastToAPInt();
3254 const uint64_t *api_data = api.getRawData();
3255 *low = api_data[0];
3256 *high = api_data[1];
3257}
3258
32483259struct ZigClangSourceLocation ZigClangFloatingLiteral_getBeginLoc(const struct ZigClangFloatingLiteral *self) {
32493260 auto casted = reinterpret_cast<const clang::FloatingLiteral *>(self);
32503261 return bitcast(casted->getBeginLoc());
src/zig_clang.h+1
......@@ -1510,6 +1510,7 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangDeclStmt_getBeginLoc(const st
15101510ZIG_EXTERN_C unsigned ZigClangAPFloat_convertToHexString(const struct ZigClangAPFloat *self, char *DST,
15111511 unsigned HexDigits, bool UpperCase, enum ZigClangAPFloat_roundingMode RM);
15121512ZIG_EXTERN_C double ZigClangFloatingLiteral_getValueAsApproximateDouble(const ZigClangFloatingLiteral *self);
1513ZIG_EXTERN_C void ZigClangFloatingLiteral_getValueAsApproximateQuadBits(const ZigClangFloatingLiteral *self, uint64_t *low, uint64_t *high);
15131514ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangFloatingLiteral_getBeginLoc(const struct ZigClangFloatingLiteral *);
15141515ZIG_EXTERN_C ZigClangAPFloatBase_Semantics ZigClangFloatingLiteral_getRawSemantics(const ZigClangFloatingLiteral *self);
15151516
test/translate_c.zig+8-6
......@@ -1240,6 +1240,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
12401240 \\extern const float my_float = 1.0f;
12411241 \\extern const double my_double = 1.0;
12421242 \\extern const long double my_longdouble = 1.0l;
1243 \\extern const long double my_extended_precision_longdouble = 1.0000000000000003l;
12431244 , &([_][]const u8{
12441245 "pub const foo = @as(f32, 3.14);",
12451246 "pub const bar = @as(c_longdouble, 16.0e-2);",
......@@ -1250,13 +1251,14 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
12501251 "pub const foobar = -@as(c_longdouble, 73.0);",
12511252 "pub export const my_float: f32 = 1.0;",
12521253 "pub export const my_double: f64 = 1.0;",
1253 } ++ if (@bitSizeOf(c_longdouble) != 64) .{
1254 // TODO properly translate non-64-bit long doubles
1255 "source.h:10:42: warning: unsupported floating point constant format",
1256 "source.h:10:26: warning: unable to translate variable initializer, demoted to extern",
1257 "pub extern const my_longdouble: c_longdouble;",
1258 } else .{
12591254 "pub export const my_longdouble: c_longdouble = 1.0;",
1255 switch (@bitSizeOf(c_longdouble)) {
1256 // TODO implement decimal format for f128 <https://github.com/ziglang/zig/issues/1181>
1257 // (so that f80/f128 values not exactly representable as f64 can be emitted in decimal form)
1258 80 => "pub export const my_extended_precision_longdouble: c_longdouble = 0x1.000000000000159ep0;",
1259 128 => "pub export const my_extended_precision_longdouble: c_longdouble = 0x1.000000000000159e05f1e2674d21p0;",
1260 else => "pub export const my_extended_precision_longdouble: c_longdouble = 1.0000000000000002;",
1261 },
12601262 }));
12611263
12621264 cases.add("macro defines hexadecimal float",