Commit aa18f65a by Eric Botcazou Committed by Eric Botcazou

trans.c (build_binary_op_trapv): If no operand is a constant, use the generic…

trans.c (build_binary_op_trapv): If no operand is a constant, use the generic implementation of the middle-end...

	* gcc-interface/trans.c (build_binary_op_trapv): If no operand is a
	constant, use the generic implementation of the middle-end; otherwise
	turn the dynamic conditions into static conditions and simplify.

From-SVN: r237328
parent a31d78c6
2016-06-11 Eric Botcazou <ebotcazou@adacore.com> 2016-06-11 Eric Botcazou <ebotcazou@adacore.com>
* gcc-interface/trans.c (build_binary_op_trapv): If no operand is a
constant, use the generic implementation of the middle-end; otherwise
turn the dynamic conditions into static conditions and simplify.
2016-06-11 Eric Botcazou <ebotcazou@adacore.com>
* gcc-interface/trans.c (Case_Statement_to_gnu): Deal with characters. * gcc-interface/trans.c (Case_Statement_to_gnu): Deal with characters.
2016-06-11 Pierre-Marie de Rodat <derodat@adacore.com> 2016-06-11 Pierre-Marie de Rodat <derodat@adacore.com>
......
...@@ -8875,19 +8875,16 @@ build_binary_op_trapv (enum tree_code code, tree gnu_type, tree left, ...@@ -8875,19 +8875,16 @@ build_binary_op_trapv (enum tree_code code, tree gnu_type, tree left,
tree rhs = gnat_protect_expr (right); tree rhs = gnat_protect_expr (right);
tree type_max = TYPE_MAX_VALUE (gnu_type); tree type_max = TYPE_MAX_VALUE (gnu_type);
tree type_min = TYPE_MIN_VALUE (gnu_type); tree type_min = TYPE_MIN_VALUE (gnu_type);
tree zero = build_int_cst (gnu_type, 0); tree gnu_expr, check;
tree gnu_expr, rhs_lt_zero, tmp1, tmp2; int sgn;
tree check_pos, check_neg, check;
/* Assert that the precision is a power of 2. */ /* Assert that the precision is a power of 2. */
gcc_assert ((precision & (precision - 1)) == 0); gcc_assert ((precision & (precision - 1)) == 0);
/* Prefer a constant or known-positive rhs to simplify checks. */ /* Prefer a constant on the RHS to simplify checks. */
if (!TREE_CONSTANT (rhs) if (TREE_CODE (rhs) != INTEGER_CST
&& commutative_tree_code (code) && TREE_CODE (lhs) == INTEGER_CST
&& (TREE_CONSTANT (lhs) && (code == PLUS_EXPR || code == MULT_EXPR))
|| (!tree_expr_nonnegative_p (rhs)
&& tree_expr_nonnegative_p (lhs))))
{ {
tree tmp = lhs; tree tmp = lhs;
lhs = rhs; lhs = rhs;
...@@ -8898,151 +8895,149 @@ build_binary_op_trapv (enum tree_code code, tree gnu_type, tree left, ...@@ -8898,151 +8895,149 @@ build_binary_op_trapv (enum tree_code code, tree gnu_type, tree left,
/* If we can fold the expression to a constant, just return it. /* If we can fold the expression to a constant, just return it.
The caller will deal with overflow, no need to generate a check. */ The caller will deal with overflow, no need to generate a check. */
if (TREE_CONSTANT (gnu_expr)) if (TREE_CODE (gnu_expr) == INTEGER_CST)
return gnu_expr; return gnu_expr;
rhs_lt_zero = tree_expr_nonnegative_p (rhs) /* If no operand is a constant, we use the generic implementation. */
? boolean_false_node if (TREE_CODE (lhs) != INTEGER_CST && TREE_CODE (rhs) != INTEGER_CST)
: build_binary_op (LT_EXPR, boolean_type_node, rhs, zero);
/* ??? Should use more efficient check for operand_equal_p (lhs, rhs, 0) */
/* Try a few strategies that may be cheaper than the general
code at the end of the function, if the rhs is not known.
The strategies are:
- Call library function for 64-bit multiplication (complex)
- Widen, if input arguments are sufficiently small
- Determine overflow using wrapped result for addition/subtraction. */
if (!TREE_CONSTANT (rhs))
{ {
/* Even for add/subtract double size to get another base type. */ /* Never inline a 64-bit mult for a 32-bit target, it's way too long. */
const unsigned int needed_precision = precision * 2; if (code == MULT_EXPR && precision == 64 && BITS_PER_WORD < 64)
if (code == MULT_EXPR && precision == 64)
{ {
tree int_64 = gnat_type_for_size (64, 0); tree int64 = gnat_type_for_size (64, 0);
return convert (gnu_type, build_call_n_expr (mulv64_decl, 2, return convert (gnu_type, build_call_n_expr (mulv64_decl, 2,
convert (int_64, lhs), convert (int64, lhs),
convert (int_64, rhs))); convert (int64, rhs)));
} }
if (needed_precision <= BITS_PER_WORD enum internal_fn icode;
|| (code == MULT_EXPR && needed_precision <= LONG_LONG_TYPE_SIZE))
{
tree wide_type = gnat_type_for_size (needed_precision, 0);
tree wide_result = build_binary_op (code, wide_type,
convert (wide_type, lhs),
convert (wide_type, rhs));
check = build_binary_op
(TRUTH_ORIF_EXPR, boolean_type_node,
build_binary_op (LT_EXPR, boolean_type_node, wide_result,
convert (wide_type, type_min)),
build_binary_op (GT_EXPR, boolean_type_node, wide_result,
convert (wide_type, type_max)));
return switch (code)
emit_check (check, gnu_expr, CE_Overflow_Check_Failed, gnat_node);
}
if (code == PLUS_EXPR || code == MINUS_EXPR)
{ {
tree unsigned_type = gnat_type_for_size (precision, 1); case PLUS_EXPR:
tree wrapped_expr icode = IFN_ADD_OVERFLOW;
= convert (gnu_type, break;
build_binary_op (code, unsigned_type, case MINUS_EXPR:
convert (unsigned_type, lhs), icode = IFN_SUB_OVERFLOW;
convert (unsigned_type, rhs))); break;
case MULT_EXPR:
/* Overflow when (rhs < 0) ^ (wrapped_expr < lhs)), for addition icode = IFN_MUL_OVERFLOW;
or when (rhs < 0) ^ (wrapped_expr > lhs) for subtraction. */ break;
check default:
= build_binary_op (TRUTH_XOR_EXPR, boolean_type_node, rhs_lt_zero, gcc_unreachable ();
build_binary_op (code == PLUS_EXPR
? LT_EXPR : GT_EXPR,
boolean_type_node,
wrapped_expr, lhs));
return
emit_check (check, gnu_expr, CE_Overflow_Check_Failed, gnat_node);
} }
tree gnu_ctype = build_complex_type (gnu_type);
tree call
= build_call_expr_internal_loc (UNKNOWN_LOCATION, icode, gnu_ctype, 2,
lhs, rhs);
tree tgt = save_expr (call);
gnu_expr = build1 (REALPART_EXPR, gnu_type, tgt);
check
= convert (boolean_type_node, build1 (IMAGPART_EXPR, gnu_type, tgt));
return
emit_check (check, gnu_expr, CE_Overflow_Check_Failed, gnat_node);
} }
/* If one operand is a constant, we expose the overflow condition to enable
a subsequent simplication or even elimination. */
switch (code) switch (code)
{ {
case PLUS_EXPR: case PLUS_EXPR:
/* When rhs >= 0, overflow when lhs > type_max - rhs. */ sgn = tree_int_cst_sgn (rhs);
check_pos = build_binary_op (GT_EXPR, boolean_type_node, lhs, if (sgn > 0)
build_binary_op (MINUS_EXPR, gnu_type, /* When rhs > 0, overflow when lhs > type_max - rhs. */
type_max, rhs)), check = build_binary_op (GT_EXPR, boolean_type_node, lhs,
build_binary_op (MINUS_EXPR, gnu_type,
/* When rhs < 0, overflow when lhs < type_min - rhs. */ type_max, rhs));
check_neg = build_binary_op (LT_EXPR, boolean_type_node, lhs, else if (sgn < 0)
build_binary_op (MINUS_EXPR, gnu_type, /* When rhs < 0, overflow when lhs < type_min - rhs. */
type_min, rhs)); check = build_binary_op (LT_EXPR, boolean_type_node, lhs,
build_binary_op (MINUS_EXPR, gnu_type,
type_min, rhs));
else
return gnu_expr;
break; break;
case MINUS_EXPR: case MINUS_EXPR:
/* When rhs >= 0, overflow when lhs < type_min + rhs. */ if (TREE_CODE (lhs) == INTEGER_CST)
check_pos = build_binary_op (LT_EXPR, boolean_type_node, lhs, {
build_binary_op (PLUS_EXPR, gnu_type, sgn = tree_int_cst_sgn (lhs);
type_min, rhs)), if (sgn > 0)
/* When lhs > 0, overflow when rhs < lhs - type_max. */
/* When rhs < 0, overflow when lhs > type_max + rhs. */ check = build_binary_op (LT_EXPR, boolean_type_node, rhs,
check_neg = build_binary_op (GT_EXPR, boolean_type_node, lhs, build_binary_op (MINUS_EXPR, gnu_type,
build_binary_op (PLUS_EXPR, gnu_type, lhs, type_max));
type_max, rhs)); else if (sgn < 0)
/* When lhs < 0, overflow when rhs > lhs - type_min. */
check = build_binary_op (GT_EXPR, boolean_type_node, rhs,
build_binary_op (MINUS_EXPR, gnu_type,
lhs, type_min));
else
return gnu_expr;
}
else
{
sgn = tree_int_cst_sgn (rhs);
if (sgn > 0)
/* When rhs > 0, overflow when lhs < type_min + rhs. */
check = build_binary_op (LT_EXPR, boolean_type_node, lhs,
build_binary_op (PLUS_EXPR, gnu_type,
type_min, rhs));
else if (sgn < 0)
/* When rhs < 0, overflow when lhs > type_max + rhs. */
check = build_binary_op (GT_EXPR, boolean_type_node, lhs,
build_binary_op (PLUS_EXPR, gnu_type,
type_max, rhs));
else
return gnu_expr;
}
break; break;
case MULT_EXPR: case MULT_EXPR:
/* The check here is designed to be efficient if the rhs is constant, sgn = tree_int_cst_sgn (rhs);
but it will work for any rhs by using integer division. if (sgn > 0)
Four different check expressions determine whether X * C overflows, {
depending on C. if (integer_onep (rhs))
C == 0 => false return gnu_expr;
C > 0 => X > type_max / C || X < type_min / C
C == -1 => X == type_min tree lb = build_binary_op (TRUNC_DIV_EXPR, gnu_type, type_min, rhs);
C < -1 => X > type_min / C || X < type_max / C */ tree ub = build_binary_op (TRUNC_DIV_EXPR, gnu_type, type_max, rhs);
tmp1 = build_binary_op (TRUNC_DIV_EXPR, gnu_type, type_max, rhs); /* When rhs > 1, overflow outside [type_min/rhs; type_max/rhs]. */
tmp2 = build_binary_op (TRUNC_DIV_EXPR, gnu_type, type_min, rhs); check
= build_binary_op (TRUTH_ORIF_EXPR, boolean_type_node,
check_pos build_binary_op (LT_EXPR, boolean_type_node,
= build_binary_op (TRUTH_ANDIF_EXPR, boolean_type_node, lhs, lb),
build_binary_op (NE_EXPR, boolean_type_node, zero, build_binary_op (GT_EXPR, boolean_type_node,
rhs), lhs, ub));
build_binary_op (TRUTH_ORIF_EXPR, boolean_type_node, }
build_binary_op (GT_EXPR, else if (sgn < 0)
boolean_type_node, {
lhs, tmp1), tree lb = build_binary_op (TRUNC_DIV_EXPR, gnu_type, type_max, rhs);
build_binary_op (LT_EXPR, tree ub = build_binary_op (TRUNC_DIV_EXPR, gnu_type, type_min, rhs);
boolean_type_node,
lhs, tmp2))); if (integer_minus_onep (rhs))
/* When rhs == -1, overflow if lhs == type_min. */
check_neg check
= fold_build3 (COND_EXPR, boolean_type_node, = build_binary_op (EQ_EXPR, boolean_type_node, lhs, type_min);
build_binary_op (EQ_EXPR, boolean_type_node, rhs, else
build_int_cst (gnu_type, -1)), /* When rhs < -1, overflow outside [type_max/rhs; type_min/rhs]. */
build_binary_op (EQ_EXPR, boolean_type_node, lhs, check
type_min), = build_binary_op (TRUTH_ORIF_EXPR, boolean_type_node,
build_binary_op (TRUTH_ORIF_EXPR, boolean_type_node, build_binary_op (LT_EXPR, boolean_type_node,
build_binary_op (GT_EXPR, lhs, lb),
boolean_type_node, build_binary_op (GT_EXPR, boolean_type_node,
lhs, tmp2), lhs, ub));
build_binary_op (LT_EXPR, }
boolean_type_node, else
lhs, tmp1))); return gnu_expr;
break; break;
default: default:
gcc_unreachable (); gcc_unreachable ();
} }
check = fold_build3 (COND_EXPR, boolean_type_node, rhs_lt_zero, check_neg,
check_pos);
return emit_check (check, gnu_expr, CE_Overflow_Check_Failed, gnat_node); return emit_check (check, gnu_expr, CE_Overflow_Check_Failed, gnat_node);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment