[comp.compilers] 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.

Preston Briggs <preston@rice.edu> (07/31/90)

Herman Rubin 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.

>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.

Sometimes these substitutions can be done via peephole optimizations.  It's
particularly useful when the patterns are generated automatically from a
machine description (as in the work of Davidson and Fraser).  The example
given looks sort of complex, but could easily be added by hand.  The
usefulness of such effort depends on the instruction set of the machine -- for
a machine with quite complicated instructions, a lot of effort seems
justified.

>continue to crop up as long as the language primitives are restricted as
>they now are.

I'm up for interesting language primitives too, though perhaps for different
reasons.  Consider Fortran's MAX and MIN functions.  If they weren't provided
as functions, people would have to code them by hand, as if statements.  But
the optimizer would rather see them as simple functions (expressions) so that
CSE elimination and loop invariant code motion can be performed.  The same
idea is even more important when vectorizing compilers are used.

-- 
Preston Briggs				looking for the great leap forward
preston@titan.rice.edu
-- 
Send compilers articles to compilers@esegue.segue.boston.ma.us
{spdcc | ima | lotus| world}!esegue.  Meta-mail to compilers-request@esegue.

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.