trans.c (build_binary_op_trapv): Use more efficient overflow check for addition/subtraction if...

* gcc-interface/trans.c (build_binary_op_trapv): Use more efficient
	overflow check for addition/subtraction if neither operand is constant.

From-SVN: r141665
This commit is contained in:
Geert Bosch 2008-11-07 09:32:43 +00:00 committed by Eric Botcazou
parent 218da97236
commit 4ae393838b
2 changed files with 74 additions and 30 deletions

View File

@ -1,3 +1,8 @@
2008-11-07 Geert Bosch <bosch@adacore.com>
* gcc-interface/trans.c (build_binary_op_trapv): Use more efficient
overflow check for addition/subtraction if neither operand is constant.
2008-11-06 Eric Botcazou <ebotcazou@adacore.com>
* gcc-interface/Makefile.in (SPARC/Solaris): Use a common set of

View File

@ -5998,59 +5998,94 @@ build_binary_op_trapv (enum tree_code code,
tree gnu_expr;
tree tmp1, tmp2;
tree zero = convert (gnu_type, integer_zero_node);
tree rhs_ge_zero;
tree rhs_lt_zero;
tree check_pos;
tree check_neg;
tree check;
int precision = TYPE_PRECISION (gnu_type);
/* Prefer a constant rhs to simplify checks */
gcc_assert (!(precision & (precision - 1))); /* ensure power of 2 */
if (TREE_CONSTANT (lhs) && !TREE_CONSTANT (rhs)
&& commutative_tree_code (code))
/* Prefer a constant or known-positive rhs to simplify checks */
if (!TREE_CONSTANT (rhs)
&& commutative_tree_code (code)
&& (TREE_CONSTANT (lhs) || (!tree_expr_nonnegative_p (rhs)
&& tree_expr_nonnegative_p (lhs))))
{
tree tmp = lhs;
lhs = rhs;
rhs = tmp;
}
/* In the case the right-hand size is still not constant, try to
use an exact operation in a wider type. */
rhs_lt_zero = tree_expr_nonnegative_p (rhs)
? integer_zero_node
: build_binary_op (LT_EXPR, integer_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))
{
int needed_precision = code == MULT_EXPR ? 2 * precision : precision + 1;
/* Even for add/subtract double size in order to get another basetype */
int needed_precision = precision * 2;
if (code == MULT_EXPR && precision == 64)
{
return build_call_2_expr (mulv64_decl, lhs, rhs);
}
else if (needed_precision <= LONG_LONG_TYPE_SIZE)
else if (needed_precision <= BITS_PER_WORD
|| (code == MULT_EXPR
&& needed_precision <= LONG_LONG_TYPE_SIZE))
{
tree calc_type = gnat_type_for_size (needed_precision, 0);
tree result;
tree check;
tree wide_type = gnat_type_for_size (needed_precision, 0);
result = build_binary_op (code, calc_type,
convert (calc_type, lhs),
convert (calc_type, rhs));
tree wide_result = build_binary_op (code, wide_type,
convert (wide_type, lhs),
convert (wide_type, rhs));
check = build_binary_op
tree check = build_binary_op
(TRUTH_ORIF_EXPR, integer_type_node,
build_binary_op (LT_EXPR, integer_type_node, result,
convert (calc_type, type_min)),
build_binary_op (GT_EXPR, integer_type_node, result,
convert (calc_type, type_max)));
build_binary_op (LT_EXPR, integer_type_node, wide_result,
convert (wide_type, type_min)),
build_binary_op (GT_EXPR, integer_type_node, wide_result,
convert (wide_type, type_max)));
result = convert (gnu_type, result);
tree result = convert (gnu_type, wide_result);
return emit_check (check, result, CE_Overflow_Check_Failed);
}
else if (code == PLUS_EXPR || code == MINUS_EXPR)
{
tree unsigned_type = gnat_type_for_size (precision, 1);
tree wrapped_expr = convert
(gnu_type, build_binary_op (code, unsigned_type,
convert (unsigned_type, lhs),
convert (unsigned_type, rhs)));
tree result = convert
(gnu_type, build_binary_op (code, gnu_type, lhs, rhs));
/* Overflow when (rhs < 0) ^ (wrapped_expr < lhs)), for addition
or when (rhs < 0) ^ (wrapped_expr > lhs) for subtraction */
tree check = build_binary_op
(TRUTH_XOR_EXPR, integer_type_node, rhs_lt_zero,
build_binary_op (code == PLUS_EXPR ? LT_EXPR : GT_EXPR,
integer_type_node, wrapped_expr, lhs));
return emit_check (check, result, CE_Overflow_Check_Failed);
}
}
gnu_expr = build_binary_op (code, gnu_type, lhs, rhs);
rhs_ge_zero = build_binary_op (GE_EXPR, integer_type_node, rhs, zero);
switch (code)
{
case PLUS_EXPR:
@ -6079,6 +6114,7 @@ build_binary_op_trapv (enum tree_code code,
case MULT_EXPR:
/* The check here is designed to be efficient if the rhs is constant,
but it will work for any rhs by using integer division.
Four different check expressions determine wether X * C overflows,
depending on C.
C == 0 => false
@ -6108,9 +6144,12 @@ build_binary_op_trapv (enum tree_code code,
gcc_unreachable();
}
return emit_check (fold_build3 (COND_EXPR, integer_type_node, rhs_ge_zero,
check_pos, check_neg),
gnu_expr, CE_Overflow_Check_Failed);
gnu_expr = build_binary_op (code, gnu_type, lhs, rhs);
check = fold_build3 (COND_EXPR, integer_type_node,
rhs_lt_zero, check_neg, check_pos);
return emit_check (check, gnu_expr, CE_Overflow_Check_Failed);
}
/* Emit code for a range check. GNU_EXPR is the expression to be checked,