mirror of git://gcc.gnu.org/git/gcc.git
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
This commit is contained in:
parent
a31d78c6a0
commit
aa18f65ad4
|
|
@ -1,3 +1,9 @@
|
||||||
|
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>
|
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.
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
||||||
/* 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
|
|
||||||
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);
|
|
||||||
tree wrapped_expr
|
|
||||||
= convert (gnu_type,
|
|
||||||
build_binary_op (code, unsigned_type,
|
|
||||||
convert (unsigned_type, lhs),
|
|
||||||
convert (unsigned_type, rhs)));
|
|
||||||
|
|
||||||
/* Overflow when (rhs < 0) ^ (wrapped_expr < lhs)), for addition
|
|
||||||
or when (rhs < 0) ^ (wrapped_expr > lhs) for subtraction. */
|
|
||||||
check
|
|
||||||
= build_binary_op (TRUTH_XOR_EXPR, boolean_type_node, rhs_lt_zero,
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (code)
|
switch (code)
|
||||||
{
|
{
|
||||||
case PLUS_EXPR:
|
case PLUS_EXPR:
|
||||||
/* When rhs >= 0, overflow when lhs > type_max - rhs. */
|
icode = IFN_ADD_OVERFLOW;
|
||||||
check_pos = build_binary_op (GT_EXPR, boolean_type_node, lhs,
|
break;
|
||||||
build_binary_op (MINUS_EXPR, gnu_type,
|
case MINUS_EXPR:
|
||||||
type_max, rhs)),
|
icode = IFN_SUB_OVERFLOW;
|
||||||
|
break;
|
||||||
|
case MULT_EXPR:
|
||||||
|
icode = IFN_MUL_OVERFLOW;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
gcc_unreachable ();
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
case PLUS_EXPR:
|
||||||
|
sgn = tree_int_cst_sgn (rhs);
|
||||||
|
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 (MINUS_EXPR, gnu_type,
|
||||||
|
type_max, rhs));
|
||||||
|
else if (sgn < 0)
|
||||||
/* When rhs < 0, overflow when lhs < type_min - rhs. */
|
/* When rhs < 0, overflow when lhs < type_min - rhs. */
|
||||||
check_neg = build_binary_op (LT_EXPR, boolean_type_node, lhs,
|
check = build_binary_op (LT_EXPR, boolean_type_node, lhs,
|
||||||
build_binary_op (MINUS_EXPR, gnu_type,
|
build_binary_op (MINUS_EXPR, gnu_type,
|
||||||
type_min, rhs));
|
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,
|
{
|
||||||
|
sgn = tree_int_cst_sgn (lhs);
|
||||||
|
if (sgn > 0)
|
||||||
|
/* When lhs > 0, overflow when rhs < lhs - type_max. */
|
||||||
|
check = build_binary_op (LT_EXPR, boolean_type_node, rhs,
|
||||||
|
build_binary_op (MINUS_EXPR, gnu_type,
|
||||||
|
lhs, type_max));
|
||||||
|
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,
|
build_binary_op (PLUS_EXPR, gnu_type,
|
||||||
type_min, rhs)),
|
type_min, rhs));
|
||||||
|
else if (sgn < 0)
|
||||||
/* When rhs < 0, overflow when lhs > type_max + rhs. */
|
/* When rhs < 0, overflow when lhs > type_max + rhs. */
|
||||||
check_neg = build_binary_op (GT_EXPR, boolean_type_node, lhs,
|
check = build_binary_op (GT_EXPR, boolean_type_node, lhs,
|
||||||
build_binary_op (PLUS_EXPR, gnu_type,
|
build_binary_op (PLUS_EXPR, gnu_type,
|
||||||
type_max, rhs));
|
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
|
|
||||||
C < -1 => X > type_min / C || X < type_max / C */
|
|
||||||
|
|
||||||
tmp1 = build_binary_op (TRUNC_DIV_EXPR, gnu_type, type_max, rhs);
|
tree lb = build_binary_op (TRUNC_DIV_EXPR, gnu_type, type_min, rhs);
|
||||||
tmp2 = build_binary_op (TRUNC_DIV_EXPR, gnu_type, type_min, rhs);
|
tree ub = build_binary_op (TRUNC_DIV_EXPR, gnu_type, type_max, rhs);
|
||||||
|
|
||||||
check_pos
|
/* When rhs > 1, overflow outside [type_min/rhs; type_max/rhs]. */
|
||||||
= build_binary_op (TRUTH_ANDIF_EXPR, boolean_type_node,
|
check
|
||||||
build_binary_op (NE_EXPR, boolean_type_node, zero,
|
= build_binary_op (TRUTH_ORIF_EXPR, boolean_type_node,
|
||||||
rhs),
|
build_binary_op (LT_EXPR, boolean_type_node,
|
||||||
build_binary_op (TRUTH_ORIF_EXPR, boolean_type_node,
|
lhs, lb),
|
||||||
build_binary_op (GT_EXPR,
|
build_binary_op (GT_EXPR, boolean_type_node,
|
||||||
boolean_type_node,
|
lhs, ub));
|
||||||
lhs, tmp1),
|
}
|
||||||
build_binary_op (LT_EXPR,
|
else if (sgn < 0)
|
||||||
boolean_type_node,
|
{
|
||||||
lhs, tmp2)));
|
tree lb = build_binary_op (TRUNC_DIV_EXPR, gnu_type, type_max, rhs);
|
||||||
|
tree ub = build_binary_op (TRUNC_DIV_EXPR, gnu_type, type_min, rhs);
|
||||||
|
|
||||||
check_neg
|
if (integer_minus_onep (rhs))
|
||||||
= fold_build3 (COND_EXPR, boolean_type_node,
|
/* When rhs == -1, overflow if lhs == type_min. */
|
||||||
build_binary_op (EQ_EXPR, boolean_type_node, rhs,
|
check
|
||||||
build_int_cst (gnu_type, -1)),
|
= build_binary_op (EQ_EXPR, boolean_type_node, lhs, type_min);
|
||||||
build_binary_op (EQ_EXPR, boolean_type_node, lhs,
|
else
|
||||||
type_min),
|
/* When rhs < -1, overflow outside [type_max/rhs; type_min/rhs]. */
|
||||||
build_binary_op (TRUTH_ORIF_EXPR, boolean_type_node,
|
check
|
||||||
build_binary_op (GT_EXPR,
|
= build_binary_op (TRUTH_ORIF_EXPR, boolean_type_node,
|
||||||
boolean_type_node,
|
build_binary_op (LT_EXPR, boolean_type_node,
|
||||||
lhs, tmp2),
|
lhs, lb),
|
||||||
build_binary_op (LT_EXPR,
|
build_binary_op (GT_EXPR, boolean_type_node,
|
||||||
boolean_type_node,
|
lhs, ub));
|
||||||
lhs, tmp1)));
|
}
|
||||||
|
else
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue