[comp.arch] Double Width Integer Multiplication and Division

crowl@cs.rochester.edu (Lawrence Crowl) (06/27/89)

In article <22218@winchester.mips.COM> mash@mips.COM (John Mashey) writes:
>When we were [designing integer division support on the MIPS processors], we
>couldn't think of any languages whose natural implementation on a 32-bit
>machine would expect generation of a 64 by 32 bit divide as the expected
>operation.  Does anybody know of any such, or is this strictly something one
>would use in handcoded routines?
 
Languages which support fixed-point arithmetic, e.g. PL/I and Ada, could use
such a feature to substantially improve fixed-point performance.  In addition,  languages that support a more general notion of integers, such as Lisp bignums 
or declared ranges, can use them to improve the performance on integers larger
than a single word.  There are not a lot of languages currently that support 
such arithmetic, but newer languages are more likely to.
 
Such support is especially crucial for fixed-point arithmetic.  Fixed-point
arithmetic is often a win in both time and precision over floating point.  I
did a Mandelbrot program using fixed-point (via the 68020 32x32->64 multiply)
that was 50% faster than using the FPA on a Sun 3/260 and 480% (5x) faster
than  using the 68881.  However, such advantages cannot be effectively
realized without language support.  Fixed point is generally not used much
because most languages do not support it.
-- 
  Lawrence Crowl		716-275-9499	University of Rochester
		      crowl@cs.rochester.edu	Computer Science Department
...!{allegra,decvax,rutgers}!rochester!crowl	Rochester, New York,  14627

pcg@aber-cs.UUCP (Piercarlo Grandi) (06/28/89)

In article <1989Jun26.195044.4197@cs.rochester.edu> crowl@cs.rochester.edu
(Lawrence Crowl) writes:
     
    Languages which support fixed-point arithmetic, e.g. PL/I and Ada, could
    use such a feature to substantially improve fixed-point performance.  In
    addition, languages that support a more general notion of integers, such
    as Lisp bignums or declared ranges, can use them to improve the
    performance on integers larger than a single word. [ .... ]
     
    Such support is especially crucial for fixed-point arithmetic.
    Fixed-point arithmetic is often a win in both time and precision over
    floating point.  I did a Mandelbrot program using fixed-point (via the
    68020 32x32->64 multiply) that was 50% faster than using the FPA on a
    Sun 3/260 and 480% (5x) faster than using the 68881.  However, such
    advantages cannot be effectively realized without language support.

All true, but for language support. There is an enlightening article by
Martin Richards in a a very old Sw.Pract.&Exp. issue (middle seventies I
think) in which he uses a "muldiv()" function (implemented in assembler)
for BCPL that multiplies two single length numbers, giving a double length
result that is divided by a single length number, returning a single length
result. Something like (but more efficiently):

	int muldiv(a,b,c) { return (int) (((long) a * (long) b)/(long) c); }

This is almost enough to make fixed point comfortable in any language, however
inhospitable (BCPL for example has only one length of integer).

Richards describes in his paper having been able thanks to "muldiv()" to
run a realistic flight simulator on a quite slow and small machine without
floating point.

Final remarks on the issue of RISC vs. multiplication/division:

	[1] You are not supposed to write assembler programs on a RISC
	    machine. The compilers have sophisticated algorithms to generate
	    "optimal" multiplication/division out of simpler instructions.

	[2] Fixed point and the "muldiv()" idea to me look very much in the
	    RISC philosphy of minimalism. Fixed point, and multiple precision
	    integer arithmetic, are in many many cases preferable to floating
	    point and its complexities. Too bad that mathematicians are lulled
	    in a false sense of familiarity by floating point's "scientific
	    notation" and apparent support for real numbers.
-- 
Piercarlo "Peter" Grandi           | ARPA: pcg%cs.aber.ac.uk@nsfnet-relay.ac.uk
Dept of CS, UCW Aberystwyth        | UUCP: ...!mcvax!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk

bartho@obs.unige.ch (PAUL BARTHOLDI) (06/30/89)

In article <1989Jun26.195044.4197@cs.rochester.edu>, crowl@cs.rochester.edu (Lawrence Crowl) writes:
>>When we were [designing integer division support on the MIPS processors], we
>>couldn't think of any languages whose natural implementation on a 32-bit
>>machine would expect generation of a 64 by 32 bit divide as the expected
>>operation.  Does anybody know of any such, or is this strictly something one
>>would use in handcoded routines?
>  
> Languages which support fixed-point arithmetic, e.g. PL/I and Ada, could use
> such a feature to substantially improve fixed-point performance ...

FORTH is the only language to my knowledge that use explicitely integer double
precision intermediate for multiplication/division/modulo.  The original and
standard forth have 16 bits integers, so double are 32 bits.  The point is
that, as soon you do integer arithmetic, you need to (re)scale your operands,
specialy during multiplication/division.  So, most of time, a multiplication
is immediately followed by a division, or a division preceded by a 
multiplication, and a double precision intermediate results means you do not
loose precision.  For example, suppose you want to multiply a number by 'pi'.
You would then multiply this number by 31415 and divide the result by 10000
(16 bits) or by 314159265 and 100000000 ...  Notice that the high level
operator has now three operands and should be called 'ternary'.  This is
exactely what forth does.  forth use reverse polish notation, which is very
nice for ternary operators :    a b c */  ('*/" is the operator) means
take the product of a and b in double precision, and divide it immediately by
c, getting a single precision integer as result.  you can have *mod that would
leave a*b mod c or even */mod that will leave both quotient and modulo of
a*b by c.  all these operators have been extremely usefull in process control,
data acquisition etc, in particular with small computer or micro without FPA.

for more information about forth, see :
  MG Kelly and N Spies : FORTH: a text and reference , Prentice-Hall 1986
  L Brodie             : Starting FORTH,               Prentice-HAll 1982
  L Brodie             : Thinking FORTH,               Prentice-Hall 1984

regards,               Paul Bartholdi

  Dr Paul Bartholdi                 bartho@cgeuge54.bitnet
  Observatoire de Geneve            bartho@obs.unige.ch
  CH-1290 Sauverny                  02284682161350::bartho (psi)
  Switzerland                       20579::ugobs::bartho

cik@l.cc.purdue.edu (Herman Rubin) (06/30/89)

In article <1035@aber-cs.UUCP>, pcg@aber-cs.UUCP (Piercarlo Grandi) writes:
> In article <1989Jun26.195044.4197@cs.rochester.edu> crowl@cs.rochester.edu
> (Lawrence Crowl) writes:
> 
> 	[1] You are not supposed to write assembler programs on a RISC
> 	    machine. The compilers have sophisticated algorithms to generate
> 	    "optimal" multiplication/division out of simpler instructions.

I find this attitude arrogant.  Neither you nor anyone else knows what 
complicated operations I want to do.  Why should you try to make it 
difficult for me to use the power of the computer?

> 	[2] Fixed point and the "muldiv()" idea to me look very much in the
> 	    RISC philosphy of minimalism. Fixed point, and multiple precision
> 	    integer arithmetic, are in many many cases preferable to floating
> 	    point and its complexities. Too bad that mathematicians are lulled
> 	    in a false sense of familiarity by floating point's "scientific
> 	    notation" and apparent support for real numbers.

Do any mathematicians take this attitude?  Maybe if their only knowledge of
computers comes from HLLs and they swallow the hype.  Outside of the field
of numerical analysis, the "scientific notation" is rarely used in mathematics.

Mathematicians who know that floating point numbers are, in reality, only
approximations to real numbers will not be so confused.  And good numerical
analysts do not make this confusion.  In fact, mathematicians complain that
they are only offered limited precision floating point.
-- 
Herman Rubin, Dept. of Statistics, Purdue Univ., West Lafayette IN47907
Phone: (317)494-6054
hrubin@l.cc.purdue.edu (Internet, bitnet, UUCP)

mdeale@mira.acs.calpoly.edu (Myron Deale) (07/01/89)

In article <1370@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:
>In article <1035@aber-cs.UUCP>, pcg@aber-cs.UUCP (Piercarlo Grandi) writes:
>> In article <1989Jun26.195044.4197@cs.rochester.edu> crowl@cs.rochester.edu
>> (Lawrence Crowl) writes:
>> 
>> 	[1] You are not supposed to write assembler programs on a RISC
>> 	    machine. The compilers have sophisticated algorithms to generate
>> 	    "optimal" multiplication/division out of simpler instructions.
>
>I find this attitude arrogant.  Neither you nor anyone else knows what 
>complicated operations I want to do.  Why should you try to make it 
>difficult for me to use the power of the computer?

   A good point. I spend much of my time "on the outside of the envelope"
writing strange/ugly code because I'm always doing something that isn't
supported by the HLL's I have access to. For example, my present experiment
has to do with division techniques. My routine requires a 64-bit product
which the 68020 has but my compiler won't generate. Tweak time.

   It would be inane for me to complain to the compiler writers about not
having feature X. What happens is that next week I'll want feature Y, etc.
Let the compiler writers make a robust, production oriented tool, but I
won't be happy if there isn't a way to work around a problem, the HLL
itself, within reason.

   Now that it is possible to make 1 million transistor chips, it is
lame not to have capable arithmetic. The RISC generation knows how to
read and write, but their math could use working on. I'd pay a certain
price for better extended- and multi-precision arithmetic, although I
realize us math types aren't exactly a driving force in the marketplace.

>
>> 	[2] Fixed point and the "muldiv()" idea to me look very much in the
>> 	    RISC philosphy of minimalism. Fixed point, and multiple precision
>> 	    integer arithmetic, are in many many cases preferable to floating
...
>
>Mathematicians who know that floating point numbers are, in reality, only
>approximations to real numbers will not be so confused.  And good numerical
>analysts do not make this confusion.  In fact, mathematicians complain that
>they are only offered limited precision floating point.

   Perhaps it would be wise to concentrate on porting Mathematica (TM)
to an i860 or BIT SPARC, etc., in a few years. It's not fast, but it is
the best I've run across as far as multi-precision math ... and you could
afford to waste some MFLOPs on those machines. Not that you'd want to,
but for home-grown experimental code this may be an adequate approach.

Myron
// mdeale@cosmos.acs.calpoly.edu

stevew@wyse.wyse.com (Steve Wilson xttemp dept303) (07/01/89)

In article <1370@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:
>In article <1035@aber-cs.UUCP>, pcg@aber-cs.UUCP (Piercarlo Grandi) writes:
>> In article <1989Jun26.195044.4197@cs.rochester.edu> crowl@cs.rochester.edu
>> (Lawrence Crowl) writes:
>> 
>> 	[1] You are not supposed to write assembler programs on a RISC
>> 	    machine. The compilers have sophisticated algorithms to generate
>> 	    "optimal" multiplication/division out of simpler instructions.
>
>I find this attitude arrogant.  Neither you nor anyone else knows what 
>complicated operations I want to do.  Why should you try to make it 
>difficult for me to use the power of the computer?

Ok, now we have a chance to start the HLL versus assembly wars again ;-)

Seriously, there are machines that were NEVER meant to be programmed in
assembly!  Try programming a VLIW machine in assembly.  Chances are that unless
your the guy that architected it, or wrote the compilers for it, you can't.
(I've worked on such a box, at best it certainly ain't easy ;-)

I don't find it an arrogant attitude so much as an assumption that in
the GENERAL case allows the computer architect to make the best trade-
offs he can to allow the machine go as fast as possible for the largest
number of users in the machine's targeted market place.  


Steve Wilson 

pcg@aber-cs.UUCP (Piercarlo Grandi) (07/01/89)

In article <1370@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:

    In article <1035@aber-cs.UUCP>, pcg@aber-cs.UUCP (Piercarlo Grandi) writes:
    > In article <1989Jun26.195044.4197@cs.rochester.edu> crowl@cs.rochester.edu
    > (Lawrence Crowl) writes:
    > 
    > 	[1] You are not supposed to write assembler programs on a RISC
    > 	    machine. The compilers have sophisticated algorithms to generate
    > 	    "optimal" multiplication/division out of simpler instructions.
    
    I find this attitude arrogant.  Neither you nor anyone else knows what 
    complicated operations I want to do.

Ehi! Two notes: First, I wrote point [1]. Lawrence Crowl has no fault :->.

Second, The attitude of RISC designers is not "arrogant"; one of the well
publicized tenets of their philosophy is to make the instruction set
simpler, and compensate at the compiler level. This is in most cases
remarkably successful.

    Why should you try to make it difficult for me to use the power of the
    computer?

Well, RISC advocates don't try to make life harder for you; they try to make
the machine faster by having simpler hardware at the price of more complex
code generation. They don't see more complex code generation as a goal in
itself (well, occasionally I have my doubts :->).
    
    > 	[2] Fixed point and the "muldiv()" idea to me look very much in the
    > 	    RISC philosphy of minimalism. Fixed point, and multiple precision
    > 	    integer arithmetic, are in many many cases preferable to floating
    > 	    point and its complexities. Too bad that mathematicians are lulled
    > 	    in a false sense of familiarity by floating point's "scientific
    > 	    notation" and apparent support for real numbers.
    
    Do any mathematicians take this attitude?

A lot, a lot. Most mathematicians doing programming are not numerical
analysts (and even numerical analysts often are not as diffident of floating
point as they should be).

    Maybe if their only knowledge of computers comes from HLLs and they
    swallow the hype.

Fortran is very popular, isn't it?  Well, I would also argue that Fortran is
so popular with mathematicians precisely because it was *designed* as a
FORmula TRANslator, i.e. to give the illusion of dealing in familiar
mathematical formulas, to the point of using the same temrinology.
    
    Mathematicians who know that floating point numbers are, in reality, only
    approximations to real numbers will not be so confused.

But in my experience many mathematicians (or physicists) are not numerical
analysts at all, and expect the computer to do their calculations as they
intend them to be. Such people are not aware of the fact that floating point
is *not* (emphatically!) an approximation of R^n; floating point is
*radically* different from R^n (even fundamental properties of arithmetic are
not the same!).

As a palliative, a lot of effort in the design of the IEEE floating point
standard has been devoted to make floating point calculations *safer*, i.e.
a bit less surprising to those that think in terms of R^n. A laudable goal,
in some respects, but one that has costs in architectural terms, and in
perpetuating comfortable delusions.
-- 
Piercarlo "Peter" Grandi           | ARPA: pcg%cs.aber.ac.uk@nsfnet-relay.ac.uk
Dept of CS, UCW Aberystwyth        | UUCP: ...!mcvax!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk

cik@l.cc.purdue.edu (Herman Rubin) (07/03/89)

In article <1046@aber-cs.UUCP>, pcg@aber-cs.UUCP (Piercarlo Grandi) writes:
> In article <1370@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:
> 
>     In article <1035@aber-cs.UUCP>, pcg@aber-cs.UUCP (Piercarlo Grandi) writes:
>     > In article <1989Jun26.195044.4197@cs.rochester.edu> crowl@cs.rochester.edu
>     > (Lawrence Crowl) writes:
>     > 
>     > 	[1] You are not supposed to write assembler programs on a RISC
>     > 	    machine. The compilers have sophisticated algorithms to generate
>     > 	    "optimal" multiplication/division out of simpler instructions.
>     
>     I find this attitude arrogant.  Neither you nor anyone else knows what 
>     complicated operations I want to do.
> 
> Ehi! Two notes: First, I wrote point [1]. Lawrence Crowl has no fault :->.
> 
> Second, The attitude of RISC designers is not "arrogant"; one of the well
> publicized tenets of their philosophy is to make the instruction set
> simpler, and compensate at the compiler level. This is in most cases
> remarkably successful.

You do not understand.  A very good optimizinng compiler (and I have not
seen one) can do a better job of doing a particular complex operation than
I can, PROVIDED that I want to do that operation.  If my algorithm is
necessarily broken down into operations that the compiler writers have
taken into consideration. the compiler might be able to do better than
I can.  But even this has a caveat; I am not confined to a single algorithm,
and my choice of algorithms is decidedly influenced by machine properties.
In most cases, I do not need run-time profiling; I know my algorithms.

>     Why should you try to make it difficult for me to use the power of the
>     computer?

And what if my algorithms use instructions which I can form out of the machine
primitives easily, but which are at best very clumsy in the HLL operations the
unknowing HLL designer provides me?  You do not know what I have thought of.
I do not know what I will think of tomorrow.  I have both "portable" and
highly non-portable examples of this.

> Well, RISC advocates don't try to make life harder for you; they try to make
> the machine faster by having simpler hardware at the price of more complex
> code generation. They don't see more complex code generation as a goal in
> itself (well, occasionally I have my doubts :->).
>     
>     > 	[2] Fixed point and the "muldiv()" idea to me look very much in the
>     > 	    RISC philosphy of minimalism. Fixed point, and multiple precision
>     > 	    integer arithmetic, are in many many cases preferable to floating
>     > 	    point and its complexities. Too bad that mathematicians are lulled
>     > 	    in a false sense of familiarity by floating point's "scientific
>     > 	    notation" and apparent support for real numbers.
>     
>     Do any mathematicians take this attitude?
> 
> A lot, a lot. Most mathematicians doing programming are not numerical
> analysts (and even numerical analysts often are not as diffident of floating
> point as they should be).

>     Maybe if their only knowledge of computers comes from HLLs and they
>     swallow the hype.
> 
> Fortran is very popular, isn't it?  Well, I would also argue that Fortran is
> so popular with mathematicians precisely because it was *designed* as a
> FORmula TRANslator, i.e. to give the illusion of dealing in familiar
> mathematical formulas, to the point of using the same temrinology.

It is.  And anybody who thinks that Fortran is an adequate language for any
serious mathematics is either ignorant or stupid.  To quote the number
theorist D. H. Lehmer, pioneer in computational number theory even BC (before
computers) [quote from memory; may be inaccurate]

	None of the high level languages are adequate for number theory.
	Furthermore, I would not be able to design an adequate one.

Now Algol had already been produced, with the claim that it was a good
language for any problem in numerical mathematics.  It is not.

Few mathematicians know, unlike Lehmer and me, just how a computer works.
They are given to believe that it is a black box executing Fortran or
whatever.  Like the example cited by Klamkin of the use of oodles of
computer time to solve an initial value problem, where < 5 minutes of
thinking (I am not assuming that they are very good) and a small amount
of computation give an accurate answer, more accurate than they achieved.

What is there to know?  I have not seen a computer instruction set where
the user instructions (I am excluding much of the supervisory and kernel
stuff, which a user will not be able to access on a multi-user system,
although the user should be able to discuss problems with those who can)
are anywhere near as complicated as Fortran or C.

>     Mathematicians who know that floating point numbers are, in reality, only
>     approximations to real numbers will not be so confused.
> 
> But in my experience many mathematicians (or physicists) are not numerical
> analysts at all, and expect the computer to do their calculations as they
> intend them to be. Such people are not aware of the fact that floating point
> is *not* (emphatically!) an approximation of R^n; floating point is
> *radically* different from R^n (even fundamental properties of arithmetic are
> not the same!).

So why not let them know what's going on?

> As a palliative, a lot of effort in the design of the IEEE floating point
> standard has been devoted to make floating point calculations *safer*, i.e.
> a bit less surprising to those that think in terms of R^n. A laudable goal,
> in some respects, but one that has costs in architectural terms, and in
> perpetuating comfortable delusions.

This is, as stated, a palliative.  Palliatives do not always work.  BTW, I
find the IEEE standard frequently a pain; I would prefer the option of
having unnormalized floating point when I want it.  If you cannot see
why I want it, and I assure you that those cases are not handled as well
with normalized, you are in no position to criticize.

I am not saying that these HLLs may not save time.  I use them myself.  But
they are not adequate.  They are not complete.  They do not have the ability
to accommodate simple extensions.  And no matter what you put in, tomorrow
somebody will want something else, easily handled at the machine level.

I had to write a function program in our horribly designed assembler language
because the HLLs available did not have the constructs needed.  The machine
is easy to understand, but most assembler languages are designed to make life
difficult for the programmer.  Some HLL ideas can, and should, be used at the
assembler level.  Some should not even be considered.
-- 
Herman Rubin, Dept. of Statistics, Purdue Univ., West Lafayette IN47907
Phone: (317)494-6054
hrubin@l.cc.purdue.edu (Internet, bitnet, UUCP)

hascall@atanasoff.cs.iastate.edu (John Hascall) (07/03/89)

In article <1380@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:
>In article <1046@aber-cs.UUCP>, pcg@aber-cs.UUCP (Piercarlo Grandi) writes:
>> In article <1370@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:
>>  In article <1035@aber-cs.UUCP>, pcg@aber-cs.UUCP (Piercarlo Grandi) writes:
>>  > In article <1989Jun26.195044.4197@cs.rochester.edu> crowl@cs.rochester.edu
>>  > (Lawrence Crowl) writes:
>>  > 
>>  > 	[1] You are not supposed to write assembler programs on a RISC
>>  > 	    machine. The compilers have sophisticated algorithms to generate
>>  > 	    "optimal" multiplication/division out of simpler instructions.
 
