pdg@chinet.chi.il.us (Paul Guthrie) (07/28/89)
Here is a case where 68000 compilers don't seem to agree.
In the following code segment, the two shorts multiplied together
exceed the size of a short, but the question is, is the result
of the multiplication really a short to be converted to a
long, or a long already?
main()
{
short x=0x18,y=0x588;
long z=0x100000;
printf("%lx\n",z+(x*y));
}
Should the compiler generate
move -2(fp),d0
muls -4(fp),d0
extl d0
addl -8(fp),d0
or should the extl instruction be left out? muls has already
generated a long result. Both the Sun and GNU compilers
do not use the extl, but I have seen other compilers that
would.
--
Paul Guthrie
chinet!nsacray!paul
roelof@idca.tds.PHILIPS.nl (R. Vuurboom) (07/28/89)
In article <9092@chinet.chi.il.us> pdg@chinet.chi.il.us (Paul Guthrie) writes: >Here is a case where 68000 compilers don't seem to agree. >In the following code segment, the two shorts multiplied together >exceed the size of a short, but the question is, is the result >of the multiplication really a short to be converted to a >long, or a long already? > > main() > { > > short x=0x18,y=0x588; > long z=0x100000; > printf("%lx\n",z+(x*y)); > } > Following the "usual arithmetic conversions" (cf. Ansi Draft 3.2, K&R v.2 pp. 197) x and y being shorts will be promoted to ints (not necessarily longs) and a normal int multiplication will take place. Depending on what the compiler considers to be natural int size the code below may or may not be correct. If the int size is the same as that of long then the code is not correct since the multiplication with subsequent extl will give wrong answers if the result does not fit into a short (your example). If on the other hand the int size is considered a short then the is correct since the extl extends the result of the multiplication to the long int size for the addition (again a "usual arithmetic conversion"). >Should the compiler generate > move -2(fp),d0 > muls -4(fp),d0 > extl d0 > addl -8(fp),d0 >or should the extl instruction be left out? Conclusion: If your compiler considers the int size to be the same as that of long (normal for motorola compilers) then the generated code is incorrect. If your compiler considers the natural int size to be the same as that of short then the generated code can be considered correct. (But maybe you should get yourself a new compiler :-). -- I don't know what the question means, but the answer is yes... (overheard on comp.lang.misc) Roelof Vuurboom SSP/V3 Philips TDS Apeldoorn, The Netherlands +31 55 432226 domain: roelof@idca.tds.philips.nl uucp: ...!mcvax!philapd!roelof
chris@mimsy.UUCP (Chris Torek) (07/28/89)
In article <9092@chinet.chi.il.us> pdg@chinet.chi.il.us (Paul Guthrie) writes: >Here is a case where 68000 compilers don't seem to agree. Most likely because 68000 compilers do not agree as to whether `int' is the same as `short' or as `long'. >In the following code segment, the two shorts multiplied together >exceed the size of a short, but the question is, is the result >of the multiplication really a short to be converted to a >long, or a long already? Neither. The result of shortvar*shortvar has type int. (If one variable is unsigned short, then according to the pANS, the result type is machine dependent, although the result value is not, provided it does not overflow.) If the result overflows, the value is undefined; a clever int-is-16-bits-style 68000 compiler may produce the correct value by noticing that the 68000 `muls' instruction produces a 32 bit result, and carrying that around even though its result type (int) was supposed to have been only 16 bits. This is not required by any C standard, but is permitted since the result value is undefined. Of course, if you stuff the 32-bit value into a 16-bit slot, something has to give; usually it is the value that breaks, rather than the slot. The result of (intexpr) + (longexpr) has type long. Anyway: > short x=0x18,y=0x588; > long z=0x100000; > printf("%lx\n",z+(x*y)); >Should the compiler generate > move -2(fp),d0 > muls -4(fp),d0 > extl d0 > addl -8(fp),d0 This sequence is typical of a dumb int-is-16-bits compiler. >or should the extl instruction be left out? The sequence without the extl is typical of a smarter compiler of either flavour. >Both the Sun and GNU compilers do not use the extl, but I have >seen other compilers that would. Sun's cc is a moderatly intelligent int-is-32-bit 680x0 compiler derived from PCC. GCC as configured for a 680x0 is a smart int-is-32-bit compiler. Both realise that there is no point in doing extl -2(fp),d0 extl -4(fp),d1 jsr lmult | or some other name addl -8(fp),d0 when the result of both `extl's will have either 0 or 0xffff in the top 16 bits, and therefore the result from `muls' will be the same as that from the `multiply two longs' subroutine. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
gwyn@smoke.BRL.MIL (Doug Gwyn) (07/30/89)
In article <9092@chinet.chi.il.us> pdg@chinet.chi.il.us (Paul Guthrie) writes: >In the following code segment, the two shorts multiplied together >exceed the size of a short, but the question is, is the result >of the multiplication really a short to be converted to a >long, or a long already? Arithmetic operators that have the same arithmetic type for their operands always produce the same type for the result. If you want the result to be a different type, convert at least one of the operands via a cast before applying the arithmetic operator.
md@sco.COM (Michael Davidson) (07/31/89)
In article <9092@chinet.chi.il.us> pdg@chinet.chi.il.us (Paul Guthrie) writes: >Here is a case where 68000 compilers don't seem to agree. >In the following code segment, the two shorts multiplied together >exceed the size of a short, but the question is, is the result >of the multiplication really a short to be converted to a >long, or a long already? Neither - the two (short) variables are promoted to (int) before the multiplication, the result of the multiplication is an (int) which gets promoted to a (long) before the addition is performed. Of course your compiler can generate any code it likes provided that it gets the answer which is "correct" according to these rules. (To see why understanding this particular bit of pedantry is important consider what happens on machines with 16 bit shorts, 16 bit ints and 32 bit longs)
gwyn@smoke.BRL.MIL (Doug Gwyn) (07/31/89)
In article <869@fiasco.sco.COM> md@sco.COM (Michael Davidson) writes: >Neither - the two (short) variables are promoted to (int) before the >multiplication, the result of the multiplication is an (int) ... >Of course your compiler can generate any code it likes provided that >it gets the answer which is "correct" according to these rules. Oops, Michael is correct. I misstated this in an earlier response. The "usual arithmetic conversions" are applied to the operands before the operation occurs, and the result has the common promoted type. Anyway, the important point is that the programmer needs to use explicit casts to force the shorts to longs if the result might not fit in a short.
msb@sq.sq.com (Mark Brader) (08/01/89)
Chris Torek has already answered this at length, but I think it deserves answering briefly also. > In the following code segment, the two shorts multiplied together > exceed the size of a short, but the question is, is the result > of the multiplication really a short to be converted to a > long, or a long already? The short answer is, it's really an int to be converted to a long, but because the semantics of C on integer overflow are explicitly undefined, it is legitimate for a compiler to treat it as if it was a long already. To be safe, if there is any chance that the result of the multiplication will exceed a magnitude of 32767, and you want a long result, you should cast at least one of the operands to long. -- Mark Brader, SoftQuad Inc., Toronto, utzoo!sq!msb, msb@sq.com #define MSB(type) (~(((unsigned type)-1)>>1)) This article is in the public domain.
prl@inteloa.intel.com (Philip Lantz) (08/03/89)
In article <18795@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes: > The result of shortvar*shortvar has type int. > ... If the result overflows, the value is undefined; a clever > int-is-16-bits-style 68000 compiler may produce the correct value by > noticing that the 68000 `muls' instruction produces a 32 bit result, > and carrying that around even though its result type (int) was supposed > to have been only 16 bits. This is not required by any C standard, > but is permitted since the result value is undefined. I agree that if the multiplication overflows, the result is undefined, but its type is still an int; if a later conversion to long is required, isn't the compiler required to perform the 'extl' anyway? That is, shouldn't the result of a conversion from int to long be in the numeric range of an int, even if the original int value was undefined? Or is the compiler free to do whatever it likes with an entire expression if any sub-expression overflows? Philip Lantz P.S. Sorry to pick nits, Chris; your articles are without fail the most accurate and informative in this newsgroup.
chris@mimsy.UUCP (Chris Torek) (08/04/89)
>In article <18795@mimsy.UUCP> I noted that >>The result of shortvar*shortvar has type int. ... If the result >>overflows, the value is undefined; a clever int-is-16-bits-style 68000 >>compiler [might omit an extl instruction in a subsequent conversion to >>long, yeilding the `right' value]. This is not required by any C standard, >>but is permitted since the result value is undefined. In article <4750@omepd.UUCP> prl@inteloa.intel.com (Philip Lantz) writes: >I agree that if the multiplication overflows, the result is undefined, >but its type is still an int; if a later conversion to long is >required, isn't the compiler required to perform the 'extl' anyway? >That is, shouldn't the result of a conversion from int to long be in >the numeric range of an int, even if the original int value was >undefined? Or is the compiler free to do whatever it likes with an >entire expression if any sub-expression overflows? The last is more or less true: as long as the compiler always produces the right answer when asked to do things that are defined (such as `300 * 65 => 19500', which cannot overflow---the pANS requires that `int' be at least 16 bits), it can do anything it wants when the result is implementation-defined or undefined. I like to use ridiculous actions, such as having the computer explode, or change into a butterfly, as examples of permitted behaviour. (Incidentally, you really could rig a computer to explode on arithmetic overflow, particularly if it has an exception for such events. But I have not figured out how to change one into a butterfly. I keep getting Luna moths. :-) ) -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
boyne@hplvli.HP.COM (Art Boyne) (08/04/89)
chris@mimsy.UUCP (Chris Torek) writes: > it can do anything it wants when the result is >implementation-defined or undefined. I like to use ridiculous actions, >such as having the computer explode, or change into a butterfly, as >examples of permitted behaviour. Or you could do what I actually saw one FORTRAN compiler do one day: the compiler lost a pointer to its symbol table, and when it tried to sort the symbol table for the cross reference, it instead sorted itself by opcode. Made for an interesting core dump. Art Boyne, boyne@hplvla.hp.com
kdb@chinet.chi.il.us (Karl Botts) (08/10/89)
>I agree that if the multiplication overflows, the result is undefined, >but its type is still an int; if a later conversion to long is >required, isn't the compiler required to perform the 'extl' anyway? >That is, shouldn't the result of a conversion from int to long be in >the numeric range of an int, even if the original int value was >undefined? Or is the compiler free to do whatever it likes with an >entire expression if any sub-expression overflows? It most certainly is not -- this would break reams of code, inclouding lots of mine. Casting an integral value to an integral value of a know size is a time-honored and legitimate method of truncation. For instance: int i; i = (char)i; truncates the value in i to the width of a char. Note that this is not necessarily the same as either: i &= 0xFF; or i &= 0x7F; Also, on many architectures a compiler can figure out that it can do: i = (char)i; by fiddling with registers, like by clearing th uuper half of a chameleon-size register or stuffing the wide value into a char-sized register. Some might be able to figure this out for: i &= 0xFF; but they couldn't for i &= 0x7F; This register operation is liable to be faster than fetching a constant and doing the bitwise AND. Hence the truncation cast is not only legal, but useful -- you can't break it.