[Patch, fortran] PR90903 - Implement runtime checks for bit manipulation intrinsics
Harald Anlauf
anlauf@gmx.de
Sun Jun 23 21:37:00 GMT 2019
Dear all,
the attached patch provides run-time checks for the bit manipulation
intrinsic functions (IBSET/IBCLR/BTEST/SHIFT[RLA]/ISHFT/ISHFTC).
I am using only one testcase whose purpose is mainly to verify that
there are no false positives, which I consider essential, and one
"failing" test at the end.
What is still missing are run-time checks for the subroutine MVBITS.
I am not sure yet how to handle that case (frontend or library?),
and I am open to suggestions. For this purpose I intend to leave
the PR open until a good solution is found.
Regtested on x86_64-pc-linux-gnu. OK for trunk?
Harald
2019-06-23 Harald Anlauf <anlauf@gmx.de>
PR fortran/90903
* libgfortran.h: Add mask for -fcheck=bits option.
* options.c (gfc_handle_runtime_check_option): Add option "bits"
to run-time checks selectable via -fcheck.
* trans-intrinsic.c (gfc_conv_intrinsic_btest)
(gfc_conv_intrinsic_singlebitop, gfc_conv_intrinsic_ibits)
(gfc_conv_intrinsic_shift, gfc_conv_intrinsic_ishft)
(gfc_conv_intrinsic_ishftc): Implement run-time checks for the
POS, LEN, SHIFT, and SIZE arguments.
* gfortran.texi: Document run-time checks for bit manipulation
intrinsics.
* invoke.texi: Document new -fcheck=bits option.
2019-06-23 Harald Anlauf <anlauf@gmx.de>
PR fortran/90903
* gfortran.dg/check_bits_1.f90: New testcase.
-------------- next part --------------
Index: gcc/fortran/gfortran.texi
===================================================================
--- gcc/fortran/gfortran.texi (revision 272560)
+++ gcc/fortran/gfortran.texi (working copy)
@@ -3790,7 +3790,8 @@
Default: enabled.
@item @var{option}[6] @tab Enables run-time checking. Possible values
are (bitwise or-ed): GFC_RTCHECK_BOUNDS (1), GFC_RTCHECK_ARRAY_TEMPS (2),
-GFC_RTCHECK_RECURSION (4), GFC_RTCHECK_DO (16), GFC_RTCHECK_POINTER (32).
+GFC_RTCHECK_RECURSION (4), GFC_RTCHECK_DO (16), GFC_RTCHECK_POINTER (32),
+GFC_RTCHECK_BITS (64).
Default: disabled.
@item @var{option}[7] @tab Unused.
@item @var{option}[8] @tab Show a warning when invoking @code{STOP} and
Index: gcc/fortran/invoke.texi
===================================================================
--- gcc/fortran/invoke.texi (revision 272560)
+++ gcc/fortran/invoke.texi (working copy)
@@ -183,7 +183,7 @@
@gccoptlist{-faggressive-function-elimination -fblas-matmul-limit=@var{n} @gol
-fbounds-check -ftail-call-workaround -ftail-call-workaround=@var{n} @gol
-fcheck-array-temporaries @gol
--fcheck=@var{<all|array-temps|bounds|do|mem|pointer|recursion>} @gol
+-fcheck=@var{<all|array-temps|bits|bounds|do|mem|pointer|recursion>} @gol
-fcoarray=@var{<none|single|lib>} -fexternal-blas -ff2c
-ffrontend-loop-interchange @gol
-ffrontend-optimize @gol
@@ -1558,6 +1558,7 @@
@item -fcheck=@var{<keyword>}
@opindex @code{fcheck}
@cindex array, bounds checking
+@cindex bit intrinsics checking
@cindex bounds checking
@cindex pointer checking
@cindex memory checking
@@ -1582,6 +1583,10 @@
Note: The warning is only printed once per location.
+@item @samp{bits}
+Enable generation of run-time checks for invalid arguments to the bit
+manipulation intrinsics.
+
@item @samp{bounds}
Enable generation of run-time checks for array subscripts
and against the declared minimum and maximum values. It also
Index: gcc/fortran/libgfortran.h
===================================================================
--- gcc/fortran/libgfortran.h (revision 272560)
+++ gcc/fortran/libgfortran.h (working copy)
@@ -73,9 +73,11 @@
#define GFC_RTCHECK_DO (1<<3)
#define GFC_RTCHECK_POINTER (1<<4)
#define GFC_RTCHECK_MEM (1<<5)
+#define GFC_RTCHECK_BITS (1<<6)
#define GFC_RTCHECK_ALL (GFC_RTCHECK_BOUNDS | GFC_RTCHECK_ARRAY_TEMPS \
| GFC_RTCHECK_RECURSION | GFC_RTCHECK_DO \
- | GFC_RTCHECK_POINTER | GFC_RTCHECK_MEM)
+ | GFC_RTCHECK_POINTER | GFC_RTCHECK_MEM \
+ | GFC_RTCHECK_BITS)
/* Special unit numbers used to convey certain conditions. Numbers -4
thru -9 available. NEWUNIT values start at -10. */
Index: gcc/fortran/options.c
===================================================================
--- gcc/fortran/options.c (revision 272560)
+++ gcc/fortran/options.c (working copy)
@@ -580,12 +580,12 @@
int result, pos = 0, n;
static const char * const optname[] = { "all", "bounds", "array-temps",
"recursion", "do", "pointer",
- "mem", NULL };
+ "mem", "bits", NULL };
static const int optmask[] = { GFC_RTCHECK_ALL, GFC_RTCHECK_BOUNDS,
GFC_RTCHECK_ARRAY_TEMPS,
GFC_RTCHECK_RECURSION, GFC_RTCHECK_DO,
GFC_RTCHECK_POINTER, GFC_RTCHECK_MEM,
- 0 };
+ GFC_RTCHECK_BITS, 0 };
while (*arg)
{
Index: gcc/fortran/trans-intrinsic.c
===================================================================
--- gcc/fortran/trans-intrinsic.c (revision 272560)
+++ gcc/fortran/trans-intrinsic.c (working copy)
@@ -6166,6 +6166,24 @@
gfc_conv_intrinsic_function_args (se, expr, args, 2);
type = TREE_TYPE (args[0]);
+ /* Optionally generate code for runtime argument check. */
+ if (gfc_option.rtcheck & GFC_RTCHECK_BITS)
+ {
+ tree below = fold_build2_loc (input_location, LT_EXPR,
+ logical_type_node, args[1],
+ build_int_cst (TREE_TYPE (args[1]), 0));
+ tree nbits = build_int_cst (TREE_TYPE (args[1]), TYPE_PRECISION (type));
+ tree above = fold_build2_loc (input_location, GE_EXPR,
+ logical_type_node, args[1], nbits);
+ tree scond = fold_build2_loc (input_location, TRUTH_ORIF_EXPR,
+ logical_type_node, below, above);
+ gfc_trans_runtime_check (true, false, scond, &se->pre, &expr->where,
+ "POS argument (%ld) out of range 0:%ld "
+ "in intrinsic BTEST",
+ fold_convert (long_integer_type_node, args[1]),
+ fold_convert (long_integer_type_node, nbits));
+ }
+
tmp = fold_build2_loc (input_location, LSHIFT_EXPR, type,
build_int_cst (type, 1), args[1]);
tmp = fold_build2_loc (input_location, BIT_AND_EXPR, type, args[0], tmp);
@@ -6236,6 +6254,32 @@
gfc_conv_intrinsic_function_args (se, expr, args, 2);
type = TREE_TYPE (args[0]);
+ /* Optionally generate code for runtime argument check. */
+ if (gfc_option.rtcheck & GFC_RTCHECK_BITS)
+ {
+ tree below = fold_build2_loc (input_location, LT_EXPR,
+ logical_type_node, args[1],
+ build_int_cst (TREE_TYPE (args[1]), 0));
+ tree nbits = build_int_cst (TREE_TYPE (args[1]), TYPE_PRECISION (type));
+ tree above = fold_build2_loc (input_location, GE_EXPR,
+ logical_type_node, args[1], nbits);
+ tree scond = fold_build2_loc (input_location, TRUTH_ORIF_EXPR,
+ logical_type_node, below, above);
+ size_t len_name = strlen (expr->value.function.isym->name);
+ char *name = XALLOCAVEC (char, len_name + 1);
+ for (size_t i = 0; i < len_name; i++)
+ name[i] = TOUPPER (expr->value.function.isym->name[i]);
+ name[len_name] = '\0';
+ tree iname = gfc_build_addr_expr (pchar_type_node,
+ gfc_build_cstring_const (name));
+ gfc_trans_runtime_check (true, false, scond, &se->pre, &expr->where,
+ "POS argument (%ld) out of range 0:%ld "
+ "in intrinsic %s",
+ fold_convert (long_integer_type_node, args[1]),
+ fold_convert (long_integer_type_node, nbits),
+ iname);
+ }
+
tmp = fold_build2_loc (input_location, LSHIFT_EXPR, type,
build_int_cst (type, 1), args[1]);
if (set)
@@ -6261,6 +6305,42 @@
gfc_conv_intrinsic_function_args (se, expr, args, 3);
type = TREE_TYPE (args[0]);
+ /* Optionally generate code for runtime argument check. */
+ if (gfc_option.rtcheck & GFC_RTCHECK_BITS)
+ {
+ tree tmp1 = fold_convert (long_integer_type_node, args[1]);
+ tree tmp2 = fold_convert (long_integer_type_node, args[2]);
+ tree nbits = build_int_cst (long_integer_type_node,
+ TYPE_PRECISION (type));
+ tree below = fold_build2_loc (input_location, LT_EXPR,
+ logical_type_node, args[1],
+ build_int_cst (TREE_TYPE (args[1]), 0));
+ tree above = fold_build2_loc (input_location, GT_EXPR,
+ logical_type_node, tmp1, nbits);
+ tree scond = fold_build2_loc (input_location, TRUTH_ORIF_EXPR,
+ logical_type_node, below, above);
+ gfc_trans_runtime_check (true, false, scond, &se->pre, &expr->where,
+ "POS argument (%ld) out of range 0:%ld "
+ "in intrinsic IBITS", tmp1, nbits);
+ below = fold_build2_loc (input_location, LT_EXPR,
+ logical_type_node, args[2],
+ build_int_cst (TREE_TYPE (args[2]), 0));
+ above = fold_build2_loc (input_location, GT_EXPR,
+ logical_type_node, tmp2, nbits);
+ scond = fold_build2_loc (input_location, TRUTH_ORIF_EXPR,
+ logical_type_node, below, above);
+ gfc_trans_runtime_check (true, false, scond, &se->pre, &expr->where,
+ "LEN argument (%ld) out of range 0:%ld "
+ "in intrinsic IBITS", tmp2, nbits);
+ above = fold_build2_loc (input_location, PLUS_EXPR,
+ long_integer_type_node, tmp1, tmp2);
+ scond = fold_build2_loc (input_location, GT_EXPR,
+ logical_type_node, above, nbits);
+ gfc_trans_runtime_check (true, false, scond, &se->pre, &expr->where,
+ "POS(%ld)+LEN(%ld)>BIT_SIZE(%ld) "
+ "in intrinsic IBITS", tmp1, tmp2, nbits);
+ }
+
mask = build_int_cst (type, -1);
mask = fold_build2_loc (input_location, LSHIFT_EXPR, type, mask, args[2]);
mask = fold_build1_loc (input_location, BIT_NOT_EXPR, type, mask);
@@ -6382,6 +6462,32 @@
gcc requires a shift width < BIT_SIZE(I), so we have to catch this
special case. */
num_bits = build_int_cst (TREE_TYPE (args[1]), TYPE_PRECISION (type));
+
+ /* Optionally generate code for runtime argument check. */
+ if (gfc_option.rtcheck & GFC_RTCHECK_BITS)
+ {
+ tree below = fold_build2_loc (input_location, LT_EXPR,
+ logical_type_node, args[1],
+ build_int_cst (TREE_TYPE (args[1]), 0));
+ tree above = fold_build2_loc (input_location, GT_EXPR,
+ logical_type_node, args[1], num_bits);
+ tree scond = fold_build2_loc (input_location, TRUTH_ORIF_EXPR,
+ logical_type_node, below, above);
+ size_t len_name = strlen (expr->value.function.isym->name);
+ char *name = XALLOCAVEC (char, len_name + 1);
+ for (size_t i = 0; i < len_name; i++)
+ name[i] = TOUPPER (expr->value.function.isym->name[i]);
+ name[len_name] = '\0';
+ tree iname = gfc_build_addr_expr (pchar_type_node,
+ gfc_build_cstring_const (name));
+ gfc_trans_runtime_check (true, false, scond, &se->pre, &expr->where,
+ "SHIFT argument (%ld) out of range 0:%ld "
+ "in intrinsic %s",
+ fold_convert (long_integer_type_node, args[1]),
+ fold_convert (long_integer_type_node, num_bits),
+ iname);
+ }
+
cond = fold_build2_loc (input_location, GE_EXPR, logical_type_node,
args[1], num_bits);
@@ -6436,6 +6542,20 @@
gcc requires a shift width < BIT_SIZE(I), so we have to catch this
special case. */
num_bits = build_int_cst (TREE_TYPE (args[1]), TYPE_PRECISION (type));
+
+ /* Optionally generate code for runtime argument check. */
+ if (gfc_option.rtcheck & GFC_RTCHECK_BITS)
+ {
+ tree outside = fold_build2_loc (input_location, GT_EXPR,
+ logical_type_node, width, num_bits);
+ gfc_trans_runtime_check (true, false, outside, &se->pre, &expr->where,
+ "SHIFT argument (%ld) out of range -%ld:%ld "
+ "in intrinsic ISHFT",
+ fold_convert (long_integer_type_node, args[1]),
+ fold_convert (long_integer_type_node, num_bits),
+ fold_convert (long_integer_type_node, num_bits));
+ }
+
cond = fold_build2_loc (input_location, GE_EXPR, logical_type_node, width,
num_bits);
se->expr = fold_build3_loc (input_location, COND_EXPR, type, cond,
@@ -6454,6 +6574,7 @@
tree lrot;
tree rrot;
tree zero;
+ tree nbits;
unsigned int num_args;
num_args = gfc_intrinsic_argument_list_length (expr);
@@ -6461,12 +6582,14 @@
gfc_conv_intrinsic_function_args (se, expr, args, num_args);
+ type = TREE_TYPE (args[0]);
+ nbits = build_int_cst (long_integer_type_node, TYPE_PRECISION (type));
+
if (num_args == 3)
{
/* Use a library function for the 3 parameter version. */
tree int4type = gfc_get_int_type (4);
- type = TREE_TYPE (args[0]);
/* We convert the first argument to at least 4 bytes, and
convert back afterwards. This removes the need for library
functions for all argument sizes, and function will be
@@ -6480,6 +6603,32 @@
args[1] = convert (int4type, args[1]);
args[2] = convert (int4type, args[2]);
+ /* Optionally generate code for runtime argument check. */
+ if (gfc_option.rtcheck & GFC_RTCHECK_BITS)
+ {
+ tree size = fold_convert (long_integer_type_node, args[2]);
+ tree below = fold_build2_loc (input_location, LE_EXPR,
+ logical_type_node, size,
+ build_int_cst (TREE_TYPE (args[1]), 0));
+ tree above = fold_build2_loc (input_location, GT_EXPR,
+ logical_type_node, size, nbits);
+ tree scond = fold_build2_loc (input_location, TRUTH_ORIF_EXPR,
+ logical_type_node, below, above);
+ gfc_trans_runtime_check (true, false, scond, &se->pre, &expr->where,
+ "SIZE argument (%ld) out of range 1:%ld "
+ "in intrinsic ISHFTC", size, nbits);
+ tree width = fold_convert (long_integer_type_node, args[1]);
+ width = fold_build1_loc (input_location, ABS_EXPR,
+ long_integer_type_node, width);
+ scond = fold_build2_loc (input_location, GT_EXPR,
+ logical_type_node, width, size);
+ gfc_trans_runtime_check (true, false, scond, &se->pre, &expr->where,
+ "SHIFT argument (%ld) out of range -%ld:%ld "
+ "in intrinsic ISHFTC",
+ fold_convert (long_integer_type_node, args[1]),
+ size, size);
+ }
+
switch (expr->ts.kind)
{
case 1:
@@ -6505,12 +6654,26 @@
return;
}
- type = TREE_TYPE (args[0]);
/* Evaluate arguments only once. */
args[0] = gfc_evaluate_now (args[0], &se->pre);
args[1] = gfc_evaluate_now (args[1], &se->pre);
+ /* Optionally generate code for runtime argument check. */
+ if (gfc_option.rtcheck & GFC_RTCHECK_BITS)
+ {
+ tree width = fold_convert (long_integer_type_node, args[1]);
+ width = fold_build1_loc (input_location, ABS_EXPR,
+ long_integer_type_node, width);
+ tree outside = fold_build2_loc (input_location, GT_EXPR,
+ logical_type_node, width, nbits);
+ gfc_trans_runtime_check (true, false, outside, &se->pre, &expr->where,
+ "SHIFT argument (%ld) out of range -%ld:%ld "
+ "in intrinsic ISHFTC",
+ fold_convert (long_integer_type_node, args[1]),
+ nbits, nbits);
+ }
+
/* Rotate left if positive. */
lrot = fold_build2_loc (input_location, LROTATE_EXPR, type, args[0], args[1]);
-------------- next part --------------
Index: gcc/testsuite/gfortran.dg/check_bits_1.f90
===================================================================
--- gcc/testsuite/gfortran.dg/check_bits_1.f90 (nonexistent)
+++ gcc/testsuite/gfortran.dg/check_bits_1.f90 (working copy)
@@ -0,0 +1,49 @@
+! { dg-do run }
+! { dg-options "-fcheck=bits -fdump-tree-original" }
+! { dg-shouldfail "Fortran runtime error: SIZE argument (0) out of range 1:32 in intrinsic ISHFTC" }
+! { dg-output "At line 44 .*" }
+!
+! Verify that the runtime checks for the bit manipulation intrinsic functions
+! do not generate false-positives
+program check
+ implicit none
+ integer :: i, k, pos, len, shift, size, nb
+ nb = bit_size (i)
+ i = 0
+ do pos = 0, nb-1
+ k = ibset (i, pos)
+ i = ibclr (k, pos)
+ if (btest (i, pos)) stop 1
+ end do
+ do pos = 0, nb
+ do len = 0, nb-pos
+ i = ibits (i, pos, len)
+ end do
+ end do
+ do shift = 0, nb
+ k = ishft (i, shift)
+ i = ishft (k, -shift)
+ end do
+ do shift = 0, nb
+ k = shiftl (i, shift) ! Fortran 2008
+ i = shiftr (k, shift)
+ i = shifta (i, shift)
+ k = lshift (i, shift) ! GNU extensions
+ i = rshift (k, shift)
+ end do
+ do shift = 0, nb
+ k = ishftc (i, shift)
+ i = ishftc (k, -shift)
+ do size = max (1,shift), nb
+ k = ishftc (i, shift, size)
+ i = ishftc (k, -shift, size)
+ end do
+ end do
+ size = 0
+ ! The following line should fail with a runtime error:
+ k = ishftc (i, 0, size)
+ ! Should never get here with -fcheck=bits
+ stop 2
+end program check
+
+! { dg-final { scan-tree-dump-times "_gfortran_runtime_error_at" 21 "original" } }
More information about the Gcc-patches
mailing list