>You do not understand.  A very good optimizinng compiler (and I have not
>seen one) can do a better job of doing a particular complex operation than
>I can, PROVIDED that I want to do that operation.  If my algorithm is
>necessarily broken down into operations that the compiler writers have
>taken into consideration. the compiler might be able to do better than
>I can.  But even this has a caveat; I am not confined to a single algorithm,
>and my choice of algorithms is decidedly influenced by machine properties.
>In most cases, I do not need run-time profiling; I know my algorithms.
 
    [...continues this thread for a while (quite well, I might add)...]

  Then makes the bold statement:

>What is there to know?  I have not seen a computer instruction set where
>the user instructions(1)
>are anywhere near as complicated as Fortran or C.

   I would venture some of the VAX instructions are more "complicated"
   than Fortran or C (this is why "RISC"ers love to flame it so much--
   besides its commercial success, of course).  A few examples:

      FF{CS}         Find First {Clear|Set} Bit in a field.
      CMP[Z]V        Compare [un]signed bitfield with [un]signed integer
      INDEX          Compute (multi-dimensional) array subscript
      {INS|REM}QUE   Insert/Remove entry to/from a queue
      EMOD           Extended Multiply and Integerize
      POLY           Polynomial Evaluation
      MATCHC         Find substring in a string
      MOVTUC         Move translated until ("escape") character
      SPANC          Span Characters (read the descripion of this one sometime)
      CRC            Calculate Cyclic Redundancy Check
      EDITPC         Edit Packed to Character Strings (i.e., COBOL PICtures)

(1) [original parenthetical material moved here for clarity]
>  (I am excluding much of the supervisory and kernel stuff, which a user
>  will not be able to access on a multi-user system, although the user
>  should be able to discuss problems with those who can)

    I would assert that the above instructions are at least as complicated
    as any single concept (statement-type, operator, etc.) in Fortran or C.
    (And we haven't even looked at addressing modes yet.)

John Hascall  /  ISU Comp Center  /  Ames, IA

pmontgom@sonia.math.ucla.edu (Peter Montgomery) (07/04/89)

In article <1370@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:
>In article <1035@aber-cs.UUCP>, pcg@aber-cs.UUCP (Piercarlo Grandi) writes:
>> In article <1989Jun26.195044.4197@cs.rochester.edu> crowl@cs.rochester.edu
>> (Lawrence Crowl) writes:
>> 
>> 	[1] You are not supposed to write assembler programs on a RISC
>> 	    machine. The compilers have sophisticated algorithms to generate
>> 	    "optimal" multiplication/division out of simpler instructions.
>
>I find this attitude arrogant.  Neither you nor anyone else knows what 
>complicated operations I want to do.  Why should you try to make it 
>difficult for me to use the power of the computer?
>
	Given integers A, B, C where  0 <= A, B, < C,  I want to be able to
find q, r such that A*B = q*C + r and 0 <= r < C.  I do multiple-precision
arithmetic with large numbers, and this is such an important operation that
I cannot afford to call a subroutine every time I do it.
So rather than have just a small assembly routine to do this function,
I write the entire loop or the entire procedure in assembly code.

	I want to be able to define primitives like this in my language,
telling the compiler which sequence of instructions to generate whenever it
encounters my primitive (this sequence of instructions will be defined
ONCE, in the machine dependent part of my program, but the code
referencing the primitives will be scattered throughout).  Many languages
allow one to define user primitives in terms of other language elements
(macros), but few languages allow us to go deeper and say things like (MC68020)

	"DEFINE QUOT_REM_64(arg1:unsigned long, register type D;
	                    arg2:unsigned long, register type D;
			    arg3:unsigned long, register type D),
	        RETURNS    (arg4:unsigned long, register type D;
			    arg5:unsigned long, register type D);
		LOCAL upper: register type D;
		LOCAL lower: register type D;
			movl  arg1, lower
			mulul arg2, upper:lower  /* 64-bit product arg1*arg2 */
			divul arg3, upper:lower  /* Divide by arg3 */
			movl  lower, arg4	 /* quotient */
			movl  upper, arg5	 /* remainder */
	END QUOT_REM;"

	When the compiler subsequently encounters an expression like 
(q, r) := QUOT_REM_64(A, B, C), the compiler would evaluate A, B, and C, 
converting them to unsigned long if necessary.  Each time these are 
referenced in the body, the values would be moved to a D register and 
the appropriate operation done.  The outputs get assigned to q and r. 
With a good optimizing compiler, the movl's could probably be eliminated
(and the compiler would be allowed to interchange arg1 and arg2 in the 
mulul since the instruction is computationally commutative).  The 
programmer expresses his algorithm in terms of the available instructions, 
while the compiler worries about the things it is good at (e.g., storage 
and register allocation, common subexpression recognition, loop invariants).  
The body of the definition would be allowed to reference more registers than 
are available, with the compiler responsible for handling the overflow.

	Note the ASM primitive of C is unsatisfactory, for it forces
the programmer to know where the compiler has put the operands.  I once
used FORTRAN statement functions on the Control Data 7600 to do
double-length integer multiplies (the same hardware instruction was
used for the upper half of floating and integer multiplications, and
I was able to tell the compiler to treat my original operands
as floating point without changing the bit-pattern), but nowhere
else have I succeeded. 
--------
        Peter Montgomery
        pmontgom@MATH.UCLA.EDU 

lewitt@Alliant.COM (Martin E. Lewitt) (07/04/89)

In article <2274@wyse.wyse.com> stevew@wyse.UUCP (Steve Wilson xttemp dept303) writes:
--- much deleted ---

>Seriously, there are machines that were NEVER meant to be programmed in
>assembly!  Try programming a VLIW machine in assembly.  Chances are that unless
>your the guy that architected it, or wrote the compilers for it, you can't.
>(I've worked on such a box, at best it certainly ain't easy ;-)

--- some deleted ---

>Steve Wilson 

I'm not sure what machines out there are giving you the impression that
VLIW assembly is difficult, but that wasn't my experience at all.  I found
programming the FPS AP-120B array processors and their 64 bit mini-super
follow-on products, simple and elegant, especially compared to CISCs such
as the 68000 and the VAX.  I don't think I'd want to try the 8086.

With the FPS products, I seldom had to consult an architecture or
assembly manual.  I just kept a diagram of the architecture in front
of me and pictured the data being latched onto this bus or into that
register or functional unit.  There were a few simple rules to follow,
and most of the restrictions made sense, like don't latch two things
onto the same bus in the same instruction.  At the end you counted
your instructions and you knew how fast you were running.

On the CISCs, there are multiple cycle instructions and addressing modes,
instruction alignment and resource dependency stalls, etc.  At the end
of a task, I still find myself wondering if I missed some instruction or
addressing mode which might have been faster.

Maybe some VLIWs out there are more difficult because they are pushing
the technology harder, trying to encode more in an instruction word or
something.  They might sacrifice some of the generality that their
bus structure diagram would lead you to believe was there.  I'm curious
about these experiences with other VLIW architectures.

lewitt@Alliant.COM (Martin E. Lewitt) (07/04/89)

Sorry about the missing signature on that last posting, some kind of
permission problem on my signature file.  It should be appended now:
-- 
Phone: (206) 931-8364			Martin E. Lewitt      My opinions are
Domain: lewitt@alliant.COM		2945 Scenic Dr. SE    my own, not my
UUCP: {linus|mit-eddie}!alliant!lewitt  Auburn, WA 98002      employer's. 

cik@l.cc.purdue.edu (Herman Rubin) (07/05/89)

In article <255@obs.unige.ch>, bartho@obs.unige.ch (PAUL BARTHOLDI) writes:
> In article <1989Jun26.195044.4197@cs.rochester.edu>, crowl@cs.rochester.edu (Lawrence Crowl) writes:
> >>When we were [designing integer division support on the MIPS processors], we
> >>couldn't think of any languages whose natural implementation on a 32-bit
> >>machine would expect generation of a 64 by 32 bit divide as the expected
> >>operation.  Does anybody know of any such, or is this strictly something one
> >>would use in handcoded routines?
> >  
> > Languages which support fixed-point arithmetic, e.g. PL/I and Ada, could use
> > such a feature to substantially improve fixed-point performance ...

	[There follows a discussion of how this happens in FORTH]

The language designers did not put something in the language, therefore the
hardware people do not put it on the machine, therefore the new language
designers do not put it in the language, and the recursion continues.  Despite
the claims of the Algolniks, there has been no language which comes close to
treating what I consider the hardware-natural operations which I would find
useful.  Many of them were present 35 years ago on a larger proportion of
machines than now.

What languages, other than Lisp and some similar ones, have the idea that
an operation or function can return a string of values?  What languages allow
the user to introduce addition operator symbols?  A few allow additional types.
Now these deficiencies in languages go back to day 1.

What is double precision?  We first need to know what single precision is.
And (no surprise) it is different on different machines, and can even be
different for addition and multiplication on the same machine.  On the
CRAY, for integer addition one can use 64-bit 2's complement numbers.
But for multiplication, only 48-bit sign-magnitude mantissas.  However,
the best integer multiplication possible is 24x24->48.  And all division
has to be programmed.

The CYBER 205 has only 48-bit 2's complement numbers for arithmetic.
There are even problems in integer addition.  But a double length product
can be produced with two multiplies, with minor problems.  Floating point
division is present, but fixed point must be programmed.  Interesting
things can be done with vectors, and a programmed 32x32->64 inner product
might be a good idea.

Now if you define single precision as 16 bits, a 32-bit machine is 
automatically double-precision.  If you define it as 256 bits, you
would need 256x256->512 and 512/256 giving a 256-bit quotient and
remainder to have double precision.  For those needing good integer
arithmetic, the bigger the better.  And a few machine operations make
a big difference.
-- 
Herman Rubin, Dept. of Statistics, Purdue Univ., West Lafayette IN47907
Phone: (317)494-6054
hrubin@l.cc.purdue.edu (Internet, bitnet, UUCP)

mac@mrk.ardent.com (Michael McNamara) (07/06/89)

	[ An ongoing discussion of the lack of certain interesting
math primitives in HLLs, unease of assembly programming risc chips,
and the need for extensible HLLs ].  

	On the extensible HLL front, why not go to the authors of the
extensible editor? the FSF's C compiler, GCC, has extended asm macro
support which allow you to symbolicly hook up your asm routines to HLL
variables. An excerpt from the gcc manual appears below.  You can
obtain gcc via ftp from a number of sites, as well as via uucp from
osu-cis.

	Note this is a bit of a long posting, but, gcc is USEFUL to
allow mortals to use the whole machine...

>  In article <1333@sunset.MATH.UCLA.EDU> pmontgom@sonia.math.ucla.edu
>  (Peter Montgomery) writes:
>  	Given integers A, B, C where  0 <= A, B, < C,  I want to be able to
>  find q, r such that A*B = q*C + r and 0 <= r < C.  I do multiple-precision
>  arithmetic with large numbers, and this is such an important operation that
>  I cannot afford to call a subroutine every time I do it.
>  So rather than have just a small assembly routine to do this function,
>  I write the entire loop or the entire procedure in assembly code.
>  
>  	I want to be able to define primitives like this in my language,
>  telling the compiler which sequence of instructions to generate whenever it
>  encounters my primitive (this sequence of instructions will be defined
>  ONCE, in the machine dependent part of my program, but the code
>  referencing the primitives will be scattered throughout).  Many languages
>  allow one to define user primitives in terms of other language elements
>  (macros), but few languages allow us to go deeper and say things like (MC68020)
>  
>  	"DEFINE QUOT_REM_64(arg1:unsigned long, register type D;
>  	                    arg2:unsigned long, register type D;
>  			    arg3:unsigned long, register type D),
>  	        RETURNS    (arg4:unsigned long, register type D;
>  			    arg5:unsigned long, register type D);
>  		LOCAL upper: register type D;
>  		LOCAL lower: register type D;
>  			movl  arg1, lower
>  			mulul arg2, upper:lower  /* 64-bit product arg1*arg2 */
>  			divul arg3, upper:lower  /* Divide by arg3 */
>  			movl  lower, arg4	 /* quotient */
>  			movl  upper, arg5	 /* remainder */
>  	END QUOT_REM;"
>  
>  	When the compiler subsequently encounters an expression like 
>  (q, r) := QUOT_REM_64(A, B, C), the compiler would evaluate A, B, and C, 
>  converting them to unsigned long if necessary.  Each time these are 
>  referenced in the body, the values would be moved to a D register and 
>  the appropriate operation done.  The outputs get assigned to q and r. 
>  With a good optimizing compiler, the movl's could probably be eliminated
>  (and the compiler would be allowed to interchange arg1 and arg2 in the 
>  mulul since the instruction is computationally commutative).  The 
>  programmer expresses his algorithm in terms of the available instructions, 
>  while the compiler worries about the things it is good at (e.g., storage 
>  and register allocation, common subexpression recognition, loop invariants).  
>  The body of the definition would be allowed to reference more registers than 
>  are available, with the compiler responsible for handling the overflow.
>  
>  	Note the ASM primitive of C is unsatisfactory, for it forces
>  the programmer to know where the compiler has put the operands.  I once
>  used FORTRAN statement functions on the Control Data 7600 to do
>  double-length integer multiplies (the same hardware instruction was
>  used for the upper half of floating and integer multiplications, and
>  I was able to tell the compiler to treat my original operands
>  as floating point without changing the bit-pattern), but nowhere
>  else have I succeeded. 
>  --------
>          Peter Montgomery
>          pmontgom@MATH.UCLA.EDU 

From the GCC info node, Extended Asm Support:

Assembler Instructions with C Expression Operands
=================================================

In an assembler instruction using `asm', you can now specify the
operands of the instruction using C expressions.  This means no more
guessing which registers or memory locations will contain the data you want
to use.

You must specify an assembler instruction template much like what appears
in a machine description, plus an operand constraint string for each
operand.

For example, here is how to use the 68881's `fsinx' instruction:

     asm ("fsinx %1,%0" : "=f" (result) : "f" (angle));

Here `angle' is the C expression for the input operand while
`result' is that of the output operand.  Each has `"f"' as its
operand constraint, saying that a floating-point register is required.  The
constraints use the same language used in the machine description
(*Note Constraints::).

Each operand is described by an operand-constraint string followed by the C
expression in parentheses.  A colon separates the assembler template from
the first output operand, and another separates the last output operand
from the first input, if any.  Commas separate output operands and separate
inputs.  The number of operands is limited to the maximum number of
operands in any instruction pattern in the machine description.

Output operand expressions must be lvalues; the compiler can check this.
The input operands need not be lvalues.  The compiler cannot check whether
the operands have data types that are reasonable for the instruction being
executed.  It does not parse the assembler instruction template and does
not know what it means, or whether it is valid assembler input.  The
extended `asm' feature is most often used for machine instructions
that the compiler itself does not know exist.

If there are no output operands, and there are input operands, then you
should write two colons in a row where the output operands would go.

The output operands must be write-only; GNU CC will assume that the values
in these operands before the instruction are dead and need not be
generated.  For an operand that is read-write, or in which not all bits are
written and the other bits contain useful information, you must logically
split its function into two separate operands, one input operand and one
write-only output operand.  The connection between them is expressed by
constraints which say they need to be in the same location when the
instruction executes.  You can use the same C expression for both operands,
or different expressions.  For example, here we write the (fictitious)
`combine' instruction with `bar' as its read-only source operand
and `foo' as its read-write destination:

     asm ("combine %2,%0" : "=r" (foo) : "0" (foo), "g" (bar));

The constraint `"0"' for operand 1 says that it must occupy the same
location as operand 0.

