[comp.sys.amiga] Lattice C 5.02 REVIEW, By Matthew Dillon

dillon@POSTGRES.BERKELEY.EDU (Matt Dillon) (06/30/89)

	Well, here is a review of Lattice C V5.02 vs (if you insist) Manx C
	V3.6a

	First of all, I would like to say that Lattice has been much more
	responsive to reported bugs and are generally easier to reach than 
	Manx.  I had several long talks with John Toebes at DevCon and really
	like the direction Lattice is going.

	I would like to thank Jim Goodnow for kindly giving me Manx C 3.4a 
	a couple of years ago and the update to 3.6a several months ago.

	I would like to thank John Toebes for kindly giving me the most
	recent release of Lattice C, V5.02

	Please keep in mind that I am comparing the very latest Lattice C
	with a relatively old Manx C (but still the latest I've heard about).
	I do not know what will be in the next release of Manx C but I do know
	that it will make or break the company (in regard to C on the Amiga).
	
	I always keep an open mind.

			#asm, who needs #asm?

	One of my first questions was, is #asm supported in Lattice V5.02 ?
	It isn't, but John assured me that I really didn't need it and it
	turns out he was absolutely correct!

	Lattice V5.02 supports keywords that effectively remove the need to
	hack assembly tags and other things that crop up.   The keywords
	associated with this feature:

	__asm		-These allow you to specify that subroutines takes
	__d0 to __d7,	 parameters in specific registers instead of on the 
			 stack
	__a0 to __a6

			__asm blah (register __d0 int a, register __a0 int *b);

			currently you may only declare that data registers pass
			non pointers and address registers pass pointers.

	__saveds	-This automatically saves the data segment register
			 (A4) and then loads it with the proper value.  On
			 return the original contents of A4 is restored.

	The above effectively removes nearly all #asm's I had to put in 
	under Aztec C.

	Lattice conforms to using only D0-D1/A0-A1 as scratch.  Manx by
	default also uses D2-D3 as scratch with an option to save/restore
	them (but code generated using this option saves/restores D2/D3
	whether they are used or not so I try not to use it).  Since Lattice
	conforms to the Amiga spec in this regard no hacks are required to
	get it to save/restore nonconforming registers.

	This type of support allows one to write library functions and 
	interrupts directly in C without any assembly tags files.  The latter 
	is something you could not do in Manx C even *with* the #asm
	construct, at least not with any assurance.

	And for those of you who are crying about loosing 'standard C', I would
	say that using the new keywords are at least as portable as using
	#asm or .asm files.  Lattice will allow you to insert direct machine
	code into any C subroutine via the 'emit' inline function.  I will
	probably never find the need to use it.

	In anycase, the few remaining #asm's I had in my code were really so I
	could have the entire program in one file and it wasn't difficult to
	remove these to separate .asm files and assemble them with lattice's
	asm program.  They were mainly incredibly optimized subroutines I
	had written.

			OPTIMALITY, OPTIMIZATION, CODE SIZE

	Lattice now has a 'registerization of parameters' option.  If you give
	lc the -rr option, arguments to all fully prototyped functions will
	be passed in registers.  The first two data items and first two 
	pointer items to be specific (remaining arguments are passed on the
	stack).

	This is completely transparent and needs no special keywords other than
	using standard ANSI prototypes.  The prototypes themselves can be
	made compatible with non ANSI C compilers with a simple #define
	
	#ifdef LATTICE
	#define ARGS(blah) blah
	#else
	#define ARGS(blah) ()
	#endif

	extern int charlie ARGS((int, int, char *, short));

	There are a couple benign bugs with this feature that show up as
	blink errors.  Lattice in 5.02 accidently forgot to add the __stdargs
	keyword to the prototypes for AMIGA.LIB (__stdargs forces all 
	arguments to be passed on the stack even when you compile with -rr).

	Lattice C names routines differently if they expect registerized
	parameters vs. normal stacked parameters.  an '@' is used instead of
	an '_'.  THIS IS GOOD... Thus the link error.  For example, when I
	compiled under Lattice -rr and called NewList() (in Amiga.LIB) I
	got a blink error '@NewList' not found.  Of course, because the
	NewList() in AMIGA.LIB is '_NewList' as it is a normal C 
	subroutine.
		
	These minor bugs are easily fixed by fixing the #include files.
	Unfortunately the registerized library (LCR.LIB instead of LC.LIB)
	also makes this mistake and one doesn't have library source.  When
	such cases pop up you need to write your own NewList() (and other
	functions, but so far only NewList() has bit me from LCR.LIB, in
	the onbreak() call).  Since you have fully prototyped it it *will* 
	be registerized and LCR.LIB will use it.  I figure Lattice will
	fix this one real fast.

	Using registerized parameters removes a lot of overhead and reduces
	code size considerably.  No stack pushes or pops after the fact,
	and sometimes even the link #, unlk # statements are optimized out.

	Even without -rr Lattice now does defered popping of the stack.
	Manx 3.6a also has defered popping of the stack.

			REGISTER ASSIGNMENT, MANX COMPARISON, GLOBAL OPT.

	Manx still wins on short->long optimizations (only applies if you
	use 32 bit integers like I do).  Manx still does register allocation
	better but to take advantage of it you must specifically use the
	'register' keyword ala normal C.

	Lattice C will now place variables in registers by default.  You do
	not need to specify the 'register' keyword unless you really want to.
	Unfortunately, Lattice still doesn't take advantage of scope to 
	reuse registers.  The global optimizer fixes this to a degree but
	sometimes makes bad decisions.  Overall Lattice could improve on this
	quite a bit.

	Both Manx and Lattice still generate some stupid code.  With the 
	advent of the peephole optimizer (I believe this is in LC1 and
	transparent) and the global optimizer (GO, the program), Lattice
	now makes so many other optimizations that I as a programmer no 
	longer worry about writing my C code optimally.  GO is not a minor
	program, it does some pretty complex stuff.

	Comparing code generation without the global optimizer I would put
	Lattice ahead of Manx and with the global optimizer I would put Lattice
	wayyyy ahead of Manx.  Personally, both compilers still make grevious
	non-optimal mistakes but at least now Lattice has the transport layer
	into which it can insert fixes (the peephole optimizer and the global
	optimizer, which only lacks a proper register allocation scheme).

	I won't list the dozen+ things GO will optimize.  It does a good job
	for the most part and I expect it will be improved in future releases.
	It mainly optimizes for speed but I still see a code size improvment
	from it.

				ASSEMBLY OUTPUT, SORRY,
				BUT, NOT REALLY A PROBLEM. &
					LATTICE ASM

	No.  Lattice C does not output assembly.  LC1 outputs a quad file which
	LC2 digests.  Normally one just runs 'LC' which makes this two pass
	business transparent. 

	So far it hasn't been a problem.  I liked Manx C's ability to generate
	assembly but used it ONLY to checkout the quality of generated code and
	find compiler bugs.  ALSO NOTE THAT MANX NEVER PRODUCED PROPER
	ASSEMBLY.  It generated stuff that only its assembler could assemble
	and relied heavily on its assembler making certain optimizations.  It
	also relied on the assembler handling the data model.  Thus, no big
	loss.

	Lattice still has OMD and it appears to no longer barf on large object
	files.  This suffices just fine.

	Lattice now has an assembler.  ASM.  I found a few minor bugs like
	the fact that you could not have a comment after a macro which has
	no arguments.

			ENABLE		;this is a comment 

	Doesn't work (the bad english is for effect).  Remember, the assembly 
	language programmer doesn't need all the optimizations the Manx 
	Assembler did for you.  Nobody in their right mind uses MOVEM when
	they have only one register to move.

	Lattice ASM will optimize reverse branches only.  Forward branches
	default to word but you can use .S to force them to short.  This isn't
	really a problem.  Manx AS will optimize forward branches.

	Lattice ASM uses a superior method to reference A4 relative vs
	absolute location.  In fact, you can use ANY address register to
	access items relative.  You specify the addressing mode just as you
	would an address register relative addressing mode.  In Manx AS you
	would specify an absolute addressing mode and Manx AS would convert
	it depending on which model was being assembled for.  This made 
	mixing difficult and also is the cause of several bugs in Manx AS.

	You have to be a bit more on the ball and stick code and data in
	the proper SECTIONs... you need a SECTION directive before you can
	begin laying down code.  This isn't a problem either, I was just lazy
	before.

	Finally, note that Manx AS is still buggy with certain MOVEM and MOVE
	constructions (generates assembly errors when the code is perfectly
	valid).  Most such bugs are related to the small data model.

	Manx AS never implemented CNOP properly and the Manx linker does not
	place items in like-named sections together (i.e. SECTION is not
	fully implemented).

	By the way, Lattice ASM is strict on (A1,D1.x)... you need to say
	0(A1,D1.x) or Lattice ASM thinks it's a 68020 instruction and
	returns an error.

	Oh yah, one final note:  Lattice ASM and Lattice C V5.02 fully
	support code generation for 68010's 020's and 030's.  As John told me,
	"We use every 68020 instruction in the book".. well, almost
	every.  Obviously things like TAS and other atomic multiple cycle
	68020 instructions cannot be used on the Amiga.

	For example, LEA 0(A1,D7.L*4),A0 will be used on a 68020 instead of
	putting D7 into a temporary and then shifting it by 2 and *then*
	using LEA 0(A1,D7.L) .


				   PRAGMAS

	Lattice C V5.02 has the ability to call library functions directly
	without having to go through any kind of assembly tags.  This is
	a transparent feature.  You simply #include the appropriate proto/*.h
	file.  I normally just #include <proto/all.h> so I don't have to
	worry about it.

	If a library call is made inside a subroutine, A6 is automatically
	saved beforehand and restored on return.  Lattice tracks the contents
	of A6 and will not reload it unless necessary.  That means that if
	you make several system calls to a given library A6 is loaded only
	once with that library's base pointer.

				 THE MANUAL

	Comes in two IBM style binders and is much better organized than
	its predecessors and much much better than the Manx 3.4 manual
	(I got a set of pages to insert in it when I received my 3.6a
	update).  Well, it wasn't *that* bad but my comparison stands.

	The Lattice manual, like most manuals, has a neeto keen index that
	manages to list a billion topics EXCEPT the one I'm trying to look
	up.

	the Lattice manual is skimpy on the use of the __asm and associated 
	keywords but on the whole quite well done.

				COMPILER BUGS

	Manx C V3.6 has quite a few compiler bugs.  Lattice does too but at
	least they are all minor! (so far).  For example, Manx C has problems
	with constant expressions in if() statements and  ? : statements...
	it will generate bad assembly and even sometimes good assembly that
	does the wrong thing.  Manx C does not optimize out (remove) dead
	code.  Lattice does so via the global optimizer.
				
	Lattice C bugs are relatively minor.  The worst bug so far is that 
	LC2 gets confused sometimes and exits with CXERR 26 on perfectly 
	valid code... even simple C code sometimes!  I've had to munge the
	code a little to get it to compile.  This occured three times while I 
	was porting DME, SUP32.LIB, and DNET to Lattice C (that is, I fixed
	the programs to compile under both Manx and Lattice now).

	The -rrb option (generate both registerized (@) and nonregisterized (_)
	entry points) does not work.  It gets the labels mixed up (I caught
	that one *real* fast!).

	Precompiled include files don't appear to work 100%.  I couldn't pin
	it down, but apparently #pragmas would be lost from time to time, and
	maybe some prototypes too.

				   SPEED

	Unfortunately, Lattice C is still quite slow.  

	Manx C is still around 2x Lattice C under equivalent conditions
	(precompiled include file in ram and all executables rez'd or
	resident).  Poo.  Lattice C is almost 3x slower if you are forced
	not to use precompiled include files.

				FINAL OPINION

	I could find only two major faults with Lattice C (things that I
	think will not be fixed as quickly as the myrid minor problems I
	found):

	(1) precompiled include files don't work all the time (??)
	    I'll experiment more on this

	(2) Lattice C could be faster 

	Minor Faults:

	(1) CXERR 26 occurs on valid code somtimes.  minor hassle.

	(2) Lattice accidently screwed up the prototypes for AMIGA.LIB in
	    that they forgot to use the __stdargs keyword, causing undefined
	    symbols at blink time (when using the -rr option to LC).

	(3) -rrb doesn't work (so just don't use it for now, just think of
	    it as a preview to a future feature :-)).

	(4) LC+LC1+[GO]+LC2 take a bit more memory.  You really need a 1.5MB+
	    system to be able to make them all resident.  Manx has the
	    virtue of being relatively small.

	(5) Since functions are prototyped, you get many more warning 
	    messages about ptr->ptr conversion.  For ARGUMENTS to functions 
	    now.  I talked to John Toebes on this point and he already had
	    some good ideas on how to fix it.

	    Specifically, I want a function expecting a pointer to 
	    structure X as an argument to NOT display a warning message if
	    I pass a pointer to structure Y where structure Y is a superset
	    of structure X.

	*** Major Advantages ***

	(1) Registerized parameters (these are transparent)

	(2) Pragmas (these are transparent) makes overhead for system library
	    calls nearly nil.

	(3) Automatic placement of local variables into registers

	(4) Global Optimizer

	(5) *true* Support for 68010, 020, 030

	(6) Ability to specify that arguments are in specific registers (no 
	    need for assembly tags files if you are writing a library or 
	    device)

	(7) PROTOTYPES (very useful for all those 16 bit int programmers as
			well as making #(1) above possible)

	*** Minor Advantages ***

	(1) Builtin functions (inline code for common string calls)


	Overall I like Lattice C .  OK, I'll say it:  I like Lattice
	C better than I like Manx C.  There, I said it.  Sure there are
	problems, but I think Lattice is moving very quickly to fix them
	as well as thinking up lots of new features to add.

	Final note:  Note that Lattice C also comes with an object module
	librarian, object module disassembler, assembler, the latest BLink,
	grep, diff, and many other utilities including an editor (but I just
	use DME so I can't comment on their editor).  Manx C, at least the
	package I got, came with an equally prolific set of utilities.


						-Matt

phils@tekigm2.MEN.TEK.COM (Philip E Staub) (07/01/89)

In article <8906300559.AA12971@postgres.Berkeley.EDU> dillon@POSTGRES.BERKELEY.EDU (Matt Dillon) writes:
>
>	Well, here is a review of Lattice C V5.02 vs (if you insist) Manx C
>	V3.6a

[many good comments deleted]
>
>	......................  Nobody in their right mind uses MOVEM when
>	they have only one register to move.

Minor nit to pick here. I regularly use MOVEM with one (data) register to 
move if I don't want to disturb the contents of the flags. Example:

	movem.l	d0,-(sp)
	jsr	some_function	;returns a status in the zero flag
	movem.l	(sp)+,d0	;no change of zero flag here
	beq	success
failure:
	...

>
>
>						-Matt

Overall, a very helpful review. I've been seriously considering
making the switch, since it appears that a new Manx release is not likely
in the *reasonable* future. Thanks for helping make up my mind.

-Phil



-- 
------------------------------------------------------------------------------
Phil Staub, phils@tekigm2.MEN.TEK.COM
Definition: BUG: A feature (present or absent) which is (at best) inconvenient.

dillon@POSTGRES.BERKELEY.EDU (Matt Dillon) (07/02/89)

:>	......................  Nobody in their right mind uses MOVEM when
:>	they have only one register to move.
:
:Minor nit to pick here. I regularly use MOVEM with one (data) register to 
:move if I don't want to disturb the contents of the flags. Example:
:
:	movem.l	d0,-(sp)

	Ah!  Good answer, good answer!  Didn't think of that.  I was really
    only refering to the special case where the Manx compiler generates
    movem.l instructions to save/restore registers in procedures.  The
    compiler generates a movem.l instruction with a forward referenced
    register EQR and then expects the assembler to:

	(1) optimize the movem.l out completely if the EQR specs no regs
	(2) optimize the movem.l to a move.l if the EQR specs one reg
	(3) keep the movem.l if the EQR specs >1 reg

    And, in fact, in your case you would not want to use Manx AS for 
    exactly this reason, it screws up your movem.l by changing it to a
    move.l

				-Matt