mirror of
https://github.com/gcc-mirror/gcc.git
synced 2026-05-06 14:59:39 +02:00
ada: Implement overflow checking for unsigned types
The implementation is essentially mirrored from the one for signed types. gcc/ada/ChangeLog: * gcc-interface/gigi.h (standard_datatypes): Add ADT_uns_mulv64_decl and ADT_uns_mulv128_decl. (uns_mulv64_decl): New macro. (uns_mulv128_decl): Likewise. * gcc-interface/trans.cc (gigi): Create the uns_mulv64_decl and uns_mulv128_decl declarations. (gnat_to_gnu) <N_Op_Add>: Perform an overflow check for unsigned integer addition, subtraction and multiplication if required. <N_Op_Minus>: Perform an overflow check for unsigned integer negation if required. (build_unary_op_trapv): Add support for unsigned types. (build_binary_op_trapv): Likewise. <MINUS_EXPR>: Perform the check if the LHS is zero in the signed case as well.
This commit is contained in:
committed by
Marc Poulhiès
parent
571088f4d6
commit
3a3854f3ea
@@ -388,11 +388,13 @@ enum standard_datatypes
|
||||
/* Function declaration node for run-time reallocation function. */
|
||||
ADT_realloc_decl,
|
||||
|
||||
/* Function decl node for 64-bit multiplication with overflow checking. */
|
||||
/* Function decl nodes for 64-bit multiplication with overflow checking. */
|
||||
ADT_mulv64_decl,
|
||||
ADT_uns_mulv64_decl,
|
||||
|
||||
/* Function decl node for 128-bit multiplication with overflow checking. */
|
||||
/* Function decl nodes for 128-bit multiplication with overflow checking. */
|
||||
ADT_mulv128_decl,
|
||||
ADT_uns_mulv128_decl,
|
||||
|
||||
/* Identifier for the name of the _Parent field in tagged record types. */
|
||||
ADT_parent_name_id,
|
||||
@@ -455,7 +457,9 @@ extern GTY(()) tree gnat_raise_decls_ext[(int) LAST_REASON_CODE + 1];
|
||||
#define free_decl gnat_std_decls[(int) ADT_free_decl]
|
||||
#define realloc_decl gnat_std_decls[(int) ADT_realloc_decl]
|
||||
#define mulv64_decl gnat_std_decls[(int) ADT_mulv64_decl]
|
||||
#define uns_mulv64_decl gnat_std_decls[(int) ADT_uns_mulv64_decl]
|
||||
#define mulv128_decl gnat_std_decls[(int) ADT_mulv128_decl]
|
||||
#define uns_mulv128_decl gnat_std_decls[(int) ADT_uns_mulv128_decl]
|
||||
#define parent_name_id gnat_std_decls[(int) ADT_parent_name_id]
|
||||
#define not_handled_by_others_name_id \
|
||||
gnat_std_decls[(int) ADT_not_handled_by_others_name_id]
|
||||
|
||||
@@ -319,7 +319,7 @@ gigi (Node_Id gnat_root,
|
||||
{
|
||||
Node_Id gnat_iter;
|
||||
Entity_Id gnat_literal;
|
||||
tree t, ftype, int64_type;
|
||||
tree t, ftype;
|
||||
struct elab_info *info;
|
||||
int i;
|
||||
|
||||
@@ -466,7 +466,7 @@ gigi (Node_Id gnat_root,
|
||||
false, NULL, Empty);
|
||||
|
||||
/* This is used for 64-bit multiplication with overflow checking. */
|
||||
int64_type = gnat_type_for_size (64, 0);
|
||||
tree int64_type = gnat_type_for_size (64, 0);
|
||||
mulv64_decl
|
||||
= create_subprog_decl (get_identifier ("__gnat_mulv64"), NULL_TREE,
|
||||
build_function_type_list (int64_type, int64_type,
|
||||
@@ -475,6 +475,15 @@ gigi (Node_Id gnat_root,
|
||||
false, NULL, Empty);
|
||||
strub_make_callable (mulv64_decl);
|
||||
|
||||
tree uint64_type = gnat_type_for_size (64, 1);
|
||||
uns_mulv64_decl
|
||||
= create_subprog_decl (get_identifier ("__gnat_uns_mulv64"), NULL_TREE,
|
||||
build_function_type_list (uint64_type, uint64_type,
|
||||
uint64_type, NULL_TREE),
|
||||
NULL_TREE, is_default, true, true, true, false,
|
||||
false, NULL, Empty);
|
||||
strub_make_callable (uns_mulv64_decl);
|
||||
|
||||
if (Enable_128bit_Types)
|
||||
{
|
||||
tree int128_type = gnat_type_for_size (128, 0);
|
||||
@@ -487,6 +496,17 @@ gigi (Node_Id gnat_root,
|
||||
NULL_TREE, is_default, true, true, true, false,
|
||||
false, NULL, Empty);
|
||||
strub_make_callable (mulv128_decl);
|
||||
|
||||
tree uint128_type = gnat_type_for_size (128, 1);
|
||||
uns_mulv128_decl
|
||||
= create_subprog_decl (get_identifier ("__gnat_uns_mulv128"), NULL_TREE,
|
||||
build_function_type_list (uint128_type,
|
||||
uint128_type,
|
||||
uint128_type,
|
||||
NULL_TREE),
|
||||
NULL_TREE, is_default, true, true, true, false,
|
||||
false, NULL, Empty);
|
||||
strub_make_callable (uns_mulv128_decl);
|
||||
}
|
||||
|
||||
/* Name of the _Parent field in tagged record types. */
|
||||
@@ -7504,12 +7524,11 @@ gnat_to_gnu (Node_Id gnat_node)
|
||||
gnu_max_shift = convert (gnu_type, gnu_max_shift);
|
||||
}
|
||||
|
||||
/* For signed integer addition, subtraction and multiplication, do an
|
||||
/* For integer addition, subtraction and multiplication, perform an
|
||||
overflow check if required. */
|
||||
if (Do_Overflow_Check (gnat_node)
|
||||
&& (code == PLUS_EXPR || code == MINUS_EXPR || code == MULT_EXPR)
|
||||
&& !TYPE_UNSIGNED (gnu_type)
|
||||
&& !FLOAT_TYPE_P (gnu_type))
|
||||
if ((code == PLUS_EXPR || code == MINUS_EXPR || code == MULT_EXPR)
|
||||
&& !FLOAT_TYPE_P (gnu_type)
|
||||
&& Do_Overflow_Check (gnat_node))
|
||||
gnu_result
|
||||
= build_binary_op_trapv (code, gnu_type, gnu_lhs, gnu_rhs,
|
||||
gnat_node);
|
||||
@@ -7590,11 +7609,11 @@ gnat_to_gnu (Node_Id gnat_node)
|
||||
gnu_expr = gnat_to_gnu (Right_Opnd (gnat_node));
|
||||
gnu_result_type = get_unpadded_type (Etype (gnat_node));
|
||||
|
||||
/* For signed integer negation and absolute value, do an overflow check
|
||||
/* For integer negation and absolute value, perform an overflow check
|
||||
if required. */
|
||||
if (Do_Overflow_Check (gnat_node)
|
||||
&& !TYPE_UNSIGNED (gnu_result_type)
|
||||
&& !FLOAT_TYPE_P (gnu_result_type))
|
||||
if ((gnu_codes[kind] == NEGATE_EXPR || gnu_codes[kind] == ABS_EXPR)
|
||||
&& !FLOAT_TYPE_P (gnu_result_type)
|
||||
&& Do_Overflow_Check (gnat_node))
|
||||
gnu_result
|
||||
= build_unary_op_trapv (gnu_codes[kind], gnu_result_type, gnu_expr,
|
||||
gnat_node);
|
||||
@@ -9959,12 +9978,25 @@ build_unary_op_trapv (enum tree_code code, tree gnu_type, tree operand,
|
||||
{
|
||||
gcc_assert (code == NEGATE_EXPR || code == ABS_EXPR);
|
||||
|
||||
tree gnu_expr, check;
|
||||
|
||||
operand = gnat_protect_expr (operand);
|
||||
|
||||
return emit_check (build_binary_op (EQ_EXPR, boolean_type_node,
|
||||
operand, TYPE_MIN_VALUE (gnu_type)),
|
||||
build_unary_op (code, gnu_type, operand),
|
||||
CE_Overflow_Check_Failed, gnat_node);
|
||||
gnu_expr = build_unary_op (code, gnu_type, operand);
|
||||
|
||||
if (TYPE_UNSIGNED (gnu_type))
|
||||
{
|
||||
if (code == ABS_EXPR)
|
||||
return gnu_expr;
|
||||
else
|
||||
check = build_binary_op (NE_EXPR, boolean_type_node,
|
||||
operand, TYPE_MIN_VALUE (gnu_type));
|
||||
}
|
||||
else
|
||||
check = build_binary_op (EQ_EXPR, boolean_type_node,
|
||||
operand, TYPE_MIN_VALUE (gnu_type));
|
||||
|
||||
return emit_check (check, gnu_expr, CE_Overflow_Check_Failed, gnat_node);
|
||||
}
|
||||
|
||||
/* Make a binary operation of kind CODE using build_binary_op, but guard
|
||||
@@ -10017,21 +10049,29 @@ build_binary_op_trapv (enum tree_code code, tree gnu_type, tree left,
|
||||
/* Never inline a 64-bit mult for a 32-bit target, it's way too long. */
|
||||
if (code == MULT_EXPR && precision == 64 && BITS_PER_WORD < 64)
|
||||
{
|
||||
tree int64 = gnat_type_for_size (64, 0);
|
||||
tree int64 = gnat_type_for_size (64, TYPE_UNSIGNED (gnu_type));
|
||||
Check_Restriction_No_Dependence_On_System (Name_Arith_64, gnat_node);
|
||||
return convert (gnu_type, build_call_n_expr (mulv64_decl, 2,
|
||||
convert (int64, lhs),
|
||||
convert (int64, rhs)));
|
||||
return
|
||||
convert (gnu_type, build_call_n_expr (TYPE_UNSIGNED (gnu_type)
|
||||
? uns_mulv64_decl
|
||||
: mulv64_decl,
|
||||
2,
|
||||
convert (int64, lhs),
|
||||
convert (int64, rhs)));
|
||||
}
|
||||
|
||||
/* Likewise for a 128-bit mult and a 64-bit target. */
|
||||
else if (code == MULT_EXPR && precision == 128 && BITS_PER_WORD < 128)
|
||||
{
|
||||
tree int128 = gnat_type_for_size (128, 0);
|
||||
tree int128 = gnat_type_for_size (128, TYPE_UNSIGNED (gnu_type));
|
||||
Check_Restriction_No_Dependence_On_System (Name_Arith_128, gnat_node);
|
||||
return convert (gnu_type, build_call_n_expr (mulv128_decl, 2,
|
||||
convert (int128, lhs),
|
||||
convert (int128, rhs)));
|
||||
return
|
||||
convert (gnu_type, build_call_n_expr (TYPE_UNSIGNED (gnu_type)
|
||||
? uns_mulv128_decl
|
||||
: mulv128_decl,
|
||||
2,
|
||||
convert (int128, lhs),
|
||||
convert (int128, rhs)));
|
||||
}
|
||||
|
||||
enum internal_fn icode;
|
||||
@@ -10065,7 +10105,7 @@ build_binary_op_trapv (enum tree_code code, tree gnu_type, tree left,
|
||||
}
|
||||
|
||||
/* If one operand is a constant, we expose the overflow condition to enable
|
||||
a subsequent simplication or even elimination. */
|
||||
a subsequent simplification or even elimination. */
|
||||
switch (code)
|
||||
{
|
||||
case PLUS_EXPR:
|
||||
@@ -10085,21 +10125,24 @@ build_binary_op_trapv (enum tree_code code, tree gnu_type, tree left,
|
||||
break;
|
||||
|
||||
case MINUS_EXPR:
|
||||
if (TREE_CODE (lhs) == INTEGER_CST)
|
||||
if (TREE_CODE (lhs) == INTEGER_CST && TYPE_UNSIGNED (gnu_type))
|
||||
/* In the unsigned case, 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 if (TREE_CODE (lhs) == INTEGER_CST)
|
||||
{
|
||||
sgn = tree_int_cst_sgn (lhs);
|
||||
if (sgn > 0)
|
||||
/* When lhs > 0, overflow when rhs < lhs - type_max. */
|
||||
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)
|
||||
else
|
||||
/* 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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user