static enum fixed_value_range_code check_real_for_fixed_mode (REAL_VALUE_TYPE *real_value, machine_mode mode) { REAL_VALUE_TYPE max_value, min_value, epsilon_value; real_2expN (&max_value, GET_MODE_IBIT (mode), VOIDmode); real_2expN (&epsilon_value, -GET_MODE_FBIT (mode), VOIDmode); if (SIGNED_FIXED_POINT_MODE_P (mode)) min_value = real_value_negate (&max_value); else real_from_string (&min_value, "0.0"); if (real_compare (LT_EXPR, real_value, &min_value)) return FIXED_UNDERFLOW; if (real_compare (EQ_EXPR, real_value, &max_value)) return FIXED_MAX_EPS; real_arithmetic (&max_value, MINUS_EXPR, &max_value, &epsilon_value); if (real_compare (GT_EXPR, real_value, &max_value)) return FIXED_GT_MAX_EPS; return FIXED_OK; }
static bool fold_const_call_sss (real_value *result, built_in_function fn, const wide_int_ref &arg0, const real_value *arg1, const real_format *format) { switch (fn) { CASE_FLT_FN (BUILT_IN_JN): return do_mpfr_arg2 (result, mpfr_jn, arg0, arg1, format); CASE_FLT_FN (BUILT_IN_YN): return (real_compare (GT_EXPR, arg1, &dconst0) && do_mpfr_arg2 (result, mpfr_yn, arg0, arg1, format)); default: return false; } }
static bool fold_const_call_sss (real_value *result, combined_fn fn, const wide_int_ref &arg0, const real_value *arg1, const real_format *format) { switch (fn) { CASE_CFN_JN: return do_mpfr_arg2 (result, mpfr_jn, arg0, arg1, format); CASE_CFN_YN: return (real_compare (GT_EXPR, arg1, &dconst0) && do_mpfr_arg2 (result, mpfr_yn, arg0, arg1, format)); default: return false; } }
static bool fold_const_call_ss (real_value *result, built_in_function fn, const real_value *arg, const real_format *format) { switch (fn) { CASE_FLT_FN (BUILT_IN_SQRT): return (real_compare (GE_EXPR, arg, &dconst0) && do_mpfr_arg1 (result, mpfr_sqrt, arg, format)); CASE_FLT_FN (BUILT_IN_CBRT): return do_mpfr_arg1 (result, mpfr_cbrt, arg, format); CASE_FLT_FN (BUILT_IN_ASIN): return (real_compare (GE_EXPR, arg, &dconstm1) && real_compare (LE_EXPR, arg, &dconst1) && do_mpfr_arg1 (result, mpfr_asin, arg, format)); CASE_FLT_FN (BUILT_IN_ACOS): return (real_compare (GE_EXPR, arg, &dconstm1) && real_compare (LE_EXPR, arg, &dconst1) && do_mpfr_arg1 (result, mpfr_acos, arg, format)); CASE_FLT_FN (BUILT_IN_ATAN): return do_mpfr_arg1 (result, mpfr_atan, arg, format); CASE_FLT_FN (BUILT_IN_ASINH): return do_mpfr_arg1 (result, mpfr_asinh, arg, format); CASE_FLT_FN (BUILT_IN_ACOSH): return (real_compare (GE_EXPR, arg, &dconst1) && do_mpfr_arg1 (result, mpfr_acosh, arg, format)); CASE_FLT_FN (BUILT_IN_ATANH): return (real_compare (GE_EXPR, arg, &dconstm1) && real_compare (LE_EXPR, arg, &dconst1) && do_mpfr_arg1 (result, mpfr_atanh, arg, format)); CASE_FLT_FN (BUILT_IN_SIN): return do_mpfr_arg1 (result, mpfr_sin, arg, format); CASE_FLT_FN (BUILT_IN_COS): return do_mpfr_arg1 (result, mpfr_cos, arg, format); CASE_FLT_FN (BUILT_IN_TAN): return do_mpfr_arg1 (result, mpfr_tan, arg, format); CASE_FLT_FN (BUILT_IN_SINH): return do_mpfr_arg1 (result, mpfr_sinh, arg, format); CASE_FLT_FN (BUILT_IN_COSH): return do_mpfr_arg1 (result, mpfr_cosh, arg, format); CASE_FLT_FN (BUILT_IN_TANH): return do_mpfr_arg1 (result, mpfr_tanh, arg, format); CASE_FLT_FN (BUILT_IN_ERF): return do_mpfr_arg1 (result, mpfr_erf, arg, format); CASE_FLT_FN (BUILT_IN_ERFC): return do_mpfr_arg1 (result, mpfr_erfc, arg, format); CASE_FLT_FN (BUILT_IN_TGAMMA): return do_mpfr_arg1 (result, mpfr_gamma, arg, format); CASE_FLT_FN (BUILT_IN_EXP): return do_mpfr_arg1 (result, mpfr_exp, arg, format); CASE_FLT_FN (BUILT_IN_EXP2): return do_mpfr_arg1 (result, mpfr_exp2, arg, format); CASE_FLT_FN (BUILT_IN_EXP10): CASE_FLT_FN (BUILT_IN_POW10): return do_mpfr_arg1 (result, mpfr_exp10, arg, format); CASE_FLT_FN (BUILT_IN_EXPM1): return do_mpfr_arg1 (result, mpfr_expm1, arg, format); CASE_FLT_FN (BUILT_IN_LOG): return (real_compare (GT_EXPR, arg, &dconst0) && do_mpfr_arg1 (result, mpfr_log, arg, format)); CASE_FLT_FN (BUILT_IN_LOG2): return (real_compare (GT_EXPR, arg, &dconst0) && do_mpfr_arg1 (result, mpfr_log2, arg, format)); CASE_FLT_FN (BUILT_IN_LOG10): return (real_compare (GT_EXPR, arg, &dconst0) && do_mpfr_arg1 (result, mpfr_log10, arg, format)); CASE_FLT_FN (BUILT_IN_LOG1P): return (real_compare (GT_EXPR, arg, &dconstm1) && do_mpfr_arg1 (result, mpfr_log1p, arg, format)); CASE_FLT_FN (BUILT_IN_J0): return do_mpfr_arg1 (result, mpfr_j0, arg, format); CASE_FLT_FN (BUILT_IN_J1): return do_mpfr_arg1 (result, mpfr_j1, arg, format); CASE_FLT_FN (BUILT_IN_Y0): return (real_compare (GT_EXPR, arg, &dconst0) && do_mpfr_arg1 (result, mpfr_y0, arg, format)); CASE_FLT_FN (BUILT_IN_Y1): return (real_compare (GT_EXPR, arg, &dconst0) && do_mpfr_arg1 (result, mpfr_y1, arg, format)); CASE_FLT_FN (BUILT_IN_FLOOR): if (!REAL_VALUE_ISNAN (*arg) || !flag_errno_math) { real_floor (result, format, arg); return true; } return false; CASE_FLT_FN (BUILT_IN_CEIL): if (!REAL_VALUE_ISNAN (*arg) || !flag_errno_math) { real_ceil (result, format, arg); return true; } return false; CASE_FLT_FN (BUILT_IN_TRUNC): real_trunc (result, format, arg); return true; CASE_FLT_FN (BUILT_IN_ROUND): if (!REAL_VALUE_ISNAN (*arg) || !flag_errno_math) { real_round (result, format, arg); return true; } return false; CASE_FLT_FN (BUILT_IN_LOGB): return fold_const_logb (result, arg, format); CASE_FLT_FN (BUILT_IN_SIGNIFICAND): return fold_const_significand (result, arg, format); default: return false; } }
tree ubsan_instrument_float_cast (location_t loc, tree type, tree expr) { tree expr_type = TREE_TYPE (expr); tree t, tt, fn, min, max; enum machine_mode mode = TYPE_MODE (expr_type); int prec = TYPE_PRECISION (type); bool uns_p = TYPE_UNSIGNED (type); /* Float to integer conversion first truncates toward zero, so even signed char c = 127.875f; is not problematic. Therefore, we should complain only if EXPR is unordered or smaller or equal than TYPE_MIN_VALUE - 1.0 or greater or equal than TYPE_MAX_VALUE + 1.0. */ if (REAL_MODE_FORMAT (mode)->b == 2) { /* For maximum, TYPE_MAX_VALUE might not be representable in EXPR_TYPE, e.g. if TYPE is 64-bit long long and EXPR_TYPE is IEEE single float, but TYPE_MAX_VALUE + 1.0 is either representable or infinity. */ REAL_VALUE_TYPE maxval = dconst1; SET_REAL_EXP (&maxval, REAL_EXP (&maxval) + prec - !uns_p); real_convert (&maxval, mode, &maxval); max = build_real (expr_type, maxval); /* For unsigned, assume -1.0 is always representable. */ if (uns_p) min = build_minus_one_cst (expr_type); else { /* TYPE_MIN_VALUE is generally representable (or -inf), but TYPE_MIN_VALUE - 1.0 might not be. */ REAL_VALUE_TYPE minval = dconstm1, minval2; SET_REAL_EXP (&minval, REAL_EXP (&minval) + prec - 1); real_convert (&minval, mode, &minval); real_arithmetic (&minval2, MINUS_EXPR, &minval, &dconst1); real_convert (&minval2, mode, &minval2); if (real_compare (EQ_EXPR, &minval, &minval2) && !real_isinf (&minval)) { /* If TYPE_MIN_VALUE - 1.0 is not representable and rounds to TYPE_MIN_VALUE, we need to subtract more. As REAL_MODE_FORMAT (mode)->p is the number of base digits, we want to subtract a number that will be 1 << (REAL_MODE_FORMAT (mode)->p - 1) times smaller than minval. */ minval2 = dconst1; gcc_assert (prec > REAL_MODE_FORMAT (mode)->p); SET_REAL_EXP (&minval2, REAL_EXP (&minval2) + prec - 1 - REAL_MODE_FORMAT (mode)->p + 1); real_arithmetic (&minval2, MINUS_EXPR, &minval, &minval2); real_convert (&minval2, mode, &minval2); } min = build_real (expr_type, minval2); } } else if (REAL_MODE_FORMAT (mode)->b == 10) { /* For _Decimal128 up to 34 decimal digits, - sign, dot, e, exponent. */ char buf[64]; mpfr_t m; int p = REAL_MODE_FORMAT (mode)->p; REAL_VALUE_TYPE maxval, minval; /* Use mpfr_snprintf rounding to compute the smallest representable decimal number greater or equal than 1 << (prec - !uns_p). */ mpfr_init2 (m, prec + 2); mpfr_set_ui_2exp (m, 1, prec - !uns_p, GMP_RNDN); mpfr_snprintf (buf, sizeof buf, "%.*RUe", p - 1, m); decimal_real_from_string (&maxval, buf); max = build_real (expr_type, maxval); /* For unsigned, assume -1.0 is always representable. */ if (uns_p) min = build_minus_one_cst (expr_type); else { /* Use mpfr_snprintf rounding to compute the largest representable decimal number less or equal than (-1 << (prec - 1)) - 1. */ mpfr_set_si_2exp (m, -1, prec - 1, GMP_RNDN); mpfr_sub_ui (m, m, 1, GMP_RNDN); mpfr_snprintf (buf, sizeof buf, "%.*RDe", p - 1, m); decimal_real_from_string (&minval, buf); min = build_real (expr_type, minval); } mpfr_clear (m); } else return NULL_TREE; if (flag_sanitize_undefined_trap_on_error) fn = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0); else { /* Create the __ubsan_handle_float_cast_overflow fn call. */ tree data = ubsan_create_data ("__ubsan_float_cast_overflow_data", NULL, NULL, ubsan_type_descriptor (expr_type), ubsan_type_descriptor (type), NULL_TREE); enum built_in_function bcode = flag_sanitize_recover ? BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW : BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT; fn = builtin_decl_explicit (bcode); fn = build_call_expr_loc (loc, fn, 2, build_fold_addr_expr_loc (loc, data), ubsan_encode_value (expr, false)); } t = fold_build2 (UNLE_EXPR, boolean_type_node, expr, min); tt = fold_build2 (UNGE_EXPR, boolean_type_node, expr, max); return fold_build3 (COND_EXPR, void_type_node, fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t, tt), fn, integer_zero_node); }
static bool fold_const_call_ss (real_value *result, combined_fn fn, const real_value *arg, const real_format *format) { switch (fn) { CASE_CFN_SQRT: return (real_compare (GE_EXPR, arg, &dconst0) && do_mpfr_arg1 (result, mpfr_sqrt, arg, format)); CASE_CFN_CBRT: return do_mpfr_arg1 (result, mpfr_cbrt, arg, format); CASE_CFN_ASIN: return (real_compare (GE_EXPR, arg, &dconstm1) && real_compare (LE_EXPR, arg, &dconst1) && do_mpfr_arg1 (result, mpfr_asin, arg, format)); CASE_CFN_ACOS: return (real_compare (GE_EXPR, arg, &dconstm1) && real_compare (LE_EXPR, arg, &dconst1) && do_mpfr_arg1 (result, mpfr_acos, arg, format)); CASE_CFN_ATAN: return do_mpfr_arg1 (result, mpfr_atan, arg, format); CASE_CFN_ASINH: return do_mpfr_arg1 (result, mpfr_asinh, arg, format); CASE_CFN_ACOSH: return (real_compare (GE_EXPR, arg, &dconst1) && do_mpfr_arg1 (result, mpfr_acosh, arg, format)); CASE_CFN_ATANH: return (real_compare (GE_EXPR, arg, &dconstm1) && real_compare (LE_EXPR, arg, &dconst1) && do_mpfr_arg1 (result, mpfr_atanh, arg, format)); CASE_CFN_SIN: return do_mpfr_arg1 (result, mpfr_sin, arg, format); CASE_CFN_COS: return do_mpfr_arg1 (result, mpfr_cos, arg, format); CASE_CFN_TAN: return do_mpfr_arg1 (result, mpfr_tan, arg, format); CASE_CFN_SINH: return do_mpfr_arg1 (result, mpfr_sinh, arg, format); CASE_CFN_COSH: return do_mpfr_arg1 (result, mpfr_cosh, arg, format); CASE_CFN_TANH: return do_mpfr_arg1 (result, mpfr_tanh, arg, format); CASE_CFN_ERF: return do_mpfr_arg1 (result, mpfr_erf, arg, format); CASE_CFN_ERFC: return do_mpfr_arg1 (result, mpfr_erfc, arg, format); CASE_CFN_TGAMMA: return do_mpfr_arg1 (result, mpfr_gamma, arg, format); CASE_CFN_EXP: return do_mpfr_arg1 (result, mpfr_exp, arg, format); CASE_CFN_EXP2: return do_mpfr_arg1 (result, mpfr_exp2, arg, format); CASE_CFN_EXP10: CASE_CFN_POW10: return do_mpfr_arg1 (result, mpfr_exp10, arg, format); CASE_CFN_EXPM1: return do_mpfr_arg1 (result, mpfr_expm1, arg, format); CASE_CFN_LOG: return (real_compare (GT_EXPR, arg, &dconst0) && do_mpfr_arg1 (result, mpfr_log, arg, format)); CASE_CFN_LOG2: return (real_compare (GT_EXPR, arg, &dconst0) && do_mpfr_arg1 (result, mpfr_log2, arg, format)); CASE_CFN_LOG10: return (real_compare (GT_EXPR, arg, &dconst0) && do_mpfr_arg1 (result, mpfr_log10, arg, format)); CASE_CFN_LOG1P: return (real_compare (GT_EXPR, arg, &dconstm1) && do_mpfr_arg1 (result, mpfr_log1p, arg, format)); CASE_CFN_J0: return do_mpfr_arg1 (result, mpfr_j0, arg, format); CASE_CFN_J1: return do_mpfr_arg1 (result, mpfr_j1, arg, format); CASE_CFN_Y0: return (real_compare (GT_EXPR, arg, &dconst0) && do_mpfr_arg1 (result, mpfr_y0, arg, format)); CASE_CFN_Y1: return (real_compare (GT_EXPR, arg, &dconst0) && do_mpfr_arg1 (result, mpfr_y1, arg, format)); CASE_CFN_FLOOR: if (!REAL_VALUE_ISNAN (*arg) || !flag_errno_math) { real_floor (result, format, arg); return true; } return false; CASE_CFN_CEIL: if (!REAL_VALUE_ISNAN (*arg) || !flag_errno_math) { real_ceil (result, format, arg); return true; } return false; CASE_CFN_TRUNC: real_trunc (result, format, arg); return true; CASE_CFN_ROUND: if (!REAL_VALUE_ISNAN (*arg) || !flag_errno_math) { real_round (result, format, arg); return true; } return false; CASE_CFN_LOGB: return fold_const_logb (result, arg, format); CASE_CFN_SIGNIFICAND: return fold_const_significand (result, arg, format); default: return false; } }