public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [patch, Fortran] PR 58146, enable array slice compile-time bounds checking
@ 2013-08-14 21:46 Thomas Koenig
  2013-08-22 21:33 ` Mikael Morin
  0 siblings, 1 reply; 4+ messages in thread
From: Thomas Koenig @ 2013-08-14 21:46 UTC (permalink / raw)
  To: fortran, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1510 bytes --]

Hello world,

the attached patch enables more sophisticated bounds-checking on
array slices by using gfc_dep_difference to calculate extents.
The information may also be useful in other places of the
front end, I don't really know.

There is one wrinkle (alluded to in the comments) which makes
this harder.  When somebody changes the value of a variable
used in detemining the size of an array, such as

subroutine foo(a,n)
  real, dimension(n) :: a

  n = n -2

  print *,ubound(a(n-1:))

we cannot compare n-1 against n and think that their difference is
one :-(

This is why I restricted myself to expressions where all
indices are specified, e.g. in a(n+1:n+4) or none are specified,
as in a(:).

In order for this to work on 64-bit systems, it was necessary
to look through widening integer conversions, so I added that
functionality to discard_nops.  Using this function in
gfc_dep_compare_expr made this function shorter and cleaner.

Regression-tested.  OK for trunk?

	Thomas

2013-08-14  Thomas Koenig  <tkoenig@gcc.gnu.org>

        PR fortran/58146
        * array.c (gfc_ref_dimen_size):  If possible, use
        gfc_dep_difference to calculate array refrence
        sizes.  Fall back to integer code otherwise.
        * dependency.c (discard_nops).  Move up.
        Also discarde widening integer conversions.
        (gfc_dep_compare_expr):  Use discard_nops.

2013-08-14  Thomas Koenig  <tkoenig@gcc.gnu.org>

        PR fortran/58146
        * gfortran.dg/bounds_check_18.f90:  New test.

[-- Attachment #2: p5.diff --]
[-- Type: text/x-patch, Size: 5902 bytes --]

Index: array.c
===================================================================
--- array.c	(Revision 201648)
+++ array.c	(Arbeitskopie)
@@ -2112,6 +2112,7 @@ bool
 gfc_ref_dimen_size (gfc_array_ref *ar, int dimen, mpz_t *result, mpz_t *end)
 {
   mpz_t upper, lower, stride;
+  mpz_t diff;
   bool t;
 
   if (dimen < 0 || ar == NULL || dimen > ar->dimen - 1)
@@ -2130,9 +2131,63 @@ gfc_ref_dimen_size (gfc_array_ref *ar, int dimen,
       break;
 
     case DIMEN_RANGE:
+
+      mpz_init (stride);
+
+      if (ar->stride[dimen] == NULL)
+	mpz_set_ui (stride, 1);
+      else
+	{
+	  if (ar->stride[dimen]->expr_type != EXPR_CONSTANT)
+	    {
+	      mpz_clear (stride);
+	      return false;
+	    }
+	  mpz_set (stride, ar->stride[dimen]->value.integer);
+	}
+
+      /* Calculate the number of elements via gfc_dep_differce, but only if
+	 start and end are both supplied in the reference or the array spec.
+	 This is to guard against strange but valid code like
+
+	 subroutine foo(a,n)
+	 real a(1:n)
+	 n = 3
+	 print *,size(a(n-1:))
+
+	 where the user changes the value of a variable.  If we have to
+	 determine end as well, we cannot do this using gfc_dep_difference.
+	 Fall back to the constants-only code then.  */
+
+      if (end == NULL)
+	{
+	  bool use_dep;
+
+	  use_dep = gfc_dep_difference (ar->end[dimen], ar->start[dimen],
+					&diff);
+	  if (!use_dep && ar->end[dimen] == NULL && ar->start[dimen] == NULL)
+	    use_dep = gfc_dep_difference (ar->as->upper[dimen],
+					    ar->as->lower[dimen], &diff);
+
+	  if (use_dep)
+	    {
+	      mpz_init (*result);
+	      mpz_add (*result, diff, stride);
+	      mpz_div (*result, *result, stride);
+	      if (mpz_cmp_ui (*result, 0) < 0)
+		mpz_set_ui (*result, 0);
+
+	      mpz_clear (stride);
+	      mpz_clear (diff);
+	      return true;
+	    }
+
+	}
+
+      /*  Constant-only code here, which covers more cases
+	  like a(:4) etc.  */
       mpz_init (upper);
       mpz_init (lower);
-      mpz_init (stride);
       t = false;
 
       if (ar->start[dimen] == NULL)
@@ -2163,15 +2218,6 @@ gfc_ref_dimen_size (gfc_array_ref *ar, int dimen,
 	  mpz_set (upper, ar->end[dimen]->value.integer);
 	}
 
-      if (ar->stride[dimen] == NULL)
-	mpz_set_ui (stride, 1);
-      else
-	{
-	  if (ar->stride[dimen]->expr_type != EXPR_CONSTANT)
-	    goto cleanup;
-	  mpz_set (stride, ar->stride[dimen]->value.integer);
-	}
-
       mpz_init (*result);
       mpz_sub (*result, upper, lower);
       mpz_add (*result, *result, stride);
Index: dependency.c
===================================================================
--- dependency.c	(Revision 201648)
+++ dependency.c	(Arbeitskopie)
@@ -240,6 +240,46 @@ gfc_dep_compare_functions (gfc_expr *e1, gfc_expr
 	return -2;      
 }
 
+/* Helper function to look through parens, unary plus and widening
+   integer conversions.  */
+
+static gfc_expr*
+discard_nops (gfc_expr *e)
+{
+  gfc_actual_arglist *arglist;
+
+  if (e == NULL)
+    return NULL;
+
+  while (true)
+    {
+      if (e->expr_type == EXPR_OP
+	  && (e->value.op.op == INTRINSIC_UPLUS
+	      || e->value.op.op == INTRINSIC_PARENTHESES))
+	{
+	  e = e->value.op.op1;
+	  continue;
+	}
+
+      if (e->expr_type == EXPR_FUNCTION && e->value.function.isym
+	  && e->value.function.isym->id == GFC_ISYM_CONVERSION
+	  && e->ts.type == BT_INTEGER)
+	{
+	  arglist = e->value.function.actual;
+	  if (arglist->expr->ts.type == BT_INTEGER
+	      && e->ts.kind > arglist->expr->ts.kind)
+	    {
+	      e = arglist->expr;
+	      continue;
+	    }
+	}
+      break;
+    }
+
+  return e;
+}
+
+
 /* Compare two expressions.  Return values:
    * +1 if e1 > e2
    * 0 if e1 == e2
@@ -255,57 +295,13 @@ gfc_dep_compare_expr (gfc_expr *e1, gfc_expr *e2)
   gfc_actual_arglist *args1;
   gfc_actual_arglist *args2;
   int i;
-  gfc_expr *n1, *n2;
 
-  n1 = NULL;
-  n2 = NULL;
-
   if (e1 == NULL && e2 == NULL)
     return 0;
 
-  /* Remove any integer conversion functions to larger types.  */
-  if (e1->expr_type == EXPR_FUNCTION && e1->value.function.isym
-      && e1->value.function.isym->id == GFC_ISYM_CONVERSION
-      && e1->ts.type == BT_INTEGER)
-    {
-      args1 = e1->value.function.actual;
-      if (args1->expr->ts.type == BT_INTEGER
-	  && e1->ts.kind > args1->expr->ts.kind)
-	n1 = args1->expr;
-    }
+  e1 = discard_nops (e1);
+  e2 = discard_nops (e2);
 
-  if (e2->expr_type == EXPR_FUNCTION && e2->value.function.isym
-      && e2->value.function.isym->id == GFC_ISYM_CONVERSION
-      && e2->ts.type == BT_INTEGER)
-    {
-      args2 = e2->value.function.actual;
-      if (args2->expr->ts.type == BT_INTEGER
-	  && e2->ts.kind > args2->expr->ts.kind)
-	n2 = args2->expr;
-    }
-
-  if (n1 != NULL)
-    {
-      if (n2 != NULL)
-	return gfc_dep_compare_expr (n1, n2);
-      else
-	return gfc_dep_compare_expr (n1, e2);
-    }
-  else
-    {
-      if (n2 != NULL)
-	return gfc_dep_compare_expr (e1, n2);
-    }
-  
-  if (e1->expr_type == EXPR_OP
-      && (e1->value.op.op == INTRINSIC_UPLUS
-	  || e1->value.op.op == INTRINSIC_PARENTHESES))
-    return gfc_dep_compare_expr (e1->value.op.op1, e2);
-  if (e2->expr_type == EXPR_OP
-      && (e2->value.op.op == INTRINSIC_UPLUS
-	  || e2->value.op.op == INTRINSIC_PARENTHESES))
-    return gfc_dep_compare_expr (e1, e2->value.op.op1);
-
   if (e1->expr_type == EXPR_OP && e1->value.op.op == INTRINSIC_PLUS)
     {
       /* Compare X+C vs. X, for INTEGER only.  */
@@ -501,21 +497,6 @@ gfc_dep_compare_expr (gfc_expr *e1, gfc_expr *e2)
 }
 
 
-/* Helper function to look through parens and unary plus.  */
-
-static gfc_expr*
-discard_nops (gfc_expr *e)
-{
-
-  while (e && e->expr_type == EXPR_OP
-	 && (e->value.op.op == INTRINSIC_UPLUS
-	     || e->value.op.op == INTRINSIC_PARENTHESES))
-    e = e->value.op.op1;
-
-  return e;
-}
-
-
 /* Return the difference between two expressions.  Integer expressions of
    the form 
 

[-- Attachment #3: bounds_check_18.f90 --]
[-- Type: text/x-fortran, Size: 231 bytes --]

! { dg-do compile }
program main
  implicit none
  integer :: n
  real, dimension(10) :: a
  n = 0
  call random_number(a)
  if (any(a(n+1:n+5) > [1.0, 2.0, 3.0])) print *,"Hello!" ! { dg-error "not conformable" }
end program main

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [patch, Fortran] PR 58146, enable array slice compile-time bounds checking
  2013-08-14 21:46 [patch, Fortran] PR 58146, enable array slice compile-time bounds checking Thomas Koenig
@ 2013-08-22 21:33 ` Mikael Morin
  2013-08-25 17:05   ` Mikael Morin
  0 siblings, 1 reply; 4+ messages in thread
From: Mikael Morin @ 2013-08-22 21:33 UTC (permalink / raw)
  To: Thomas Koenig; +Cc: fortran, gcc-patches

Le 14/08/2013 23:46, Thomas Koenig a écrit :
> Hello world,
> 
> the attached patch enables more sophisticated bounds-checking on
> array slices by using gfc_dep_difference to calculate extents.
> The information may also be useful in other places of the
> front end, I don't really know.
> 
> There is one wrinkle (alluded to in the comments) which makes
> this harder.  When somebody changes the value of a variable
> used in detemining the size of an array, such as
> 
> subroutine foo(a,n)
>   real, dimension(n) :: a
> 
>   n = n -2
> 
>   print *,ubound(a(n-1:))
> 
> we cannot compare n-1 against n and think that their difference is
> one :-(
> 
> This is why I restricted myself to expressions where all
> indices are specified, e.g. in a(n+1:n+4) or none are specified,
> as in a(:).
> 
> In order for this to work on 64-bit systems, it was necessary
> to look through widening integer conversions, so I added that
> functionality to discard_nops.  Using this function in
> gfc_dep_compare_expr made this function shorter and cleaner.
> 
> Regression-tested.  OK for trunk?
> 
This looks mostly good.  The dependency.c cleanup is nice, I have yet to
understand what the 'end == NULL' condition is for.  I come back to you
soon.

Mikael

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [patch, Fortran] PR 58146, enable array slice compile-time bounds checking
  2013-08-22 21:33 ` Mikael Morin
