[comp.lang.misc] What is the FORTRAN for ?

cik@l.cc.purdue.edu (Herman Rubin) (07/31/90)

This appeared in an article posted to comp.lang.fortran, and I believe it
illustrates a point about the weaknesses of HLLs (The poster was referring
to C at this point) and compilers.

In article <4922@munnari.oz.au>, ok@mudla.cs.mu.OZ (Richard O'Keefe) writes:

> 	double nint(double x)
> 	    {
> 		return ceil(x + 0.5) - (fmod(x*0.5 + 0.25, 1.0) != 0);
> 	    }
> 
>      is all it takes.]

I have not checked the code, but this points out what is wrong with the 
present HLLs and that compilers cannot be expected to overcome the problem.
If this #define were used, then this block of code would be inserted wherever
this is wanted, even if there is a machine instruction which does the job.

I do not see how a compiler can recognize that the complicated body of code
can be replaced by a single instruction and make that substitution.  One 
could put that one in, but another will arise, and another.  And these will
continue to crop up as long as the language primitives are restricted as
they now are.
-- 
Herman Rubin, Dept. of Statistics, Purdue Univ., West Lafayette IN47907
Phone: (317)494-6054
hrubin@l.cc.purdue.edu (Internet, bitnet)	{purdue,pur-ee}!l.cc!cik(UUCP)
-- 
Send compilers articles to compilers@esegue.segue.boston.ma.us
{spdcc | ima | lotus| world}!esegue.  Meta-mail to compilers-request@esegue.

gl8f@astsun7.astro.Virginia.EDU (Greg Lindahl) (07/31/90)

In article <1990Jul30.180439.26627@esegue.segue.boston.ma.us> cik@l.cc.purdue.edu (Herman Rubin) writes:
>This appeared in an article posted to comp.lang.fortran, and I believe it
>illustrates a point about the weaknesses of HLLs (The poster was referring
>to C at this point) and compilers.

Actually, this "weakness" can be solved by a smart linker.

>In article <4922@munnari.oz.au>, ok@mudla.cs.mu.OZ (Richard O'Keefe) writes:
>
>> 	double nint(double x)
>> 	    {
>> 		return ceil(x + 0.5) - (fmod(x*0.5 + 0.25, 1.0) != 0);
>> 	    }
>> 
>>      is all it takes.]
>
>I have not checked the code, but this points out what is wrong with the 
>present HLLs and that compilers cannot be expected to overcome the problem.
>If this #define were used, then this block of code would be inserted wherever
>this is wanted, even if there is a machine instruction which does the job.

Right. It's a bit silly to expect the compiler author to support any
possible hardware instruction and any possible piece of convoluted
code that might use that instruction. However, if you have a list of
things you want done fast, then write a set of HLL subroutines that
accomplish these things. (NINT() is a Fortran intrinsic already, for
example.) This is your portable library. Then for each architecture
with a smart linker, write the necessary inline assembly files and
link appropriately. (For NINT(), the vendor's Fortran should do it for
you.) I note that the MIPS linker, Sun's linker, and gcc all have no
problems doing this efficiently. The subroutine linkage gets optimized
away and you are left with only the single, efficient instruction.

>I do not see how a compiler can recognize that the complicated body of code
>can be replaced by a single instruction and make that substitution.  One 
>could put that one in, but another will arise, and another.  And these will
>continue to crop up as long as the language primitives are restricted as
>they now are.

You have to have list of these primitives somewhere, because your
language is going to need either operators or functions which
accomplish your tasks. That is, unless you want a mind-reading
compiler that is a wizard with instruction sets. Your little portable
routines define "Herman's Lovely Language", and things can run
everywhere at top speed.

So, in summary, this is how you do what you want, today, with today's
HLLs. Doing it this way is much easier than designing your own HLL,
which you have declined to do.

--
"In fact you should not be involved in IRC." -- Phil Howard

