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:
Eric Botcazou
2025-07-23 00:19:10 +02:00
committed by Marc Poulhiès
parent 571088f4d6
commit 3a3854f3ea
2 changed files with 79 additions and 32 deletions

View File

@@ -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]

View File

@@ -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
{