ARSC T3E Users' Newsletter 170, June 4, 1999
Passing Fortran CHARACTER arrays to C functions
One of our J90 users ran into trouble this week when calling a C function from Fortran. The troublesome parameter was a Fortran character array.The solution is to include "fortran.h" in the C program, make string variables of type "_fcd" instead of "char*", and use SGI's access functions, _cptofcd, _fcdtocp, _fcdlen, _btol, and _ltob to access the string.
What follows is a longer version of this story. It goes into more depth concerning interlanguage communication as did a related article in newsletter #125:
/arsc/support/news/t3enews/t3enews125/index.xml
To get started, our J90 user knew the rule that Fortran is always pass-by-reference. He made the logical assumption that the C function parameters should be addresses, and in particular, that the string should be a character address. From the code:FORTRAN:
integer ierr
character*50 command
ierr = 0
command = 'ls -l'//char(0)
call simple(command,ierr)
print *,'Back in Fortran: ierr=', ierr
C:
void SIMPLE (command,ierr)
char* command;
int* ierr;
This sort of worked.
The Fortran syntax is correct. The line:
command = 'ls -l'//char(0)
uses the Fortran character concatenation operator, "//", to append an ASCII NULL (i.e., the "char(0)") to the end of the desired string. This is for the benefit of C, which expects NULL terminated strings.
The line:
call simple(command,ierr)
is also correct. On the Fortran side, you pass actual parameters as-is.
Things are more interesting on the C-side. For starters, the function declaration:
void SIMPLE (command,ierr)
is correct. For the CF90 compiler to find the C function, the name MUST be shifted to uppercase. Fortran is a case-insensitive language, and CF90 implements this by shifting the entire Fortran code to uppercase. C and the linker, however, are sensitive to case and don't alter it.
Thus, in this example, the linker will expect an external function, SIMPLE (the CF90-shifted version of "simple"), and it will find it because the C code explicitly names the function "SIMPLE." (NOTE: the SGI MIPSpro7 Fortran 90 compiler for IRIX reverses this rule: it shifts everything to lower-case and appends a "_" to function names. Thus, in IRIX, the correct name for the C function would be "simple_". Confusing?)
Also correct is the "void" type given the C function. The function is treated on the Fortran side as a subroutine (not a function), and thus it shouldn't return a value.
Also correct is the declaration of the "ierr" dummy parameter:
int* ierr;
Incorrect
is the declaration of the string:
char* command;
Here's the entire C function:
void SIMPLE (command, ierr)
char* command;
int* ierr;
{
printf ("In C: value of command: \"%s\"\n", command);
*ierr = system ( command );
if (*ierr != 0)
perror ("In C: error from \"system\": ");
}
The incorrect declaration produced different failures on the J90 and T3E. On either system, the initial "printf" worked. Here are two sample runs:
J90 RUN:
chilkoot$ ./bad
In C: value of command: "ls -l"
In C: error from "system": : Bad address
Back in Fortran: ierr= 32512
chilkoot$
T3E RUN:
yukon$ ./bad
In C: value of command: "ls -l"
total 13344
-rw------- 1 baring staff 186 Jun 3 16:57 FCD
-rw------- 1 baring staff 143 Jun 3 14:39 Makefile
-rwx------ 1 baring staff 1365232 Jun 4 11:37 a.out
SIGNAL: Operand range error ( [32] memory management fault)
Beginning of Traceback (PE 0):
Interrupt at address 0x800001388 in routine 'SIMPLE'.
Called from line 10 (address 0x8000014e8) in routine 'SIMPLEF'.
Called from line 475 (address 0x800000c98) in routine '$START$'.
End of Traceback.
Operand range error(coredump)
yukon$
EXPLANATION?
J90:
"printf" liked the address of "command", but "system" didn't.
"system" calls "fork" which calls "exec" which starts "/bin/sh" which is passed the original character address. I'm assuming that the Fortran address space on the J90 is outside the bounds for one of these calls.
"system" handled the error and returned, "perror" printed the message "Bad address," the C function returned, and the Fortran program was able to continue. (See "man perror" for more on this useful routine.)
T3E:Both "printf" AND "system" liked the address of "command." As the output shows, "system" successfully executed "ls -l" and then the program crashed.
The operand range error occurred when "system" returned and the process tried to store the return value to *ierr. The problem is misalignment of the "ierr" variable and it's caused by the same programming mistake, declaring "char* command."
Given the C function declaration:
void SIMPLE (command,ierr)
char* command;
int* ierr;
"command" is expected on byte 0 of the argument stack, and "ierr" is expected on byte 8.
On the J90, a Fortran character descriptor takes 8 bytes. Thus, given this call:
call simple(command,ierr)
Fortran puts the address of ierr at byte 8 of the argument stack. This happens to coincide with the C function's view of the call stack.
On the T3E, however, a Fortran character descriptor takes 16 bytes (twice the size of the C character pointer). Thus, Fortran puts the address for ierr at byte 16. The C function treats the second byte of "command" as the address of ierr and, not surprisingly, it causes an ORE.
THE CORRECT WAYFrom the "Cray C/C++ Reference Manual"
Sec. 8.1.2.4
Logical and character data need special treatment for calls between
C/C++ and Fortran. Fortran has a character descriptor that is
incompatible with a character pointer in C/C++.
The solution is to include "fortran.h" in the C program, declare "command" to be of type "_fcd", and use SGI's access functions. From "man cptofcd":
NAME
_cptofcd, _fcdtocp, _fcdlen, _btol, _ltob
Passes character strings and logical values between Standard C and
Fortran
CORRECTED PROGRAM
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! Fortran program: simplef.f
program simplef
integer ierr
character*50 command
ierr = 0
command = 'ls -l'//char(0)
call simple(command,ierr)
print *,'Back in Fortran: ierr=', ierr
end
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
/***********************************************************
* C program: goodsimplec.c
***********************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <fortran.h>
void SIMPLE(command, ierr)
_fcd command;
int* ierr;
{
printf ("In C: value of command: \"%s\"\n", _fcdtocp(command) );
*ierr = system ( _fcdtocp(command) );
if (*ierr != 0)
perror ("In C: error from \"system\": ");
}
/***********************************************************/
Compiling and Linking:
yukon$ cc -c goodsimplec.c; yukon$ f90 simplef.f goodsimplec.o -o goodRunning:
yukon$ ./good In C: value of command: "ls -l" total 9248 -rw------- 1 baring staff 143 Jun 3 14:38 Makefile -rwx------ 1 baring staff 1474672 Jun 4 09:40 a.out -rwx------ 1 baring staff 1488352 Jun 4 11:50 bad Back in Fortran: ierr= 0Final Note:
The Fortran function, ISHELL, lets Fortran programmers run shell commands from within a program. Our J90 user didn't need a wrapper to the C "system" routine: he could just use ISHELL.
New (to us) Qstat Modes
[ Thanks to Alan Wallcraft of NRL for this tip. ]The "-m" option can be given to qstat in combination with other options, such as "-a" or "-c". It adds columns detailing resource requests, limits, and usage. It's a fast way to see how much time your request has used (for lots of detail, use "qstat -f <IDENTIFIER>").
yukon 1> qstat -cm
-------------------------------------------
NQS 3.3.0.5 QUEUE COMPLEX MPP LIMITS: yukon
-------------------------------------------
QUEUE COMPLEX RUN PE'S
--------------- --- --- ------ ------
cgcp 2/1 256/128
cnormal 60/3 256/120
yukon 2> qstat -am
-------------------------------------
NQS 3.3.0.5 BATCH REQUEST MPP SUMMARY
-------------------------------------
IDENTIFIER NAME USER PE'S REQUEST-TIME P-TIME ST
LIM USED LIM USED LIM
------------- ------- ------- ---- ---- ------ ------ ------ ---
1891.yukon myjob smithw 128 128 28800 2109 <28800> R03
1880.yukon myproj wsmith 128 300 <1800> Qge
1881.yukon mywk_1 jones 128 300 <1800> Qge
1895.yukon mywk_2 jones 70 70 1800 671 <1800> R04
1892.yukon mytask johns 18 18 28800 6352 <28800> R03
1885.yukon myprog johnsj 32 32 28800 16933 28800 R04
1868.yukon myprj jonhsj 100 14400 <14400> Qge
1897.yukon mywd joans 64 14400 14400 Qge
no pipe queue entries
SGI On-line Technical Publications
http://techpubs.sgi.com/ is SGI's new server for its library of technical publications. It has the search function on page one and seems fast. I used "techpubs" a lot for the above article, searching on "interlanguage" in both the "Unicos/mk" and "IRIX 6.5" libraries to started.
Quick-Tip Q & A
A:{{ What's the best restaurant in Minneapolis and why? }}
Thanks to those who responded.
Buca's - Italian family restaurants - huge portions nice real food.
Decent price also. Minneapolis - downtown
Vintage - Great wines by the bottle, excellent food, live music Sunday
- Tuesday. Price is about $20.00/plate St. Paul
Mud Pie - Vegetarian place, good food, good prices Minneapolis,
'Uptown' area.
Da Afgan - excellent middle eastern food, good prices - Near the
airport
La Cucaracha - Downtown. You wouldn't expect great Mexican food so far
from the border, 'less you were from Fairbanks. Decent chile
relleno.
Chiang Mai Thai - Uptown. Spiciness index: "mild, medium, hot, or
crazy hot".
Q: C header files always end in ".h". Why the different conventions
for Fortran? For instance:
include 'mpif.h'
include 'VT.inc'
include "mpp/shmem.fh"
[ Answers, questions, and tips graciously accepted. ]
Current Editors:
E-mail Subscriptions:
Ed Kornkven ARSC HPC Specialist ph: 907-450-8669 Kate Hedstrom ARSC Oceanographic Specialist ph: 907-450-8678 Arctic Region Supercomputing Center University of Alaska Fairbanks PO Box 756020 Fairbanks AK 99775-6020
-
Subscribe to (or unsubscribe from) the e-mail edition of the
ARSC HPC Users' Newsletter.
-
Back issues of the ASCII e-mail edition of the ARSC T3D/T3E/HPC Users' Newsletter are available by request. Please contact the editors.
