[net.micro.amiga] printf

dillon@PAVEPAWS.BERKELEY.EDU (Matt Dillon) (06/27/86)

	This should lay to rest any further arguments about printf().


(linking only with AMIGA.LIB and Astartup.obj,  Lattice C 3.03)

source				executable

main() {}			1880
main() {printf("hello\n");}	2376

	As you can see, printf() takes 496 bytes, PERIOD, when called from 
amiga.lib  (it may or may not use the DOS run-time library, but that doesn't
really matter).

						-Matt

chapman@pavepaws.berkeley.edu (Brent Chapman) (06/27/86)

In article <8606270438.AA07486@pavepaws> dillon@PAVEPAWS.BERKELEY.EDU (Matt Dillon) writes:
>
>	This should lay to rest any further arguments about printf().
>
>
>(linking only with AMIGA.LIB and Astartup.obj,  Lattice C 3.03)
>
>source				executable
>
>main() {}			1880
>main() {printf("hello\n");}	2376
>
>	As you can see, printf() takes 496 bytes, PERIOD, when called from 
>amiga.lib  (it may or may not use the DOS run-time library, but that doesn't
>really matter).
>
>						-Matt

What you've just done is pretty useless.  You could just as well use puts()
for that.  printf() is "print FORMATTED".  Give us some REAL examples, that
actually do some formatting, to see whether or not the code size increases
(in other words, whether or not the compiler is smart enough not to include
the formatting routines it doesn't need).  I'm guessing, but I'd expect
code size to increase (I don't have my Amiga yet, so I can't test it
myself).


Brent

--

Brent Chapman
chapman@pavepaws.berkeley.edu
ucbvax!pavepaws!chapman

TANSTAAFL!  (There Ain't No Such Thing As A Free Lunch!)

jimm@amiga.UUCP (James D. Mackraz) (06/27/86)

In article <742@ucbcad.BERKELEY.EDU> chapman@pavepaws.UUCP (Brent Chapman) writes:
>In article <8606270438.AA07486@pavepaws> dillon@PAVEPAWS.BERKELEY.EDU (Matt Dillon) writes:
>>
>>source				executable
>>
>>main() {}			1880
>>main() {printf("hello\n");}	2376
>>
>
>What you've just done is pretty useless.  You could just as well use puts()
>for that.  printf() is "print FORMATTED".  Give us some REAL examples, that
>actually do some formatting, to see whether or not the code size increases
>(in other words, whether or not the compiler is smart enough not to include
>the formatting routines it doesn't need).  I'm guessing, but I'd expect
>code size to increase (I don't have my Amiga yet, so I can't test it
>myself).
>Brent

Let me be the first in a long line of people commenting on this one.
(It's 8:30 am pdt, june 27).

It is not the business of the compiler (any compiler?) to interpret the
data present at an address passed at run-time to a subroutine, and 
thereby to determine that certain functions called by that subroutine 
(in this case, formatters) won't actually actually be needed, and so
on to indicate somehow to the linker that those references can be
left unresolved.  I guess that what I am saying is that you should buy
an amiga and program a whole lot and learn a whole lot.

			jimm

Also, I am told that the printf in the kernel (kprintf and the stuff it
uses) don't handle floating point.  The intent of those routines is 
for debugging use.  I think I am in favor of building printf right
into your system, but that wasn't the intent of kprintf, as I understand it.

But then again, poof:  I may be wrong.

dillon@PAVEPAWS.BERKELEY.EDU (Matt Dillon) (06/27/86)

>What you've just done is pretty useless.  You could just as well use puts()
>for that.  printf() is "print FORMATTED".  Give us some REAL examples, that
>actually do some formatting, to see whether or not the code size increases
>(in other words, whether or not the compiler is smart enough not to include
>the formatting routines it doesn't need).  I'm guessing, but I'd expect
>code size to increase (I don't have my Amiga yet, so I can't test it
>myself).
>Brent

	Dummy, how does the linker know you aren't printf()'ing any 
formatted stuff?  it doesn't... it has to include the code anyway.  
Thus:

main(){printf("%ld %8lx\n", 20, 30);}

code size = 2392

	up 16 bytes from 2376 (the extra text)

	satisfied?

			-Matt

rico@oscvax.UUCP (06/29/86)

In article <8606270438.AA07486@pavepaws> dillon@PAVEPAWS.BERKELEY.EDU.UUCP writes:
>
>	This should lay to rest any further arguments about printf().
>
>
>(linking only with AMIGA.LIB and Astartup.obj,  Lattice C 3.03)
>
>source				executable
>
>main() {}			1880
>main() {printf("hello\n");}	2376
>
>	As you can see, printf() takes 496 bytes, PERIOD, when called from 
>amiga.lib  (it may or may not use the DOS run-time library, but that doesn't
>really matter).
>
>						-Matt

Just for comparison, I thought it would be interesting to list figures
for Aztec C.  These numbers are with the standard 16 and 32 bit libraries,
there is no distinction between 'amiga.lib' and 'lc.lib' or whatever
as is found in Lattice (also no startup code to link in....)

 source				executable

 main() {}			1580			16 bit ints
				1636			32 bit ints

 main() {printf("hello\n");}	4712			16 bit ints
				4944			32 bit ints

Another FYI .... on Fish disk #23, there are four versions of microemacs V30,
two of the were compiled with Lattice and two with Manx... presumably, the
same source was used for both


file				executable

xemacs.amigados.lattice		68356
xemacs.amigados.manx16		39928
gemacs.intuition.lattice	80312
gemacs.intuition.manx16		46976


and on fish disk #24, we find Matt Dillon's csh-ish shell (thanks Matt!!)

csh.lattice			38052
csh.manx32			21180



Lattice seems to have blown more than just their run time library (I say
that a lot don't I :-) )

	-Rico

fnf@unisoft.UUCP (Fred Fish) (06/30/86)

Ok folks, one thing nobody seems to have mentioned yet in these 
Manx vs Lattice file size comparisons is the effect of object file
overhead (symbol table, hunk headers, etc).  Only after someone
writes the equivalent of the Unix "size" command, can we begin
to do any meaningful size comparisons.

-Fred

mykes@3comvax.UUCP (Mike Schwartz) (07/01/86)

In article <8606270438.AA07486@pavepaws> dillon@PAVEPAWS.BERKELEY.EDU (Matt Dillon) writes:
>
>	This should lay to rest any further arguments about printf().
>
>
>(linking only with AMIGA.LIB and Astartup.obj,  Lattice C 3.03)
>
>source				executable
>
>main() {}			1880
>main() {printf("hello\n");}	2376
>
>	As you can see, printf() takes 496 bytes, PERIOD, when called from 
>amiga.lib  (it may or may not use the DOS run-time library, but that doesn't
>really matter).
>
>						-Matt

Is there any chance that Amiga.lib's printf() calls a printf() that is in
rom somewhere (like in ROMWack???)?  It does not make sense that even a 
simple printf() would be so small.

mykes@3comvax.UUCP (Mike Schwartz) (07/01/86)

In article <84@unisoft.UUCP> fnf@unisoft.UUCP (Fred Fish) writes:
>Ok folks, one thing nobody seems to have mentioned yet in these 
>Manx vs Lattice file size comparisons is the effect of object file
>overhead (symbol table, hunk headers, etc).  Only after someone
>writes the equivalent of the Unix "size" command, can we begin
>to do any meaningful size comparisons.
>
>-Fred

A friend of mine is porting a Mac game to the Amiga (Deja Vu - a.k.a.
Amiga Vu).  I brought my compiler over to his house the other day and
compiled his program.  The executable went from 55K down to 32K, without
any editting (i.e. removal of code).  As much as I do not like the Lattice
implementation, I find it hard to believe that this 23K reduction was due
to library calls alone, but I also find it hard to believe that Lattice
does not generate reasonable code.  

Manx, using the +l option, is not a very good code generating compiler -
it just buries Lattice in terms of compiling speed, program size (the
size of the compiler, linker and assember is the size of LC1 alone).  But
I cringe when I look at the code generated, I am amazed at how  poor it is.
For example, the following statement:
	if (var == NULL) ...
causes var to be first loaded, extended to a word, extended to a long, then
compared against 0, EVEN when var is declared as a UBYTE...


Oh, well... I guess someone will make a good 'C' compiler someday...

ewhac@well.UUCP (Leo 'Bols Ewhac' Schwab) (07/03/86)

[ Sorry, but the line eating bug is on a diet. ]

In article <562@3comvax.UUCP> mykes@3comvax.UUCP (Mike Schwartz) writes:
>In article <8606270438.AA07486@pavepaws> dillon@PAVEPAWS.BERKELEY.EDU (Matt Dillon) writes:
>>	This should lay to rest any further arguments about printf().
>>(linking only with AMIGA.LIB and Astartup.obj,  Lattice C 3.03)
>>
>>source				executable
>>main() {}			1880
>>main() {printf("hello\n");}	2376
>>
>>	As you can see, printf() takes 496 bytes, PERIOD, when called from 
>>amiga.lib  (it may or may not use the DOS run-time library, but that doesn't
>>really matter).
>>						-Matt
>Is there any chance that Amiga.lib's printf() calls a printf() that is in
>rom somewhere (like in ROMWack???)?  It does not make sense that even a 
>simple printf() would be so small.

	Part of Kiskstart is, in fact, the debugging library.  The routines
documented in debug.lib are:

KDoFmt:		Format data into character stream
KGetChar:	Get a character from the debug console (9600 baud terminal)
KMayGetChar:	Return char from debug console iff present (doesn't block)
KPutChar:	Send character to debug console
KPutFmt:	Print formatted data to debug console
KPutStr:	Put a string to debug console

	I'm sure they figured out a way to fool these routines into doing
the formatting without sending data to the debug console (KDoFmt is probably
the key).  Note:  This is all educated guessing, so I'm obviously wrong.

	For more complete information on the debugging library, check the
pages just before Appendix G in the RKM, vol 2.

--------

	On a completely different tack:  What is the Clist library for?
Yes, I know what it does (the auto docs explain that), but what's it *for*?
I'm having trouble discovering a use for it.  Is it for manipulating a list
of strings, or what?  [[ the Clist.lib autodocs start at page A-47, RKM, vol
2 ]]

_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
Leo L. Schwab				ihnp4!ptsfa!well!ewhac
						..or..
"Work FOR?  I don't work FOR		well ---\
anybody!  I'm just having fun."		dual ----> !unicom!ewhac
					hplabs -/       ("AE-wack")

ewhac@well.UUCP (Leo 'Bols Ewhac' Schwab) (07/03/86)

[ Go ahead.  Eat me.  See if I care. ]

In article <566@3comvax.UUCP> mykes@3comvax.UUCP (Mike Schwartz) writes:
>Manx, using the +l option, is not a very good code generating compiler -
>it just buries Lattice in terms of compiling speed, program size (the
>size of the compiler, linker and assember is the size of LC1 alone).  But
>I cringe when I look at the code generated, I am amazed at how  poor it is.
>For example, the following statement:
>	if (var == NULL) ...
>causes var to be first loaded, extended to a word, extended to a long, then
>compared against 0, EVEN when var is declared as a UBYTE...
>
>Oh, well... I guess someone will make a good 'C' compiler someday...

	It never fails to amaze me just how many people do this:

	if (foo == NULL) { ... }

	or:

	if (foo == 0) { ... }

	The reason the compiler is exhibiting the behavior you describe is
because you told it to.  You are asking the compiler to compare an unsigned
byte to the literal value of NULL.  If you look in exec/types.h, you'll find
NULL defined this way:

#define NULL		0L

	So you're asking the compiler to compare a byte to a long.  So it
expands the byte to a long, then makes the comparison using a CMP
instruction.  That *IS* what you told it to do.

	What I can't understand is why more people don't do this:

	if (!foo) { ... }

	This will do what you want, which is TST the byte to see if it's
non-zero.

	If you *really* care about optimized code, compile to an assembly
source file and bang on it yourself.  That's something Lettuce *definitely*
won't let you do.

--------I N T E R L U D E--------
I really hate this damn compiler.
I wish that they would sell it.
It never does just what I want,
But only what I tell it.
--------

	Think about it (not too much; it's intended to be humorous).

_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
Leo L. Schwab				ihnp4!ptsfa!well!ewhac
						..or..
"Work FOR?  I don't work FOR		well ---\
anybody!  I'm just having fun."		dual ----> !unicom!ewhac
					hplabs -/       ("AE-wack")

mwm@eris.berkeley.edu (07/08/86)

In article <1385@well.UUCP> ewhac@well.UUCP (Leo 'Bols Ewhac' Schwab) writes:
>	It never fails to amaze me just how many people do this:
>		if (foo == NULL) { ... }
>	or:
>		if (foo == 0) { ... }
[instead of...]
>		if (!foo) { ... }

Well, I wrote it the way you think people ought to for 8 years before
changing to coding the test explicitly. I changed because the (!foo)
notation is less readable than the (foo == 0) notation.

Also, if foo is a pointer type, the test (!foo) is wrong. NULL doesn't
have to be zero.

>	The reason the compiler is exhibiting the behavior you describe is
>because you told it to.  You are asking the compiler to compare an unsigned
>byte to the literal value of NULL.

Uh, what he got was:

>>causes var to be first loaded, extended to a word, extended to a long, then
>>compared against 0, EVEN when var is declared as a UBYTE...

What's the second extend doing in there? Anything claiming to be a
reasonable compiler should generate ONE extend. Anything claiming to
be not bad (much less good) should realize what's going on, and
generate the CMP.B instruction. Of course, a good compiler will check
to see if the Z condition code is set correctly already (from a move
to/from var, or the evaluation into var), and just generate the
branch. If it's slightly intelligent, it'll be willing to re-arrange
code to make that pre-condition happen.

>	If you *really* care about optimized code, compile to an assembly
>source file and bang on it yourself.  That's something Lettuce *definitely*
>won't let you do.

Well, I'd rather get the source to the compiler and bang on that so it
generates good code (that way I only have to do it once). But I expect
the compiler to at least generate non-abysmal code.

>>Oh, well... I guess someone will make a good 'C' compiler someday...

DEC has one for VMS. Convex has one for their box (though it may not
be released yet). Anybody know of any others?

	<mike

guy@sun.uucp (Guy Harris) (07/09/86)

> >	It never fails to amaze me just how many people do this:
> >		if (foo == NULL) { ... }
> >	or:
> >		if (foo == 0) { ... }
> [instead of...]
> >		if (!foo) { ... }
> 
> Well, I wrote it the way you think people ought to for 8 years before
> changing to coding the test explicitly. I changed because the (!foo)
> notation is less readable than the (foo == 0) notation.

Amen.  The three constructs given have exactly the same meaning in C (yes,
wait for the explanation, it's true) and any good compiler will produce
exactly the same code for them.  However, "!foo" is suggestive of testing
for the falsity of a Boolean (yes, I know C doesn't have a formal Boolean
type, but that's the proper way to think of something with values meaning
"true" and "false"), while "foo == NULL" is suggestive of testing whether a
pointer is null or not, and "foo == 0" is suggestive of testing whether an
integral value is zero or not.

> Also, if foo is a pointer type, the test (!foo) is wrong. NULL doesn't
> have to be zero.

Oh, yes it does.  A null pointer need not have a bit pattern consisting of
all zeroes.  However, the value of a null pointer is constructed in
C by converting the integral constant 0 to a pointer type.  In the construct
"foo == 0", the LHS is a pointer, so the compiler knows enough to convert
the "0" on the RHS to a pointer of the same type, hence a null pointer of
the appropriate type.  (No, I won't cite the K&R/ANSI C draft which says
this; seek and ye shall find.)

As such, NULL should be #defined as 0 - NOT "(char *)0", because that
causes the compiler to get annoyed when you compare an "int *" against NULL,
since you're comparing two different kinds of pointers.

> >	The reason the compiler is exhibiting the behavior you describe is
> >because you told it to.  You are asking the compiler to compare an unsigned
> >byte to the literal value of NULL.

Which should always be 0.  As such, in the example given, 

	if (var == NULL) ...

is identical to

	if (var == 0) ...

and should generate the same code; as the author of the response to the
above comment points out, there is NO reason to extend a 1-byte variable
"var" to 4 bytes when doing this.
-- 
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com (or guy@sun.arpa)

pete@valid.UUCP (Pete Zakel) (07/10/86)

> >>causes var to be first loaded, extended to a word, extended to a long, then
> >>compared against 0, EVEN when var is declared as a UBYTE...
> 
> What's the second extend doing in there? Anything claiming to be a
> reasonable compiler should generate ONE extend.

Not if the processer is a 68000 or 68010.  There is NO instruction to extend
byte to long.  Fortunately that was fixed in the 68020, but that doesn't help
unless you have a compiler that understands 68020 AND you know all your
target machines have 68020s.
-- 
-Pete Zakel (..!{hplabs,amd,pyramid,ihnp4}!pesnta!valid!pete)

mykes@3comvax.UUCP (07/11/86)

In article <4907@sun.uucp> guy@sun.uucp (Guy Harris) writes:
>
>Which should always be 0.  As such, in the example given, 
>
>	if (var == NULL) ...
>
>is identical to
>
>	if (var == 0) ...
>
>and should generate the same code; as the author of the response to the
>above comment points out, there is NO reason to extend a 1-byte variable
>"var" to 4 bytes when doing this.
>-- 
>	Guy Harris
>	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
>	guy@sun.com (or guy@sun.arpa)

As author of the original article, I would just like to point out that
the NULL in he case I described was NOT 0L (I removed all the Ls form the
include files - i use +l all the time so I dont need them).  I had hoped
that removing the Ls, this would make the "problem" go away but no luck.
Simple peephole optimization hould catch most of the stuff I see wrong
wit MANX.  I also acknowledge that the +l option was an "afterthought",
having been added maybe days before 3.20A shipped.  I still would rather
stick with their product for the next few years, because they do have the
better compiler, a head start on everyone else (at making a good Amiga
library), excellent support and a bright future.  I will probably pay for
updates forever...

This discussion has brought out a few interesting points: if you consider
the compiler a tool to generate the program you want (which  I do), then
you have to play some tricks and use a style that makes the compiler generate
better code.  On one hand, future compilers (versions of compilers) may not
generate optimal code for this style, but what you get today is closest to
the binary program that you want (i.e. fast, compact, etc.) as doable with
the current version of the compiler.  On the other hand, if you code for
portability, there are less of these tricks (there probably are a few though)
that work across all compilers and all machines.  Matt Dillon's comments 
about longs and shorts and ints are valid and his approach is valid, when
you consider his intentions (purism aside).  For now, I do use:
	if (!pointer) ...
instead of
	if (pointer == NULL) ...
because no matter how much RAM I have, I seem to always want more.  Coding
this way saves me RAM all the time.