IARGCOUNT, IARGPTR

IARGCOUNT, IARGPTR

Is there anything equivalent to the VMS Fortran functions IARGCOUNT and
IARGPTR available for the Intel Fortran compiler on Linux (IA32)?

In case the answer is no, I had a look at the assembler output of the
compiler. It seems that it should be possible to write this function
with a bit of assembler code, assuming the qualifier
-fno-omit-frame-pointer was used to compile the Fortran code. Comments,
suggestions, pointers, internal Fortran RTL routines?

16 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

No - unlike on VMS there is no count information in the argument list so IARGCOUNT is not feasible. IARGPTR has limited usefulness, especially on Intel 64 and IA-64 where arguments may be in registers or the stack.

Retired 12/31/2016

Steve, I can't tell you how much I miss the VAX processor. One day I build my own with a bunch of FPGAs.

I can get the number of arguments for a caller and callee which were
compiled with -fno-omit-frame-pointer and -O0 using esp and ebp. Too
bad that you do not clean up your stack after each call in the
optimized code. Is there a compiler option to do that, other than -O0?

Perhaps you could elaborate a little bit your statement about limited
usefulness of IARGPTR on Intel Itanium. AFAIK the VMS Fortran
documentation of IARGPTR does not limit IARGPTR to the VAX and Alpha architecture (I am familiar with the OpenVMS Calling Standard for all three processors).

oh_moose,

Could you use Generic Interfaces?

interface YourSub
subroutine YourSub_3(A,B,C)
real :: A,B,C
end subroutine YourSub_3
subroutine YourSub_2(A,B)
real :: A,B
end subroutine YourSub_2
end interface

Then either dispense with IARGPTR and IARGCOUNT

Note, on IA64 and X64 fastcall calling format is in effect. That is the first so many arguments (4?) are in registers as opposed to on stack. You are unable to point to a register therefor IARGPTR is meaningless.

Jim Dempsey

I already thought about using INTERFACE and the OPTIONAL attribute. Of
coure that definition would have to come from somewhere. Since the two
routines (LIB$SIGNAL and LIB$STOP, two VMS RTL routines) for which I
need a variable argument list are subroutines, there is no
interface (as traditional external function) defined in an include
file. Therefore an additional INCLUDE statement in every source file
which uses these routines, to make it work on Linux, would be totally
redundant on VMS, my development platform. That is no disaster, but not
great since error checking on the development platform would be
incomplete. A lack of the INCLUDE '(LIB$ROUTINES)' would go undetected
on VMS and end in a disaster without prior detection by the compiler on
Linux. A working implementation of IARGCOUNT and IARGPTR would be the
better solution.

IARGCOUNT depends on the VMS calling convention, which provides an argument count. It is not dependent on the processor architecture - neither Alpha nor Itanium have the equivalent of the VAX CALLS/CALLG instruction so the generated code needs to create the count and put it in a standard location as specified by the VMS architects. Since the standard calling conventions for Windows, Linux and MacOS omit such a count, we can't implement IARGCOUNT.

Note that C's "varargs" concept actually passes a list structure with its own count. Fortran does not have anything similar.

I have never seen an argument count be added to a call from Fortran, so I am not sure what you are seeing. Since we're in the Linux/Mac forum, the caller knows (at compile time) how many bytes were pushed on the stack and generates the necessary cleanup instruction afterward. That number does not necesarily correlate to the number of arguments, depending on architecture.

Retired 12/31/2016

oh_moose,

Can you avoid

INCLUDE '(LIB$ROUTINES)'

by placing the equivilent functionality in a module and require something like

USE LIB_ROUTINES

And where LIB_ROUTINES is platform dependent.

Then on the Linux side (IVF side) you could use the generic interface to pick off the number of args and set a global variable in the module LIB_ROUTINES called IARGCOUNT (implies you cannot nest calls to LIB$SIGNAL,...).

