@@ -851,6 +851,8 @@
expr = save_expr (expr);
tree check = ubsan_instrument_float_cast (loc, type, expr);
expr = build1 (FIX_TRUNC_EXPR, type, expr);
+ if (check == NULL)
+ return expr;
return fold_build2 (COMPOUND_EXPR, TREE_TYPE (expr), check, expr);
}
else
@@ -903,17 +903,62 @@
ubsan_instrument_float_cast (location_t loc, tree type, tree expr)
{
tree expr_type = TREE_TYPE (expr);
- tree t, tt, fn;
+ 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);
- tree min = TYPE_MIN_VALUE (type);
- tree max = TYPE_MAX_VALUE (type);
- /* Add/subtract 1.0 so we can avoid truncating the value of EXPR. */
- min = fold_build2 (MINUS_EXPR, expr_type,
- build_real_from_int_cst (expr_type, min),
- build_one_cst (expr_type));
- max = fold_build2 (PLUS_EXPR, expr_type,
- build_real_from_int_cst (expr_type, max),
- build_one_cst (expr_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);
+ }
+ }
+ /* Only binary floating point supported right now. */
+ else
+ return NULL_TREE;
if (flag_sanitize_undefined_trap_on_error)
fn = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);