Only a digit in the constraint can guarantee that one operand will be in
the same place as another.  The mere fact that `foo' is the value of
both operands is not enough to guarantee that they will be in the same
place in the generated assembler code.  The following would not work:

     asm ("combine %2,%0" : "=r" (foo) : "r" (foo), "g" (bar));

Various optimizations or reloading could cause operands 0 and 1 to be in
different registers; GNU CC knows no reason not to do so.  For example, the
compiler might find a copy of the value of `foo' in one register and
use it for operand 1, but generate the output operand 0 in a different
register (copying it afterward to `foo''s own address).  Of course,
since the register for operand 1 is not even mentioned in the assembler
code, the result will not work, but GNU CC can't tell that.

Unless an output operand has the `&' constraint modifier, GNU CC may
allocate it in the same register as an unrelated input operand, on the
assumption that the inputs are consumed before the outputs are produced.
This assumption may be false if the assembler code actually consists of
more than one instruction.  In such a case, use `&' for each output
operand that may not overlap an input.  *Note Modifiers::.

Some instructions clobber specific hard registers.  To describe this,
write a third colon after the input operands, followed by the names of
the clobbered hard registers (given as strings).  For example, on the vax,

     asm volatile ("movc3 %0,%1,%2"
                   : /* no outputs */
                   : "g" (from), "g" (to), "g" (count)
                   : "r0", "r1", "r2", "r3", "r4", "r5");

Usually the most convenient way to use these `asm' instructions is to
encapsulate them in macros that look like functions.  For example,

     #define sin(x)       \
     ({ double __value, __arg = (x);   \
        asm ("fsinx %1,%0": "=f" (__value): "f" (__arg));  \
        __value; })

Here the variable `__arg' is used to make sure that the instruction
operates on a proper `double' value, and to accept only those
arguments `x' which can convert automatically to a `double'.

Another way to make sure the instruction operates on the correct data type
is to use a cast in the `asm'.  This is different from using a
variable `__arg' in that it converts more different types.  For
example, if the desired type were `int', casting the argument to
`int' would accept a pointer with no complaint, while assigning the
argument to an `int' variable named `__arg' would warn about
using a pointer unless the caller explicitly casts it.

GNU CC assumes for optimization purposes that these instructions have no
side effects except to change the output operands.  This does not mean that
instructions with a side effect cannot be used, but you must be careful,
because the compiler may eliminate them if the output operands aren't used,
or move them out of loops, or replace two with one if they constitute a
common subexpression.  Also, if your instruction does have a side effect on
a variable that otherwise appears not to change, the old value of the
variable may be reused later if it happens to be found in a register.

You can prevent an `asm' instruction from being deleted, moved or
combined by writing the keyword `volatile' after the `asm'.  For
example:

     #define set_priority(x)  \
     asm volatile ("set_priority %0": /* no outputs */ : "g" (x))

It is a natural idea to look for a way to give access to the condition
code left by the assembler instruction.  However, when we attempted to
implement this, we found no way to make it work reliably.  The problem
is that output operands might need reloading, which would result in
additional following "store" instructions.  On most machines, these
instructions would alter the condition code before there was time to
test it.  This problem doesn't arise for ordinary "test" and
"compare" instructions because they don't have any output operands.


--
_________________
Michael McNamara 
  mac@ardent.com 

suitti@haddock.ima.isc.com (Stephen Uitti) (07/06/89)

In article <1380@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:
(and various others wrote:)
>[1] You are not supposed to write assembler programs on a RISC
>machine. The compilers have sophisticated algorithms to generate
>"optimal" multiplication/division out of simpler instructions.
>>     I find this attitude arrogant.  Neither you nor anyone else knows what 
>>     complicated operations I want to do.

Still, I haven't needed to learn an assembly language just to
make some operation faster for over five years.  The machine I
have on my desk is four times faster than the machine I used to
share with 35 people at a time (a VAX 780).  It has as much disk
space (and much more for me).  It has more RAM (9 MB vs 4 or 8).
It runs the same operating system, though it has improved
capabilities (bitmapped screen, other local devices).  The reason
i can have it on my desk is that it costs 35 times less than the
old VAX.  Cost of ownership is also lower in similar proportion.

>You do not understand.  A very good optimizing compiler (and I have not
>seen one) can do a better job of doing a particular complex operation than
>I can, PROVIDED that I want to do that operation.

The current gcc output is OK, but i can do better.  I can
generally do minimally 20% better than any compiler.  If my
original idea maps well to the hardware, but not to the compiler,
then speedups can be an order of magnitude.  Then again, in one
case, the compiler writer provided a long divide, and i was able
to speed it up by 15% or so.  Three points: Computers are getting
real fast, so coding for speed is not required as often (which
means code will tend to get sloppier, which means that our
throughput will not increase as fast as our machine speeds...  of
course, i tend not to write that much stuff that needs to run for
very long).  Compilers do not code as well as a trained human can
(this will probably always be true - on the other hand, compilers
do their work faster than humans, and this will also continue to
be true.)  Any non-trivial piece of work can be improved on with
the addition of more work and attention (the more complex the
problem or system, the more that can be gained by additional attention).

>In most cases, I do not need run-time profiling; I know my algorithms.

Profiling often turns up surprises.  One graphics application
looked to be I/O bound.  Profiling showed that significant time
was being spent converting bitmaps.  An algorithmic change
yielded a factor of ten speed improvement.  Since no similar
improvement could be made with the addition of assembler, none
was added (it was tried, but it didn't help that much, was a lot
of work to finish since there were so many possible high-use
paths, and it made the application non-portable).

I've also used profiling for interactive programs to reduce the
time to respond to the user.  It turned out that the program was
performing a floating point calculation that could be precomputed
into a small table which could compiled into the program.  It just
wasn't known how slow the floating point was on the machine in
question.

>What is there to know?  I have not seen a computer instruction set where
>the user instructions
>are anywhere near as complicated as Fortran or C.

The VAX instruction set is pretty complex.  There are 16
registers, some have various dedicated purposes (stack, frame,
temporaries for bcopy, etc.).  There are lots of addressing
modes.  There are simply lots of instructions, and one needs to
know many of them.  Then you have to learn the local syscalls...
Fortunately, UNIX tends to be pretty good here.  Most OS's are
pretty complex.  Try opening a file under VMS...  It can be hard,
especially for a novice, to get started.

Fortran and C are pretty complex, but one doesn't need to know
that much to get started.  Further, there are structures that
allow readable representations, which allows the user to write
maintainable code (it doesn't mean he'll do it, but at least he can).
Of course, then there's portability.  How long will your current
machine last?  How long do you want to use your program?  Chances
are, by the time you upgrade your machine, you'll have forgotten
how your program worked.  Of course, you could just buy a CDC 6600
and hang onto it for a quarter century.  Then you could just retire.

>BTW, I find the IEEE standard frequently a pain; I would prefer
>the option of having unnormalized floating point when I want it.

Not all machines give you this capability.  One architecture i
worked with recently had entries for many instructions stating
that if you somehow managed to feed this instruction an
unnormalized value that it would either produce an incorrect
result or a floating point exception.  Given that all floating point
instructions produced normalized results on this machine, it was
hard to imagine how you'd get into a such a situation.

There are apparently good reasons for hardware types to restrict
the use of the hardware.  Most of them are probably speed and cost.

The main problem that i've had with the IEEE standard is that
it specifies more precision than my home computer really had the
power to support.  That's changed.  I bought a faster computer.
My C64 died...  and a good thing too - i'd still be in the dark ages.

>I am not saying that these HLLs may not save time.  I use them myself.  But
>they are not adequate.  They are not complete.  They do not have the ability
>to accommodate simple extensions.

I have yet to see a C compiler without hooks to allow assembly
language access.  Some have in-line capability.  Some have
function call and separate compilation capability.  Some have
both.  Most Fortrans, and even COBOLs i've seen have similar
capability.  I don't see a problem with this.  I assume you
aren't talking about interpreted BASIC...

What i'd like to see in language support is more extensive
libraries.  UNIX comes with all sort of neat libraries for C.
People have had various Fortran libraries for a long time.  Yet,
i still constantly write and rewrite time/date parsing functions,
misc database-like routines, etc.  With a rich library of code
and proper documentation, even assembler can be reasonable to
deal with.  Of course, now we're getting way off the beaten
comp.arch track...

Stephen.

suitti@haddock.ima.isc.com (Stephen Uitti) (07/06/89)

In article <1387@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:
>What languages, other than Lisp and some similar ones, have the idea that
>an operation or function can return a string of values?

C.  One can pass structures and unions back & forth by name or
value.  While it is true that some C compilers are broken when
passing large things by value, it has been defined in the
language since 1978.  OK, so the syntax is a little awkward in
that you have to define the structures, but at least it tends not
to add large amounts of additional overhead.  Also, in C, people
commonly use the code sequences:
caller:
	int i, j;
	char *p;
	i = foo(&j, &p);
callie:
	int foo(j, p)
	int *j;
	char **p;
	{
		*j = secondvalue;
		strcpy(*p, "third value");
		return value;
	}
Which is OK, as long as the interface is properly documented.

Now you want the language to use syntax that YOU defined?
Compiler writers are doing that now too.  I'd rather they spent
their time on designing unambiguous and coherant languages for
which code can be easily written, read and modified, that have
real scope rules for libraries and other seperately compiled
environments, and produce more optimal code for real machines,
and provide for special needs of real people in the way of
specialized preprocessors (lex, yacc) and prewritten library
code.

>What languages allow the user to introduce additional operator
>symbols?  A few allow additional types.  Now these deficiencies
>in languages go back to day 1.

C++ and Ada.  My wristwatch (Casio) has more compute resources
than was available on day 1.  I don't expect it to allow
additional operators and types either.  It does have a 64 bit
(BCD) divide.  Throw out your '205 and get a Casio.

Stephen.

khb@gammara.Sun.COM (gammara) (07/06/89)

In article <13943@haddock.ima.isc.com> suitti@haddock.ima.isc.com (Stephen Uitti) writes:
>In article <1380@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:
>(and various others wrote:)

>very long).  Compilers do not code as well as a trained human can
>(this will probably always be true - on the other hand, compilers

Depends on the compiler and the machine. The famed PL/.8 for the IBM
801 often produced provably perfect code (say those involved with the
project). Try hand coding a Multiflow Trace/28 or a Cydra-5 ... from
scratch (taking the f77 compiler output and hacking is cheating! :>).
There are some humans who can do better than the compiler on these
machines ... but for non-trivial codes the number of people is very
small, and cost very high.

The overall cost of a project is typically "maintence" bound (meaning
changing the definition of the problem now that we have a trial
solution ... all in the guise of making a few modest code changes).
Then comes coding and design costs, etc. 

Handcrafted assembly language is seldom economically viable today.
Given advances in compilers, and hardware evolution, it is often
counter productive (coded SAXPY is slower than a compiler which
inlines and outer loop unrolls).

Specialized instructions which are of great interest to (say) 5% of
the population are simply not worth putting into hardware (since it
means that "real estate" can be dedicated to solving 95% of the worlds
problems better). 

When folks have an instruction which is important enough to them (say
$200M) they can probably get it in. The vast majority of purchasers
have been voting with their feet ... simpler is more popular. (I am
counting the 80xxx as simpler than a mainframe ... this is not just
RISC vs. CISC this is thinking in economic, bottom line terms).



Keith H. Bierman      |*My thoughts are my own. Only my work belongs to Sun*
It's Not My Fault     |	Marketing Technical Specialist    ! kbierman@sun.com
I Voted for Bill &    |   Languages and Performance Tools. 
Opus  (* strange as it may seem, I do more engineering now     *)

khb@gammara.Sun.COM (gammara) (07/06/89)

In article <13946@haddock.ima.isc.com> suitti@haddock.ima.isc.com (Stephen Uitti) writes:
>In article <1387@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:
>
>>What languages allow the user to introduce additional operator
>>symbols?  A few allow additional types.  Now these deficiencies
>>in languages go back to day 1.
>
>C++ and Ada.  My wristwatch (Casio) has more compute resources
>than was available on day 1.  I don't expect it to allow
>additional operators and types either.  It does have a 64 bit
>(BCD) divide.  Throw out your '205 and get a Casio.

Well, as long as we are leaving .arch and moving to .lang :>,
Fortran8x has operator overloading and user defined operators.



Keith H. Bierman      |*My thoughts are my own. Only my work belongs to Sun*
It's Not My Fault     |	Marketing Technical Specialist    ! kbierman@sun.com
I Voted for Bill &    |   Languages and Performance Tools. 
Opus  (* strange as it may seem, I do more engineering now     *)

cik@l.cc.purdue.edu (Herman Rubin) (07/06/89)

In article <13946@haddock.ima.isc.com>, suitti@haddock.ima.isc.com (Stephen Uitti) writes:
> In article <1387@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:
> >What languages, other than Lisp and some similar ones, have the idea that
> >an operation or function can return a string of values?
> 
> C.  One can pass structures and unions back & forth by name or
> value.

NO!.  For the n-th time, a list is not a struct.  A struct is a memory
allocation scheme.  It presumes a particular "physical" ordering of the
items.  Even if one can implement a struct in registers, it still 
restricts which registers are used far more than a list does.  For
example, on a machine in which there is division yielding a quotient
and a remainder, I want to write

	q,r = x/y;

On the VAX, for example, q could be in register 3 and r in register 10.
Since the hardware allows simple things like this, the language should.
At the present time and in the future, loads and stores must slow things
down unless they can be overlapped.  The overlapping problem is greatly
exascerbated by having to do too many of them.
> 
> Now you want the language to use syntax that YOU defined?
> Compiler writers are doing that now too.  I'd rather they spent
> their time on designing unambiguous and coherant languages for
> which code can be easily written, read and modified, that have
> real scope rules for libraries and other seperately compiled
> environments, and produce more optimal code for real machines,
> and provide for special needs of real people in the way of
> specialized preprocessors (lex, yacc) and prewritten library
> code.

How many people would have difficulty with the above quotient-remainder
notation even without explanation?  With commenting, lots more could be
done without difficulty.  A struct is more complicated than a new type.
>
> >What languages allow the user to introduce additional operator
> >symbols?  A few allow additional types.  Now these deficiencies
> >in languages go back to day 1.
> 
> C++ and Ada.  My wristwatch (Casio) has more compute resources
> than was available on day 1.  I don't expect it to allow
> additional operators and types either.  It does have a 64 bit
> (BCD) divide.  Throw out your '205 and get a Casio.
> 
I am not very familiar with Ada.  C++ allows the introduction of new
types, although they are not called types.  The stupid use of the word
"typedef" in C rather than the more accurater "typealias" is responsible
for this.  But I do not believe that C++ or Ada allows one to define
symbols or symbol combinations as infix operators.  As a simple case,
I should be able to tell the compiler that \ is the bit clear operation,
which &~ may or may not handle properly, and has the same machine
structure as OR on the VAX (or any other machine which has it in
hardware.)  If I am using a 205, I will use an algorithm appropriate
for the 205.  If I am using a CRAY, I will use an appropriate one.
If it involves double precision multiplication or square root, I will try
hard to find a workaround on the CRAY.


-- 
Herman Rubin, Dept. of Statistics, Purdue Univ., West Lafayette IN47907
Phone: (317)494-6054
hrubin@l.cc.purdue.edu (Internet, bitnet, UUCP)

suitti@haddock.ima.isc.com (Stephen Uitti) (07/07/89)

In article <1388@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:
>> >What languages, other than Lisp and some similar ones, have
>> >the idea that an operation or function can return a string of values?
>> C.  One can pass structures and unions back & forth by name or
>> value.
>NO!.  For the n-th time, a list is not a struct.  It presumes a
>particular "physical" ordering of the items.

LISP does not actually pass whole lists around - it uses pointers
and linked lists.  In C, linked lists are typically implemented
with a struct for each node.  There are no lists in C, but
structs can be lists.

You wanted to have a function be able to return more than one
item.  I suggested two syntactic solutions: returning a struct,
or having the caller specify where to put the answers.  When it
comes down to it, you don't want the compiler to necessarily
generate a function with call/return overhead.  You really want
functions or intrinsics that the compiler can turn into inline
code.  You are thinking of inline code as a single instruction,
though i'm not limiting this to anything that simple.

Is there anything really evil with this:
	q = divrem(top, bottom, &remainder);
Divrem can "return" q, and stuff "remainder" where it is supposed
to go.  Compilers are getting smart enough to do this the way you
want it.  This is documentable.  This is manageable.  Type
checking can be done everywhere to reduce the error rate.

Let's say you have a '205 and a VAX.  You want to develop your
code on the VAX, because it has better debuggers, faster response,
and it is much cheaper than '205 time.  If you break the grammer
of your standard language, then you have to break it for both
machines.  The above 'divrem' function can be written in real C,
and a (slow) version can run, and be debugged on the VAX.  Then,
the very same code can be run on the target machine.

The way '205 C's vector stuff was actually done broke all sorts
of things.  Writing portable high speed code required heavy
ifdefs for each application.  It could have been done in a manner
that a simple library could be written for the VAX (etc.) that
would allow the code to be just run.

>I want to write
>	q,r = x/y;
Unfortunately, this already means something in C.

>On the VAX, for example, q could be in register 3 and r in register 10.

The syntax i suggested above would not require things to be in
any given place.  The compiler should be able to figure this
sort of thing out.

>Since the hardware allows simple things like this, the language should.

The language does.  It just doesn't allow you to do it with the
syntax you want.  This is (by comparison to register allocation
and procedure inlining) a hard problem.  Languages that have
fixed syntax are difficult enough to prove unambiguous.  If the
language allows free redefinition, it is easy for the user to
generate something that can't be parsed.  It took years to get a
real Ada compiler to work.

>At the present time and in the future, loads and stores must slow things
>down unless they can be overlapped.

Or optimized out.

>The stupid use of the word "typedef" in C rather than the more
>accurater "typealias" is responsible for this.

OK, so the terminology for C is a little broken.  I've used
languages that have (for example) German words for keywords.
That didn't mean that i felt the need to change the language's
syntax or terminology.

>As a simple case,
>I should be able to tell the compiler that \ is the bit clear operation,
>which &~ may or may not handle properly, and has the same machine
>structure as OR on the VAX.

How do you want to tell it what the operator means?  Do you want
to give it an assembler template?  Do you want to give it an
algorithm already expressible in the language?  If it is
assembler, you probably want gcc's asm feature that allows
symbolic names to be given to assembler arguments.  Then, if it
turns out to be more complicated, you might try preprocessor
macros.  If it turns out to be more complicated than that, you
might put it into an inlineable function.  Otherwise, it should
probably be a true function.  You may already have the tools you need.

Stephen.

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

In article <13961@haddock.ima.isc.com>, suitti@haddock.ima.isc.com (Stephen Uitti) writes:
> In article <1388@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:
> >> >What languages, other than Lisp and some similar ones, have
> >> >the idea that an operation or function can return a string of values?
> >> C.  One can pass structures and unions back & forth by name or
> >> value.
> >NO!.  For the n-th time, a list is not a struct.  It presumes a
> >particular "physical" ordering of the items.
> 
> LISP does not actually pass whole lists around - it uses pointers
> and linked lists.  In C, linked lists are typically implemented
> with a struct for each node.  There are no lists in C, but
> structs can be lists.

And this is the reason that production programs and library subroutines
should not be written in Lisp.  But it is possible to get much done
efficiently.  This should be done by enabling those who can see how
to do things to do them and to communicate that to others.  Anything
can be done on a universal Turing machine, but I am not willing to
wait a month for it to multiply two 10-digit numbers by reducing
everything to successor.

> You wanted to have a function be able to return more than one
> item.  I suggested two syntactic solutions: returning a struct,
> or having the caller specify where to put the answers.  When it
> comes down to it, you don't want the compiler to necessarily
> generate a function with call/return overhead.  You really want
> functions or intrinsics that the compiler can turn into inline
> code.  You are thinking of inline code as a single instruction,
> though i'm not limiting this to anything that simple.
> 
> Is there anything really evil with this:
> 	q = divrem(top, bottom, &remainder);
> Divrem can "return" q, and stuff "remainder" where it is supposed
> to go.  Compilers are getting smart enough to do this the way you
> want it.  This is documentable.  This is manageable.  Type
> checking can be done everywhere to reduce the error rate.

There are three things wrong with this.  First, I do not know of 
any version of C which can pass the address of a register.  In
fact, I know of few machines in which that construct makes sense.
And how would you pass it, anyhow?

The second is that I do not want the compiler to even think that this
should be a subroutine call.  For what I consider obvious, I should not
have to rely on the compiler writer being equally astute.  Of course,
this is only the case if the hardware is on the machine.  But even if
not, it probably should be inlined.

The third is, in my opinion, the most important.  It is why I execrate
most of the present assembler languages.  Notation is very important.
For example, I consider the introduction of algebraic symbols by
Diophantus as one of the really great contributions to mathematical
communication.  In fact, I would make it THE mathematical requirement
for admission to college.  But the same holds for algebraic operators.

I am no more willing to write 	q = divrem(top, bottom, &remainder);
every time I want to use it than you are to write  x = sum(y,z);
In fact, I would consider the first as more confusing notationally
than the second.  Is it any easier for a human to read than

	q,r = top/bottom;

What about

	exp,mant =UP (d);

to unpack a double into an int exponent and a double long mant, both to
be in registers?  I have used this.

> Let's say you have a '205 and a VAX.  You want to develop your
> code on the VAX, because it has better debuggers, faster response,
> and it is much cheaper than '205 time.  If you break the grammer
> of your standard language, then you have to break it for both
> machines.  The above 'divrem' function can be written in real C,
> and a (slow) version can run, and be debugged on the VAX.  Then,
> the very same code can be run on the target machine.
> 
What gives you the impression that I would want to use the same
code on a 205 and a VAX?  I have used both machines, and I would
consider it a mistake much of the time.  On the VAX I would use
a long long, but on the 205 only a one-word integer.  To me they
are somewhat different.  Only at the bright human level should one
consider a common code.  For division with remainder, the code should
be machine language, not C.  Would you use C to do machine-independent
implementation of division?  You would not think of it.  Neither would
I think of making similar operations such as frexp, divrem, exponentiation,
absolute value, integer part, etc., machine independently coded in C.
I would not consider coding the =UP above in C.  It is an additional
primitive operation, like +, *, /, &.

> The way '205 C's vector stuff was actually done broke all sorts
> of things.  Writing portable high speed code required heavy
> ifdefs for each application.  It could have been done in a manner
> that a simple library could be written for the VAX (etc.) that
> would allow the code to be just run.

I consider the 205 hardware the best designed vector hardware I have
seen.  It is the C language which is inadequate.  And there is a severe
limit to what should be portable.

> >I want to write
> >	q,r = x/y;
> Unfortunately, this already means something in C.

What does it mean?  Would putting the q,r in parentheses or something else
avoid the conflict?  The use of a list to the left of the = should have
been noticed as a deficiency in the languages 35 years ago.  

> >On the VAX, for example, q could be in register 3 and r in register 10.
> 
> The syntax i suggested above would not require things to be in
> any given place.  The compiler should be able to figure this
> sort of thing out.

I have yet to see a fair compiler.

> >Since the hardware allows simple things like this, the language should.
> 
> The language does.  It just doesn't allow you to do it with the
> syntax you want.  This is (by comparison to register allocation
> and procedure inlining) a hard problem.  Languages that have
> fixed syntax are difficult enough to prove unambiguous.  If the
> language allows free redefinition, it is easy for the user to
> generate something that can't be parsed.  It took years to get a
> real Ada compiler to work.

It is not hard.  I will very gladly settle for a highly flexible easily
written simple language.  The problem is that there are a limited number
of basic macro substitutions in the present languages, and because of this,
elegance is attempted.  If the language had to allow for the user using the
same types of macros, more simplicity would be required.

> >At the present time and in the future, loads and stores must slow things
> >down unless they can be overlapped.
> 
> Or optimized out.

I consider a programmer quite inept who does not know what is temporary and
what is not.  I consider someone quite bright who can look over code not
making this distinction and figure it out.  A compiler is fast, but does
not compare in intelligence with a human imbecile.

> >The stupid use of the word "typedef" in C rather than the more
> >accurater "typealias" is responsible for this.
> 
> OK, so the terminology for C is a little broken.  I've used
> languages that have (for example) German words for keywords.
> That didn't mean that i felt the need to change the language's
> syntax or terminology.

There is a difference between using a German word and using an incorrect
word.  There were languages before C which allowed the user to define types.
K&R even comment that "typedef" does not allow one to define a new type.

Also, computer language and system people seem prone to taking standard
mathematical symbols and using them for other purposes, even to the extent
that the standard use is precluded by it.

> >As a simple case,
> >I should be able to tell the compiler that \ is the bit clear operation,
> >which &~ may or may not handle properly, and has the same machine
> >structure as OR on the VAX.
> 
> How do you want to tell it what the operator means?  Do you want
> to give it an assembler template?  Do you want to give it an
> algorithm already expressible in the language?  If it is
> assembler, you probably want gcc's asm feature that allows
> symbolic names to be given to assembler arguments.  Then, if it
> turns out to be more complicated, you might try preprocessor
> macros.  If it turns out to be more complicated than that, you
> might put it into an inlineable function.  Otherwise, it should
> probably be a true function.  You may already have the tools you need.

Whatever is appropriate.  But the C preprocessor, or any other present
macro language in my ken, will not do it.  A macro template processor,
in which the macro "name" is scattered through the macro, and which is
weakly typed and may even use storage classes, would do the job.

For example, I would consider x ={t} y - z the "=-" macro with arguments
x, y, and z.  Its precise meaning involves the types of the arguments,
unless the optional t field is present, which overrides the type.  A more
complicated one is the basic vector operation collection on the 205, which
could be written

	c{'z} ={t} {-}{|} a{'x} OP{m} {|} b{'y} {/\{~}w}

Here we have the "=OP" macro with 3-7 arguments and 10 optional fields.
Since the fields operate independently and simply, this is not really
difficult.  If a or b is scalar, the displacement fiels following
them must be absent, and if OP is Boolean, the - and | fields must
be absent, etc.  So what?  With such a macro processor, I would not
be too inconvenienced by the lack of a compiler.

> Stephen.

The value of a compiler and language is ease of programming.  In this,
notation and flexibility are extremely important.  If a mathematician
finds notation missing, he invents it for his paper.  It may or may not
be adopted by others, but that does not prevent people from reading his
paper.

The same should hold for languages and compilers.  If I see a missing
construct, I should be able to define it using the power of the machine
on which I intend to run it.  A simple example of the lack of desirability
of C portability is the addition of two vectors.  The good coding of this
depends on the costs of the *x++ in loading and storing and the use of
index in loading and storing.  These depend both on the machine and the
storage classes of the adresses.  There are others.  A good programmer
must know these things and discuss the alternatives with the user.  This
is even the case if the programmer and user are the same person.
flexibility are important.
-- 
Herman Rubin, Dept. of Statistics, Purdue Univ., West Lafayette IN47907
Phone: (317)494-6054
hrubin@l.cc.purdue.edu (Internet, bitnet, UUCP)

peter@ficc.uu.net (Peter da Silva) (07/07/89)

In article <1388@l.cc.purdue.edu>, cik@l.cc.purdue.edu (Herman Rubin) writes:
> For example, on a machine in which there is division yielding a quotient
> and a remainder, I want to write

> 	q,r = x/y;

(1) What does this do when no such operation exists?
(2) How does this fit in with the rest of the expression syntax?
(3) Get a good optimiser and say:
	q = x/y;
	r = x%y;
(4) Is it obvious which is the quotient and which is the remainder
    when you have something like:
	tens,relx = muscle0.fiber/potential;
(5) What if x or y contain other operations that could require a
    quotient or remainder?
(6) What does this mean:
	q,r,s = x/y/z;
(7) What does this sort of micro-optimisation gain you? Except in a
    tight loop, very little. Since you're intending to be machine-
    specific anyway, why not squirrel that loop away in an assembly
    routine?
-- 
Peter da Silva, Xenix Support, Ferranti International Controls Corporation.
Business: peter@ficc.uu.net, +1 713 274 5180. | "WHAT HAPPENED TO ALL
Personal: peter@sugar.hackercorp.com.   `-_-' |  THE WOMEN IN TEXAS?"
Quote: Have you hugged your wolf today?  'U`  |  -- ACS1W@jane.uh.edu (meesh)

chansen@NeXT.UUCP (Craig Hansen) (07/08/89)

There's been some discussion of why double-length multiply and divide
were or were not included on various machines. However, the real
reasons behind why RISC machines don't like double-length multiply and
divide hasn't been hit. I can speak most authoritatively on the Mips
RISC processor.

Most operations on a RISC processor can be expressed as a function of
the contents of two general registers, yielding a single result.
Double-length multiply (two sources; two results) and divide (three
sources; two results) both violate this generalization, which makes
them more expensive to implement. A Nice Clean Architecture would just
add register specifiers and read and write ports until there were
enough, but that's not way of RISC.

The Mips R2000 uses two special-purpose registers, each of which hold
half of the double-length result of a multiply, or the quotient and
remainder of a divide. These registers are very carefully handled in
the implementation to permit them to be written into immediately on
starting up the operation, in order to avoid additional bypass and
staging latches - they're written into two cycles earlier than the
general registers.

This is the reason why operations that modify these registers must not
occur within two cycles after an instruction that reads them: if they
are any closer, an interrupt or exception may require restarting the
instruction stream at the special-register read, even though the
register was previously modified by an instruction that modified the
register and was aborted.

For double-length divides, you'd need three words of source operand,
and the R2000 only have two general register read ports. Yes, there's
room in the instruction encoding for another register specifier, but
there's no hardware to get a third value from the register file.
Because of the way the special-purpose registers are not bypassed,
it would not be possible to use one them to hold the third value;
if an interrupt or exception required restarting a double-length
divide, that third value would be corrupted.

So that's why there's no double-length divide: although the divider
itself is intrinsically able to perform the operation, it would have
cost more latches and multiplexors to get the data into the unit.
Latches and multiplexors make up a surprising amount of the cost of a
RISC processor; one works very hard to minimize the number of them.

There are other reasons not to bother: a double-length divide can
overflow in difficult to detect ways; a single-length divide can also
overflow, but it's easy to detect divide by zero and divide of MININT
by -1 in software while the divide is running in parallel. Of course,
as has been mentioned before, there's no expression of this operation
in C, so a C compiler won't generate it.

Finally, it should be noted that an integer divide is actually more
complex (time x space-wise) than a floating-point divide: there are
fast redundant-representation techniques that only work for normalized
numbers. You'd probably find that a multiple-precision divide can be
implemented faster using floating-point arithmetic than fixed-point on
most RISC machines.

Regards,
Craig Hansen

byrd@portia.Stanford.EDU (Greg Byrd) (07/08/89)

In article <13961@haddock.ima.isc.com> suitti@haddock.ima.isc.com (Stephen Uitti) writes:

[original posting about returning multiple values...]

>LISP does not actually pass whole lists around - it uses pointers
>and linked lists.  [stuff deleted]
>
>You wanted to have a function be able to return more than one
>item.  [more stuff deleted]

Just wanted to point out that you *can* return multiple values in
Lisp without returning a list -- e.g., the 'values' and 
'multiple-value-bind' forms in Common Lisp.


...Greg Byrd
   Stanford University

stevew@wyse.wyse.com (Steve Wilson xttemp dept303) (07/08/89)

In article <3243@alliant.Alliant.COM> lewitt@Alliant.COM (Martin E. Lewitt) writes:
>I'm not sure what machines out there are giving you the impression that
>VLIW assembly is difficult, but that wasn't my experience at all.  I found
> stuff deleted...

The Cydra-5.  I worked on this hardware directly and have a fair idea 
of the complexity for that specific architecture.  Things are easy
enough when your running linear code, but when you have 4 interations
of the same loop hot at the same time, and each resource is involved
with a different interation during the same clock, and how do you 
deal with the register allocation such that you don't cause the various
interations to colide with each other, i.e. don't break the dependencies,
etc.  It just gets worse from here, i.e. my hats off to the compiler
guys that could get this thing to run!

Steve Wilson  

suitti@haddock.ima.isc.com (Stephen Uitti) (07/08/89)

>For division with remainder, the code should
>be machine language, not C.  Would you use C to do machine-independent
>implementation of division?  You would not think of it.  Neither would
>I think of making similar operations such as frexp, divrem, exponentiation,
>absolute value, integer part, etc., machine independently coded in C.
>I would not consider coding the =UP above in C.  It is an additional
>primitive operation, like +, *, /, &.

Not only would i think of it, i've done it.  A check is made to see that
the compiler really generates the right instructions.

>I consider the 205 hardware the best designed vector hardware I have
>seen.

No real debate here.  The '205 was a real ambitious project.
Very, very difficult and costly to maintain, but real nice.
Current RISC machines are also ambitious, but in a differant way.
The goal is to make something simple enough to support that is
also fast enough to be viable.  The '205 lost the commercial race.
Mr Cray, who has been designing RISC architectures since the
dawn of time, is one of the winners.

>  It is the C language which is inadequate.  And there is a severe
>limit to what should be portable.
>> >I want to write
>> >	q,r = x/y;
>> Unfortunately, this already means something in C.
>
>What does it mean?

It means "evaluate the expression 'q', then the expression 'r =
x/y'.  The fact that 'q' is a variable doesn't mean anything.  It
is treated as an 'rvalue' (one that gives a result) rather than
as an 'lvalue' (where to put a result).  The comma operator is
extremely basic to the language.  It is probably a flaw in the
language design that it is programmer visible.  It is not a flaw
that i would suggest fixing.

>Would putting the q,r in parentheses or something else
>avoid the conflict?

Parenthesis will screw up something.  There is probably
a fix, but it won't be provable off the top of my head.

>> >At the present time and in the future, loads and stores must slow things
>> >down unless they can be overlapped.
>> 
>> Or optimized out.
>
>I consider a programmer quite inept who does not know what is temporary and
>what is not.

A compiler evaluating an expression generally produces all sorts
of temporary variables.  These are typically kept in registers,
but sometimes overflow - often to the stack.  On a machine like
the 205, with its near infinite register count, one would imagine
that there would never be register overflow.

>making this distinction and figure it out.  A compiler is fast, but does
>not compare in intelligence with a human imbecile.

Sure it compares.  The compiler is faster.  The compiler is
correct more often.  The human probably knows how to breath and
eat.  The compiler has no need for breathing or eating.  Of
course, there's the 'idiot savant', but i've never met one who
was actually much of an imbecile.

>The value of a compiler and language is ease of programming.  In this,
>notation and flexibility are extremely important.  If a mathematician
>finds notation missing, he invents it for his paper.  It may or may not
>be adopted by others, but that does not prevent people from reading his
>paper.

While i find such papers hard to read (especially since the
definition of the new notation syntax is seldom given), for
programs it is worse.  Even with the C preprocessor's limited
capabilities, one can write code that doesn't bear any
resemblence to C.  Maintanence of this code is nearly impossible.
Thus, sadly, notation flexibiltiy and maintainability are at odds.

>A simple example of the lack of desirability
>of C portability is the addition of two vectors.  The good coding of this
>depends on the costs of the *x++ in loading and storing and the use of
>index in loading and storing.  These depend both on the machine and the
>storage classes of the adresses.

The code sequence is:

void lvecadd(a, b, c, s) /* a = b + c, length s */
long *a; long *b; long *c; long s;
{
	do {
		*a++ = *b++ + *c++;
	} while (--s != 0);
}

or:

void lvecadd(a, b, c, s) /* a = b + c, length s */
long *a; long *b; long *c; long s;
{
	register long t;
	for (t = 0; i < s; t++)
		a[t] = b[t] + c[t];
}

A '205 C compiler should (doesn't but should) take either
piece of code and translate it into the instruction.

I've seen a C compiler which did better for the second example
than the first.  What was odd was that the target machine, a
68000, does better with the first algorithm.  Worse, the second
peice of code was effectively translated into the first, using
redundant temporary variables for the pointers.  Somehow, the
compiler thought that extra work had to take place for the first
example.  The point, though, is that the programmer visible
syntax need not bear any resemblence to the quality of the
produced code.

Portability has its costs - usually about 10 to 20% of the
optimum machine speed.  Compiler technology has been attempting
to narrow this.  However, it has enourmous benifits.  UNIX, being
somewhat portable, allows new architectures to become useful in
an extremely short time after manufacture (something like a
year).  The speed penalty that comes from UNIX not being designed
for the architecture is offset by the years of development that
it takes to create a new OS.  By that time, the architecture may
well be obsolete.  User applications are the same way.  The value
of using a portable language is more than just being able to use
code that was written on one machine on another.  It takes time
for programmers to become proficient in a language.  If you write
a new language for every program you write, no one will be able
to make heads or tails of what you've done.

dik@cwi.nl (Dik T. Winter) (07/08/89)

Note also the reply why it is difficult to define syntax/semantics in
article <4909@ficc.uu.net> by peter@ficc.uu.net (Peter da Silva) and
the excellent explanation why MIPS does not provide a double width by
single width division in article <4016@bauhaus.NeXT.UUCP> by
chansen@NeXT.UUCP (Craig Hansen).  And I would also like to have
the double width operations directly visible, but I think it is more
difficult than Herman Rubin envisages.  In my opinion the gcc solution,
where you can use your own variables in asm statements, looks best.

A correction to the next statement:
In article <13970@haddock.ima.isc.com> suitti@haddock.ima.isc.com (Stephen Uitti) writes:
 > The code sequence is:
[a routine to add two vectors, using pointers]
 > or:
[an alternative]
 > A '205 C compiler should (doesn't but should) take either
 > piece of code and translate it into the instruction.