@ 2013-08-25 17:05   ` Mikael Morin
  0 siblings, 0 replies; 4+ messages in thread
From: Mikael Morin @ 2013-08-25 17:05 UTC (permalink / raw)
  To: Thomas Koenig; +Cc: fortran, gcc-patches

Le 22/08/2013 23:30, Mikael Morin a écrit :
> Le 14/08/2013 23:46, Thomas Koenig a écrit :
>> Hello world,
>>
>> the attached patch enables more sophisticated bounds-checking on
>> array slices by using gfc_dep_difference to calculate extents.
>> The information may also be useful in other places of the
>> front end, I don't really know.
>>
>> There is one wrinkle (alluded to in the comments) which makes
>> this harder.  When somebody changes the value of a variable
>> used in detemining the size of an array, such as
>>
>> subroutine foo(a,n)
>>   real, dimension(n) :: a
>>
>>   n = n -2
>>
>>   print *,ubound(a(n-1:))
>>
>> we cannot compare n-1 against n and think that their difference is
>> one :-(
>>
>> This is why I restricted myself to expressions where all
>> indices are specified, e.g. in a(n+1:n+4) or none are specified,
>> as in a(:).
>>
>> In order for this to work on 64-bit systems, it was necessary
>> to look through widening integer conversions, so I added that
>> functionality to discard_nops.  Using this function in
>> gfc_dep_compare_expr made this function shorter and cleaner.
>>
>> Regression-tested.  OK for trunk?

OK.
By the way, it seems that arg1 and arg2 declarations should be removed
in gfc_dep_compare_expr.

>>
> This looks mostly good.  The dependency.c cleanup is nice, I have yet to
> understand what the 'end == NULL' condition is for.  I come back to you
> soon.
> 
For what it's worth, 'end' is non-NULL in only one place and there the
gfc_ref_dimen_size result is discarded.  Thus its handling should better
be inlined or have its own function separate from gfc_ref_dimen_size.
It seems to be a lot of trouble for little gain though.

Mikael

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [patch, Fortran] PR 58146, enable array slice compile-time bounds checking
@ 2013-08-15 19:19 Dominique Dhumieres
  0 siblings, 0 replies; 4+ messages in thread
From: Dominique Dhumieres @ 2013-08-15 19:19 UTC (permalink / raw)
  To: fortran; +Cc: gcc-patches, tkoenig

Thomas,

The two lines

   gfc_actual_arglist *args1;
   gfc_actual_arglist *args2;

in gfc_dep_compare_expr must be removed. Otherwise
the compilation aborts with

.../../work/gcc/fortran/dependency.c: In function 'int gfc_dep_compare_expr(gfc_expr*, gfc_expr*)':
../../work/gcc/fortran/dependency.c:295:23: error: unused variable 'args1' [-Werror=unused-variable]
   gfc_actual_arglist *args1;
                       ^
../../work/gcc/fortran/dependency.c:296:23: error: unused variable 'args2' [-Werror=unused-variable]
   gfc_actual_arglist *args2;
                       ^

TIA

Dominique

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2013-08-25 16:48 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-08-14 21:46 [patch, Fortran] PR 58146, enable array slice compile-time bounds checking Thomas Koenig
2013-08-22 21:33 ` Mikael Morin
2013-08-25 17:05   ` Mikael Morin
2013-08-15 19:19 Dominique Dhumieres

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).