| author | |
| committer | |
| log | f476463cd2552d384d0ef6f43f250b3abd45e4cd |
| tree | d4730eb7bec3d2554fd39872d7c445deceaba232 |
| parent | eb010ce65ddddb23ceddae86d377432cf1b2b01c |
| parent | d4d3a9dcc93d517ffec43c9b4572977c05ee4d4b |
| signature |
translate-c: handle NAN and INFINITY macros7 files changed, 115 insertions(+), 5 deletions(-)
lib/std/zig/c_builtins.zig+43| ... | @@ -191,6 +191,49 @@ pub inline fn __builtin_expect(expr: c_long, c: c_long) c_long { | ... | @@ -191,6 +191,49 @@ pub inline fn __builtin_expect(expr: c_long, c: c_long) c_long { |
| 191 | return expr; | 191 | return expr; |
| 192 | } | 192 | } |
| 193 | 193 | ||
| 194 | /// returns a quiet NaN. Quiet NaNs have many representations; tagp is used to select one in an | ||
| 195 | /// implementation-defined way. | ||
| 196 | /// This implementation is based on the description for __builtin_nan provided in the GCC docs at | ||
| 197 | /// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005fnan | ||
| 198 | /// Comment is reproduced below: | ||
| 199 | /// Since ISO C99 defines this function in terms of strtod, which we do not implement, a description | ||
| 200 | /// of the parsing is in order. | ||
| 201 | /// The string is parsed as by strtol; that is, the base is recognized by leading ‘0’ or ‘0x’ prefixes. | ||
| 202 | /// The number parsed is placed in the significand such that the least significant bit of the number is | ||
| 203 | /// at the least significant bit of the significand. | ||
| 204 | /// The number is truncated to fit the significand field provided. | ||
| 205 | /// The significand is forced to be a quiet NaN. | ||
| 206 | /// | ||
| 207 | /// If tagp contains any non-numeric characters, the function returns a NaN whose significand is zero. | ||
| 208 | /// If tagp is empty, the function returns a NaN whose significand is zero. | ||
| 209 | pub inline fn __builtin_nanf(tagp: []const u8) f32 { | ||
| 210 | const parsed = std.fmt.parseUnsigned(c_ulong, tagp, 0) catch 0; | ||
| 211 | const bits = @truncate(u23, parsed); // single-precision float trailing significand is 23 bits | ||
| 212 | return @bitCast(f32, @as(u32, bits) | std.math.qnan_u32); | ||
| 213 | } | ||
| 214 | |||
| 215 | pub inline fn __builtin_huge_valf() f32 { | ||
| 216 | return std.math.inf(f32); | ||
| 217 | } | ||
| 218 | |||
| 219 | pub inline fn __builtin_inff() f32 { | ||
| 220 | return std.math.inf(f32); | ||
| 221 | } | ||
| 222 | |||
| 223 | pub inline fn __builtin_isnan(x: anytype) c_int { | ||
| 224 | return @boolToInt(std.math.isNan(x)); | ||
| 225 | } | ||
| 226 | |||
| 227 | pub inline fn __builtin_isinf(x: anytype) c_int { | ||
| 228 | return @boolToInt(std.math.isInf(x)); | ||
| 229 | } | ||
| 230 | |||
| 231 | /// Similar to isinf, except the return value is -1 for an argument of -Inf and 1 for an argument of +Inf. | ||
| 232 | pub inline fn __builtin_isinf_sign(x: anytype) c_int { | ||
| 233 | if (!std.math.isInf(x)) return 0; | ||
| 234 | return if (std.math.isPositiveInf(x)) 1 else -1; | ||
| 235 | } | ||
| 236 | |||
| 194 | // __builtin_alloca_with_align is not currently implemented. | 237 | // __builtin_alloca_with_align is not currently implemented. |
| 195 | // It is used in a run-translated-c test and a test-translate-c test to ensure that non-implemented | 238 | // It is used in a run-translated-c test and a test-translate-c test to ensure that non-implemented |
| 196 | // builtins are correctly demoted. If you implement __builtin_alloca_with_align, please update the | 239 | // builtins are correctly demoted. If you implement __builtin_alloca_with_align, please update the |
src/clang.zig+5| ... | @@ -269,6 +269,11 @@ pub const CharacterLiteral = opaque { | ... | @@ -269,6 +269,11 @@ pub const CharacterLiteral = opaque { |
| 269 | extern fn ZigClangCharacterLiteral_getValue(*const CharacterLiteral) c_uint; | 269 | extern fn ZigClangCharacterLiteral_getValue(*const CharacterLiteral) c_uint; |
| 270 | }; | 270 | }; |
| 271 | 271 | ||
| 272 | pub const ChooseExpr = opaque { | ||
| 273 | pub const getChosenSubExpr = ZigClangChooseExpr_getChosenSubExpr; | ||
| 274 | extern fn ZigClangChooseExpr_getChosenSubExpr(*const ChooseExpr) *const Expr; | ||
| 275 | }; | ||
| 276 | |||
| 272 | pub const CompoundAssignOperator = opaque { | 277 | pub const CompoundAssignOperator = opaque { |
| 273 | pub const getType = ZigClangCompoundAssignOperator_getType; | 278 | pub const getType = ZigClangCompoundAssignOperator_getType; |
| 274 | extern fn ZigClangCompoundAssignOperator_getType(*const CompoundAssignOperator) QualType; | 279 | extern fn ZigClangCompoundAssignOperator_getType(*const CompoundAssignOperator) QualType; |
src/translate_c.zig+6-2| ... | @@ -1308,6 +1308,10 @@ fn transStmt( | ... | @@ -1308,6 +1308,10 @@ fn transStmt( |
| 1308 | const shuffle_vec_node = try transShuffleVectorExpr(c, scope, shuffle_vec_expr); | 1308 | const shuffle_vec_node = try transShuffleVectorExpr(c, scope, shuffle_vec_expr); |
| 1309 | return maybeSuppressResult(c, scope, result_used, shuffle_vec_node); | 1309 | return maybeSuppressResult(c, scope, result_used, shuffle_vec_node); |
| 1310 | }, | 1310 | }, |
| 1311 | .ChooseExprClass => { | ||
| 1312 | const choose_expr = @ptrCast(*const clang.ChooseExpr, stmt); | ||
| 1313 | return transExpr(c, scope, choose_expr.getChosenSubExpr(), result_used); | ||
| 1314 | }, | ||
| 1311 | // When adding new cases here, see comment for maybeBlockify() | 1315 | // When adding new cases here, see comment for maybeBlockify() |
| 1312 | .GCCAsmStmtClass, | 1316 | .GCCAsmStmtClass, |
| 1313 | .GotoStmtClass, | 1317 | .GotoStmtClass, |
| ... | @@ -1969,7 +1973,7 @@ fn transBuiltinFnExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: | ... | @@ -1969,7 +1973,7 @@ fn transBuiltinFnExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: |
| 1969 | const node = try transExpr(c, scope, expr, used); | 1973 | const node = try transExpr(c, scope, expr, used); |
| 1970 | if (node.castTag(.identifier)) |ident| { | 1974 | if (node.castTag(.identifier)) |ident| { |
| 1971 | const name = ident.data; | 1975 | const name = ident.data; |
| 1972 | if (!isBuiltinDefined(name)) return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "TODO implement function '{s}' in std.c.builtins", .{name}); | 1976 | if (!isBuiltinDefined(name)) return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "TODO implement function '{s}' in std.zig.c_builtins", .{name}); |
| 1973 | } | 1977 | } |
| 1974 | return node; | 1978 | return node; |
| 1975 | } | 1979 | } |
| ... | @@ -5574,7 +5578,7 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N | ... | @@ -5574,7 +5578,7 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N |
| 5574 | .Identifier => { | 5578 | .Identifier => { |
| 5575 | const mangled_name = scope.getAlias(slice); | 5579 | const mangled_name = scope.getAlias(slice); |
| 5576 | if (mem.startsWith(u8, mangled_name, "__builtin_") and !isBuiltinDefined(mangled_name)) { | 5580 | if (mem.startsWith(u8, mangled_name, "__builtin_") and !isBuiltinDefined(mangled_name)) { |
| 5577 | try m.fail(c, "TODO implement function '{s}' in std.c.builtins", .{mangled_name}); | 5581 | try m.fail(c, "TODO implement function '{s}' in std.zig.c_builtins", .{mangled_name}); |
| 5578 | return error.ParseError; | 5582 | return error.ParseError; |
| 5579 | } | 5583 | } |
| 5580 | const identifier = try Tag.identifier.create(c.arena, builtin_typedef_map.get(mangled_name) orelse mangled_name); | 5584 | const identifier = try Tag.identifier.create(c.arena, builtin_typedef_map.get(mangled_name) orelse mangled_name); |
src/zig_clang.cpp+5| ... | @@ -2832,6 +2832,11 @@ unsigned ZigClangCharacterLiteral_getValue(const struct ZigClangCharacterLiteral | ... | @@ -2832,6 +2832,11 @@ unsigned ZigClangCharacterLiteral_getValue(const struct ZigClangCharacterLiteral |
| 2832 | return casted->getValue(); | 2832 | return casted->getValue(); |
| 2833 | } | 2833 | } |
| 2834 | 2834 | ||
| 2835 | const struct ZigClangExpr *ZigClangChooseExpr_getChosenSubExpr(const struct ZigClangChooseExpr *self) { | ||
| 2836 | auto casted = reinterpret_cast<const clang::ChooseExpr *>(self); | ||
| 2837 | return reinterpret_cast<const ZigClangExpr *>(casted->getChosenSubExpr()); | ||
| 2838 | } | ||
| 2839 | |||
| 2835 | const struct ZigClangExpr *ZigClangAbstractConditionalOperator_getCond(const struct ZigClangAbstractConditionalOperator *self) { | 2840 | const struct ZigClangExpr *ZigClangAbstractConditionalOperator_getCond(const struct ZigClangAbstractConditionalOperator *self) { |
| 2836 | auto casted = reinterpret_cast<const clang::AbstractConditionalOperator *>(self); | 2841 | auto casted = reinterpret_cast<const clang::AbstractConditionalOperator *>(self); |
| 2837 | return reinterpret_cast<const struct ZigClangExpr *>(casted->getCond()); | 2842 | return reinterpret_cast<const struct ZigClangExpr *>(casted->getCond()); |
src/zig_clang.h+3| ... | @@ -104,6 +104,7 @@ struct ZigClangCStyleCastExpr; | ... | @@ -104,6 +104,7 @@ struct ZigClangCStyleCastExpr; |
| 104 | struct ZigClangCallExpr; | 104 | struct ZigClangCallExpr; |
| 105 | struct ZigClangCaseStmt; | 105 | struct ZigClangCaseStmt; |
| 106 | struct ZigClangCharacterLiteral; | 106 | struct ZigClangCharacterLiteral; |
| 107 | struct ZigClangChooseExpr; | ||
| 107 | struct ZigClangCompoundAssignOperator; | 108 | struct ZigClangCompoundAssignOperator; |
| 108 | struct ZigClangCompoundStmt; | 109 | struct ZigClangCompoundStmt; |
| 109 | struct ZigClangConditionalOperator; | 110 | struct ZigClangConditionalOperator; |
| ... | @@ -1242,6 +1243,8 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangCharacterLiteral_getBeginLoc( | ... | @@ -1242,6 +1243,8 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangCharacterLiteral_getBeginLoc( |
| 1242 | ZIG_EXTERN_C enum ZigClangCharacterLiteral_CharacterKind ZigClangCharacterLiteral_getKind(const struct ZigClangCharacterLiteral *); | 1243 | ZIG_EXTERN_C enum ZigClangCharacterLiteral_CharacterKind ZigClangCharacterLiteral_getKind(const struct ZigClangCharacterLiteral *); |
| 1243 | ZIG_EXTERN_C unsigned ZigClangCharacterLiteral_getValue(const struct ZigClangCharacterLiteral *); | 1244 | ZIG_EXTERN_C unsigned ZigClangCharacterLiteral_getValue(const struct ZigClangCharacterLiteral *); |
| 1244 | 1245 | ||
| 1246 | ZIG_EXTERN_C const struct ZigClangExpr *ZigClangChooseExpr_getChosenSubExpr(const struct ZigClangChooseExpr *); | ||
| 1247 | |||
| 1245 | ZIG_EXTERN_C const struct ZigClangExpr *ZigClangAbstractConditionalOperator_getCond(const struct ZigClangAbstractConditionalOperator *); | 1248 | ZIG_EXTERN_C const struct ZigClangExpr *ZigClangAbstractConditionalOperator_getCond(const struct ZigClangAbstractConditionalOperator *); |
| 1246 | ZIG_EXTERN_C const struct ZigClangExpr *ZigClangAbstractConditionalOperator_getTrueExpr(const struct ZigClangAbstractConditionalOperator *); | 1249 | ZIG_EXTERN_C const struct ZigClangExpr *ZigClangAbstractConditionalOperator_getTrueExpr(const struct ZigClangAbstractConditionalOperator *); |
| 1247 | ZIG_EXTERN_C const struct ZigClangExpr *ZigClangAbstractConditionalOperator_getFalseExpr(const struct ZigClangAbstractConditionalOperator *); | 1250 | ZIG_EXTERN_C const struct ZigClangExpr *ZigClangAbstractConditionalOperator_getFalseExpr(const struct ZigClangAbstractConditionalOperator *); |
test/run_translated_c.zig+51-1| ... | @@ -1243,7 +1243,7 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { | ... | @@ -1243,7 +1243,7 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { |
| 1243 | \\} | 1243 | \\} |
| 1244 | , ""); | 1244 | , ""); |
| 1245 | 1245 | ||
| 1246 | // See __builtin_alloca_with_align comment in std.c.builtins | 1246 | // See __builtin_alloca_with_align comment in std.zig.c_builtins |
| 1247 | cases.add("use of unimplemented builtin in unused function does not prevent compilation", | 1247 | cases.add("use of unimplemented builtin in unused function does not prevent compilation", |
| 1248 | \\#include <stdlib.h> | 1248 | \\#include <stdlib.h> |
| 1249 | \\void unused() { | 1249 | \\void unused() { |
| ... | @@ -1659,4 +1659,54 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { | ... | @@ -1659,4 +1659,54 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { |
| 1659 | \\ return 0; | 1659 | \\ return 0; |
| 1660 | \\} | 1660 | \\} |
| 1661 | , ""); | 1661 | , ""); |
| 1662 | |||
| 1663 | cases.add("__builtin_choose_expr (unchosen expression is not evaluated)", | ||
| 1664 | \\#include <stdlib.h> | ||
| 1665 | \\int main(void) { | ||
| 1666 | \\ int x = 0.0; | ||
| 1667 | \\ int y = 0.0; | ||
| 1668 | \\ int res; | ||
| 1669 | \\ res = __builtin_choose_expr(1, 1, x / y); | ||
| 1670 | \\ if (res != 1) abort(); | ||
| 1671 | \\ res = __builtin_choose_expr(0, x / y, 2); | ||
| 1672 | \\ if (res != 2) abort(); | ||
| 1673 | \\ return 0; | ||
| 1674 | \\} | ||
| 1675 | , ""); | ||
| 1676 | |||
| 1677 | // TODO: add isnan check for long double once bitfield support is added | ||
| 1678 | // (needed for x86_64-windows-gnu) | ||
| 1679 | // TODO: add isinf check for long double once std.math.isInf supports c_longdouble | ||
| 1680 | cases.add("NAN and INFINITY", | ||
| 1681 | \\#include <math.h> | ||
| 1682 | \\#include <stdint.h> | ||
| 1683 | \\#include <stdlib.h> | ||
| 1684 | \\union uf { uint32_t u; float f; }; | ||
| 1685 | \\#define CHECK_NAN(STR, VAL) { \ | ||
| 1686 | \\ union uf unpack = {.f = __builtin_nanf(STR)}; \ | ||
| 1687 | \\ if (!isnan(unpack.f)) abort(); \ | ||
| 1688 | \\ if (unpack.u != VAL) abort(); \ | ||
| 1689 | \\} | ||
| 1690 | \\int main(void) { | ||
| 1691 | \\ float f_nan = NAN; | ||
| 1692 | \\ if (!isnan(f_nan)) abort(); | ||
| 1693 | \\ double d_nan = NAN; | ||
| 1694 | \\ if (!isnan(d_nan)) abort(); | ||
| 1695 | \\ CHECK_NAN("0", 0x7FC00000); | ||
| 1696 | \\ CHECK_NAN("", 0x7FC00000); | ||
| 1697 | \\ CHECK_NAN("1", 0x7FC00001); | ||
| 1698 | \\ CHECK_NAN("0x7FC00000", 0x7FC00000); | ||
| 1699 | \\ CHECK_NAN("0x7FC0000F", 0x7FC0000F); | ||
| 1700 | \\ CHECK_NAN("0x7FC000F0", 0x7FC000F0); | ||
| 1701 | \\ CHECK_NAN("0x7FC00F00", 0x7FC00F00); | ||
| 1702 | \\ CHECK_NAN("0x7FC0F000", 0x7FC0F000); | ||
| 1703 | \\ CHECK_NAN("0x7FCF0000", 0x7FCF0000); | ||
| 1704 | \\ CHECK_NAN("0xFFFFFFFF", 0x7FFFFFFF); | ||
| 1705 | \\ float f_inf = INFINITY; | ||
| 1706 | \\ if (!isinf(f_inf)) abort(); | ||
| 1707 | \\ double d_inf = INFINITY; | ||
| 1708 | \\ if (!isinf(d_inf)) abort(); | ||
| 1709 | \\ return 0; | ||
| 1710 | \\} | ||
| 1711 | , ""); | ||
| 1662 | } | 1712 | } |
test/translate_c.zig+2-2| ... | @@ -3461,11 +3461,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -3461,11 +3461,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 3461 | \\pub const MAY_NEED_PROMOTION_OCT = @import("std").zig.c_translation.promoteIntLiteral(c_int, 0o20000000000, .octal); | 3461 | \\pub const MAY_NEED_PROMOTION_OCT = @import("std").zig.c_translation.promoteIntLiteral(c_int, 0o20000000000, .octal); |
| 3462 | }); | 3462 | }); |
| 3463 | 3463 | ||
| 3464 | // See __builtin_alloca_with_align comment in std.c.builtins | 3464 | // See __builtin_alloca_with_align comment in std.zig.c_builtins |
| 3465 | cases.add("demote un-implemented builtins", | 3465 | cases.add("demote un-implemented builtins", |
| 3466 | \\#define FOO(X) __builtin_alloca_with_align((X), 8) | 3466 | \\#define FOO(X) __builtin_alloca_with_align((X), 8) |
| 3467 | , &[_][]const u8{ | 3467 | , &[_][]const u8{ |
| 3468 | \\pub const FOO = @compileError("TODO implement function '__builtin_alloca_with_align' in std.c.builtins"); | 3468 | \\pub const FOO = @compileError("TODO implement function '__builtin_alloca_with_align' in std.zig.c_builtins"); |
| 3469 | }); | 3469 | }); |
| 3470 | 3470 | ||
| 3471 | cases.add("null sentinel arrays when initialized from string literal. Issue #8256", | 3471 | cases.add("null sentinel arrays when initialized from string literal. Issue #8256", |