diff --git a/lib/std/zig/c_builtins.zig b/lib/std/zig/c_builtins.zig index afa3ae0a87..479f7a3b0e 100644 --- a/lib/std/zig/c_builtins.zig +++ b/lib/std/zig/c_builtins.zig @@ -191,6 +191,49 @@ pub inline fn __builtin_expect(expr: c_long, c: c_long) c_long { return expr; } +/// returns a quiet NaN. Quiet NaNs have many representations; tagp is used to select one in an +/// implementation-defined way. +/// This implementation is based on the description for __builtin_nan provided in the GCC docs at +/// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005fnan +/// Comment is reproduced below: +/// Since ISO C99 defines this function in terms of strtod, which we do not implement, a description +/// of the parsing is in order. +/// The string is parsed as by strtol; that is, the base is recognized by leading ‘0’ or ‘0x’ prefixes. +/// The number parsed is placed in the significand such that the least significant bit of the number is +/// at the least significant bit of the significand. +/// The number is truncated to fit the significand field provided. +/// The significand is forced to be a quiet NaN. +/// +/// If tagp contains any non-numeric characters, the function returns a NaN whose significand is zero. +/// If tagp is empty, the function returns a NaN whose significand is zero. +pub inline fn __builtin_nanf(tagp: []const u8) f32 { + const parsed = std.fmt.parseUnsigned(c_ulong, tagp, 0) catch 0; + const bits = @truncate(u23, parsed); // single-precision float trailing significand is 23 bits + return @bitCast(f32, @as(u32, bits) | std.math.qnan_u32); +} + +pub inline fn __builtin_huge_valf() f32 { + return std.math.inf(f32); +} + +pub inline fn __builtin_inff() f32 { + return std.math.inf(f32); +} + +pub inline fn __builtin_isnan(x: anytype) c_int { + return @boolToInt(std.math.isNan(x)); +} + +pub inline fn __builtin_isinf(x: anytype) c_int { + return @boolToInt(std.math.isInf(x)); +} + +/// Similar to isinf, except the return value is -1 for an argument of -Inf and 1 for an argument of +Inf. +pub inline fn __builtin_isinf_sign(x: anytype) c_int { + if (!std.math.isInf(x)) return 0; + return if (std.math.isPositiveInf(x)) 1 else -1; +} + // __builtin_alloca_with_align is not currently implemented. // It is used in a run-translated-c test and a test-translate-c test to ensure that non-implemented // builtins are correctly demoted. If you implement __builtin_alloca_with_align, please update the diff --git a/src/clang.zig b/src/clang.zig index 023d1aba4b..0632c5ab67 100644 --- a/src/clang.zig +++ b/src/clang.zig @@ -269,6 +269,11 @@ pub const CharacterLiteral = opaque { extern fn ZigClangCharacterLiteral_getValue(*const CharacterLiteral) c_uint; }; +pub const ChooseExpr = opaque { + pub const getChosenSubExpr = ZigClangChooseExpr_getChosenSubExpr; + extern fn ZigClangChooseExpr_getChosenSubExpr(*const ChooseExpr) *const Expr; +}; + pub const CompoundAssignOperator = opaque { pub const getType = ZigClangCompoundAssignOperator_getType; extern fn ZigClangCompoundAssignOperator_getType(*const CompoundAssignOperator) QualType; diff --git a/src/translate_c.zig b/src/translate_c.zig index 395a314f7c..fb40344b34 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -1308,6 +1308,10 @@ fn transStmt( const shuffle_vec_node = try transShuffleVectorExpr(c, scope, shuffle_vec_expr); return maybeSuppressResult(c, scope, result_used, shuffle_vec_node); }, + .ChooseExprClass => { + const choose_expr = @ptrCast(*const clang.ChooseExpr, stmt); + return transExpr(c, scope, choose_expr.getChosenSubExpr(), result_used); + }, // When adding new cases here, see comment for maybeBlockify() .GCCAsmStmtClass, .GotoStmtClass, @@ -1969,7 +1973,7 @@ fn transBuiltinFnExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: const node = try transExpr(c, scope, expr, used); if (node.castTag(.identifier)) |ident| { const name = ident.data; - if (!isBuiltinDefined(name)) return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "TODO implement function '{s}' in std.c.builtins", .{name}); + if (!isBuiltinDefined(name)) return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "TODO implement function '{s}' in std.zig.c_builtins", .{name}); } return node; } @@ -5574,7 +5578,7 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N .Identifier => { const mangled_name = scope.getAlias(slice); if (mem.startsWith(u8, mangled_name, "__builtin_") and !isBuiltinDefined(mangled_name)) { - try m.fail(c, "TODO implement function '{s}' in std.c.builtins", .{mangled_name}); + try m.fail(c, "TODO implement function '{s}' in std.zig.c_builtins", .{mangled_name}); return error.ParseError; } const identifier = try Tag.identifier.create(c.arena, builtin_typedef_map.get(mangled_name) orelse mangled_name); diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp index 7c923021d8..4fe1dfc286 100644 --- a/src/zig_clang.cpp +++ b/src/zig_clang.cpp @@ -2832,6 +2832,11 @@ unsigned ZigClangCharacterLiteral_getValue(const struct ZigClangCharacterLiteral return casted->getValue(); } +const struct ZigClangExpr *ZigClangChooseExpr_getChosenSubExpr(const struct ZigClangChooseExpr *self) { + auto casted = reinterpret_cast(self); + return reinterpret_cast(casted->getChosenSubExpr()); +} + const struct ZigClangExpr *ZigClangAbstractConditionalOperator_getCond(const struct ZigClangAbstractConditionalOperator *self) { auto casted = reinterpret_cast(self); return reinterpret_cast(casted->getCond()); diff --git a/src/zig_clang.h b/src/zig_clang.h index 634534dfb4..dc35df3772 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -104,6 +104,7 @@ struct ZigClangCStyleCastExpr; struct ZigClangCallExpr; struct ZigClangCaseStmt; struct ZigClangCharacterLiteral; +struct ZigClangChooseExpr; struct ZigClangCompoundAssignOperator; struct ZigClangCompoundStmt; struct ZigClangConditionalOperator; @@ -1242,6 +1243,8 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangCharacterLiteral_getBeginLoc( ZIG_EXTERN_C enum ZigClangCharacterLiteral_CharacterKind ZigClangCharacterLiteral_getKind(const struct ZigClangCharacterLiteral *); ZIG_EXTERN_C unsigned ZigClangCharacterLiteral_getValue(const struct ZigClangCharacterLiteral *); +ZIG_EXTERN_C const struct ZigClangExpr *ZigClangChooseExpr_getChosenSubExpr(const struct ZigClangChooseExpr *); + ZIG_EXTERN_C const struct ZigClangExpr *ZigClangAbstractConditionalOperator_getCond(const struct ZigClangAbstractConditionalOperator *); ZIG_EXTERN_C const struct ZigClangExpr *ZigClangAbstractConditionalOperator_getTrueExpr(const struct ZigClangAbstractConditionalOperator *); ZIG_EXTERN_C const struct ZigClangExpr *ZigClangAbstractConditionalOperator_getFalseExpr(const struct ZigClangAbstractConditionalOperator *); diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index 4953896636..dab200af0d 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -1243,7 +1243,7 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\} , ""); - // See __builtin_alloca_with_align comment in std.c.builtins + // See __builtin_alloca_with_align comment in std.zig.c_builtins cases.add("use of unimplemented builtin in unused function does not prevent compilation", \\#include \\void unused() { @@ -1659,4 +1659,54 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\ return 0; \\} , ""); + + cases.add("__builtin_choose_expr (unchosen expression is not evaluated)", + \\#include + \\int main(void) { + \\ int x = 0.0; + \\ int y = 0.0; + \\ int res; + \\ res = __builtin_choose_expr(1, 1, x / y); + \\ if (res != 1) abort(); + \\ res = __builtin_choose_expr(0, x / y, 2); + \\ if (res != 2) abort(); + \\ return 0; + \\} + , ""); + + // TODO: add isnan check for long double once bitfield support is added + // (needed for x86_64-windows-gnu) + // TODO: add isinf check for long double once std.math.isInf supports c_longdouble + cases.add("NAN and INFINITY", + \\#include + \\#include + \\#include + \\union uf { uint32_t u; float f; }; + \\#define CHECK_NAN(STR, VAL) { \ + \\ union uf unpack = {.f = __builtin_nanf(STR)}; \ + \\ if (!isnan(unpack.f)) abort(); \ + \\ if (unpack.u != VAL) abort(); \ + \\} + \\int main(void) { + \\ float f_nan = NAN; + \\ if (!isnan(f_nan)) abort(); + \\ double d_nan = NAN; + \\ if (!isnan(d_nan)) abort(); + \\ CHECK_NAN("0", 0x7FC00000); + \\ CHECK_NAN("", 0x7FC00000); + \\ CHECK_NAN("1", 0x7FC00001); + \\ CHECK_NAN("0x7FC00000", 0x7FC00000); + \\ CHECK_NAN("0x7FC0000F", 0x7FC0000F); + \\ CHECK_NAN("0x7FC000F0", 0x7FC000F0); + \\ CHECK_NAN("0x7FC00F00", 0x7FC00F00); + \\ CHECK_NAN("0x7FC0F000", 0x7FC0F000); + \\ CHECK_NAN("0x7FCF0000", 0x7FCF0000); + \\ CHECK_NAN("0xFFFFFFFF", 0x7FFFFFFF); + \\ float f_inf = INFINITY; + \\ if (!isinf(f_inf)) abort(); + \\ double d_inf = INFINITY; + \\ if (!isinf(d_inf)) abort(); + \\ return 0; + \\} + , ""); } diff --git a/test/translate_c.zig b/test/translate_c.zig index 1ceaf15e5c..b75d3b5cac 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -3461,11 +3461,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const MAY_NEED_PROMOTION_OCT = @import("std").zig.c_translation.promoteIntLiteral(c_int, 0o20000000000, .octal); }); - // See __builtin_alloca_with_align comment in std.c.builtins + // See __builtin_alloca_with_align comment in std.zig.c_builtins cases.add("demote un-implemented builtins", \\#define FOO(X) __builtin_alloca_with_align((X), 8) , &[_][]const u8{ - \\pub const FOO = @compileError("TODO implement function '__builtin_alloca_with_align' in std.c.builtins"); + \\pub const FOO = @compileError("TODO implement function '__builtin_alloca_with_align' in std.zig.c_builtins"); }); cases.add("null sentinel arrays when initialized from string literal. Issue #8256",