This is not true.  Because 'noalias' is out, the compiler has no knowledge
whether the sources overlap the destination.  So a lot more has to be done.
Unless the compiler is willing to do inlining, in which case it might (but
need not) have knowledge about overlap.

Another remark from the same article:
 > >I consider the 205 hardware the best designed vector hardware I have
 > >seen.
 > 
 > No real debate here.  The '205 was a real ambitious project.
 > Very, very difficult and costly to maintain, but real nice.
Some debate here (or simply, I do not agree).  However, the real problem
with the 205 is the abominable compiler.  Architecture is not really an
issue here; given a good compiler it would perform well.  Given the
compiler it has you have a heavy task to get acceptable performance.

On to the RISC vs. CISC debate:  how many of the 60000+ instructions
of the 205 do you think are ever used?  I know quite a lot that are
never used unless you use 'special calls', which is nothing more than
'asm' in C.

For discussions about the 205 hardware please follow up with a
different subject, or do it by mail.
-- 
dik t. winter, cwi, amsterdam, nederland
INTERNET   : dik@cwi.nl
BITNET/EARN: dik@mcvax

mwm@eris.berkeley.edu (Mike (I'll think of something yet) Meyer) (07/08/89)

In article <1395@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin)
writes his usual continuing diatribe against modern (or, in the case
of C, not-so-modern) programming language designers.

Herman wants a language that is both notationally flexible enough to
allow him to use whatever notation is comfortable for him, and
sufficiently smart that he can write code that obviously maps 1-1 onto
machine hardware for the machine he's writing it for.

Since modern programming language designers are more concerned with
features like portability, maintainability and reusability (and the
ability to say *ability :-), they tend to ignore the things he wants.
Notational flexibility leads to nearly unreadable code, and incredible
support libraries, the antithesis of readability and maintainability.
The ability to write code specific to a machine architechture is
obviously antagonistic to the goal of portability.

Herman isn't the only person running around claiming some major area
of CS is going in the wrong direction. He isn't even the only one to
argue about it a lot, and do nothing to prove his point. He may even
be right.

However, flaming about it on USENet isn't going to do anything other
than make AT&T a lot of money.

Herman, you need to do three things. 1) Write or find a language that
is what you want. 2) Arrange to give it, along with enough
documentation for it to be useable, to anyone who wants it. 3) Write a
paper acceptable to a refereed journal (or SNot) describing the
language, and why it's superior to everything else available.