If you still require the address of the list of references in IARGPTR (on Linux) then the template routines inside the generic interface call a C wrapper function passing aribitrary values in the fastcall registers (first so many arguments, I think 4) followed by the stack held remaining references. The C wrapper routine can copy the stack list of arguments into an array held in the module LIB_ROUTINES (e.g. IARGLIST(n)) then point IARGPTR at the first element using LOC%(IARGLIST(1)). Now you have a count of args in IARGCOUNT, a CRAY pointer in IARGPTR and, a list of references.

This should provide source compatibility between the VMS and Linux platforms and provide interface checking (at least on the IVF side). But does require platform specific file to gen the module.

Jim Dempsey

The LIB$ROUTINES include file (and others) already exists on VMS.
Unfortunately there are no Fortran 95 modules available for the VMS
Fortran compiler (thanks to hp, I guess). For my Linux implementation,
I will stick with what I have on VMS, without changing the source code
of my application. I may adapt whatever is ncessary in the Linux
version of the system include files, but I will not touch the source
code of my application just to adapt it for Linux. VMS sets the
standard.

I have a problem to solve, and that problem could be easily addressed
with an implementation of IARGCOUNT and IARGPTR. Both functions could
be easily implemented. VMS shows how, and Steve knows that. As far as
Linux is concerned, the LSB refers to the SYS V i386 Unix ABI. Now, I
could accept that the i386 Unix ABI is setting the standard. But the
Intel Fortran compiler is not i386 Unix ABI compliant, because the
Intel Fortran compiler, and the Intel C++ compiler, are omitting the
frame pointer (ebp) by default. That is an important point. So, from my
perspective Intel does not give much about the i386 Unix ABI. It is not
Intel's fault that the makers of the i386 Unix ABI left out important
details like the number of arguments. And to make matters worse, the
AMD64 ABI specifies that the frame pointer is optional. That saves a few CPU clock cycles and makes debugging almost impossible. Only very desperate people do that.

Having said that, I think it is fairly simple to implement IARGCOUNT.
Simply pass the number of arguments through the accumulator from the
caller to the callee. If the callee utilizes IARGCOUNT or IARGPTR, then
it has to push eax onto the stack in the prologue of the callee.
Problem solved. Even for RISC processors we can now easily create a
vector with all the arguments in memory to implement IARGPTR, since we
now know how many arguments are needed and the register sequence used
for the arguments is well defined. There is also no problem with the
use of eax for this purpose, because a caller cannot rely on the fact
that the callee is a subroutine anyway. If the callee is being called
with a CALL statement, but the callee is actually a function instead of
a subroutine, then eax will be modified ("traditional" Fortran
programmers do that). Thus the caller should always assume that after a CALL eax might be changed. And we only need eax to pass the number of arguments at the time of the call.

As for using ebp and esp (IA-32), we can use the formula
esp-ebp=offset+4*number_of_arguments.
The problem is the offset, namely the size of the local variables. That
we could get from the prologue of the caller be examining the stack and
machine code. Not the nicest thing to do, but possible. We could also
write an interpreter wor DWARF entries (yikes). Unfortunately the whole
idea does not work with optimization enabled and the frame pointer
omitted. So let's call this attempt academic and let the Linux kernel
developers deal with such kind of problems (some of them are quite
unhappy with the AMD64 ABI and the Intel compiler defaults). I think my
simple eax-based solution is more than suitable to implement IARGCOUNT
and IARGPTR.

Apropos Cray, I recall that the Cray Fortran compiler uses descriptors
to pass character strings. Great! So does VMS. It would be really nice
to have this method available with the Intel Fortran compiler, too, at
least as an
option. The usual Windows and Linux method of passing
"hidden" arguments for the length of strings is a terrible hack. As far as IARGCOUNT is concerned, I would like to see only 1 count per string (not a 2nd for the "hidden" length hack).

Unless HP has removed them, there certainly are Fortran 90 declarations for the LIB$ routines. They're modules in the forsysdef.tlb library and you use the syntax:

INCLUDE '(LIB$ROUTINES)'

(it's been a while so I don't remember the exact module name.)

They aren't "modules" in the USE sense, I'll agree.

