[comp.lang.c] QuickC

TURGUT@TREARN.BITNET (Turgut Kalfaoglu) (08/26/88)

The other day, I was struggling with QuickC - a very simply problem,
but really intriguing. Let me know if you can interpret this:

main()
{
   int a,v;
   a = 2;
   v = square(a);
   printf("%d\n",v);
}

square(num)
int num;
{
  num = num*num;
}

OK? There is no 'return' statement in the function. However, it works!
I get '4' as an answer. So I thought maybe it was keeping the result
of the last operation, so I added some dummy lines,

square(num)
int num;
{
  int dummy;
  num = num*num;
  dummy=222;
}

but the call STILL works... Can anyone shed some light onto this?
WHY does it work?
-turgut

chris@mimsy.UUCP (Chris Torek) (08/27/88)

In article <8808261424.AA11504@ucbvax.Berkeley.EDU> TURGUT@TREARN.BITNET
(Turgut Kalfaoglu) noticed some wrong code that worked anyway:

>square(num) int num; { num = num*num; }

>... There is no 'return' statement in the function. However, it works!
>... I thought maybe it was keeping the result of the last operation,
>so I added some dummy lines,

>square(num) int num; { int dummy; num = num*num; dummy=222; }

>but the call STILL works... WHY does it work?

Luck.

More seriously: it is due to a combination of the compiler and the
machine architecture.  The compiler does a multiply by loading the
multiplicand and multiplier (in this case, num and num) into one or
more registers (here, one suffices), multiplying the registers, and
perhaps storing the result (the store may well be deleted by a
dead-store checker).  For whatever reason, it chooses the same register
that the compiler/architecture uses for integer return values.
The dummy assignment is either eliminated completely by a dead-store
checker, or else is done by a memory-to-memory or immediate-to-memory
move, which does not overwrite that register.  Then, since there is no
return statement at all, nothing is put into the return-value register,
leaving the old num*num result.

A more common occurrence of this same bug appears as follows:

	/* caller: */
		var = fn(args);
		...

	int fn(args) { ...; otherfn(otherargs); }

	int otherfn(args) { ...; return (expr); }

On architectures with a common return-value register---e.g., VAX (r0),
Intel 80*86 (ax), MC680X0 (d0)---this works.  Fortunately, lint catches
this sort of error, before you attempt to run the code on a Pyramid....
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

swilson%thetone@Sun.COM (Scott Wilson) (08/27/88)

In article <8808261424.AA11504@ucbvax.Berkeley.EDU> TURGUT@TREARN.BITNET (Turgut Kalfaoglu) writes:
>main()
>{
>   int a,v;
>   a = 2;
>   v = square(a);
>   printf("%d\n",v);
>}
>
>square(num)
>int num;
>{
>  num = num*num;
>}
>WHY does it work?

I get the same behavior on a Sun running 3.5 (but not 4.0).  The reason
it works is that the scratch register used for the multiplication happens
to be the same register that is used to return the function's value (in
my case d0).  The reason that adding the assignment to dummy didn't change
anything is that this most likely didn't involve any registers, the value
was stored directly in memory since dummy is on the stack.

Under SunOS 4.0 this gives the answer 0.  For some reason all functions
without an explicit return statement return 0.  I think it has something
to do with compatibility with 4.3 BSD where return(x) in main() is the
same as exit(x).  Since main() often ends without a return (or exit()
being called) it could cause problems if a random register value were used
for the exit code.  So, main() without a return returns zero which is
a "successfull" termination in UNIX.


--
Scott Wilson		arpa: swilson@sun.com
Sun Microsystems	uucp: ...!sun!swilson
Mt. View, CA

vch@attibr.UUCP (Vincent C. Hatem) (08/27/88)

In article <8808261424.AA11504@ucbvax.Berkeley.EDU>, TURGUT@TREARN.BITNET (Turgut Kalfaoglu) writes:
> The other day, I was struggling with QuickC - a very simply problem,
> but really intriguing. Let me know if you can interpret this:
> main()
> {
>    int a,v;
>    a = 2;
>    v = square(a);
>    printf("%d\n",v);
> }
> square(num)
> int num;
> {
>   num = num*num;
> }
> OK? There is no 'return' statement in the function. However, it works!
> I get '4' as an answer. So I thought maybe it was keeping the result

I took the above program, copied it with the swipe of a mouse, and compiled
it...

I get:
$ cc test1.c -o test1
$ test1
-1073610208

Hmm... What's this QuickC??? An interpreter? I suspect that the compiler is
putting the variable "num" on the stack, which is being picked up by the 
main(). 

I guess AT&T got something right ;-}

Vince



-- 
Vincent C. Hatem                            | att ---->\ (available from any
AT&T International                          | ulysses ->\ Action Central site)
International Operations Technical Support  | bellcore ->\   
1200 Mt Kemble Ave, Basking Ridge, NJ 07920 | ihnp4 ----->\__ !attibr!vch  