I predict that you'll be roundly ignored, as what you want is really
little more than a flexible template macro processor on top of an
assembler. Spiffy assemblers have been proposed before (Johnson at
Bell Labs wrote one; Whitesmith sold one with their C compilers), and
were roundly ignored.

	<mike
--
Cheeseburger in paradise                                Mike Meyer
Making the best of every virtue and vice                mwm@berkeley.edu
Worth every damn bit of sacrifice                       ucbvax!mwm
To get a cheeseburger in paradise                       mwm@ucbjade.BITNET

cik@l.cc.purdue.edu (Herman Rubin) (07/08/89)

In article <4909@ficc.uu.net>, peter@ficc.uu.net (Peter da Silva) writes:
> In article <1388@l.cc.purdue.edu>, cik@l.cc.purdue.edu (Herman Rubin) writes:
> > For example, on a machine in which there is division yielding a quotient
> > and a remainder, I want to write
> 
> > 	q,r = x/y;
> 
> (1) What does this do when no such operation exists?

What does q = x/y; get you if division does not exist?  There are such
machines.  Would it be totally rash to suggest that the reason that this
does not exist on more machines may be related to the fact that it does
not exist in the languages?

> (2) How does this fit in with the rest of the expression syntax?

I do not see the problem here.  The fact that there are a fair number
of people now using it is a good reason to keep it.  If you mean, how
does it fit into compound expressions, there are ways of doing it, but
I am not sure that I care.  If I have to give them up, but can specify
local temporaries, I will.

