Bug 33298 - Wrong code for SPREAD on zero-sized arrays
Summary: Wrong code for SPREAD on zero-sized arrays
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: libfortran (show other bugs)
Version: 4.3.0
: P3 normal
Target Milestone: 4.3.0
Assignee: Thomas Koenig
URL:
Keywords: wrong-code
Depends on:
Blocks:
 
Reported: 2007-09-04 07:20 UTC by Toon Moene
Modified: 2007-09-06 21:23 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail: 4.3.0
Last reconfirmed: 2007-09-04 21:03:23


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Toon Moene 2007-09-04 07:20:41 UTC
This code:

      REAL X(2,3), Y(2)
      Y=[1.,2.]
      CALL SUB(X,Y)
      DO I = 1, 3
         PRINT*,X(:,I)
      ENDDO
      END
      SUBROUTINE SUB(A,B)
      REAL A(:,:), B(:)
      A(:,:) = SPREAD(B(:),2,SIZE(A,2))
      END

results in:

   0.000000       0.000000    
   0.000000       0.000000    
   0.000000       0.000000    

using:

/usr/snp/bin/gfortran -static -v -g -O2 -fbacktrace spread.f
Driving: /usr/snp/bin/gfortran -static -v -g -O2 -fbacktrace spread.f -lgfortranbegin -lgfortran -lm
Using built-in specs.
Target: x86_64-unknown-linux-gnu
Configured with: ../trunk/configure --prefix=/usr/snp/ --disable-multilib --disable-nls --enable-languages=fortran
Thread model: posix
gcc version 4.3.0 20070903 (experimental) (GCC) 
 /usr/snp/bin/../libexec/gcc/x86_64-unknown-linux-gnu/4.3.0/f951 spread.f -ffixed-form -quiet -dumpbase spread.f -mtune=generic -auxbase spread -g -O2 -version -fbacktrace -fintrinsic-modules-path /usr/snp/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.3.0/finclude -o /tmp/ccjGK59d.s
GNU F95 (GCC) version 4.3.0 20070903 (experimental) (x86_64-unknown-linux-gnu)
        compiled by GNU C version 4.3.0 20070903 (experimental), GMP version 4.2.1, MPFR version 2.3.0-rc1.
GGC heuristics: --param ggc-min-expand=30 --param ggc-min-heapsize=4096
 as -V -Qy -o /tmp/cc53anSf.o /tmp/ccjGK59d.s
GNU assembler version 2.17.90 (x86_64-linux-gnu) using BFD version (GNU Binutils for Debian) 2.17.90.20070812
 /usr/snp/bin/../libexec/gcc/x86_64-unknown-linux-gnu/4.3.0/collect2 -m elf_x86_64 -static /usr/lib/../lib64/crt1.o /usr/lib/../lib64/crti.o /usr/snp/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.3.0/crtbeginT.o -L/usr/snp/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.3.0 -L/usr/snp/bin/../lib/gcc -L/usr/snp/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.3.0/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/snp/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.3.0/../../.. /tmp/cc53anSf.o -lgfortranbegin -lgfortran -lm --start-group -lgcc -lgcc_eh -lc --end-group /usr/snp/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.3.0/crtend.o /usr/lib/../lib64/crtn.o

Debian's testing gfortran-4.2 gave me another bunch of nonsense:
 -4.5149084E-10  4.5916347E-41
 -4.5151327E-10  4.5916347E-41
 -4.5151283E-10  4.5916347E-41

Trying to print the SPREAD expression directly gave me a segmentation fault using the above gfortran 4.2 and an empty line using 4.3.
Comment 1 jpr 2007-09-04 07:49:03 UTC
The code is invalid without explicit interface to SUB().
Modified code:

      REAL X(2,3), Y(2)
      Y=[1.,2.]
      CALL SUB(X,Y)
      DO I = 1, 3
         PRINT*,X(:,I)
      ENDDO

CONTAINS

      SUBROUTINE SUB(A,B)
      REAL A(:,:), B(:)
      A(:,:) = SPREAD(B(:),2,SIZE(A,2))
      END SUBROUTINE SUB
      END

works just fine. Alternatively

      REAL X(2,3), Y(2)
      Y=[1.,2.]
      CALL SUB(X,Y,size(y,1),size(y,2))
      DO I = 1, 3
         PRINT*,X(:,I)
      ENDDO
      END

      SUBROUTINE SUB(A,B,n,m)
      INTEGER n,m
      REAL A(n,m), B(n)
      A(:,:) = SPREAD(B(:),2,SIZE(A,2))
      END

also works as expected.

If the main program and SUB are compiled together, the compiler could
of course diagnose this, as e.g. pathscale does:

      SUBROUTINE SUB(A,B)
                 ^
pathf95-1277 pathf90: ERROR SUB, File = t.f90, Line = 9, Column = 18
  Procedure "SUB" is referenced at line 3 (t.f90).  It must have an explicit interface specified.

Regards, Juha
Comment 2 Tobias Burnus 2007-09-04 07:57:37 UTC
CLOSE as invalid as subroutines with assumed-shaped dummy arguments require an explicit interface.

The whole-file checking - which would diagnose this as error - is planned for GCC 4.4.0. See http://gcc.gnu.org/wiki/GFortran43 and, e.g., PR 26227.
Comment 3 Toon Moene 2007-09-04 08:11:30 UTC
Yeah, I have to come up with a better example.  In the original code that I reduced, the interface came from a module file.
Comment 4 Toon Moene 2007-09-04 10:15:34 UTC
Second try, now with interface and using zero sized arrays:

$ cat spread.f
      INTERFACE SUB
      SUBROUTINE SUB(P,Q)
      REAL, INTENT(OUT) :: P(:,:)
      REAL, INTENT(IN)  :: Q(:)
      END SUBROUTINE
      END INTERFACE
      REAL, ALLOCATABLE :: X(:,:), Y(:)
      ALLOCATE(X(0,3))
      ALLOCATE(Y(0))
!     Y=[1.,2.]
      CALL SUB(X,Y)
      DO I = 1, 3
         PRINT*,X(:,I)
      ENDDO
      END
      SUBROUTINE SUB(A,B)
      REAL, INTENT(OUT) :: A(:,:)
      REAL, INTENT(IN)  :: B(:)
      A(:,:) = SPREAD(B(:),2,SIZE(A,2))
      END

$ /usr/snp/bin/gfortran -g -O2 -static -fbacktrace spread.f
$ ./a.out

Program received signal 11 (SIGSEGV): Segmentation fault.

Backtrace for this error:
  + function __restore_rt (0x4185D0)
    from file libgcc2.c
  + function memcpy (0x432D85)
  + function spread_internal (0x40A50F)
    at line 145 of file spread_generic.c
  + function sub_ (0x400404)
    at line 16 of file spread.f
  + in the main program
    at line 11 of file spread.f
  + function __libc_start_main (0x4138E7)
Comment 5 Francois-Xavier Coudert 2007-09-04 10:51:29 UTC
Reduced testcase:
  real :: x(0,3), y(0)
  x = spread(y,2,3)
  end

Backtrace:
#0  0x0000000000431600 in memcpy ()
#1  0x000000000040400f in spread_internal (ret=<value optimized out>,
    source=<value optimized out>, along=<value optimized out>,
    pncopies=<value optimized out>, size=4)
    at ../../../../trunk2/libgfortran/intrinsics/spread_generic.c:148
#2  0x0000000000400342 in MAIN__ () at a.f90:2
Comment 6 Toon Moene 2007-09-04 13:04:34 UTC
Quoting spread_generic.c:

145      for (n = 0; n < ncopies; n++)
146        {
147          memcpy (dest, sptr, size);
148          dest += rdelta;
149        }

The C 99 Standard has the following to say about the mem* functions (7.21.2.1 ff):   

Where an argument declared as size_t n specifies the length of the array for a
function, n can have the value zero on a call to that function. Unless explicitly stated otherwise in the description of a particular function in this subclause, pointer arguments on such a call shall still have valid values, as described in 7.1.4.

So "size" can be zero, *but the the pointer arguments on such a call shall still have valid values.*
Comment 7 Thomas Koenig 2007-09-04 21:03:23 UTC
This one should be fairly straightforward.

Mine :-)
Comment 8 Toon Moene 2007-09-06 08:56:43 UTC
Wouldn't it be an option to simply bail out early (i.e., after the error checks) in case of size == 0 ?

E.g., like this:

     62
     63   rrank = srank + 1;
     64   if (rrank > GFC_MAX_DIMENSIONS)
     65     runtime_error ("return rank too large in spread()");
     66
     67   if (*along > rrank)
     68       runtime_error ("dim outside of rank in spread()");

          if (size == 0)
            return

Or do we actually have to set something on return ?
Comment 9 patchapp@dberlin.org 2007-09-06 11:57:21 UTC
Subject: Bug number PR 33298

A patch for this bug has been added to the patch tracker.
The mailing list url for the patch is http://gcc.gnu.org/ml/gcc-patches/2007-09/msg00394.html
Comment 10 Thomas Koenig 2007-09-06 19:25:46 UTC
Subject: Bug 33298

Author: tkoenig
Date: Thu Sep  6 19:25:30 2007
New Revision: 128206

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=128206
Log:
2007-09-06  Thomas Koenig  <tkoenig@gcc.gnu.org>

	PR fortran/33298
	* intrinsics/spread_generic.c(spread_internal): Enable
	bounds checking by comparing extents if the bounds_check
	option has been set.  If any extent is <=0, return early.

2007-09-06  Thomas Koenig  <tkoenig@gcc.gnu.org>

	PR fortran/33298
	* spread_zerosize_1.f90:  New test case.
	* spread_bounds_1.f90:  New test case.


Added:
    trunk/gcc/testsuite/gfortran.dg/spread_bounds_1.f90
    trunk/gcc/testsuite/gfortran.dg/spread_zerosize_1.f90
Modified:
    trunk/gcc/testsuite/ChangeLog
    trunk/libgfortran/ChangeLog
    trunk/libgfortran/intrinsics/spread_generic.c

Comment 11 tkoenig@alice-dsl.net 2007-09-06 21:13:53 UTC
Subject: Re:  Wrong code for SPREAD on zero-sized
	arrays

Hi Toon,

> 
> ------- Comment #8 from toon at moene dot indiv dot nluug dot nl  2007-09-06 08:56 -------
> Wouldn't it be an option to simply bail out early (i.e., after the error
> checks) in case of size == 0 ?
> 

I think size is the size of an element in the array (in bytes), so
this wouldn't really help.

	Thomas

Comment 12 Thomas Koenig 2007-09-06 21:23:14 UTC
Fixed on trunk.  Closing.