leo@philmds.UUCP (Leo de Wit) (08/27/88)

In article <8808261424.AA11504@ucbvax.Berkeley.EDU> TURGUT@TREARN.BITNET (Turgut Kalfaoglu) writes:
|
|The other day, I was struggling with QuickC - a very simply problem,
|but really intriguing. Let me know if you can interpret this:
|
|main()
|{
|   int a,v;
|   a = 2;
|   v = square(a);
|   printf("%d\n",v);
|}
|
|square(num)
|int num;
|{
|  num = num*num;
|}
|
|OK? There is no 'return' statement in the function. However, it works!
|I get '4' as an answer. So I thought maybe it was keeping the result
|of the last operation, so I added some dummy lines,
|
|square(num)
|int num;
|{
|  int dummy;
|  num = num*num;
|  dummy=222;
|}
|
|but the call STILL works... Can anyone shed some light onto this?
|WHY does it work?
|-turgut

My guesses (I don't know QuickC, but I can understand why it would behave
like this):

In the first case:

The return value of a C function is typically returned in a register
(if it fits in one); often the compiler uses one and the same
register.  Also for temporary values mostly a small set of registers is
used; if the temporary value num * num happens to be put into the same
register, the above behaviour is explained.

In the second case:

The '222' can be directly assigned (to dummy) so that no temporary is
needed; the num * num result is still in the 'temporaries' register.
Other possible explanation: if the compiler is really smart it could
see that dummy is assigned but never used and so optimized it away.

               Leo.

pcm@iwarpj.intel.com (Phil C. Miller) (08/29/88)

In article <8808261424.AA11504@ucbvax.Berkeley.EDU> TURGUT@TREARN.BITNET (Turgut Kalfaoglu) writes:

>The other day, I was struggling with QuickC - a very simply problem,
>but really intriguing. Let me know if you can interpret this:

>main()
>{
>   int a,v;
>   a = 2;
>   v = square(a);
>   printf("%d\n",v);
>}

>square(num)
>int num;
>{
>  num = num*num;
>}

>OK? There is no 'return' statement in the function. However, it works!
>I get '4' as an answer. So I thought maybe it was keeping the result
>of the last operation, so I added some dummy lines,

>square(num)
>int num;
>{
>  int dummy;
>  num = num*num;
>  dummy=222;
>}

>but the call STILL works... Can anyone shed some light onto this?
>WHY does it work?
>-turgut

Don't know for sure, but I'll offer a conjecture:

The subprogram calling standard for Microsoft C probably has a register doing
double duty: it holds input arguments (i.e., num) and also holds the function
result (unassigned, but with a residual value set when you assigned a value to
the input argument num.

Another conjecture: if you were using an optimizing compiler, it would see that
the value num was dead (i.e., had no further use in the function square) and 
the assignment to num would disappear, along with the 'magic' function value
assignment.

Phil Miller
{...}!tektronix!omepd!iwarp!pcm

daveh@marob.MASA.COM (Dave Hammond) (08/29/88)

In article <8808261424.AA11504@ucbvax.Berkeley.EDU> TURGUT@TREARN.BITNET (Turgut Kalfaoglu) writes:
>
>The other day, I was struggling with QuickC - a very simply problem,
>but really intriguing. Let me know if you can interpret this:
>
>[... examples pertaining to square() returning a correct value even tho' it
>(a) has no return statement or (b) has a dummy return value. ...]

My guess is that there is a QuickC library routine called square() which
is being loaded before your routine, and thus called in lieu of yours.
Another answer might be a #define somewhere, like:

#define square(x)	((x)*(x))

Is there any loader/linker warning message about multiply-defined square() ?

Dave Hammond
  UUCP: {uunet|...}!masa.com!{dsix2|marob}!daveh
DOMAIN: dsix2!daveh@masa.com
------------------------------------------------------------------------------

daveb@laidbak.UUCP (Dave Burton) (08/29/88)

In article <8808261424.AA11504@ucbvax.Berkeley.EDU> TURGUT@TREARN.BITNET (Turgut Kalfaoglu) writes:
|[in QuickC]
|square(num) int num; { num = num*num; }
|
|...I get '4' as an answer...[then I tried]
|
|square(num)
|int num;
|{
|  int dummy;
|  num = num*num;
|  dummy=222;
|}
|
|but the call STILL works... Can anyone shed some light onto this?
|WHY does it work?

The result of the _calculation_ was placed into the register used to
return the value to the caller (ax?). The addition of the _assignment_
did not have to disturb this register. Try using "dummy=222*num;" instead.
This will force another calculation, and should overwrite the `correct'
result.
-- 
Dave Burton		| ``/* You are not expected to understand this. */''
{spl1,sun}!laidbak!daveb|
(312) 505-9100 x325	| Disclaimer: I channel only for myself.