ok@goanna.cs.rmit.OZ.AU (Richard A. O'Keefe) (08/01/90)

In article <1990Jul30.180439.26627@esegue.segue.boston.ma.us>, cik@l.cc.purdue.edu (Herman Rubin) writes:
> In article <4922@munnari.oz.au>, ok@mudla.cs.mu.OZ (Richard O'Keefe) writes:

> > 	double nint(double x)
> > 	    {
> > 		return ceil(x + 0.5) - (fmod(x*0.5 + 0.25, 1.0) != 0);
> > 	    }

> I have not checked the code, but this points out what is wrong with the 
> present HLLs and that compilers cannot be expected to overcome the problem.
> If this #define were used, then this block of code would be inserted wherever
> this is wanted, even if there is a machine instruction which does the job.

Er, that's a FUNCTION, sir, not a macro (#define).
When a compiler sees a call to nint(), it will generate a procedure call.
(Yes, in C++ and gcc I could say "inline double nint(...) but I *didn't*.)

> I do not see how a compiler can recognize that the complicated body of code
> can be replaced by a single instruction and make that substitution.

Nobody else is _expecting_ the compiler to do that.  To start with, the
only reason that I gave C code for nint() at all is that some C compilers
ALREADY have it as a built-in math function but some haven't; I said
"here is what you can do if you haven't already got it".

I have commented before on the ".il" facility provided by the Sun compilers.
I would like to point out once again that they provide a comparatively clean
way of getting at any instruction you like from within C.  Here's what we do
in this case.  Suppose we have a machine running SunOS on which the way of
rounding a double precision float to integer format is
	cvtdir	<source>,<destination>
(supposing this saves me looking up manuals to find out what the instruction
is really called on a 68881) and suppose that the machine is otherwise like
a 680x0.  I would write a .il file

	#  This file defines nint() using a hardware instruction
		.inline	_nint,8
		cvtird	sp@+,r0
		.end

Then I would write in my C source code

	#ifdef	imaginary_sun_model
	extern double nint();
	#else
 	double nint(x)
	    double x;
	    {
		extern double ceil(), fmod();
		return ceil(x + 0.5) - (fmod(x*0.5 + 0.25, 1.0) != 0);
	    }
	#endif

Compiling this on the right kind of Sun machine, I'd do

	cc -O myprog.c nint.il

Compiling it on any other kind of machine, I'd do

	cc -O myprog.c

and I would have a program which exploited the 'cvtdir' instruction
IF THAT INSTRUCTION EXISTS but which ran just fine on a machine which
had no such instruction, without needing any assembly code hacking on
the latter machine.  The .il approach *WOULD* substitute exactly the
right single instruction in the right places (the .il expansion pass
is smart enough to combine
	movd	_foo,sp@-
	cvtdir	sp@+,r0
to
	cvtdir	_foo,r0
so I do mean a single instruction).

Surely this is a good approach?  We use abstraction to hide the machine-
dependent details.  We have an operation which makes sense on its own
however it is implemented; the rest of the program uses it _as if_ it
were a call to a C function, and it may be implemented by a call to C
code, a call to assembly code, inline expansion of C code, or inline
expansion of assembly code.

The V.3/386 C compiler has a similar feature, although there the
assembly definitions are included in the C source code (asm functions).

This idea can of course be applied to Pascal or Modula-2 or Fortran
just as well as it can to C, and assembly code inserts are already
part of standard Ada.

-- 
ok@goanna.cs.rmit.oz.au
-- 
Send compilers articles to compilers@esegue.segue.boston.ma.us
{spdcc | ima | lotus| world}!esegue.  Meta-mail to compilers-request@esegue.

bart@videovax.tv.tek.com (Bart Massey) (08/01/90)

In article <1990Jul30.180439.26627@esegue.segue.boston.ma.us> cik@l.cc.purdue.edu (Herman Rubin) writes:
> This appeared in an article posted to comp.lang.fortran, and I believe it
> illustrates a point about the weaknesses of HLLs (The poster was referring
> to C at this point) and compilers.
> 
> In article <4922@munnari.oz.au>, ok@mudla.cs.mu.OZ (Richard O'Keefe) writes:
> 
> > 	double nint(double x)
> > 	    {
> > 		return ceil(x + 0.5) - (fmod(x*0.5 + 0.25, 1.0) != 0);
> > 	    }
> > 
> >      is all it takes.]
...
> I do not see how a compiler can recognize that the complicated body of code
> can be replaced by a single instruction and make that substitution. ...

I tend to like the GCC approach to all this a great deal.  For the above
program, the nint() primitive above might be coded something like

	inline double const nint(double const x)
	    {
	#ifdef	frobozzco
		double result;
		asm("\tfrobozz_nint\t%1,%0":"=f"(result):"f"(x));
		return result;
	#elif	foobarco
		double result;
		asm("\tfoobar_nint\t%0,%1":"=f"(result):"f"(x));
		return result;
	#else
		return ceil(x + 0.5) - (fmod(x*0.5 + 0.25, 1.0) != 0);
	#endif
	    }

Note that the above code generates a single inline machine instruction on
machines which support it, and otherwise generates the best sequence of
inline machine instructions it can.  This is as close as I've ever seen to a
"user-extensible code generator", and it seems to solve a large class of such
problems very well, at least IMHO, by shifting the burden of code generation
in specialized applications from the compiler designer to the application
programmer, the latter presumably having a better idea of what he/she wants
or needs.

Note also that in G++, one can even declare the standard C operators to be
implemented by such inline asm functions, and thus get, for example, maximum
efficiency from a C++ complex number package on a machine which supports
complex numbers directly in the FPU.

I've seen Mr. Rubin post on this topic before, and I would humbly suggest
that, if he would be so generous, he could benefit the computing community
greatly by generating a GCC-compatible header file containing a bunch of
these inline asm functions for the complicated numerical operations he feels
are important to optimize well, and for a number of common CPUs.  I know
that I, for one, would be very interested in the results of such an exercise.
For a similar exercise in complex FPU insn utilization, at least for a
single processor, see Matthew Self's "math-68881.h", distributed with GCC.

I look at GCC's "inline asm function" feature as a way to extend the semantic
primitives of the language, in a syntactically compatible way.  So far, it
seems to me to have worked pretty well.

					Bart Massey
					..tektronix!videovax.tv.tek.com!bart
					..tektronix!reed.bitnet!bart
-- 
Send compilers articles to compilers@esegue.segue.boston.ma.us
{spdcc | ima | lotus| world}!esegue.  Meta-mail to compilers-request@esegue.

cik@l.cc.purdue.edu (Herman Rubin) (08/19/90)

In article <13071@mentor.cc.purdue.edu>, ags@seaman.cc.purdue.edu (Dave Seaman) writes:
> In article <60344@lanl.gov> jlg@lanl.gov (Jim Giles) writes:
> >From article <13035@mentor.cc.purdue.edu>, by ags@seaman.cc.purdue.edu (Dave Seaman):
> >> So what do you do if the target machine has more than one Fortran
> >> implementation and the different versions are not even compatible with each
> >> other?
 
> >I'm not interested in whether the Fortrans (or Cs, Pascals, Modulas,
> >etc.) are compatible with each other, I only want them to be _callable_
> >from each other on a way which appears consistent from the HLL point of
> >view.  If this means that several different bridges to Fortran have to
> >be present on the low-level for different Fortrans, that tough.  Of
> >course, implementors who avoid use of industry (or vendor) standard
> >calling sequence conventions are shooting themselves in the foot by
> >making their produce harder to use from within existing environments.
 
> The two examples I gave of incompatible Fortrans were both cases of
> vendor-supplied Fortrans that ran on the same machines and were reasonably
> compatible at the source language level (though with slightly different
> extensions), but which were fundamentally incompatible at the object code
> level.  They are definitely not callable from each other.  
 
> An implementor of some other language (Pascal, say) does not need to support
> every incompatible version of Fortran on the machine.  It's more cost-effective
> to pick just one Fortran to support.

There is a not too expensive alternative to this problem, and other interface
problems.  That is, to put the necessary software to handle various call and
return procedures in the various compilers.  Each compiler expects its 
input arguments in a clearly defined manner, and also its register saving
procedures, and there is no good reason why this information could not be
incorporated into another compiler.  This could even be used for machine
language calls.

I did not say that there were no costs, in particular efficiency costs, 
involved.  But it provides a kludge which is easy to implement.  It also
gets around the problems of name changes by the compiler in many cases.
In the UNIX systems I have seen, in general a Fortran program cannot call
a C program because of the name problem.  

It may be necessary to produce these interfaces in assembler, but this will
be a small cost compared to the alternatives of language translation or
recompilation.  Many algorithme at Purdue were not callable from one Fortran
to another, and this could have been handled.
-- 
Herman Rubin, Dept. of Statistics, Purdue Univ., West Lafayette IN47907
Phone: (317)494-6054
hrubin@l.cc.purdue.edu (Internet, bitnet)	{purdue,pur-ee}!l.cc!cik(UUCP)