> (3) Get a good optimiser and say:
> 	q = x/y;
> 	r = x%y;

Does your language even have long long?  The same remark holds about
temporaries.  And if I have to put them in adjacent registers because
of the hardware, I will.

> (4) Is it obvious which is the quotient and which is the remainder
>     when you have something like:
> 	tens,relx = muscle0.fiber/potential;

All languages have conventions.  I find many of them more annoying
than this problem.

> (5) What if x or y contain other operations that could require a
>     quotient or remainder?

If it is necessary to break a complex operation into simple parts, do it.
This does not apply to the original problem, as the operation wanted is,
on many machines, a single machine instruction.  If the machine can do
it in one instruction, I demand that I can put it in the language as one
statement.  I do not expect everything to be portable.

> (6) What does this mean:
> 	q,r,s = x/y/z;

I do not know.  I am not even sure what a = x/y/z; means.  I would not
use that without parentheses.  I suspect that what is wanted is a pair
of statements, with an intermediate result a declared temporary.

> (7) What does this sort of micro-optimisation gain you? Except in a
>     tight loop, very little. Since you're intending to be machine-
>     specific anyway, why not squirrel that loop away in an assembly
>     routine?

Notation can be very important.  I consider the introduction of algebraic
symbols by Diophantus a major contribution.  My complaint about assembly
language (not machine language) is that it is written in a thoroughly
obfuscated notation.  But machine language needs an "alphabet."

I am perfectly willing to write in what I call a HLL machine language.
Given a decent macro processor, in which the syntax, including the
macro name, is arbitrary, I would write that.  This is really what
a compiler is; a macro processor, possibly with an optimizer added.
Some "optimizers" do little more than add to the macro processor.

So what I am saying is that if the machine can do it, I want a reasonable
way to include it.  I do not consider the current assemblers reasonable,
and I have pointed out some of the reasons why.
> Peter da Silva, Xenix Support, Ferranti International Controls Corporation.
> Business: peter@ficc.uu.net, +1 713 274 5180. | "WHAT HAPPENED TO ALL
> Personal: peter@sugar.hackercorp.com.   `-_-' |  THE WOMEN IN TEXAS?"
> Quote: Have you hugged your wolf today?  'U`  |  -- ACS1W@jane.uh.edu (meesh)


-- 
Herman Rubin, Dept. of Statistics, Purdue Univ., West Lafayette IN47907
Phone: (317)494-6054
hrubin@l.cc.purdue.edu (Internet, bitnet, UUCP)

stuart@bms-at.UUCP (Stuart Gathman) (07/11/89)

[ previous poster wants "q,r = x/y" to assign quotient to q, remainder to r ]

Even on fairly stupid compilers,

	q = x / y;
	r = x % y;

results in what you want (unless x & y have side effects - in that case assign
to temporaries).  In the absence of branches, even simple CSE is amazingly good.
-- 
Stuart D. Gathman	<stuart@bms-at.uucp>
			<..!{vrdxhq|daitc}!bms-at!stuart>

rhorn@infinet.UUCP (Rob Horn) (07/12/89)

In article <MAC.89Jul5102157@mrk.ardent.com> mac@mrk.ardent.com (Michael McNamara) writes:
>[ A reference to GCC for a good way to integrate (Peter Montgomery)'s
>  need for access to the 68020 32x32->64bit multiply]

I can also recommend using the GCC approach for this.  I tweaked up
one mpqs routine in a couple hours work (mostly paranoid testing
because I found a gcc asm bug in the process) to replace zaddmulp.
(The routine is basically a 31x31+31+carry->62 bit muladd.)  I didn't
go further primarily because rewriting more routines into inline asm
macros then caused the optimizer to have fits and die on the large
single source module.  This was back on gcc rev 1.31.  The current rev
may well be better able to handle this.

-- 
				Rob  Horn
	UUCP:	...harvard!adelie!infinet!rhorn
		...ulowell!infinet!rhorn, ..decvax!infinet!rhorn
	Snail:	Infinet,  40 High St., North Andover, MA

jeff@aiai.ed.ac.uk (Jeff Dalton) (07/22/89)

In article <1395@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:
>In article <13961@haddock.ima.isc.com>, suitti@haddock.ima.isc.com (Stephen Uitti) writes:
>> LISP does not actually pass whole lists around - it uses pointers
>> and linked lists.
>
>And this is the reason that production programs and library subroutines
>should not be written in Lisp.

Why?  Because Lisp uses pointers?

Some of the things C library procedures do are much worse than that.

 >But it is possible to get much done efficiently.  This should be done
 >by enabling those who can see how to do things to do them and to
 >communicate that to others.  Anything can be done on a universal
 >Turing machine, but I am not willing to wait a month for it to
 >multiply two 10-digit numbers by reducing everything to successor.

I'm not sure if this is still supposed to be about Lisp.
If so, you should note that Lisp has more than one data type.