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.