I'll note that Intel Fortran accepts the above syntax if you say /vms - it turns the "module name" into a file name of the form modulename.f90 (if I recall correctly - it has been many years since I snuck that in.) The idea was primarily to help those using Sector 7's VMS migration tools.

Re: descriptors - I've suggested adding %DESCR and a DESCRIPTOR attribute, but so far the reception to the idea has been chilly. I'll admit that it doesn't really add much value. Pretty much everything on Linux and Windows is a "terrible hack", in my opinion, compared to VMS.

Retired 12/31/2016

LIB$SIGNAL and LIB$STOP are true subroutines, not functions. Therefore
they are not declared as externals in LIB$ROUTINES (I know about the
text library), which is why this include file is typically not included
in the source code of routines which call these two LIB$ routines. For
the rest of the scenario, see my prior posting...

I had trouble with the -vms option. IIRC things like INCLUDE '($AFRDEF)/NOLIST' were broken. I need to check that again.

Back to my original problem, what do you think about my eax solution? I
think I made my point about ABI compatibility (at least wrt Intel
compilers). As far as I can tell, passing the number of arguments in
eax would not break the i386 Unix ABI. I am going to implement
just another assembler hack next week and see if the idea really works.
IARGCOUNT, IARGPTR and the needed extension of the prologue would then
be patched in as inline functions into the callee's assembler file. For
the caller all I have to do is patch in a mov eax,number_of_arguments
infront of the call.

Anyone who opposes the idea of descriptors for character strings should
take a close look at what people do for example in the CERN Library to
make Fortran routines C-compatible and vice versa. None of that junk
was ever needed for VMS! I have seen plenty of other similar cases.
Even the C++ STL uses descriptors for character strings by now, two
decades after VMS introduced them. Let's fight for the descriptors!

oh_moose,
I may have an outline of a solution for you.
Assume you have an application written on VMS and whereby
your preference is to make no changes to source code to
run on your Linux system. Also assume you are willing to
accept that your make files will be different between
platforms as well as a few minimal changes in a header file(s).
Bear with me as I do not have VMS nor Linux available.
Your main program:
------------------------------
C OH_MOOSE.F
PROGRAM OH_MOOSE
INCLUDE 'INCLUDEFILE.FI'
REAL A,B,C,D,E,F

A=1.
B=2.
C=3.
D=4.
E=5.
F=6.
CALL FOO(A, B, C)
CALL FOO(D, E)
CALL FOO(F)
END PROGRAM
-------------------------------
Note, I will leave it up to you to futz with the naming
convention of the include file
Your subroutine
-------------------------------
C FOO.F
SUBROUTINE FOO(A, B, C)
INCLUDE 'INCLUDEFILE.FI'
REAL A, B, C
IF(IARGCOUNT .EQ. 1) WRITE(*,*) IARGCOUNT, IARGPTR, A
IF(IARGCOUNT .EQ. 2) WRITE(*,*) IARGCOUNT, IARGPTR, A, B
IF(IARGCOUNT .EQ. 3) WRITE(*,*) IARGCOUNT, IARGPTR, A, B, C
IF(IARGCOUNT .GT. 3) WRITE(*,*) IARGCOUNT, 'Too many args'
END SUBROUTINE
-------------------------------
Your include file on Linux
-------------------------------
C IncludeFile.FI
C --- your application include information follows ---
C ...
C --- End of your application include information ---
C Now comes the hack
 COMMON /VMS_HACK/ IARGCOUNT, IARGPTR 

