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:
parent
218da97236
commit
4ae393838b
|
@ -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>
|
2008-11-06 Eric Botcazou <ebotcazou@adacore.com>
|
||||||
|
|
||||||
* gcc-interface/Makefile.in (SPARC/Solaris): Use a common set of
|
* gcc-interface/Makefile.in (SPARC/Solaris): Use a common set of
|
||||||
|
|
|
@ -5998,58 +5998,93 @@ build_binary_op_trapv (enum tree_code code,
|
||||||
tree gnu_expr;
|
tree gnu_expr;
|
||||||
tree tmp1, tmp2;
|
tree tmp1, tmp2;
|
||||||
tree zero = convert (gnu_type, integer_zero_node);
|
tree zero = convert (gnu_type, integer_zero_node);
|
||||||
tree rhs_ge_zero;
|
tree rhs_lt_zero;
|
||||||
tree check_pos;
|
tree check_pos;
|
||||||
tree check_neg;
|
tree check_neg;
|
||||||
|
tree check;
|
||||||
|
|
||||||
int precision = TYPE_PRECISION (gnu_type);
|
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)
|
/* Prefer a constant or known-positive rhs to simplify checks */
|
||||||
&& commutative_tree_code (code))
|
|
||||||
|
if (!TREE_CONSTANT (rhs)
|
||||||
|
&& commutative_tree_code (code)
|
||||||
|
&& (TREE_CONSTANT (lhs) || (!tree_expr_nonnegative_p (rhs)
|
||||||
|
&& tree_expr_nonnegative_p (lhs))))
|
||||||
{
|
{
|
||||||
tree tmp = lhs;
|
tree tmp = lhs;
|
||||||
lhs = rhs;
|
lhs = rhs;
|
||||||
rhs = tmp;
|
rhs = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* In the case the right-hand size is still not constant, try to
|
rhs_lt_zero = tree_expr_nonnegative_p (rhs)
|
||||||
use an exact operation in a wider type. */
|
? 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))
|
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)
|
if (code == MULT_EXPR && precision == 64)
|
||||||
{
|
{
|
||||||
return build_call_2_expr (mulv64_decl, lhs, rhs);
|
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 wide_type = gnat_type_for_size (needed_precision, 0);
|
||||||
tree result;
|
|
||||||
tree check;
|
|
||||||
|
|
||||||
result = build_binary_op (code, calc_type,
|
tree wide_result = build_binary_op (code, wide_type,
|
||||||
convert (calc_type, lhs),
|
convert (wide_type, lhs),
|
||||||
convert (calc_type, rhs));
|
convert (wide_type, rhs));
|
||||||
|
|
||||||
check = build_binary_op
|
tree check = build_binary_op
|
||||||
(TRUTH_ORIF_EXPR, integer_type_node,
|
(TRUTH_ORIF_EXPR, integer_type_node,
|
||||||
build_binary_op (LT_EXPR, integer_type_node, result,
|
build_binary_op (LT_EXPR, integer_type_node, wide_result,
|
||||||
convert (calc_type, type_min)),
|
convert (wide_type, type_min)),
|
||||||
build_binary_op (GT_EXPR, integer_type_node, result,
|
build_binary_op (GT_EXPR, integer_type_node, wide_result,
|
||||||
convert (calc_type, type_max)));
|
convert (wide_type, type_max)));
|
||||||
|
|
||||||
|
tree result = convert (gnu_type, wide_result);
|
||||||
|
|
||||||
result = convert (gnu_type, result);
|
|
||||||
|
|
||||||
return emit_check (check, result, CE_Overflow_Check_Failed);
|
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)));
|
||||||
|
|
||||||
gnu_expr = build_binary_op (code, gnu_type, lhs, rhs);
|
tree result = convert
|
||||||
rhs_ge_zero = build_binary_op (GE_EXPR, integer_type_node, rhs, zero);
|
(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (code)
|
switch (code)
|
||||||
{
|
{
|
||||||
|
@ -6079,6 +6114,7 @@ build_binary_op_trapv (enum tree_code code,
|
||||||
|
|
||||||
case MULT_EXPR:
|
case MULT_EXPR:
|
||||||
/* The check here is designed to be efficient if the rhs is constant,
|
/* 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,
|
Four different check expressions determine wether X * C overflows,
|
||||||
depending on C.
|
depending on C.
|
||||||
C == 0 => false
|
C == 0 => false
|
||||||
|
@ -6108,9 +6144,12 @@ build_binary_op_trapv (enum tree_code code,
|
||||||
gcc_unreachable();
|
gcc_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
return emit_check (fold_build3 (COND_EXPR, integer_type_node, rhs_ge_zero,
|
gnu_expr = build_binary_op (code, gnu_type, lhs, rhs);
|
||||||
check_pos, check_neg),
|
|
||||||
gnu_expr, CE_Overflow_Check_Failed);
|
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,
|
/* Emit code for a range check. GNU_EXPR is the expression to be checked,
|
||||||
|
|
Loading…
Reference in New Issue