aboutsummaryrefslogtreecommitdiff
path: root/gcc/ada/gcc-interface/trans.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/ada/gcc-interface/trans.c')
-rw-r--r--gcc/ada/gcc-interface/trans.c182
1 files changed, 179 insertions, 3 deletions
diff --git a/gcc/ada/gcc-interface/trans.c b/gcc/ada/gcc-interface/trans.c
index 9d3f807..f8e1d49 100644
--- a/gcc/ada/gcc-interface/trans.c
+++ b/gcc/ada/gcc-interface/trans.c
@@ -205,6 +205,8 @@ static void process_decls (List_Id, List_Id, Node_Id, bool, bool);
static tree emit_range_check (tree, Node_Id);
static tree emit_index_check (tree, tree, tree, tree);
static tree emit_check (tree, tree, int);
+static tree build_unary_op_trapv (enum tree_code, tree, tree);
+static tree build_binary_op_trapv (enum tree_code, tree, tree, tree);
static tree convert_with_check (Entity_Id, tree, bool, bool, bool);
static bool smaller_packable_type_p (tree, tree);
static bool addressable_p (tree, tree);
@@ -3939,7 +3941,22 @@ gnat_to_gnu (Node_Id gnat_node)
gnu_rhs = convert (gnu_type, gnu_rhs);
}
- gnu_result = build_binary_op (code, gnu_type, gnu_lhs, gnu_rhs);
+ /* Instead of expanding overflow checks for addition, subtraction
+ and multiplication itself, the front end will leave this to
+ the back end when Backend_Overflow_Checks_On_Target is set.
+ As the GCC back end itself does not know yet how to properly
+ do overflow checking, do it here. The goal is to push
+ the expansions further into the back end over time. */
+ if (Do_Overflow_Check (gnat_node) && Backend_Overflow_Checks_On_Target
+ && (Nkind (gnat_node) == N_Op_Add
+ || Nkind (gnat_node) == N_Op_Subtract
+ || Nkind (gnat_node) == N_Op_Multiply)
+ && !TYPE_UNSIGNED (gnu_type)
+ && !FLOAT_TYPE_P (gnu_type))
+ gnu_result
+ = build_binary_op_trapv (code, gnu_type, gnu_lhs, gnu_rhs);
+ else
+ gnu_result = build_binary_op (code, gnu_type, gnu_lhs, gnu_rhs);
/* If this is a logical shift with the shift count not verified,
we must return zero if it is too large. We cannot compensate
@@ -4004,8 +4021,14 @@ gnat_to_gnu (Node_Id gnat_node)
gnu_result_type = get_unpadded_type (Base_Type
(Full_View (Etype (gnat_node))));
- gnu_result = build_unary_op (gnu_codes[Nkind (gnat_node)],
- gnu_result_type, gnu_expr);
+ if (Do_Overflow_Check (gnat_node)
+ && !TYPE_UNSIGNED (gnu_result_type)
+ && !FLOAT_TYPE_P (gnu_result_type))
+ gnu_result = build_unary_op_trapv (gnu_codes[Nkind (gnat_node)],
+ gnu_result_type, gnu_expr);
+ else
+ gnu_result = build_unary_op (gnu_codes[Nkind (gnat_node)],
+ gnu_result_type, gnu_expr);
break;
case N_Allocator:
@@ -5875,6 +5898,159 @@ process_decls (List_Id gnat_decls, List_Id gnat_decls2,
}
}
+/* Make a unary operation of kind CODE using build_unary_op, but guard
+ the operation by an overflow check. CODE can be one of NEGATE_EXPR
+ or ABS_EXPR. GNU_TYPE is the type desired for the result.
+ Usually the operation is to be performed in that type. */
+
+static tree
+build_unary_op_trapv (enum tree_code code,
+ tree gnu_type,
+ tree operand)
+{
+ gcc_assert ((code == NEGATE_EXPR) || (code == ABS_EXPR));
+
+ operand = save_expr (operand);
+
+ return emit_check (build_binary_op (EQ_EXPR, integer_type_node,
+ operand, TYPE_MIN_VALUE (gnu_type)),
+ build_unary_op (code, gnu_type, operand),
+ CE_Overflow_Check_Failed);
+}
+
+/* Make a binary operation of kind CODE using build_binary_op, but
+ guard the operation by an overflow check. CODE can be one of
+ PLUS_EXPR, MINUS_EXPR or MULT_EXPR. GNU_TYPE is the type desired
+ for the result. Usually the operation is to be performed in that type. */
+
+static tree
+build_binary_op_trapv (enum tree_code code,
+ tree gnu_type,
+ tree left,
+ tree right)
+{
+ tree lhs = save_expr (left);
+ tree rhs = save_expr (right);
+ tree type_max = TYPE_MAX_VALUE (gnu_type);
+ tree type_min = TYPE_MIN_VALUE (gnu_type);
+ tree gnu_expr;
+ tree tmp1, tmp2;
+ tree zero = convert (gnu_type, integer_zero_node);
+ tree rhs_ge_zero;
+ tree check_pos;
+ tree check_neg;
+
+ int precision = TYPE_PRECISION (gnu_type);
+
+ /* Prefer a constant rhs to simplify checks */
+
+ if (TREE_CONSTANT (lhs) && !TREE_CONSTANT (rhs)
+ && commutative_tree_code (code))
+ {
+ 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. */
+
+ if (!TREE_CONSTANT (rhs))
+ {
+ int needed_precision = code == MULT_EXPR ? 2 * precision : precision + 1;
+
+ if (code == MULT_EXPR && precision == 64)
+ {
+ return build_call_2_expr (mulv64_decl, lhs, rhs);
+ }
+ else if (needed_precision <= LONG_LONG_TYPE_SIZE)
+ {
+ tree calc_type = gnat_type_for_size (needed_precision, 0);
+ tree result;
+ tree check;
+
+ result = build_binary_op (code, calc_type,
+ convert (calc_type, lhs),
+ convert (calc_type, rhs));
+
+ 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)));
+
+ result = convert (gnu_type, result);
+
+ 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:
+ /* When rhs >= 0, overflow when lhs > type_max - rhs */
+ check_pos = build_binary_op (GT_EXPR, integer_type_node, lhs,
+ build_binary_op (MINUS_EXPR, gnu_type,
+ type_max, rhs)),
+
+ /* When rhs < 0, overflow when lhs < type_min - rhs */
+ check_neg = build_binary_op (LT_EXPR, integer_type_node, lhs,
+ build_binary_op (MINUS_EXPR, gnu_type,
+ type_min, rhs));
+ break;
+
+ case MINUS_EXPR:
+ /* When rhs >= 0, overflow when lhs < type_min + rhs */
+ check_pos = build_binary_op (LT_EXPR, integer_type_node, lhs,
+ build_binary_op (PLUS_EXPR, gnu_type,
+ type_min, rhs)),
+
+ /* When rhs < 0, overflow when lhs > type_max + rhs */
+ check_neg = build_binary_op (GT_EXPR, integer_type_node, lhs,
+ build_binary_op (PLUS_EXPR, gnu_type,
+ type_max, rhs));
+ break;
+
+ case MULT_EXPR:
+ /* The check here is designed to be efficient if the rhs is constant,
+ Four different check expressions determine wether X * C overflows,
+ depending on C.
+ C == 0 => false
+ 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);
+ tmp2 = build_binary_op (TRUNC_DIV_EXPR, gnu_type, type_min, rhs);
+
+ check_pos = build_binary_op (TRUTH_ANDIF_EXPR, integer_type_node,
+ build_binary_op (NE_EXPR, integer_type_node, zero, rhs),
+ build_binary_op (TRUTH_ORIF_EXPR, integer_type_node,
+ build_binary_op (GT_EXPR, integer_type_node, lhs, tmp1),
+ build_binary_op (LT_EXPR, integer_type_node, lhs, tmp2)));
+
+ check_neg = fold_build3 (COND_EXPR, integer_type_node,
+ build_binary_op (EQ_EXPR, integer_type_node, rhs,
+ build_int_cst (gnu_type, -1)),
+ build_binary_op (EQ_EXPR, integer_type_node, lhs, type_min),
+ build_binary_op (TRUTH_ORIF_EXPR, integer_type_node,
+ build_binary_op (GT_EXPR, integer_type_node, lhs, tmp2),
+ build_binary_op (LT_EXPR, integer_type_node, lhs, tmp1)));
+ break;
+
+ default:
+ 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);
+}
+
/* Emit code for a range check. GNU_EXPR is the expression to be checked,
GNAT_RANGE_TYPE the gnat type or subtype containing the bounds against
which we have to check. */