C Add line to include interfaces
INTERFACE FOO_HOOK
SUBROUTINE FOO_1(A)
REAL A
END SUBROUTINE
SUBROUTINE FOO_2(A,B)
REAL A,B
END SUBROUTINE
SUBROUTINE FOO_3(A, B, C)
REAL A,B,C
END SUBROUTINE FOO_3
END INTERFACE
----------------------------
************************************
Note 1 of 2,
OH_MOOSE.F and FOO.F are .not. built directly
Instead you build by proxy using
OH_MOOSEhook.F and FOOhook.F
The ...hook.F files use INCLUDE to include the
the file being built.
If running with Visual Studio,
Exclude from build: OH_MOOSE.F and FOO .F
Add to build: OH_MOOSEhook.F and FOOhook.F
If running from MAKE file then make using
OH_MOOSEhook.F and FOOhook.F
dependent on
OH_MOOSE.F and FOO.F
respectively
****
Note 2,
Enable the Fortran Preprocessor
OH_MOOSEhook.F
---------------------------------
C oh_mooseHook.F
#ifndef _IncludeFile_inc_
#include 'IncludeFile.inc'
#define _IncludeFile_inc_
#endif
#include 'oh_moose.f'
---------------------------------
Note, the hook file performs two FPP #include statements
The FOOhook.F file contains the hooking entry points
for the generic subroutine FOO.
Then includes the standard FOO.H subroutine
---------------------------------
C FOOHOOK.F

SUBROUTINE FOO_1(A)
COMMON /VMS_HACK/ IARGCOUNT, IARGPTR
REAL A
INTEGER IARGS(3)
IARGCOUNT = 1
IARGS(1) = LOC(A)
IARGS(2) = 0
IARGS(3) = 0
IARGPTR = LOC(IARGS(1))
CALL FOO(A)
END SUBROUTINE

SUBROUTINE FOO_2(A,B)
COMMON /VMS_HACK/ IARGCOUNT, IARGPTR
REAL A,B
INTEGER IARGS(3)
IARGCOUNT = 2
IARGS(1) = LOC(A)
IARGS(2) = LOC(B)
IARGS(3) = 0
IARGPTR = LOC(IARGS(1))
CALL FOO(A,B)
END SUBROUTINE

SUBROUTINE FOO_3(A, B, C)
COMMON /VMS_HACK/ IARGCOUNT, IARGPTR
INTEGER IARGS(3)
IARGCOUNT = 3
IARGS(1) = LOC(A)
IARGS(2) = LOC(B)
IARGS(3) = LOC(C)
IARGPTR = LOC(IARGS(1))
CALL FOO(A, B, C)
END SUBROUTINE FOO_3
 INCLUDE 'FOO.F'
-------------------------
The FPP include file
--------------------------
! IncludeFile.inc
! make substitution macro to change FOO to FOO_HOOK
#define FOO FOO_HOOK
--------------------------
Program output
--------------------------------
 3 5056448 1.000000 2.000000 3.000000
2 5056432 4.000000 5.0000 00
1 5056416 6.000000
--------------------------------
I will leave it as an exercise to you to build off of this
technique.
Jim Dempsey

With INTERFACE and OPTIONAL I can avoid the complicated procedure below "Note 2". But that is not the problem. (BTW I do not use MS software.)

The problem is that the routine LIB$SIGNAL, which has a variable argument list, is a SUBROUTINE (same for LIB$STOP). There is no INTERFACE or classic EXTERNAL for LIB$SIGNAL on VMS because in Fortran there is no need to provide an interface for a SUBROUTINE. And because there is no need to provide an INTERFACE for a SUBROUTINE, there is also no way to enforce the use of such an interface. But the argument passing mechanisms for a subroutine with optional arguments and a subroutine without optional arguments are different. Unfortunately there is no way for the compiler to check the software during compilation for something that is not needed.

I need IARGCOUNT and IARGPTR and I am not the first person who asked for this feature.
http://software.intel.com/en-us/forums//topic/44872

Steve, or anybody else, is it safe to pass the number of arguments in
eax? It looks all right to me, but maybe I missed something. It would
be good to know about any stumbling block before I try to implement
this feature myself by hacking the intermediate assembler code.

oh_moose,

I am aware that you are not using MS software. Your only requirement is to have access to Intel Fortran FPP (the preprocessor). This technique will work with MAKE files or other IDE.

In the sample program provided in my last post, the subroutine FOO was not written to take variable number of arguments yet due to the fineness of FPP to perform name changes and use of generic subroutine interfaces the fixed arg count subroutine takes (appears to take) a variable number of arguments and can obtain the knowledge of number of argument and placement of those argument (references) by use of the global variables IARGCOUNT and IARGPTR.

