Example from Scott Belmonte
Date: Tue, 23 May 2000 10:48:55 +0100 (BST)
From: "Scott A. Belmonte"
To: "L. Cranswick" [[email protected]]
Subject: Re: What does it mean when a Fortran program does this?
On Mon, 22 May 2000, L. Cranswick wrote:
>
> Hi Scott,
>
> have been increasing the arrays of a fortran
> program to get it to handle a larger problem on
> a Digital Alpha machine - but it gives an error like:
>
> 22879:maps: /sbin/loader: Fatal Error: cannot map Main
> pxsv6% ./maps
> 18152:./maps: /sbin/loader: Fatal Error: cannot map Main
>
>
> What does this means - arrays going over each-other or
> something like this?
>
I've not come across this exact problem before, and I would need to see
the code and the actual compilation instructions, but judging by what you
are doing to the code, it sounds like the arrays you are defining are to
big to be allocated on the stack. You can do two things to get round
this:
1) The stack usage is normally limited by the shell. Type ulimit -a to see
the restrictions. If you are running csh/tcsh then there is not much you
can do about is because for some reason it does not let you increase the
limit. You could try running bash, it lets you change the limits. Change
the stack limit using ulimit -s #, where # is the number of 512-byte
blocks to set the limit to, or ulimit -s unlimited to grab as much as
you're allowed. If this does not work then go to 2.
2) Dynamically allocate the memory at run time. This actually is always
the best thing to do when data arrays start to become very big and has the
advantage that you don't need to know the size of the array before you run
the program. There is no standard way to dynamically allocate objects in
f77. You will have to use the libc malloc() function. You can do dynamic
allocation in Fortran 90. I'm not very hot in Fortran 90 (I'm a C
man myself) but something like this should work:
PROGRAM alloc_example
C Allocate a 1-d real array with N elements
INTEGER N, ALLOC_ERR
REAL, ALLOCATABLE :: DATA(:)
C You would calculate N at run time or see it to the valus you want.
N = 10
C ALLOC_ERR is set to a positive integer (i.e. not zero) if an error
C occur with the allocation, such as out-of-memory.
ALLOCATE (DATA(N), STAT = ALLOC_ERR)
IF (ALLOC_ERR.NE.0) THEN
WRITE(*,*) 'ERROR: Could not alloc memory'
STOP
END IF
C Zero array, or do what ever you want to do with it.
DO I = 1,N
DATA(I) = 0.0
END DO
C Once you finished, deallocate the memory.
DEALLOCATE(DATA)
END
Digital Alpha has a Fortran 90 compiler (f90) but it might be a nightmare
trying to compile old f77 code with it.
There is another possiblity that the linker, for some reason, cannot find
the main block. This is entirely due to the compilation. But seeing
that it has compiled and worked before (I assume) then this is unlikely to
be the problem.
I hope this helps. I can help you with incorporating malloc into the code
but linking with c routines is quite non-portable so once you have changed
the code to work with Alpha compilers you may ahve to customise for any
other machine that you want to compile on.
Scott.
---------------------------------------------------------------------
Dr Scott A. Belmonte
University of Edinburgh
Room C18A
Daresbury Laboratory, Daresbury
Warrington
WA4 4AD
---------------------------------------------------------------------
Example from Dave Love
Date: Thu, 25 May 2000 16:42:49 +0100
To: [email protected]
Subject: dynamic allocation
From: Dave Love
User-Agent: Gnus/5.0807 (Gnus v5.8.7) Emacs/21.0.90
Here's a trivial example of f90 automatic arrays.
$ cat alloc.f
subroutine allocs (n)
real vector (n)
do i=1,n
vector (i) = i
end do
print *, vector
end
print *, 'Array dimension?'
read *, n
call allocs (n)
end
$ g77 alloc.f && ./a.out
Array dimension?
10
1. 2. 3. 4. 5. 6. 7. 8. 9. 10.
$
-----------------------
Here's the C part of the support I wrote for CCP4:
/* \section{Dynamic memory allocation} */
/* It's nice to be able to determine array sizes at run time to avoid */
/* messy recompilation. The only way effectively to get dynamic */
/* allocation in Fortran77 reasonably portably is to do the allocation, */
/* e.g.\ in C, and invoke the Fortran routine passed as a parameter with */
/* pointers to the allocated memory which it will treat as arrays. If we */
/* want to allow more than one array, it's more tricky. */
/* */
/* \subsection{{\tt subroutine ccpal1 (\meta{routne}, \meta{n}. */
/* \meta{type}, \meta{length})}} */
/* Arranges to call subroutine \meta{routne} with \meta{n} array */
/* arguments. Each has a type indicated by \meta{type}$(i)$ and a length */
/* given by \meta{length}($i$). \meta{type} is an integer array with */
/* values 1, 2, 3, 4 inidcating {\tt */
/* INTEGER}, {\tt REAL}, {\tt DOUBLE PRECISION} and {\tt COMPLEX} */
/* respectively. */
/* It's not immediately clear what all the Fortran/C */
/* conventions are for passing [[CHARACTER]] arrays, so we'll arrange a */
/* higher-level interface and have [[types]] here just numeric. The */
/* Fortran ([[CCPALC]]) will also do argument validation. Also the rules */
/* for passing external routines as arguments aren't clear---assume */
/* the obvious way. */
/* */
/* There's a \idx{VMS} Fortran version of this, although the code here */
/* does work fine in VMS\@. */
/* */
/* NB: there's a possibility of a hook here to use memory-mapped files on */
/* systems with the capability and insufficient VM\@. */
/* */
/* Under protest, this now allocates zeroed storage for where programs */
/* make bad assumptions. */
/* */
/* <miscellaneous routines>= */
#ifndef VMS /* we'll use the Fortran version in VMS*/
#ifndef _MVS
#if CALL_LIKE_HPUX
void ccpal1 (routne, n, type, length)
void (* routne) ();
int *n, type[], length[];
#endif
#if defined (VMS) || CALL_LIKE_STARDENT
void CCPAL1 (void (* routne) (), int *n, int type[], int length[])
#endif
#if CALL_LIKE_SUN
void ccpal1_ (void (* routne) (), int *n, int type[], int length[])
#endif
#if CALL_LIKE_MVS
void __stdcall CCPAL1 (void (* routne) (), int *n, int type[], int length[])
#endif
{
int i, size, *leng[13];
void *pointer[13];
for (i=0; i<*n; i++) {
switch (type[i]) {
case 1:
size = item_sizes[6]; break; /* integer */
case 2:
size = item_sizes[2]; break; /* real */
case 3:
size = 2*item_sizes[2]; break; /* double */
case 4:
size = 2*item_sizes[2]; break; /* complex */
case 5:
size = item_sizes[1]; break; /* bytes (logical or integer *1) */
}
pointer[i+1] = calloc ((size_t) length[i], (size_t) size);
if (pointer[i+1] == NULL) fatal ("CCPALC: can't allocate memory");
leng[i+1] = &(length[i]); /* convenience */
}
switch (*n) {
case 1:
(* routne) (leng[1], pointer[1]);
break;
case 2:
(* routne) (leng[1], pointer[1], leng[2], pointer[2]);
break;
case 3:
(* routne) (leng[1], pointer[1], leng[2], pointer[2],
leng[3], pointer[3]);
break;
case 4:
(* routne) (leng[1], pointer[1], leng[2], pointer[2],
leng[3], pointer[3], leng[4], pointer[4]);
break;
case 5:
(* routne) (leng[1], pointer[1], leng[2], pointer[2],
leng[3], pointer[3], leng[4], pointer[4],
leng[5], pointer[5]);
break;
case 6:
(* routne) (leng[1], pointer[1], leng[2], pointer[2],
leng[3], pointer[3], leng[4], pointer[4],
leng[5], pointer[5], leng[6], pointer[6]);
break;
case 7:
(* routne) (leng[1], pointer[1], leng[2], pointer[2],
leng[3], pointer[3], leng[4], pointer[4],
leng[5], pointer[5], leng[6], pointer[6],
leng[7], pointer[7]);
break;
case 8:
(* routne) (leng[1], pointer[1], leng[2], pointer[2],
leng[3], pointer[3], leng[4], pointer[4],
leng[5], pointer[5], leng[6], pointer[6],
leng[7], pointer[7], leng[8], pointer[8]);
break;
case 9:
(* routne) (leng[1], pointer[1], leng[2], pointer[2],
leng[3], pointer[3], leng[4], pointer[4],
leng[5], pointer[5], leng[6], pointer[6],
leng[7], pointer[7], leng[8], pointer[8],
leng[9], pointer[9]);
break;
case 10:
(* routne) (leng[1], pointer[1], leng[2], pointer[2],
leng[3], pointer[3], leng[4], pointer[4],
leng[5], pointer[5], leng[6], pointer[6],
leng[7], pointer[7], leng[8], pointer[8],
leng[9], pointer[9], leng[10], pointer[10]);
break;
case 11:
(* routne) (leng[1], pointer[1], leng[2], pointer[2],
leng[3], pointer[3], leng[4], pointer[4],
leng[5], pointer[5], leng[6], pointer[6],
leng[7], pointer[7], leng[8], pointer[8],
leng[9], pointer[9], leng[10], pointer[10],
leng[11], pointer[11]);
break;
case 12:
(* routne) (leng[1], pointer[1], leng[2], pointer[2],
leng[3], pointer[3], leng[4], pointer[4],
leng[5], pointer[5], leng[6], pointer[6],
leng[7], pointer[7], leng[8], pointer[8],
leng[9], pointer[9], leng[10], pointer[10],
leng[11], pointer[11], leng[12], pointer[12]);
break;
}
for (i=0; i<*n; i++)
free (pointer[i+1]);
}
-----------------------
And the Fortran:
SUBROUTINE CCPALC(ROUTNE, N, TYPE, LENGTH)
C ==========================================
C
C Arrange to call subroutine ROUTNE with N array arguments each of
C length LENGTH (i) and type indicated by TYPE (i): 'i' == integer,
C 'r' == real, 'd' == double precision, 'c' == complex, 'b' ==
C "byte" (logical*1 or integer*1, unportable and deprecated) . TYPE
C elements may have either case.
C Consider `call ccpalc (fred, 3, types, lens)' with types = (/'i',
C 'r', 'c'/) and lens = (/1000, 2000, 3000/). This effectively does
C call fred (1000, arr1, 2000, arr2, 3000, arr3)
C with
C subroutine fred (n1, foo, n2, bar, n3, baz)
C integer n1, n2, n3, foo (n1)
C real bar (n2)
C complex baz (n3)
C ...
C Obviously all communication with ROUTNE must be by COMMON (or,
C possibly, extra ENTRYs). The allocated memory is freed on return
C from ROUTNE. As a concession, it's initially filled with zeroed
C bytes.
C
C Arguments:
C ==========
C
C ROUTNE (I) EXTERNAL: routine to call
C N (I) INTEGER: number of arguments to ROUTNE (<=12)
C TYPE (I) CHARACTER*1 (*): type of arguments to ROUTNE:
C 'I': INTEGER; 'R': REAL; 'D': DOUBLE PRECISION;
C 'C': COMPLEX; 'B': LOGICAL*1 or INTEGER*1
C LENGTH (I) INTEGER*(*): number of elements in each (array)
C argument of ROUTNE
C_END_CCPALC
C
C .. Scalar Arguments ..
INTEGER N
C ..
C .. Array Arguments ..
CHARACTER TYPE (*)
INTEGER LENGTH (*)
C ..
EXTERNAL ROUTNE, CCPAL1, CCPUPC
INTEGER I, ITYPE (12)
CHARACTER TTYPE (12)
C ..
IF (N.LT.1 .OR. N.GT.12)
+ CALL CCPERR (1, 'CCPALC: bad number of arguments')
DO 10 I=1,N
TTYPE (I) = TYPE (I)
CALL CCPUPC (TTYPE (I))
ITYPE (I) = INDEX ('IRDCB', TTYPE (I))
IF (ITYPE (I) .EQ. 0) CALL CCPERR (1, 'CCPALC: bad TYPE: '//
+ TYPE (I))
IF (LENGTH (I).LE.0) CALL CCPERR (1, 'CCPALC: length <=0')
10 CONTINUE
CALL CCPAL1 (ROUTNE, N, ITYPE, LENGTH)
END
C
C
C_BEGIN_CCPALE
SUBROUTINE CCPALE(ROUTNE, N, TYPE, LENGTH, LENDEF, PRINT)
C =================================================
C
C Arrange to call subroutine ROUTNE with N array arguments each of
C length LENGTH (i) and type indicated by TYPE (i): 'i' == integer,
C 'r' == real, 'd' == double precision, 'c' == complex, 'b' == byte.
C TYPE elements may have either case. LENGTH points to an array of
C environment variable (logical) names from which integer values are
C read. The lengths default to values from LENDEF.
C This is a convenient interface to CCPALC to allow configuring of
C the memory requirements on the command line where appropriate.
C This may be useful if the memory requirements can't be determined
C initially and it's necessary to guess.
C
C Arguments:
C ==========
C
C ROUTNE (I) EXTERNAL: routine to call
C N (I) INTEGER: number of arguments to ROUTNE (<=12)
C TYPE (I) CHARACTER*1 (*): type of arguments to ROUTNE:
C 'I': INTEGER; 'R': REAL; 'D': DOUBLE PRECISION;
C 'C': COMPLEX; 'B': LOGICAL*1 or INTEGER*1
C LENGTH (I) CHARACTER *(*): logical names representing the number
C of elements in each (array) argument of ROUTNE
C LENDEF (I) INTEGER (*): default lengths for the argument arrays
C used if the appropriate LENGTH argument doesn't represent a
C defined logical
C PRINT (I) LOGICAL: whether or not to print the values of the
C array lengths
C_END_CCPALE
C
C .. Scalar Arguments ..
INTEGER N
LOGICAL PRINT
C ..
C .. Array Arguments ..
CHARACTER TYPE (*), LENGTH (*)*(*)
INTEGER LENDEF (*)
C ..
EXTERNAL ROUTNE, CCPE2I, CCPALC, LUNSTO
INTEGER I, LENG (12), CCPE2I, LUNSTO
C ..
DO 10 I=1,N
LENG (I) = CCPE2I (LENGTH (I), LENDEF (I))
10 CONTINUE
IF (PRINT) THEN
WRITE (LUNSTO(1),
+ '(/'' Memory allocation (logical name, type, elements):'')')
WRITE (LUNSTO(1), '(3X, A, 1X, A, 3X, I10)')
+ (LENGTH (I), TYPE (I), LENG (I), I=1,N)
ENDIF
CALL CCPALC (ROUTNE, N, TYPE, LENG)
END