If you were to change the example program (my last post) subroutine name FOO to LIB$SIGNAL or LIB$STOP you will find that you can enter the variable argument subroutine (using IARGCOUNT and IARGPTR) while the unspecified (to .F files) subroutine interface is undefined, and therefore assumed to have the number of arguments as indicated by the CALL in effect.

Presumably you are working underthe commandment: Thou shalt not modify source code

The work around I provided permits you to stay within this limitation. Your original source files can remain on write protected media yet compile using the hooks necessary for your platform. This technique provides a wrapper to your untouchable code.

When (if) you do use this technique you would have to set the IARGS array to the largest number of possible args for LIB$SIGNAL or LIB$STOP.

Also, at some point in time you may wish to run your application on a 64-bit Linux or Mac O/S. The calling convention on these platforms is to pass as much by register then remainder (if any)on stack. For 64-bit the IARGS integers should be 8 bytes, not 4. If you use something likeINTEGER(C_PTR) :: IARGS(nArgMaxLIB$SIGNAL) then you won't have to revisit this code later trying to figure out what happened on the 64-bit system.

The only defect of this technique as writtenis theIARGCOUNT and IARGPTR are not stacked and thus won't permitnested calls (e.g. if LIB$SIGNAL called LIB$STOP). But this can easily be fixed by adding to each of the interface hooks a saving and restoring ofIARGCOUNT and IARGPTR around the CALL (to the hooked routine).

Jim Dempsey

Jim, I spent a lot of time to eliminate a lot of preprocessor statements in the legacy code. This technique is bad by design.

I see no technical reason why Intel is unable to do what Digital achieved 3 decades ago.

oh_moose,

The suggestion offered was a method to eliminate any changes to the legacy code. If one does not touch the code one is not likely to break the code.

On the other hand, if you are willing to change the legacy code ("I spent a lot of time to eliminate a lot of preprocessor statements in the legacy code.") then by definition you must also be willing to change the calls to your LIB$... subroutines. e.g. make a shell that uses optional arguments and/or count of arguments.

Do you think hacking the ASM code is better than using an integrated tool?

The technique is elegant by design.

Using the above technique I was able to convert an application using over 750 source files where the old code used F77 COMMONs and the new code used F90 modules with named user defined types. .AND. more importantly the source code of these 750 source files was left untouched. Without this elegant technique, more than half the lines in those source files would have required changing.

>>I see no technical reason why Intel is unable to do what Digital achieved 3 decades ago.

The technical reasons are: a) this requires the use of a scarce resource, a register for IARGCOUNT, and b) when on x64 platform IARGPTR may be meaningless as you cannot point to a register. Back in the days of PDP 11/05 the registers could also be addressed by a memory address, as for VAX I cannot recall but my guess is no. On x32 and x64 systems there is no pointer to register. That is why the suggested code placed the address of the arguments into the stack local array. BTW make sure ", AUTOMATIC" is on the declaration of INTEGER(C_PTR), AUTOMATIC :: IARGS(nArgMax)

If you are going to use your ASM editing route I might suggest you consider bunging up the LIB$xxx routines such that the code following the CALL (information on stack as to where call came from) is examined. By examining the code following the call you can determine what the stack cleanup code is (i.e. how the stack pointer is cleaned up following the call instruction). Be aware that this technique may not always work if optimizaitons are enabled and if the optimization code recognizes that a subsequent call uses the same (of some of) arguments and thus optimizes out the push and eliminates the "pop/add to esp".

Jim Dempsey

Digital defined the VMS calling convention to include an argument count. The developers of UNIX and Linux saw no need for such a thing. (Ditto Windows.) Intel cannot shoehorn an argument count in when we have no control over who calls your Fortran routine.

If you need an argument count, add it yourself as an actual argument, though for something similar to LIB$SIGNAL, an array argument makes more sense to me.

The calling convention on IA-32 reserves EAX on input as holding the address of a return value. Fortran does not use this normally, though I think it does for BIND(C) routines. I do not recommend using EAX for other purposes.

Retired 12/31/2016

Leave a Comment

Please sign in to add a comment. Not a member? Join today