Browse Source

Bringing back Ken cc: first step, libcc as a common lib for compilers and utilities

Signed-off-by: Álvaro Jurado <elbingmiss@gmail.com>
Álvaro Jurado 3 years ago
parent
commit
3f99b6e72e

+ 800 - 0
sys/include/cc.h

@@ -0,0 +1,800 @@
+/*
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+
+// For Harvey
+typedef	unsigned short	ushort;
+typedef	unsigned char	uchar;
+typedef unsigned long	ulong;
+typedef unsigned int	uint;
+typedef   signed char	schar;
+typedef	long long	vlong;
+typedef	unsigned long long uvlong;
+
+#ifndef	EXTERN
+#define EXTERN	extern
+#endif
+
+typedef	struct	Node	Node;
+typedef	struct	Sym	Sym;
+typedef	struct	Type	Type;
+typedef	struct	Funct	Funct;
+typedef	struct	Decl	Decl;
+typedef	struct	Io	Io;
+typedef	struct	Hist	Hist;
+typedef	struct	Term	Term;
+typedef	struct	Init	Init;
+typedef	struct	Bits	Bits;
+
+typedef	Rune	TRune;	/* target system type */
+
+#define	NHUNK		50000L
+#define	BUFSIZ		8192
+#define	NSYMB		1500
+#define	NHASH		1024
+#define	STRINGSZ	200
+#define	HISTSZ		20
+#define YYMAXDEPTH	1500
+#define	NTERM		10
+#define	MAXALIGN	7
+
+#define	SIGN(n)		(1ULL<<(n-1))
+#define	MASK(n)		(SIGN(n)|(SIGN(n)-1))
+
+#define	BITS	5
+#define	NVAR	(BITS*sizeof(ulong)*8)
+struct	Bits
+{
+	ulong	b[BITS];
+};
+
+struct	Node
+{
+	Node*	left;
+	Node*	right;
+	void*	label;
+	long	pc;
+	int	reg;
+	long	xoffset;
+	double	fconst;		/* fp constant */
+	vlong	vconst;		/* non fp const */
+	char*	cstring;	/* character string */
+	TRune*	rstring;	/* rune string */
+
+	Sym*	sym;
+	Type*	type;
+	long	lineno;
+	char	op;
+	char	oldop;
+	char xcast;
+	char	class;
+	uint8_t	etype;
+	char	complex;
+	char	addable;
+	char	scale;
+	char	garb;
+};
+#define	Z	((Node*)0)
+
+struct	Sym
+{
+	Sym*	link;
+	Type*	type;
+	Type*	suetag;
+	Type*	tenum;
+	char*	macro;
+	long	varlineno;
+	long	offset;
+	vlong	vconst;
+	double	fconst;
+	Node*	label;
+	ushort	lexical;
+	char	*name;
+	ushort	block;
+	ushort	sueblock;
+	uint8_t	class;
+	char	sym;
+	char	aused;
+	char	sig;
+};
+#define	S	((Sym*)0)
+
+enum{
+	SIGNONE = 0,
+	SIGDONE = 1,
+	SIGINTERN = 2,
+
+	SIGNINTERN = 1729*325*1729,
+};
+
+struct	Decl
+{
+	Decl*	link;
+	Sym*	sym;
+	Type*	type;
+	long	varlineno;
+	long	offset;
+	short	val;
+	ushort	block;
+	char	class;
+	char	aused;
+};
+#define	D	((Decl*)0)
+
+struct	Type
+{
+	Sym*	sym;
+	Sym*	tag;
+	Funct*	funct;
+	Type*	link;
+	Type*	down;
+	long	width;
+	long	offset;
+	long	lineno;
+	schar	shift;
+	char	nbits;
+	uint8_t	etype;
+	char	garb;
+};
+
+#define	T	((Type*)0)
+#define	NODECL	((void(*)(int, Type*, Sym*))0)
+
+struct	Init			/* general purpose initialization */
+{
+	int	code;
+	ulong	value;
+	char*	s;
+};
+
+EXTERN struct
+{
+	char*	p;
+	int	c;
+} fi;
+
+struct	Io
+{
+	Io*	link;
+	char*	p;
+	char	b[BUFSIZ];
+	short	c;
+	short	f;
+};
+#define	I	((Io*)0)
+
+struct	Hist
+{
+	Hist*	link;
+	char*	name;
+	long	line;
+	long	offset;
+};
+#define	H	((Hist*)0)
+EXTERN Hist*	hist;
+
+struct	Term
+{
+	vlong	mult;
+	Node	*node;
+};
+
+enum
+{
+	Axxx,
+	Ael1,
+	Ael2,
+	Asu2,
+	Aarg0,
+	Aarg1,
+	Aarg2,
+	Aaut3,
+	NALIGN,
+};
+
+enum				/* also in ../{8a,0a}.h */
+{
+	Plan9	= 1<<0,
+	Unix	= 1<<1,
+	Windows	= 1<<2,
+};
+
+enum
+{
+	DMARK,
+	DAUTO,
+	DSUE,
+	DLABEL,
+};
+enum
+{
+	OXXX,
+	OADD,
+	OADDR,
+	OAND,
+	OANDAND,
+	OARRAY,
+	OAS,
+	OASI,
+	OASADD,
+	OASAND,
+	OASASHL,
+	OASASHR,
+	OASDIV,
+	OASHL,
+	OASHR,
+	OASLDIV,
+	OASLMOD,
+	OASLMUL,
+	OASLSHR,
+	OASMOD,
+	OASMUL,
+	OASOR,
+	OASSUB,
+	OASXOR,
+	OBIT,
+	OBREAK,
+	OCASE,
+	OCAST,
+	OCOMMA,
+	OCOND,
+	OCONST,
+	OCONTINUE,
+	ODIV,
+	ODOT,
+	ODOTDOT,
+	ODWHILE,
+	OENUM,
+	OEQ,
+	OFOR,
+	OFUNC,
+	OGE,
+	OGOTO,
+	OGT,
+	OHI,
+	OHS,
+	OIF,
+	OIND,
+	OINDREG,
+	OINIT,
+	OLABEL,
+	OLDIV,
+	OLE,
+	OLIST,
+	OLMOD,
+	OLMUL,
+	OLO,
+	OLS,
+	OLSHR,
+	OLT,
+	OMOD,
+	OMUL,
+	ONAME,
+	ONE,
+	ONOT,
+	OOR,
+	OOROR,
+	OPOSTDEC,
+	OPOSTINC,
+	OPREDEC,
+	OPREINC,
+	OPROTO,
+	OREGISTER,
+	ORETURN,
+	OSET,
+	OSIGN,
+	OSIZE,
+	OSTRING,
+	OLSTRING,
+	OSTRUCT,
+	OSUB,
+	OSWITCH,
+	OUNION,
+	OUSED,
+	OWHILE,
+	OXOR,
+	ONEG,
+	OCOM,
+	OPOS,
+	OELEM,
+
+	OTST,		/* used in some compilers */
+	OINDEX,
+	OFAS,
+	OREGPAIR,
+	OEXREG,
+
+	OEND
+};
+enum
+{
+	TXXX,
+	TCHAR,
+	TUCHAR,
+	TSHORT,
+	TUSHORT,
+	TINT,
+	TUINT,
+	TLONG,
+	TULONG,
+	TVLONG,
+	TUVLONG,
+	TFLOAT,
+	TDOUBLE,
+	TIND,
+	TFUNC,
+	TARRAY,
+	TVOID,
+	TSTRUCT,
+	TUNION,
+	TENUM,
+	TDOT,
+	NTYPE,
+
+	TAUTO	= NTYPE,
+	TEXTERN,
+	TSTATIC,
+	TTYPEDEF,
+	TTYPESTR,
+	TREGISTER,
+	TCONSTNT,
+	TVOLATILE,
+	TUNSIGNED,
+	TSIGNED,
+	TFILE,
+	TOLD,
+	NALLTYPES,
+
+	/* adapt size of Rune to target system's size */
+	TRUNE = sizeof(TRune)==4? TUINT: TUSHORT,
+};
+enum
+{
+	CXXX,
+	CAUTO,
+	CEXTERN,
+	CGLOBL,
+	CSTATIC,
+	CLOCAL,
+	CTYPEDEF,
+	CTYPESTR,
+	CPARAM,
+	CSELEM,
+	CLABEL,
+	CEXREG,
+	NCTYPES,
+};
+enum
+{
+	GXXX		= 0,
+	GCONSTNT	= 1<<0,
+	GVOLATILE	= 1<<1,
+	NGTYPES		= 1<<2,
+
+	GINCOMPLETE	= 1<<2,
+};
+enum
+{
+	BCHAR		= 1L<<TCHAR,
+	BUCHAR		= 1L<<TUCHAR,
+	BSHORT		= 1L<<TSHORT,
+	BUSHORT		= 1L<<TUSHORT,
+	BINT		= 1L<<TINT,
+	BUINT		= 1L<<TUINT,
+	BLONG		= 1L<<TLONG,
+	BULONG		= 1L<<TULONG,
+	BVLONG		= 1L<<TVLONG,
+	BUVLONG		= 1L<<TUVLONG,
+	BFLOAT		= 1L<<TFLOAT,
+	BDOUBLE		= 1L<<TDOUBLE,
+	BIND		= 1L<<TIND,
+	BFUNC		= 1L<<TFUNC,
+	BARRAY		= 1L<<TARRAY,
+	BVOID		= 1L<<TVOID,
+	BSTRUCT		= 1L<<TSTRUCT,
+	BUNION		= 1L<<TUNION,
+	BENUM		= 1L<<TENUM,
+	BFILE		= 1L<<TFILE,
+	BDOT		= 1L<<TDOT,
+	BCONSTNT	= 1L<<TCONSTNT,
+	BVOLATILE	= 1L<<TVOLATILE,
+	BUNSIGNED	= 1L<<TUNSIGNED,
+	BSIGNED		= 1L<<TSIGNED,
+	BAUTO		= 1L<<TAUTO,
+	BEXTERN		= 1L<<TEXTERN,
+	BSTATIC		= 1L<<TSTATIC,
+	BTYPEDEF	= 1L<<TTYPEDEF,
+	BTYPESTR	= 1L<<TTYPESTR,
+	BREGISTER	= 1L<<TREGISTER,
+
+	BINTEGER	= BCHAR|BUCHAR|BSHORT|BUSHORT|BINT|BUINT|
+				BLONG|BULONG|BVLONG|BUVLONG,
+	BNUMBER		= BINTEGER|BFLOAT|BDOUBLE,
+
+/* these can be overloaded with complex types */
+
+	BCLASS		= BAUTO|BEXTERN|BSTATIC|BTYPEDEF|BTYPESTR|BREGISTER,
+	BGARB		= BCONSTNT|BVOLATILE,
+};
+
+struct	Funct
+{
+	Sym*	sym[OEND];
+	Sym*	castto[NTYPE];
+	Sym*	castfr[NTYPE];
+};
+
+EXTERN struct
+{
+	Type*	tenum;		/* type of entire enum */
+	Type*	cenum;		/* type of current enum run */
+	vlong	lastenum;	/* value of current enum */
+	double	floatenum;	/* value of current enum */
+} en;
+
+EXTERN	int	autobn;
+EXTERN	long	autoffset;
+EXTERN	int	blockno;
+EXTERN	Decl*	dclstack;
+EXTERN	char	debug[256];
+EXTERN	Hist*	ehist;
+EXTERN	long	firstbit;
+EXTERN	Sym*	firstarg;
+EXTERN	Type*	firstargtype;
+EXTERN	Decl*	firstdcl;
+EXTERN	int	fperror;
+EXTERN	Sym*	hash[NHASH];
+EXTERN	int	hasdoubled;
+EXTERN	char*	hunk;
+EXTERN	char**	include;
+EXTERN	Io*	iofree;
+EXTERN	Io*	ionext;
+EXTERN	Io*	iostack;
+EXTERN	long	lastbit;
+EXTERN	char	lastclass;
+EXTERN	Type*	lastdcl;
+EXTERN	long	lastfield;
+EXTERN	Type*	lasttype;
+EXTERN	long	lineno;
+EXTERN	long	nearln;
+EXTERN	int	maxinclude;
+EXTERN	int	nerrors;
+EXTERN	int	newflag;
+EXTERN	long	nhunk;
+EXTERN	int	ninclude;
+EXTERN	Node*	nodproto;
+EXTERN	Node*	nodcast;
+EXTERN	Biobuf	outbuf;
+EXTERN	Biobuf	diagbuf;
+EXTERN	char*	outfile;
+EXTERN	char*	pathname;
+EXTERN	int	peekc;
+EXTERN	long	stkoff;
+EXTERN	Type*	strf;
+EXTERN	Type*	strl;
+EXTERN	char	symb[NSYMB];
+EXTERN	Sym*	symstring;
+EXTERN	int	taggen;
+EXTERN	Type*	tfield;
+EXTERN	Type*	tufield;
+EXTERN	int	thechar;
+EXTERN	char*	thestring;
+EXTERN	Type*	thisfn;
+EXTERN	long	thunk;
+EXTERN	Type*	types[NTYPE];
+EXTERN	Type*	fntypes[NTYPE];
+EXTERN	Node*	initlist;
+EXTERN	Term	term[NTERM];
+EXTERN	int	nterm;
+EXTERN	int	packflg;
+EXTERN	int	fproundflg;
+EXTERN	int	profileflg;
+EXTERN	int	ncontin;
+EXTERN	int	newvlongcode;
+EXTERN	int	canreach;
+EXTERN	int	warnreach;
+EXTERN	Bits	zbits;
+
+extern	char	*onames[], *tnames[], *gnames[];
+extern	char	*cnames[], *qnames[], *bnames[];
+extern	char	tab[NTYPE][NTYPE];
+extern	char	comrel[], invrel[], logrel[];
+extern	long	ncast[], tadd[], tand[];
+extern	long	targ[], tasadd[], tasign[], tcast[];
+extern	long	tdot[], tfunct[], tindir[], tmul[];
+extern	long	tnot[], trel[], tsub[];
+
+extern	char	typeaf[];
+extern	char	typefd[];
+extern	char	typei[];
+extern	char	typesu[];
+extern	char	typesuv[];
+extern	char	typeu[];
+extern	char	typev[];
+extern	char	typec[];
+extern	char	typeh[];
+extern	char	typeil[];
+extern	char	typeilp[];
+extern	char	typechl[];
+extern	char	typechlv[];
+extern	char	typechlvp[];
+extern	char	typechlp[];
+extern	char	typechlpfd[];
+
+EXTERN	char*	typeswitch;
+EXTERN	char*	typeword;
+EXTERN	char*	typecmplx;
+
+extern	ulong	thash1;
+extern	ulong	thash2;
+extern	ulong	thash3;
+extern	ulong	thash[];
+
+/*
+ *	compat.c/unix.c/windows.c
+ */
+int	mywait(int*);
+int	mycreat(char*, int);
+int	systemtype(int);
+int	pathchar(void);
+int	myaccess(char*);
+char*	mygetwd(char*, int);
+int	myexec(char*, char*[]);
+int	mydup(int, int);
+int	myfork(void);
+int	mypipe(int*);
+void*	mysbrk(ulong);
+
+/*
+ *	parser
+ */
+int	yyparse(void);
+int	mpatov(char*, vlong*);
+
+/*
+ *	lex.c
+ */
+void*	allocn(void*, long, long);
+void*	alloc(long);
+void	cinit(void);
+int	compile(char*, char**, int);
+void	errorexit(void);
+int	filbuf(void);
+int	getc(void);
+long	getr(void);
+int	getnsc(void);
+Sym*	lookup(void);
+void	main(int, char*[]);
+void	newfile(char*, int);
+void	newio(void);
+void	pushio(void);
+long	escchar(long, int, int);
+Sym*	slookup(char*);
+void	syminit(Sym*);
+void	unget(int);
+long	yylex(void);
+int	Lconv(Fmt*);
+int	Tconv(Fmt*);
+int	FNconv(Fmt*);
+int	Oconv(Fmt*);
+int	Qconv(Fmt*);
+int	VBconv(Fmt*);
+void	setinclude(char*);
+
+/*
+ * mac.c
+ */
+void	dodefine(char*);
+void	domacro(void);
+Sym*	getsym(void);
+long	getnsn(void);
+void	linehist(char*, int);
+void	macdef(void);
+void	macprag(void);
+void	macend(void);
+void	macexpand(Sym*, char*);
+void	macif(int);
+void	macinc(void);
+void	maclin(void);
+void	macund(void);
+
+/*
+ * dcl.c
+ */
+Node*	doinit(Sym*, Type*, long, Node*);
+Type*	tcopy(Type*);
+Node*	init1(Sym*, Type*, long, int);
+Node*	newlist(Node*, Node*);
+void	adecl(int, Type*, Sym*);
+int	anyproto(Node*);
+void	argmark(Node*, int);
+void	dbgdecl(Sym*);
+Node*	dcllabel(Sym*, int);
+Node*	dodecl(void(*)(int, Type*, Sym*), int, Type*, Node*);
+Sym*	mkstatic(Sym*);
+void	doenum(Sym*, Node*);
+void	snap(Type*);
+Type*	dotag(Sym*, int, int);
+void	edecl(int, Type*, Sym*);
+Type*	fnproto(Node*);
+Type*	fnproto1(Node*);
+void	markdcl(void);
+Type*	paramconv(Type*, int);
+void	pdecl(int, Type*, Sym*);
+Decl*	push(void);
+Decl*	push1(Sym*);
+Node*	revertdcl(void);
+long	round(long, int);
+int	rsametype(Type*, Type*, int, int);
+int	sametype(Type*, Type*);
+ulong	sign(Sym*);
+ulong	signature(Type*);
+void	sualign(Type*);
+void	tmerge(Type*, Sym*);
+void	walkparam(Node*, int);
+void	xdecl(int, Type*, Sym*);
+Node*	contig(Sym*, Node*, long);
+
+/*
+ * com.c
+ */
+void	ccom(Node*);
+void	complex(Node*);
+int	tcom(Node*);
+int	tcoma(Node*, Node*, Type*, int);
+int	tcomd(Node*);
+int	tcomo(Node*, int);
+int	tcomx(Node*);
+int	tlvalue(Node*);
+void	constas(Node*, Type*, Type*);
+Node*	uncomma(Node*);
+Node*	uncomargs(Node*);
+
+/*
+ * con.c
+ */
+void	acom(Node*);
+void	acom1(vlong, Node*);
+void	acom2(Node*, Type*);
+int	acomcmp1(const void*, const void*);
+int	acomcmp2(const void*, const void*);
+int	addo(Node*);
+void	evconst(Node*);
+
+/*
+ * funct.c
+ */
+int	isfunct(Node*);
+void	dclfunct(Type*, Sym*);
+
+/*
+ * sub.c
+ */
+void	arith(Node*, int);
+int	deadheads(Node*);
+Type*	dotsearch(Sym*, Type*, Node*, long*);
+long	dotoffset(Type*, Type*, Node*);
+void	gethunk(void);
+Node*	invert(Node*);
+int	bitno(long);
+void	makedot(Node*, Type*, long);
+int	mixedasop(Type*, Type*);
+Node*	new(int, Node*, Node*);
+Node*	new1(int, Node*, Node*);
+int	nilcast(Type*, Type*);
+int	nocast(Type*, Type*);
+void	prtree(Node*, char*);
+void	prtree1(Node*, int, int);
+void	relcon(Node*, Node*);
+int	relindex(int);
+int	simpleg(long);
+Type*	garbt(Type*, long);
+int	simplec(long);
+Type*	simplet(long);
+int	stcompat(Node*, Type*, Type*, long[]);
+int	tcompat(Node*, Type*, Type*, long[]);
+void	tinit(void);
+Type*	typ(int, Type*);
+Type*	copytyp(Type*);
+void	typeext(Type*, Node*);
+void	typeext1(Type*, Node*);
+int	side(Node*);
+int	vconst(Node*);
+int	log2(uvlong);
+int	vlog(Node*);
+int	topbit(ulong);
+void	simplifyshift(Node*);
+long	typebitor(long, long);
+void	diag(Node*, char*, ...);
+void	warn(Node*, char*, ...);
+void	yyerror(char*, ...);
+void	fatal(Node*, char*, ...);
+
+/*
+ * acid.c
+ */
+void	acidtype(Type*);
+void	acidvar(Sym*);
+
+/*
+ * pickle.c
+ */
+void	pickletype(Type*);
+
+/*
+ * bits.c
+ */
+Bits	bor(Bits, Bits);
+Bits	band(Bits, Bits);
+Bits	bnot(Bits);
+int	bany(Bits*);
+int	bnum(Bits);
+Bits	blsh(uint);
+int	beq(Bits, Bits);
+int	bset(Bits, uint);
+
+/*
+ * dpchk.c
+ */
+void	dpcheck(Node*);
+void	arginit(void);
+void	pragvararg(void);
+void	pragpack(void);
+void	pragfpround(void);
+void pragprofile(void);
+void	pragincomplete(void);
+
+/*
+ * calls to machine depend part
+ */
+void	codgen(Node*, Node*);
+void	gclean(void);
+void	gextern(Sym*, Node*, long, long);
+void	ginit(void);
+long	outstring(char*, long);
+long	outlstring(TRune*, long);
+void	xcom(Node*);
+long	exreg(Type*);
+long	align(long, Type*, int);
+long	maxround(long, long);
+
+extern	schar	ewidth[];
+
+/*
+ * com64
+ */
+int	com64(Node*);
+void	com64init(void);
+void	bool64(Node*);
+double	convvtof(vlong);
+vlong	convftov(double);
+double	convftox(double, int);
+vlong	convvtox(vlong, int);
+
+/*
+ * machcap
+ */
+int	machcap(Node*);
+
+/*
+#pragma	varargck	argpos	warn	2
+#pragma	varargck	argpos	diag	2
+#pragma	varargck	argpos	yyerror	1
+
+#pragma	varargck	type	"F"	Node*
+#pragma	varargck	type	"L"	long
+#pragma	varargck	type	"Q"	long
+#pragma	varargck	type	"O"	int
+#pragma	varargck	type	"T"	Type*
+#pragma	varargck	type	"|"	int
+*/

+ 312 - 0
sys/src/libcc/acid.c

@@ -0,0 +1,312 @@
+/*
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+
+#include "cc.h"
+
+static char *kwd[] =
+{
+	"$adt", "$aggr", "$append", "$builtin", "$complex", "$defn",
+	"$delete", "$do", "$else", "$eval", "$head", "$if",
+	"$local", "$loop", "$return", "$tail", "$then",
+	"$union", "$whatis", "$while",
+};
+
+char*
+amap(char *s)
+{
+	int i, bot, top, new;
+
+	bot = 0;
+	top = bot + nelem(kwd) - 1;
+	while(bot <= top){
+		new = bot + (top - bot)/2;
+		i = strcmp(kwd[new]+1, s);
+		if(i == 0)
+			return kwd[new];
+
+		if(i < 0)
+			bot = new + 1;
+		else
+			top = new - 1;
+	}
+	return s;
+}
+
+Sym*
+acidsue(Type *t)
+{
+	int h;
+	Sym *s;
+
+	if(t != T)
+	for(h=0; h<nelem(hash); h++)
+		for(s = hash[h]; s != S; s = s->link)
+			if(s->suetag && s->suetag->link == t)
+				return s;
+	return 0;
+}
+
+Sym*
+acidfun(Type *t)
+{
+	int h;
+	Sym *s;
+
+	for(h=0; h<nelem(hash); h++)
+		for(s = hash[h]; s != S; s = s->link)
+			if(s->type == t)
+				return s;
+	return 0;
+}
+
+char	acidchar[NTYPE];
+Init	acidcinit[] =
+{
+	{TCHAR,		'C',	0},
+	{TUCHAR,		'b',	0},
+	{TSHORT,		'd',	0},
+	{TUSHORT,	'u',	0},
+	{TLONG,		'D',	0},
+	{TULONG,		'U',	0},
+	{TVLONG,		'V',	0},
+	{TUVLONG,	'W',	0},
+	{TFLOAT,		'f',	0},
+	{TDOUBLE,	'F',	0},
+	{TARRAY,		'a',	0},
+	{TIND,		'X',	0},
+	{-1,		0,	0},
+};
+
+static void
+acidinit(void)
+{
+	Init *p;
+
+	for(p=acidcinit; p->code >= 0; p++)
+		acidchar[p->code] = p->value;
+
+	acidchar[TINT] = acidchar[TLONG];
+	acidchar[TUINT] = acidchar[TULONG];
+	if(types[TINT]->width != types[TLONG]->width) {
+		acidchar[TINT] = acidchar[TSHORT];
+		acidchar[TUINT] = acidchar[TUSHORT];
+		if(types[TINT]->width != types[TSHORT]->width)
+			warn(Z, "acidmember int not long or short");
+	}
+	if(types[TIND]->width == types[TUVLONG]->width)
+		acidchar[TIND] = 'Y';
+	
+}
+
+void
+acidmember(Type *t, long off, int flag)
+{
+	Sym *s, *s1;
+	Type *l;
+	static int acidcharinit = 0;
+
+	if(acidcharinit == 0) {
+		acidinit();
+		acidcharinit = 1;
+	}
+	s = t->sym;
+	switch(t->etype) {
+	default:
+		Bprint(&outbuf, "	T%d\n", t->etype);
+		break;
+
+	case TIND:
+		if(s == S)
+			break;
+		if(flag) {
+			for(l=t; l->etype==TIND; l=l->link)
+				;
+			if(typesu[l->etype]) {
+				s1 = acidsue(l->link);
+				if(s1 != S) {
+					Bprint(&outbuf, "	'A' %s %ld %s;\n",
+						amap(s1->name),
+						t->offset+off, amap(s->name));
+					break;
+				}
+			}
+		} else {
+			Bprint(&outbuf,
+				"\tprint(\"\t%s\t\", addr.%s\\X, \"\\n\");\n",
+				amap(s->name), amap(s->name));
+			break;
+		}
+
+	case TINT:
+	case TUINT:
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TLONG:
+	case TULONG:
+	case TVLONG:
+	case TUVLONG:
+	case TFLOAT:
+	case TDOUBLE:
+	case TARRAY:
+		if(s == S)
+			break;
+		if(flag) {
+			Bprint(&outbuf, "	'%c' %ld %s;\n",
+			acidchar[t->etype], t->offset+off, amap(s->name));
+		} else {
+			Bprint(&outbuf, "\tprint(\"\t%s\t\", addr.%s, \"\\n\");\n",
+				amap(s->name), amap(s->name));
+		}
+		break;
+
+	case TSTRUCT:
+	case TUNION:
+		s1 = acidsue(t->link);
+		if(s1 == S)
+			break;
+		if(flag) {
+			if(s == S) {
+				Bprint(&outbuf, "	{\n");
+				for(l = t->link; l != T; l = l->down)
+					acidmember(l, t->offset+off, flag);
+				Bprint(&outbuf, "	};\n");
+			} else {
+				Bprint(&outbuf, "	%s %ld %s;\n",
+					amap(s1->name),
+					t->offset+off, amap(s->name));
+			}
+		} else {
+			if(s != S) {
+				Bprint(&outbuf, "\tprint(\"%s %s {\\n\");\n",
+					amap(s1->name), amap(s->name));
+				Bprint(&outbuf, "\t%s(addr.%s);\n",
+					amap(s1->name), amap(s->name));
+				Bprint(&outbuf, "\tprint(\"}\\n\");\n");
+			} else {
+				Bprint(&outbuf, "\tprint(\"%s {\\n\");\n",
+					amap(s1->name));
+				Bprint(&outbuf, "\t\t%s(addr+%ld);\n",
+					amap(s1->name), t->offset+off);
+				Bprint(&outbuf, "\tprint(\"}\\n\");\n");
+			}
+		}
+		break;
+	}
+}
+
+void
+acidtype(Type *t)
+{
+	Sym *s;
+	Type *l;
+	Io *i;
+	int n;
+	char *an;
+
+	if(!debug['a'])
+		return;
+	if(debug['a'] > 1) {
+		n = 0;
+		for(i=iostack; i; i=i->link)
+			n++;
+		if(n > 1)
+			return;
+	}
+	s = acidsue(t->link);
+	if(s == S)
+		return;
+	switch(t->etype) {
+	default:
+		Bprint(&outbuf, "T%d\n", t->etype);
+		return;
+
+	case TUNION:
+	case TSTRUCT:
+		if(debug['s'])
+			goto asmstr;
+		an = amap(s->name);
+		Bprint(&outbuf, "sizeof%s = %ld;\n", an, t->width);
+		Bprint(&outbuf, "aggr %s\n{\n", an);
+		for(l = t->link; l != T; l = l->down)
+			acidmember(l, 0, 1);
+		Bprint(&outbuf, "};\n\n");
+
+		Bprint(&outbuf, "defn\n%s(addr) {\n\tcomplex %s addr;\n", an, an);
+		for(l = t->link; l != T; l = l->down)
+			acidmember(l, 0, 0);
+		Bprint(&outbuf, "};\n\n");
+		break;
+	asmstr:
+		if(s == S)
+			break;
+		for(l = t->link; l != T; l = l->down)
+			if(l->sym != S)
+				Bprint(&outbuf, "#define\t%s.%s\t%ld\n",
+					s->name,
+					l->sym->name,
+					l->offset);
+		break;
+	}
+}
+
+void
+acidvar(Sym *s)
+{
+	int n;
+	Io *i;
+	Type *t;
+	Sym *s1, *s2;
+
+	if(!debug['a'] || debug['s'])
+		return;
+	if(debug['a'] > 1) {
+		n = 0;
+		for(i=iostack; i; i=i->link)
+			n++;
+		if(n > 1)
+			return;
+	}
+	t = s->type;
+	while(t && t->etype == TIND)
+		t = t->link;
+	if(t == T)
+		return;
+	if(t->etype == TENUM) {
+		Bprint(&outbuf, "%s = ", amap(s->name));
+		if(!typefd[t->etype])
+			Bprint(&outbuf, "%lld;\n", s->vconst);
+		else
+			Bprint(&outbuf, "%f\n;", s->fconst);
+		return;
+	}
+	if(!typesu[t->etype])
+		return;
+	s1 = acidsue(t->link);
+	if(s1 == S)
+		return;
+	switch(s->class) {
+	case CAUTO:
+	case CPARAM:
+		s2 = acidfun(thisfn);
+		if(s2)
+			Bprint(&outbuf, "complex %s %s:%s;\n",
+				amap(s1->name), amap(s2->name), amap(s->name));
+		break;
+	
+	case CSTATIC:
+	case CEXTERN:
+	case CGLOBL:
+	case CLOCAL:
+		Bprint(&outbuf, "complex %s %s;\n",
+			amap(s1->name), amap(s->name));
+		break;
+	}
+}

+ 98 - 0
sys/src/libcc/bits.c

@@ -0,0 +1,98 @@
+/*
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+
+#include	"cc.h"
+
+Bits
+bor(Bits a, Bits b)
+{
+	Bits c;
+	int i;
+
+	for(i=0; i<BITS; i++)
+		c.b[i] = a.b[i] | b.b[i];
+	return c;
+}
+
+Bits
+band(Bits a, Bits b)
+{
+	Bits c;
+	int i;
+
+	for(i=0; i<BITS; i++)
+		c.b[i] = a.b[i] & b.b[i];
+	return c;
+}
+
+/*
+Bits
+bnot(Bits a)
+{
+	Bits c;
+	int i;
+
+	for(i=0; i<BITS; i++)
+		c.b[i] = ~a.b[i];
+	return c;
+}
+*/
+
+int
+bany(Bits *a)
+{
+	int i;
+
+	for(i=0; i<BITS; i++)
+		if(a->b[i])
+			return 1;
+	return 0;
+}
+
+int
+beq(Bits a, Bits b)
+{
+	int i;
+
+	for(i=0; i<BITS; i++)
+		if(a.b[i] != b.b[i])
+			return 0;
+	return 1;
+}
+
+int
+bnum(Bits a)
+{
+	int i;
+	long b;
+
+	for(i=0; i<BITS; i++)
+		if((b = a.b[i]))
+			return 32*i + bitno(b);
+	diag(Z, "bad in bnum");
+	return 0;
+}
+
+Bits
+blsh(uint n)
+{
+	Bits c;
+
+	c = zbits;
+	c.b[n/32] = 1L << (n%32);
+	return c;
+}
+
+int
+bset(Bits a, uint n)
+{
+	if(a.b[n/32] & (1L << (n%32)))
+		return 1;
+	return 0;
+}

+ 33 - 0
sys/src/libcc/build.json

@@ -0,0 +1,33 @@
+[
+	{
+		"Name": "libcc",
+		"Include": [
+			"../lib.json"
+		],
+		"Install": "/$ARCH/lib/",
+		"Library": "libcc.a",
+		"SourceFiles": [
+				"y.tab.c",
+				"lex.c",
+				"mac.c",
+				"dcl.c",
+				"acid.c",
+				"pickle.c",
+				"bits.c",
+				"com.c",
+				"scon.c",
+				"funct.c",
+				"sub.c",
+				"com64.c",
+				"compat.c",
+				"dpchk.c",
+				"omachcap.c"
+		],
+		"Post": [
+			"rm -f y.tab*"
+		],
+		"Pre": [
+			"yacc -d cc.y"
+		]
+	}
+]

+ 46 - 0
sys/src/libcc/c99

@@ -0,0 +1,46 @@
+C99 changes vs C89
+http://home.tiscalinet.ch/t_wolf/tw/c/c9x_changes.html
+
+(Numbers are from the web page list.)
+
+Not done (yet?):
+9, 19. Hexdecimal floating point constants.
+11. _Complex, _Imaginary, _Bool
+14. Variable arrays in parameter lists.
+33. Variable-length arrays
+34. goto restrictions for variable-length arrows
+18. Notation for universal characters \uXXXX
+25. Division and mod truncate toward zero.
+26. _Bool, float _Complex, double _Complex, long double _Complex
+
+Done:
+7. __VA_ARGS__
+11, 30, 31, 32. restrict, inline
+12. Allow declarations anywhere.
+15. for loop declarations
+28. structs ending in incomplete type.
+
+Unneeded (already had):
+1. Translation limits
+6. Empty macro arguments allowed.
+8. #line limit
+10 // comments
+16. (Type){initializer list} compound literals
+17. Named initializers
+20. LL suffix for long long constants
+21. IEEE 754 floating-point arithmetic
+22. Long long type, 64 bits wide.
+24. Initializers for auto aggregates can be non-constants.
+26. long long int, unsigned long long int, signed long long int
+27. require at least one type specifier in a declaration
+29. Idempotent type qualifiers.
+
+Unwanted:
+2. #pragma stdc
+3. _Pragma
+4. __STDC_VERSION__
+5. __STDC_IEC_559__, __STDC_IEC_559_COMPLEX__,
+	__STDC_ISO_10646__
+13. Digraph tokens
+23. __func__ identifier
+

+ 1183 - 0
sys/src/libcc/cc.y

@@ -0,0 +1,1183 @@
+%{
+#include "cc.h"
+%}
+%union	{
+	Node*	node;
+	Sym*	sym;
+	Type*	type;
+	struct
+	{
+		Type*	t;
+		uchar	c;
+	} tycl;
+	struct
+	{
+		Type*	t1;
+		Type*	t2;
+		Type*	t3;
+		uchar	c;
+	} tyty;
+	struct
+	{
+		char*	s;
+		long	l;
+	} sval;
+	long	lval;
+	double	dval;
+	vlong	vval;
+}
+%type	<sym>	ltag
+%type	<lval>	gctname gcname cname gname tname
+%type	<lval>	gctnlist gcnlist zgnlist
+%type	<type>	tlist sbody complex
+%type	<tycl>	types
+%type	<node>	zarglist arglist zcexpr
+%type	<node>	name block stmnt cexpr expr xuexpr pexpr
+%type	<node>	zelist elist adecl slist uexpr string lstring
+%type	<node>	xdecor xdecor2 labels label ulstmnt
+%type	<node>	adlist edecor tag qual qlist
+%type	<node>	abdecor abdecor1 abdecor2 abdecor3
+%type	<node>	zexpr lexpr init ilist forexpr
+
+%left	';'
+%left	','
+%right	'=' LPE LME LMLE LDVE LMDE LRSHE LLSHE LANDE LXORE LORE
+%right	'?' ':'
+%left	LOROR
+%left	LANDAND
+%left	'|'
+%left	'^'
+%left	'&'
+%left	LEQ LNE
+%left	'<' '>' LLE LGE
+%left	LLSH LRSH
+%left	'+' '-'
+%left	'*' '/' '%'
+%right	LMM LPP LMG '.' '[' '('
+
+%token	<sym>	LNAME LTYPE
+%token	<dval>	LFCONST LDCONST
+%token	<vval>	LCONST LLCONST LUCONST LULCONST LVLCONST LUVLCONST
+%token	<sval>	LSTRING LLSTRING
+%token		LAUTO LBREAK LCASE LCHAR LCONTINUE LDEFAULT LDO
+%token		LDOUBLE LELSE LEXTERN LFLOAT LFOR LGOTO
+%token	LIF LINT LLONG LREGISTER LRETURN LSHORT LSIZEOF LUSED
+%token	LSTATIC LSTRUCT LSWITCH LTYPEDEF LTYPESTR LUNION LUNSIGNED
+%token	LWHILE LVOID LENUM LSIGNED LCONSTNT LVOLATILE LSET LSIGNOF
+%token	LRESTRICT LINLINE
+%%
+prog:
+|	prog xdecl
+
+/*
+ * external declarator
+ */
+xdecl:
+	zctlist ';'
+	{
+		dodecl(xdecl, lastclass, lasttype, Z);
+	}
+|	zctlist xdlist ';'
+|	zctlist xdecor
+	{
+		lastdcl = T;
+		firstarg = S;
+		dodecl(xdecl, lastclass, lasttype, $2);
+		if(lastdcl == T || lastdcl->etype != TFUNC) {
+			diag($2, "not a function");
+			lastdcl = types[TFUNC];
+		}
+		thisfn = lastdcl;
+		markdcl();
+		firstdcl = dclstack;
+		argmark($2, 0);
+	}
+	pdecl
+	{
+		argmark($2, 1);
+	}
+	block
+	{
+		Node *n;
+
+		n = revertdcl();
+		if(n)
+			$6 = new(OLIST, n, $6);
+		if(!debug['a'] && !debug['Z'])
+			codgen($6, $2);
+	}
+
+xdlist:
+	xdecor
+	{
+		dodecl(xdecl, lastclass, lasttype, $1);
+	}
+|	xdecor
+	{
+		$1 = dodecl(xdecl, lastclass, lasttype, $1);
+	}
+	'=' init
+	{
+		doinit($1->sym, $1->type, 0L, $4);
+	}
+|	xdlist ',' xdlist
+
+xdecor:
+	xdecor2
+|	'*' zgnlist xdecor
+	{
+		$$ = new(OIND, $3, Z);
+		$$->garb = simpleg($2);
+	}
+
+xdecor2:
+	tag
+|	'(' xdecor ')'
+	{
+		$$ = $2;
+	}
+|	xdecor2 '(' zarglist ')'
+	{
+		$$ = new(OFUNC, $1, $3);
+	}
+|	xdecor2 '[' zexpr ']'
+	{
+		$$ = new(OARRAY, $1, $3);
+	}
+
+/*
+ * automatic declarator
+ */
+adecl:
+	ctlist ';'
+	{
+		$$ = dodecl(adecl, lastclass, lasttype, Z);
+	}
+|	ctlist adlist ';'
+	{
+		$$ = $2;
+	}
+
+adlist:
+	xdecor
+	{
+		dodecl(adecl, lastclass, lasttype, $1);
+		$$ = Z;
+	}
+|	xdecor
+	{
+		$1 = dodecl(adecl, lastclass, lasttype, $1);
+	}
+	'=' init
+	{
+		long w;
+
+		w = $1->sym->type->width;
+		$$ = doinit($1->sym, $1->type, 0L, $4);
+		$$ = contig($1->sym, $$, w);
+	}
+|	adlist ',' adlist
+	{
+		$$ = $1;
+		if($3 != Z) {
+			$$ = $3;
+			if($1 != Z)
+				$$ = new(OLIST, $1, $3);
+		}
+	}
+
+/*
+ * parameter declarator
+ */
+pdecl:
+|	pdecl ctlist pdlist ';'
+
+pdlist:
+	xdecor
+	{
+		dodecl(pdecl, lastclass, lasttype, $1);
+	}
+|	pdlist ',' pdlist
+
+/*
+ * structure element declarator
+ */
+edecl:
+	tlist
+	{
+		lasttype = $1;
+	}
+	zedlist ';'
+|	edecl tlist
+	{
+		lasttype = $2;
+	}
+	zedlist ';'
+
+zedlist:					/* extension */
+	{
+		lastfield = 0;
+		edecl(CXXX, lasttype, S);
+	}
+|	edlist
+
+edlist:
+	edecor
+	{
+		dodecl(edecl, CXXX, lasttype, $1);
+	}
+|	edlist ',' edlist
+
+edecor:
+	xdecor
+	{
+		lastbit = 0;
+		firstbit = 1;
+	}
+|	tag ':' lexpr
+	{
+		$$ = new(OBIT, $1, $3);
+	}
+|	':' lexpr
+	{
+		$$ = new(OBIT, Z, $2);
+	}
+
+/*
+ * abstract declarator
+ */
+abdecor:
+	{
+		$$ = (Z);
+	}
+|	abdecor1
+
+abdecor1:
+	'*' zgnlist
+	{
+		$$ = new(OIND, (Z), Z);
+		$$->garb = simpleg($2);
+	}
+|	'*' zgnlist abdecor1
+	{
+		$$ = new(OIND, $3, Z);
+		$$->garb = simpleg($2);
+	}
+|	abdecor2
+
+abdecor2:
+	abdecor3
+|	abdecor2 '(' zarglist ')'
+	{
+		$$ = new(OFUNC, $1, $3);
+	}
+|	abdecor2 '[' zexpr ']'
+	{
+		$$ = new(OARRAY, $1, $3);
+	}
+
+abdecor3:
+	'(' ')'
+	{
+		$$ = new(OFUNC, (Z), Z);
+	}
+|	'[' zexpr ']'
+	{
+		$$ = new(OARRAY, (Z), $2);
+	}
+|	'(' abdecor1 ')'
+	{
+		$$ = $2;
+	}
+
+init:
+	expr
+|	'{' ilist '}'
+	{
+		$$ = new(OINIT, invert($2), Z);
+	}
+
+qual:
+	'[' lexpr ']'
+	{
+		$$ = new(OARRAY, $2, Z);
+	}
+|	'.' ltag
+	{
+		$$ = new(OELEM, Z, Z);
+		$$->sym = $2;
+	}
+|	qual '='
+
+qlist:
+	init ','
+|	qlist init ','
+	{
+		$$ = new(OLIST, $1, $2);
+	}
+|	qual
+|	qlist qual
+	{
+		$$ = new(OLIST, $1, $2);
+	}
+
+ilist:
+	qlist
+|	init
+|	qlist init
+	{
+		$$ = new(OLIST, $1, $2);
+	}
+
+zarglist:
+	{
+		$$ = Z;
+	}
+|	arglist
+	{
+		$$ = invert($1);
+	}
+
+
+arglist:
+	name
+|	tlist abdecor
+	{
+		$$ = new(OPROTO, $2, Z);
+		$$->type = $1;
+	}
+|	tlist xdecor
+	{
+		$$ = new(OPROTO, $2, Z);
+		$$->type = $1;
+	}
+|	'.' '.' '.'
+	{
+		$$ = new(ODOTDOT, Z, Z);
+	}
+|	arglist ',' arglist
+	{
+		$$ = new(OLIST, $1, $3);
+	}
+
+block:
+	'{' slist '}'
+	{
+		$$ = invert($2);
+	//	if($2 != Z)
+	//		$$ = new(OLIST, $2, $$);
+		if($$ == Z)
+			$$ = new(OLIST, Z, Z);
+	}
+
+slist:
+	{
+		$$ = Z;
+	}
+|	slist adecl
+	{
+		$$ = new(OLIST, $1, $2);
+	}
+|	slist stmnt
+	{
+		$$ = new(OLIST, $1, $2);
+	}
+
+labels:
+	label
+|	labels label
+	{
+		$$ = new(OLIST, $1, $2);
+	}
+
+label:
+	LCASE expr ':'
+	{
+		$$ = new(OCASE, $2, Z);
+	}
+|	LDEFAULT ':'
+	{
+		$$ = new(OCASE, Z, Z);
+	}
+|	LNAME ':'
+	{
+		$$ = new(OLABEL, dcllabel($1, 1), Z);
+	}
+
+stmnt:
+	error ';'
+	{
+		$$ = Z;
+	}
+|	ulstmnt
+|	labels ulstmnt
+	{
+		$$ = new(OLIST, $1, $2);
+	}
+
+forexpr:
+	zcexpr
+|	ctlist adlist
+	{
+		$$ = $2;
+	}
+
+ulstmnt:
+	zcexpr ';'
+|	{
+		markdcl();
+	}
+	block
+	{
+		$$ = revertdcl();
+		if($$)
+			$$ = new(OLIST, $$, $2);
+		else
+			$$ = $2;
+	}
+|	LIF '(' cexpr ')' stmnt
+	{
+		$$ = new(OIF, $3, new(OLIST, $5, Z));
+		if($5 == Z)
+			warn($3, "empty if body");
+	}
+|	LIF '(' cexpr ')' stmnt LELSE stmnt
+	{
+		$$ = new(OIF, $3, new(OLIST, $5, $7));
+		if($5 == Z)
+			warn($3, "empty if body");
+		if($7 == Z)
+			warn($3, "empty else body");
+	}
+|	{ markdcl(); } LFOR '(' forexpr ';' zcexpr ';' zcexpr ')' stmnt
+	{
+		$$ = revertdcl();
+		if($$){
+			if($4)
+				$4 = new(OLIST, $$, $4);
+			else
+				$4 = $$;
+		}
+		$$ = new(OFOR, new(OLIST, $6, new(OLIST, $4, $8)), $10);
+	}
+|	LWHILE '(' cexpr ')' stmnt
+	{
+		$$ = new(OWHILE, $3, $5);
+	}
+|	LDO stmnt LWHILE '(' cexpr ')' ';'
+	{
+		$$ = new(ODWHILE, $5, $2);
+	}
+|	LRETURN zcexpr ';'
+	{
+		$$ = new(ORETURN, $2, Z);
+		$$->type = thisfn->link;
+	}
+|	LSWITCH '(' cexpr ')' stmnt
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->vconst = 0;
+		$$->type = types[TINT];
+		$3 = new(OSUB, $$, $3);
+
+		$$ = new(OCONST, Z, Z);
+		$$->vconst = 0;
+		$$->type = types[TINT];
+		$3 = new(OSUB, $$, $3);
+
+		$$ = new(OSWITCH, $3, $5);
+	}
+|	LBREAK ';'
+	{
+		$$ = new(OBREAK, Z, Z);
+	}
+|	LCONTINUE ';'
+	{
+		$$ = new(OCONTINUE, Z, Z);
+	}
+|	LGOTO ltag ';'
+	{
+		$$ = new(OGOTO, dcllabel($2, 0), Z);
+	}
+|	LUSED '(' zelist ')' ';'
+	{
+		$$ = new(OUSED, $3, Z);
+	}
+|	LSET '(' zelist ')' ';'
+	{
+		$$ = new(OSET, $3, Z);
+	}
+
+zcexpr:
+	{
+		$$ = Z;
+	}
+|	cexpr
+
+zexpr:
+	{
+		$$ = Z;
+	}
+|	lexpr
+
+lexpr:
+	expr
+	{
+		$$ = new(OCAST, $1, Z);
+		$$->type = types[TLONG];
+	}
+
+cexpr:
+	expr
+|	cexpr ',' cexpr
+	{
+		$$ = new(OCOMMA, $1, $3);
+	}
+
+expr:
+	xuexpr
+|	expr '*' expr
+	{
+		$$ = new(OMUL, $1, $3);
+	}
+|	expr '/' expr
+	{
+		$$ = new(ODIV, $1, $3);
+	}
+|	expr '%' expr
+	{
+		$$ = new(OMOD, $1, $3);
+	}
+|	expr '+' expr
+	{
+		$$ = new(OADD, $1, $3);
+	}
+|	expr '-' expr
+	{
+		$$ = new(OSUB, $1, $3);
+	}
+|	expr LRSH expr
+	{
+		$$ = new(OASHR, $1, $3);
+	}
+|	expr LLSH expr
+	{
+		$$ = new(OASHL, $1, $3);
+	}
+|	expr '<' expr
+	{
+		$$ = new(OLT, $1, $3);
+	}
+|	expr '>' expr
+	{
+		$$ = new(OGT, $1, $3);
+	}
+|	expr LLE expr
+	{
+		$$ = new(OLE, $1, $3);
+	}
+|	expr LGE expr
+	{
+		$$ = new(OGE, $1, $3);
+	}
+|	expr LEQ expr
+	{
+		$$ = new(OEQ, $1, $3);
+	}
+|	expr LNE expr
+	{
+		$$ = new(ONE, $1, $3);
+	}
+|	expr '&' expr
+	{
+		$$ = new(OAND, $1, $3);
+	}
+|	expr '^' expr
+	{
+		$$ = new(OXOR, $1, $3);
+	}
+|	expr '|' expr
+	{
+		$$ = new(OOR, $1, $3);
+	}
+|	expr LANDAND expr
+	{
+		$$ = new(OANDAND, $1, $3);
+	}
+|	expr LOROR expr
+	{
+		$$ = new(OOROR, $1, $3);
+	}
+|	expr '?' cexpr ':' expr
+	{
+		$$ = new(OCOND, $1, new(OLIST, $3, $5));
+	}
+|	expr '=' expr
+	{
+		$$ = new(OAS, $1, $3);
+	}
+|	expr LPE expr
+	{
+		$$ = new(OASADD, $1, $3);
+	}
+|	expr LME expr
+	{
+		$$ = new(OASSUB, $1, $3);
+	}
+|	expr LMLE expr
+	{
+		$$ = new(OASMUL, $1, $3);
+	}
+|	expr LDVE expr
+	{
+		$$ = new(OASDIV, $1, $3);
+	}
+|	expr LMDE expr
+	{
+		$$ = new(OASMOD, $1, $3);
+	}
+|	expr LLSHE expr
+	{
+		$$ = new(OASASHL, $1, $3);
+	}
+|	expr LRSHE expr
+	{
+		$$ = new(OASASHR, $1, $3);
+	}
+|	expr LANDE expr
+	{
+		$$ = new(OASAND, $1, $3);
+	}
+|	expr LXORE expr
+	{
+		$$ = new(OASXOR, $1, $3);
+	}
+|	expr LORE expr
+	{
+		$$ = new(OASOR, $1, $3);
+	}
+
+xuexpr:
+	uexpr
+|	'(' tlist abdecor ')' xuexpr
+	{
+		$$ = new(OCAST, $5, Z);
+		dodecl(NODECL, CXXX, $2, $3);
+		$$->type = lastdcl;
+		$$->xcast = 1;
+	}
+|	'(' tlist abdecor ')' '{' ilist '}'	/* extension */
+	{
+		$$ = new(OSTRUCT, $6, Z);
+		dodecl(NODECL, CXXX, $2, $3);
+		$$->type = lastdcl;
+	}
+
+uexpr:
+	pexpr
+|	'*' xuexpr
+	{
+		$$ = new(OIND, $2, Z);
+	}
+|	'&' xuexpr
+	{
+		$$ = new(OADDR, $2, Z);
+	}
+|	'+' xuexpr
+	{
+		$$ = new(OPOS, $2, Z);
+	}
+|	'-' xuexpr
+	{
+		$$ = new(ONEG, $2, Z);
+	}
+|	'!' xuexpr
+	{
+		$$ = new(ONOT, $2, Z);
+	}
+|	'~' xuexpr
+	{
+		$$ = new(OCOM, $2, Z);
+	}
+|	LPP xuexpr
+	{
+		$$ = new(OPREINC, $2, Z);
+	}
+|	LMM xuexpr
+	{
+		$$ = new(OPREDEC, $2, Z);
+	}
+|	LSIZEOF uexpr
+	{
+		$$ = new(OSIZE, $2, Z);
+	}
+|	LSIGNOF uexpr
+	{
+		$$ = new(OSIGN, $2, Z);
+	}
+
+pexpr:
+	'(' cexpr ')'
+	{
+		$$ = $2;
+	}
+|	LSIZEOF '(' tlist abdecor ')'
+	{
+		$$ = new(OSIZE, Z, Z);
+		dodecl(NODECL, CXXX, $3, $4);
+		$$->type = lastdcl;
+	}
+|	LSIGNOF '(' tlist abdecor ')'
+	{
+		$$ = new(OSIGN, Z, Z);
+		dodecl(NODECL, CXXX, $3, $4);
+		$$->type = lastdcl;
+	}
+|	pexpr '(' zelist ')'
+	{
+		$$ = new(OFUNC, $1, Z);
+		if($1->op == ONAME)
+		if($1->type == T)
+			dodecl(xdecl, CXXX, types[TINT], $$);
+		$$->right = invert($3);
+	}
+|	pexpr '[' cexpr ']'
+	{
+		$$ = new(OIND, new(OADD, $1, $3), Z);
+	}
+|	pexpr LMG ltag
+	{
+		$$ = new(ODOT, new(OIND, $1, Z), Z);
+		$$->sym = $3;
+	}
+|	pexpr '.' ltag
+	{
+		$$ = new(ODOT, $1, Z);
+		$$->sym = $3;
+	}
+|	pexpr LPP
+	{
+		$$ = new(OPOSTINC, $1, Z);
+	}
+|	pexpr LMM
+	{
+		$$ = new(OPOSTDEC, $1, Z);
+	}
+|	name
+|	LCONST
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TINT];
+		$$->vconst = $1;
+		$$->cstring = strdup(symb);
+	}
+|	LLCONST
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TLONG];
+		$$->vconst = $1;
+		$$->cstring = strdup(symb);
+	}
+|	LUCONST
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TUINT];
+		$$->vconst = $1;
+		$$->cstring = strdup(symb);
+	}
+|	LULCONST
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TULONG];
+		$$->vconst = $1;
+		$$->cstring = strdup(symb);
+	}
+|	LDCONST
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TDOUBLE];
+		$$->fconst = $1;
+		$$->cstring = strdup(symb);
+	}
+|	LFCONST
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TFLOAT];
+		$$->fconst = $1;
+		$$->cstring = strdup(symb);
+	}
+|	LVLCONST
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TVLONG];
+		$$->vconst = $1;
+		$$->cstring = strdup(symb);
+	}
+|	LUVLCONST
+	{
+		$$ = new(OCONST, Z, Z);
+		$$->type = types[TUVLONG];
+		$$->vconst = $1;
+		$$->cstring = strdup(symb);
+	}
+|	string
+|	lstring
+
+string:
+	LSTRING
+	{
+		$$ = new(OSTRING, Z, Z);
+		$$->type = typ(TARRAY, types[TCHAR]);
+		$$->type->width = $1.l + 1;
+		$$->cstring = $1.s;
+		$$->sym = symstring;
+		$$->etype = TARRAY;
+		$$->class = CSTATIC;
+	}
+|	string LSTRING
+	{
+		char *s;
+		int n;
+
+		n = $1->type->width - 1;
+		s = alloc(n+$2.l+MAXALIGN);
+
+		memcpy(s, $1->cstring, n);
+		memcpy(s+n, $2.s, $2.l);
+		s[n+$2.l] = 0;
+
+		$$ = $1;
+		$$->type->width += $2.l;
+		$$->cstring = s;
+	}
+
+lstring:
+	LLSTRING
+	{
+		$$ = new(OLSTRING, Z, Z);
+		$$->type = typ(TARRAY, types[TRUNE]);
+		$$->type->width = $1.l + sizeof(TRune);
+		$$->rstring = (TRune*)$1.s;
+		$$->sym = symstring;
+		$$->etype = TARRAY;
+		$$->class = CSTATIC;
+	}
+|	lstring LLSTRING
+	{
+		char *s;
+		int n;
+
+		n = $1->type->width - sizeof(TRune);
+		s = alloc(n+$2.l+MAXALIGN);
+
+		memcpy(s, $1->rstring, n);
+		memcpy(s+n, $2.s, $2.l);
+		*(TRune*)(s+n+$2.l) = 0;
+
+		$$ = $1;
+		$$->type->width += $2.l;
+		$$->rstring = (TRune*)s;
+	}
+
+zelist:
+	{
+		$$ = Z;
+	}
+|	elist
+
+elist:
+	expr
+|	elist ',' elist
+	{
+		$$ = new(OLIST, $1, $3);
+	}
+
+sbody:
+	'{'
+	{
+		$<tyty>$.t1 = strf;
+		$<tyty>$.t2 = strl;
+		$<tyty>$.t3 = lasttype;
+		$<tyty>$.c = lastclass;
+		strf = T;
+		strl = T;
+		lastbit = 0;
+		firstbit = 1;
+		lastclass = CXXX;
+		lasttype = T;
+	}
+	edecl '}'
+	{
+		$$ = strf;
+		strf = $<tyty>2.t1;
+		strl = $<tyty>2.t2;
+		lasttype = $<tyty>2.t3;
+		lastclass = $<tyty>2.c;
+	}
+
+zctlist:
+	{
+		lastclass = CXXX;
+		lasttype = types[TINT];
+	}
+|	ctlist
+
+types:
+	complex
+	{
+		$$.t = $1;
+		$$.c = CXXX;
+	}
+|	tname
+	{
+		$$.t = simplet($1);
+		$$.c = CXXX;
+	}
+|	gcnlist
+	{
+		$$.t = simplet($1);
+		$$.c = simplec($1);
+		$$.t = garbt($$.t, $1);
+	}
+|	complex gctnlist
+	{
+		$$.t = $1;
+		$$.c = simplec($2);
+		$$.t = garbt($$.t, $2);
+		if($2 & ~BCLASS & ~BGARB)
+			diag(Z, "duplicate types given: %T and %Q", $1, $2);
+	}
+|	tname gctnlist
+	{
+		$$.t = simplet(typebitor($1, $2));
+		$$.c = simplec($2);
+		$$.t = garbt($$.t, $2);
+	}
+|	gcnlist complex zgnlist
+	{
+		$$.t = $2;
+		$$.c = simplec($1);
+		$$.t = garbt($$.t, $1|$3);
+	}
+|	gcnlist tname
+	{
+		$$.t = simplet($2);
+		$$.c = simplec($1);
+		$$.t = garbt($$.t, $1);
+	}
+|	gcnlist tname gctnlist
+	{
+		$$.t = simplet(typebitor($2, $3));
+		$$.c = simplec($1|$3);
+		$$.t = garbt($$.t, $1|$3);
+	}
+
+tlist:
+	types
+	{
+		$$ = $1.t;
+		if($1.c != CXXX)
+			diag(Z, "illegal combination of class 4: %s", cnames[$1.c]);
+	}
+
+ctlist:
+	types
+	{
+		lasttype = $1.t;
+		lastclass = $1.c;
+	}
+
+complex:
+	LSTRUCT ltag
+	{
+		dotag($2, TSTRUCT, 0);
+		$$ = $2->suetag;
+	}
+|	LSTRUCT ltag
+	{
+		dotag($2, TSTRUCT, autobn);
+	}
+	sbody
+	{
+		$$ = $2->suetag;
+		if($$->link != T)
+			diag(Z, "redeclare tag: %s", $2->name);
+		$$->link = $4;
+		sualign($$);
+	}
+|	LSTRUCT sbody
+	{
+		taggen++;
+		sprint(symb, "_%d_", taggen);
+		$$ = dotag(lookup(), TSTRUCT, autobn);
+		$$->link = $2;
+		sualign($$);
+	}
+|	LUNION ltag
+	{
+		dotag($2, TUNION, 0);
+		$$ = $2->suetag;
+	}
+|	LUNION ltag
+	{
+		dotag($2, TUNION, autobn);
+	}
+	sbody
+	{
+		$$ = $2->suetag;
+		if($$->link != T)
+			diag(Z, "redeclare tag: %s", $2->name);
+		$$->link = $4;
+		sualign($$);
+	}
+|	LUNION sbody
+	{
+		taggen++;
+		sprint(symb, "_%d_", taggen);
+		$$ = dotag(lookup(), TUNION, autobn);
+		$$->link = $2;
+		sualign($$);
+	}
+|	LENUM ltag
+	{
+		dotag($2, TENUM, 0);
+		$$ = $2->suetag;
+		if($$->link == T)
+			$$->link = types[TINT];
+		$$ = $$->link;
+	}
+|	LENUM ltag
+	{
+		dotag($2, TENUM, autobn);
+	}
+	'{'
+	{
+		en.tenum = T;
+		en.cenum = T;
+	}
+	enum '}'
+	{
+		$$ = $2->suetag;
+		if($$->link != T)
+			diag(Z, "redeclare tag: %s", $2->name);
+		if(en.tenum == T) {
+			diag(Z, "enum type ambiguous: %s", $2->name);
+			en.tenum = types[TINT];
+		}
+		$$->link = en.tenum;
+		$$ = en.tenum;
+	}
+|	LENUM '{'
+	{
+		en.tenum = T;
+		en.cenum = T;
+	}
+	enum '}'
+	{
+		$$ = en.tenum;
+	}
+|	LTYPE
+	{
+		$$ = tcopy($1->type);
+	}
+
+gctnlist:
+	gctname
+|	gctnlist gctname
+	{
+		$$ = typebitor($1, $2);
+	}
+
+zgnlist:
+	{
+		$$ = 0;
+	}
+|	zgnlist gname
+	{
+		$$ = typebitor($1, $2);
+	}
+
+gctname:
+	tname
+|	gname
+|	cname
+
+gcnlist:
+	gcname
+|	gcnlist gcname
+	{
+		$$ = typebitor($1, $2);
+	}
+
+gcname:
+	gname
+|	cname
+
+enum:
+	LNAME
+	{
+		doenum($1, Z);
+	}
+|	LNAME '=' expr
+	{
+		doenum($1, $3);
+	}
+|	enum ','
+|	enum ',' enum
+
+tname:	/* type words */
+	LCHAR { $$ = BCHAR; }
+|	LSHORT { $$ = BSHORT; }
+|	LINT { $$ = BINT; }
+|	LLONG { $$ = BLONG; }
+|	LSIGNED { $$ = BSIGNED; }
+|	LUNSIGNED { $$ = BUNSIGNED; }
+|	LFLOAT { $$ = BFLOAT; }
+|	LDOUBLE { $$ = BDOUBLE; }
+|	LVOID { $$ = BVOID; }
+
+cname:	/* class words */
+	LAUTO { $$ = BAUTO; }
+|	LSTATIC { $$ = BSTATIC; }
+|	LEXTERN { $$ = BEXTERN; }
+|	LTYPEDEF { $$ = BTYPEDEF; }
+|	LTYPESTR { $$ = BTYPESTR; }
+|	LREGISTER { $$ = BREGISTER; }
+|	LINLINE { $$ = 0; }
+
+gname:	/* garbage words */
+	LCONSTNT { $$ = BCONSTNT; }
+|	LVOLATILE { $$ = BVOLATILE; }
+|	LRESTRICT { $$ = 0; }
+
+name:
+	LNAME
+	{
+		$$ = new(ONAME, Z, Z);
+		if($1->class == CLOCAL)
+			$1 = mkstatic($1);
+		$$->sym = $1;
+		$$->type = $1->type;
+		$$->etype = TVOID;
+		if($$->type != T)
+			$$->etype = $$->type->etype;
+		$$->xoffset = $1->offset;
+		$$->class = $1->class;
+		$1->aused = 1;
+	}
+tag:
+	ltag
+	{
+		$$ = new(ONAME, Z, Z);
+		$$->sym = $1;
+		$$->type = $1->type;
+		$$->etype = TVOID;
+		if($$->type != T)
+			$$->etype = $$->type->etype;
+		$$->xoffset = $1->offset;
+		$$->class = $1->class;
+	}
+ltag:
+	LNAME
+|	LTYPE
+%%

+ 1472 - 0
sys/src/libcc/com.c

@@ -0,0 +1,1472 @@
+/*
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+
+#include "cc.h"
+
+typedef struct Com Com;
+struct Com
+{
+	int	n;
+	Node	*t[500];
+};
+
+int compar(Node*, int);
+static void comma(Node*);
+static Node*	commas(Com*, Node*);
+
+void
+complex(Node *n)
+{
+
+	if(n == Z)
+		return;
+
+	nearln = n->lineno;
+	if(debug['t'])
+		if(n->op != OCONST)
+			prtree(n, "pre complex");
+	if(tcom(n))
+		return;
+	if(debug['y'] || 1)
+		comma(n);
+	if(debug['t'])
+		if(n->op != OCONST)
+			prtree(n, "t complex");
+	ccom(n);
+	if(debug['t'])
+		if(n->op != OCONST)
+			prtree(n, "c complex");
+	acom(n);
+	if(debug['t'])
+		if(n->op != OCONST)
+			prtree(n, "a complex");
+	xcom(n);
+	if(debug['t'])
+		if(n->op != OCONST)
+			prtree(n, "x complex");
+}
+
+/*
+ * evaluate types
+ * evaluate lvalues (addable == 1)
+ */
+enum
+{
+	ADDROF	= 1<<0,
+	CASTOF	= 1<<1,
+	ADDROP	= 1<<2,
+};
+
+int
+tcom(Node *n)
+{
+
+	return tcomo(n, ADDROF);
+}
+
+int
+tcomo(Node *n, int f)
+{
+	Node *l, *r;
+	Type *t;
+	int o;
+	static TRune zer;
+
+	if(n == Z) {
+		diag(Z, "Z in tcom");
+		errorexit();
+	}
+	n->addable = 0;
+	l = n->left;
+	r = n->right;
+
+	switch(n->op) {
+	default:
+		diag(n, "unknown op in type complex: %O", n->op);
+		goto bad;
+
+	case ODOTDOT:
+		/*
+		 * tcom has already been called on this subtree
+		 */
+		*n = *n->left;
+		if(n->type == T)
+			goto bad;
+		break;
+
+	case OCAST:
+		if(n->type == T)
+			break;
+		if(n->type->width == types[TLONG]->width) {
+			if(tcomo(l, ADDROF|CASTOF))
+				goto bad;
+		} else
+			if(tcom(l))
+				goto bad;
+		if(isfunct(n))
+			break;
+		if(tcompat(n, l->type, n->type, tcast))
+			goto bad;
+		break;
+
+	case ORETURN:
+		if(l == Z) {
+			if(n->type->etype != TVOID)
+				warn(n, "null return of a typed function");
+			break;
+		}
+		if(tcom(l))
+			goto bad;
+		typeext(n->type, l);
+		if(tcompat(n, n->type, l->type, tasign))
+			break;
+		constas(n, n->type, l->type);
+		if(!sametype(n->type, l->type)) {
+			l = new1(OCAST, l, Z);
+			l->type = n->type;
+			n->left = l;
+		}
+		break;
+
+	case OASI:	/* same as as, but no test for const */
+		n->op = OAS;
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+
+		typeext(l->type, r);
+		if(tlvalue(l) || tcompat(n, l->type, r->type, tasign))
+			goto bad;
+		if(!sametype(l->type, r->type)) {
+			r = new1(OCAST, r, Z);
+			r->type = l->type;
+			n->right = r;
+		}
+		n->type = l->type;
+		break;
+
+	case OAS:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(tlvalue(l))
+			goto bad;
+		if(isfunct(n))
+			break;
+		typeext(l->type, r);
+		if(tcompat(n, l->type, r->type, tasign))
+			goto bad;
+		constas(n, l->type, r->type);
+		if(!sametype(l->type, r->type)) {
+			r = new1(OCAST, r, Z);
+			r->type = l->type;
+			n->right = r;
+		}
+		n->type = l->type;
+		break;
+
+	case OASADD:
+	case OASSUB:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(tlvalue(l))
+			goto bad;
+		if(isfunct(n))
+			break;
+		typeext1(l->type, r);
+		if(tcompat(n, l->type, r->type, tasadd))
+			goto bad;
+		constas(n, l->type, r->type);
+		t = l->type;
+		arith(n, 0);
+		while(n->left->op == OCAST)
+			n->left = n->left->left;
+		if(!sametype(t, n->type) && !mixedasop(t, n->type)) {
+			r = new1(OCAST, n->right, Z);
+			r->type = t;
+			n->right = r;
+			n->type = t;
+		}
+		break;
+
+	case OASMUL:
+	case OASLMUL:
+	case OASDIV:
+	case OASLDIV:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(tlvalue(l))
+			goto bad;
+		if(isfunct(n))
+			break;
+		typeext1(l->type, r);
+		if(tcompat(n, l->type, r->type, tmul))
+			goto bad;
+		constas(n, l->type, r->type);
+		t = l->type;
+		arith(n, 0);
+		while(n->left->op == OCAST)
+			n->left = n->left->left;
+		if(!sametype(t, n->type) && !mixedasop(t, n->type)) {
+			r = new1(OCAST, n->right, Z);
+			r->type = t;
+			n->right = r;
+			n->type = t;
+		}
+		if(typeu[n->type->etype]) {
+			if(n->op == OASDIV)
+				n->op = OASLDIV;
+			if(n->op == OASMUL)
+				n->op = OASLMUL;
+		}
+		break;
+
+	case OASLSHR:
+	case OASASHR:
+	case OASASHL:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(tlvalue(l))
+			goto bad;
+		if(isfunct(n))
+			break;
+		if(tcompat(n, l->type, r->type, tand))
+			goto bad;
+		n->type = l->type;
+		n->right = new1(OCAST, r, Z);
+		n->right->type = types[TINT];
+		if(typeu[n->type->etype]) {
+			if(n->op == OASASHR)
+				n->op = OASLSHR;
+		}
+		break;
+
+	case OASMOD:
+	case OASLMOD:
+	case OASOR:
+	case OASAND:
+	case OASXOR:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(tlvalue(l))
+			goto bad;
+		if(isfunct(n))
+			break;
+		if(tcompat(n, l->type, r->type, tand))
+			goto bad;
+		t = l->type;
+		arith(n, 0);
+		while(n->left->op == OCAST)
+			n->left = n->left->left;
+		if(!sametype(t, n->type) && !mixedasop(t, n->type)) {
+			r = new1(OCAST, n->right, Z);
+			r->type = t;
+			n->right = r;
+			n->type = t;
+		}
+		if(typeu[n->type->etype]) {
+			if(n->op == OASMOD)
+				n->op = OASLMOD;
+		}
+		break;
+
+	case OPREINC:
+	case OPREDEC:
+	case OPOSTINC:
+	case OPOSTDEC:
+		if(tcom(l))
+			goto bad;
+		if(tlvalue(l))
+			goto bad;
+		if(isfunct(n))
+			break;
+		if(tcompat(n, l->type, types[TINT], tadd))
+			goto bad;
+		n->type = l->type;
+		if(n->type->etype == TIND)
+		if(n->type->link->width < 1) {
+			snap(n->type->link);
+			if(n->type->link->width < 1)
+				diag(n, "inc/dec of a void pointer");
+		}
+		break;
+
+	case OEQ:
+	case ONE:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(isfunct(n))
+			break;
+		typeext(l->type, r);
+		typeext(r->type, l);
+		if(tcompat(n, l->type, r->type, trel))
+			goto bad;
+		arith(n, 0);
+		n->type = types[TINT];
+		break;
+
+	case OLT:
+	case OGE:
+	case OGT:
+	case OLE:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(isfunct(n))
+			break;
+		typeext1(l->type, r);
+		typeext1(r->type, l);
+		if(tcompat(n, l->type, r->type, trel))
+			goto bad;
+		arith(n, 0);
+		if(typeu[n->type->etype])
+			n->op = logrel[relindex(n->op)];
+		n->type = types[TINT];
+		break;
+
+	case OCOND:
+		o = tcom(l);
+		o |= tcom(r->left);
+		if(o | tcom(r->right))
+			goto bad;
+		if(r->right->type->etype == TIND && vconst(r->left) == 0) {
+			r->left->type = r->right->type;
+			r->left->vconst = 0;
+		}
+		if(r->left->type->etype == TIND && vconst(r->right) == 0) {
+			r->right->type = r->left->type;
+			r->right->vconst = 0;
+		}
+		if(sametype(r->right->type, r->left->type)) {
+			r->type = r->right->type;
+			n->type = r->type;
+			break;
+		}
+		if(tcompat(r, r->left->type, r->right->type, trel))
+			goto bad;
+		arith(r, 0);
+		n->type = r->type;
+		break;
+
+	case OADD:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(isfunct(n))
+			break;
+		if(tcompat(n, l->type, r->type, tadd))
+			goto bad;
+		arith(n, 1);
+		break;
+
+	case OSUB:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(isfunct(n))
+			break;
+		if(tcompat(n, l->type, r->type, tsub))
+			goto bad;
+		arith(n, 1);
+		break;
+
+	case OMUL:
+	case OLMUL:
+	case ODIV:
+	case OLDIV:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(isfunct(n))
+			break;
+		if(tcompat(n, l->type, r->type, tmul))
+			goto bad;
+		arith(n, 1);
+		if(typeu[n->type->etype]) {
+			if(n->op == ODIV)
+				n->op = OLDIV;
+			if(n->op == OMUL)
+				n->op = OLMUL;
+		}
+		break;
+
+	case OLSHR:
+	case OASHL:
+	case OASHR:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(isfunct(n))
+			break;
+		if(tcompat(n, l->type, r->type, tand))
+			goto bad;
+		n->right = Z;
+		arith(n, 1);
+		n->right = new1(OCAST, r, Z);
+		n->right->type = types[TINT];
+		if(typeu[n->type->etype])
+			if(n->op == OASHR)
+				n->op = OLSHR;
+		break;
+
+	case OAND:
+	case OOR:
+	case OXOR:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(isfunct(n))
+			break;
+		if(tcompat(n, l->type, r->type, tand))
+			goto bad;
+		arith(n, 1);
+		break;
+
+	case OMOD:
+	case OLMOD:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(isfunct(n))
+			break;
+		if(tcompat(n, l->type, r->type, tand))
+			goto bad;
+		arith(n, 1);
+		if(typeu[n->type->etype])
+			n->op = OLMOD;
+		break;
+
+	case OPOS:
+		if(tcom(l))
+			goto bad;
+		if(isfunct(n))
+			break;
+
+		r = l;
+		l = new(OCONST, Z, Z);
+		l->vconst = 0;
+		l->type = types[TINT];
+		n->op = OADD;
+		n->right = r;
+		n->left = l;
+
+		if(tcom(l))
+			goto bad;
+		if(tcompat(n, l->type, r->type, tsub))
+			goto bad;
+		arith(n, 1);
+		break;
+
+	case ONEG:
+		if(tcom(l))
+			goto bad;
+		if(isfunct(n))
+			break;
+
+		if(!machcap(n)) {
+			r = l;
+			l = new(OCONST, Z, Z);
+			l->vconst = 0;
+			l->type = types[TINT];
+			n->op = OSUB;
+			n->right = r;
+			n->left = l;
+
+			if(tcom(l))
+				goto bad;
+			if(tcompat(n, l->type, r->type, tsub))
+				goto bad;
+		}
+		arith(n, 1);
+		break;
+
+	case OCOM:
+		if(tcom(l))
+			goto bad;
+		if(isfunct(n))
+			break;
+
+		if(!machcap(n)) {
+			r = l;
+			l = new(OCONST, Z, Z);
+			l->vconst = -1;
+			l->type = types[TINT];
+			n->op = OXOR;
+			n->right = r;
+			n->left = l;
+
+			if(tcom(l))
+				goto bad;
+			if(tcompat(n, l->type, r->type, tand))
+				goto bad;
+		}
+		arith(n, 1);
+		break;
+
+	case ONOT:
+		if(tcom(l))
+			goto bad;
+		if(isfunct(n))
+			break;
+		if(tcompat(n, T, l->type, tnot))
+			goto bad;
+		n->type = types[TINT];
+		break;
+
+	case OANDAND:
+	case OOROR:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		if(tcompat(n, T, l->type, tnot) |
+		   tcompat(n, T, r->type, tnot))
+			goto bad;
+		n->type = types[TINT];
+		break;
+
+	case OCOMMA:
+		o = tcom(l);
+		if(o | tcom(r))
+			goto bad;
+		n->type = r->type;
+		break;
+
+
+	case OSIGN:	/* extension signof(type) returns a hash */
+		if(l != Z) {
+			if(l->op != OSTRING && l->op != OLSTRING)
+				if(tcomo(l, 0))
+					goto bad;
+			if(l->op == OBIT) {
+				diag(n, "signof bitfield");
+				goto bad;
+			}
+			n->type = l->type;
+		}
+		if(n->type == T)
+			goto bad;
+		if(n->type->width < 0) {
+			diag(n, "signof undefined type");
+			goto bad;
+		}
+		n->op = OCONST;
+		n->left = Z;
+		n->right = Z;
+		n->vconst = convvtox(signature(n->type), TULONG);
+		n->type = types[TULONG];
+		break;
+
+	case OSIZE:
+		if(l != Z) {
+			if(l->op != OSTRING && l->op != OLSTRING)
+				if(tcomo(l, 0))
+					goto bad;
+			if(l->op == OBIT) {
+				diag(n, "sizeof bitfield");
+				goto bad;
+			}
+			n->type = l->type;
+		}
+		if(n->type == T)
+			goto bad;
+		if(n->type->width <= 0) {
+			diag(n, "sizeof undefined type");
+			goto bad;
+		}
+		if(n->type->etype == TFUNC) {
+			diag(n, "sizeof function");
+			goto bad;
+		}
+		n->op = OCONST;
+		n->left = Z;
+		n->right = Z;
+		n->vconst = convvtox(n->type->width, TINT);
+		n->type = types[TINT];
+		break;
+
+	case OFUNC:
+		o = tcomo(l, 0);
+		if(o)
+			goto bad;
+		if(l->type->etype == TIND && l->type->link->etype == TFUNC) {
+			l = new1(OIND, l, Z);
+			l->type = l->left->type->link;
+			n->left = l;
+		}
+		if(tcompat(n, T, l->type, tfunct))
+			goto bad;
+		if(o | tcoma(l, r, l->type->down, 1))
+			goto bad;
+		n->type = l->type->link;
+		if(!debug['B'])
+			if(l->type->down == T || l->type->down->etype == TOLD) {
+				nerrors--;
+				diag(n, "function args not checked: %F", l);
+			}
+		dpcheck(n);
+		break;
+
+	case ONAME:
+		if(n->type == T) {
+			diag(n, "name not declared: %F", n);
+			goto bad;
+		}
+		if(n->type->etype == TENUM) {
+			n->op = OCONST;
+			n->type = n->sym->tenum;
+			if(!typefd[n->type->etype])
+				n->vconst = n->sym->vconst;
+			else
+				n->fconst = n->sym->fconst;
+			break;
+		}
+		n->addable = 1;
+		if(n->class == CEXREG) {
+			n->op = OREGISTER;
+			if(thechar == '8')
+				n->op = OEXREG;
+			n->reg = n->sym->offset;
+			n->xoffset = 0;
+			break;
+		}
+		break;
+
+	case OLSTRING:
+		if(n->type->link != types[TRUNE]) {
+			o = outstring(0, 0);
+			while(o & 3) {
+				outlstring(&zer, sizeof(TRune));
+				o = outlstring(0, 0);
+			}
+		}
+		n->op = ONAME;
+		n->xoffset = outlstring(n->rstring, n->type->width);
+		n->addable = 1;
+		break;
+
+	case OSTRING:
+		if(n->type->link != types[TCHAR]) {
+			o = outstring(0, 0);
+			while(o & 3) {
+				outstring("", 1);
+				o = outstring(0, 0);
+			}
+		}
+		n->op = ONAME;
+		n->xoffset = outstring(n->cstring, n->type->width);
+		n->addable = 1;
+		break;
+
+	case OCONST:
+		break;
+
+	case ODOT:
+		if(tcom(l))
+			goto bad;
+		if(tcompat(n, T, l->type, tdot))
+			goto bad;
+		if(tcomd(n))
+			goto bad;
+		break;
+
+	case OADDR:
+		if(tcomo(l, ADDROP))
+			goto bad;
+		if(tlvalue(l))
+			goto bad;
+		if(l->type->nbits) {
+			diag(n, "address of a bit field");
+			goto bad;
+		}
+		if(l->op == OREGISTER) {
+			diag(n, "address of a register");
+			goto bad;
+		}
+		n->type = typ(TIND, l->type);
+		n->type->width = types[TIND]->width;
+		break;
+
+	case OIND:
+		if(tcom(l))
+			goto bad;
+		if(tcompat(n, T, l->type, tindir))
+			goto bad;
+		n->type = l->type->link;
+		n->addable = 1;
+		break;
+
+	case OSTRUCT:
+		if(tcomx(n))
+			goto bad;
+		break;
+	}
+	t = n->type;
+	if(t == T)
+		goto bad;
+	if(t->width < 0) {
+		snap(t);
+		if(t->width < 0) {
+			if(typesu[t->etype] && t->tag)
+				diag(n, "structure not fully declared %s", t->tag->name);
+			else
+				diag(n, "structure not fully declared");
+			goto bad;
+		}
+	}
+	if(typeaf[t->etype]) {
+		if(f & ADDROF)
+			goto addaddr;
+		if(f & ADDROP)
+			warn(n, "address of array/func ignored");
+	}
+	return 0;
+
+addaddr:
+	if(tlvalue(n))
+		goto bad;
+	l = new1(OXXX, Z, Z);
+	*l = *n;
+	n->op = OADDR;
+	if(l->type->etype == TARRAY)
+		l->type = l->type->link;
+	n->left = l;
+	n->right = Z;
+	n->addable = 0;
+	n->type = typ(TIND, l->type);
+	n->type->width = types[TIND]->width;
+	return 0;
+
+bad:
+	n->type = T;
+	return 1;
+}
+
+int
+tcoma(Node *l, Node *n, Type *t, int f)
+{
+	Node *n1;
+	int o;
+
+	if(t != T)
+	if(t->etype == TOLD || t->etype == TDOT)	/* .../old in prototype */
+		t = T;
+	if(n == Z) {
+		if(t != T && !sametype(t, types[TVOID])) {
+			diag(n, "not enough function arguments: %F", l);
+			return 1;
+		}
+		return 0;
+	}
+	if(n->op == OLIST) {
+		o = tcoma(l, n->left, t, 0);
+		if(t != T) {
+			t = t->down;
+			if(t == T)
+				t = types[TVOID];
+		}
+		return o | tcoma(l, n->right, t, 1);
+	}
+	if(f && t != T)
+		tcoma(l, Z, t->down, 0);
+	if(tcom(n) || tcompat(n, T, n->type, targ))
+		return 1;
+	if(sametype(t, types[TVOID])) {
+		diag(n, "too many function arguments: %F", l);
+		return 1;
+	}
+	if(t != T) {
+		typeext(t, n);
+		if(stcompat(nodproto, t, n->type, tasign)) {
+			diag(l, "argument prototype mismatch \"%T\" for \"%T\": %F",
+				n->type, t, l);
+			return 1;
+		}
+		switch(t->etype) {
+		case TCHAR:
+		case TSHORT:
+			t = types[TINT];
+			break;
+
+		case TUCHAR:
+		case TUSHORT:
+			t = types[TUINT];
+			break;
+		}
+	} else
+	switch(n->type->etype)
+	{
+	case TCHAR:
+	case TSHORT:
+		t = types[TINT];
+		break;
+
+	case TUCHAR:
+	case TUSHORT:
+		t = types[TUINT];
+		break;
+
+	case TFLOAT:
+		t = types[TDOUBLE];
+	}
+	if(t != T && !sametype(t, n->type)) {
+		n1 = new1(OXXX, Z, Z);
+		*n1 = *n;
+		n->op = OCAST;
+		n->left = n1;
+		n->right = Z;
+		n->type = t;
+		n->addable = 0;
+	}
+	return 0;
+}
+
+int
+tcomd(Node *n)
+{
+	Type *t;
+	long o;
+
+	o = 0;
+	t = dotsearch(n->sym, n->left->type->link, n, &o);
+	if(t == T) {
+		diag(n, "not a member of struct/union: %F", n);
+		return 1;
+	}
+	makedot(n, t, o);
+	return 0;
+}
+
+int
+tcomx(Node *n)
+{
+	Type *t;
+	Node *l, *r, **ar, **al;
+	int e;
+
+	e = 0;
+	if(n->type->etype != TSTRUCT) {
+		diag(n, "constructor must be a structure");
+		return 1;
+	}
+	l = invert(n->left);
+	n->left = l;
+	al = &n->left;
+	for(t = n->type->link; t != T; t = t->down) {
+		if(l == Z) {
+			diag(n, "constructor list too short");
+			return 1;
+		}
+		if(l->op == OLIST) {
+			r = l->left;
+			ar = &l->left;
+			al = &l->right;
+			l = l->right;
+		} else {
+			r = l;
+			ar = al;
+			l = Z;
+		}
+		if(tcom(r))
+			e++;
+		typeext(t, r);
+		if(tcompat(n, t, r->type, tasign))
+			e++;
+		constas(n, t, r->type);
+		if(!e && !sametype(t, r->type)) {
+			r = new1(OCAST, r, Z);
+			r->type = t;
+			*ar = r;
+		}
+	}
+	if(l != Z) {
+		diag(n, "constructor list too long");
+		return 1;
+	}
+	return e;
+}
+
+int
+tlvalue(Node *n)
+{
+
+	if(!n->addable) {
+		diag(n, "not an l-value");
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * hoist comma operators out of expressions
+ *	(a,b) OP c => (a, b OP c)
+ *	OP(a,b) =>	(a, OP b)
+ *	a OP (b,c) => (b, a OP c)
+ */
+
+static Node*
+comargs(Com *com, Node *n)
+{
+	if(n != Z && n->op == OLIST){
+		n->left = comargs(com, n->left);
+		n->right = comargs(com, n->right);
+	}
+	return commas(com, n);
+}
+
+static Node*
+commas(Com *com, Node *n)
+{
+	Node *t;
+
+	if(n == Z)
+		return n;
+	switch(n->op){
+	case OREGISTER:
+	case OINDREG:
+	case OCONST:
+	case ONAME:
+	case OSTRING:
+		/* leaf */
+		return n;
+
+	case OCOMMA:
+		t = commas(com, n->left);
+		if(com->n >= nelem(com->t))
+			fatal(n, "comma list overflow");
+		com->t[com->n++] = t;
+		return commas(com, n->right);
+
+	case OFUNC:
+		n->left = commas(com, n->left);
+		n->right = comargs(com, n->right);
+		return n;
+
+	case OCOND:
+		n->left = commas(com, n->left);
+		comma(n->right->left);
+		comma(n->right->right);
+		return n;
+
+	case OANDAND:
+	case OOROR:
+		n->left = commas(com, n->left);
+		comma(n->right);
+		return n;
+
+	case ORETURN:
+		comma(n->left);
+		return n;
+	}
+	n->left = commas(com, n->left);
+	if(n->right != Z)
+		n->right = commas(com, n->right);
+	return n;
+}
+
+static void
+comma(Node *n)
+{
+	Com com;
+	Node *nn;
+
+	com.n = 0;
+	nn = commas(&com, n);
+	if(com.n > 0){
+if(debug['y'])print("n=%d\n", com.n);
+if(debug['y']) prtree(nn, "res");
+		if(nn != n)
+			*n = *nn;
+		while(com.n > 0){
+if(debug['y']) prtree(com.t[com.n-1], "tree");
+			nn = new1(OXXX, Z, Z);
+			*nn = *n;
+			n->op = OCOMMA;
+			n->type = nn->type;
+			n->left = com.t[--com.n];
+			n->right = nn;
+			n->lineno = n->left->lineno;
+		}
+if(debug['y']) prtree(n, "final");
+	}else if(n != nn)
+		fatal(n, "odd tree");
+}
+
+/*
+ *	general rewrite
+ *	(IND(ADDR x)) ==> x
+ *	(ADDR(IND x)) ==> x
+ *	remove some zero operands
+ *	remove no op casts
+ *	evaluate constants
+ */
+void
+ccom(Node *n)
+{
+	Node *l, *r;
+	int t;
+
+loop:
+	if(n == Z)
+		return;
+	l = n->left;
+	r = n->right;
+	switch(n->op) {
+
+	case OAS:
+	case OASXOR:
+	case OASAND:
+	case OASOR:
+	case OASMOD:
+	case OASLMOD:
+	case OASLSHR:
+	case OASASHR:
+	case OASASHL:
+	case OASDIV:
+	case OASLDIV:
+	case OASMUL:
+	case OASLMUL:
+	case OASSUB:
+	case OASADD:
+		ccom(l);
+		ccom(r);
+		if(n->op == OASLSHR || n->op == OASASHR || n->op == OASASHL)
+		if(r->op == OCONST) {
+			t = n->type->width * 8;	/* bits per byte */
+			if(r->vconst >= t || r->vconst < 0)
+				warn(n, "stupid shift: %lld", r->vconst);
+		}
+		break;
+
+	case OCAST:
+		ccom(l);
+		if(l->op == OCONST) {
+			evconst(n);
+			if(n->op == OCONST)
+				break;
+		}
+		if((nocast(l->type, n->type)) &&
+		   ((!typefd[l->type->etype] || typeu[l->type->etype]) && (typeu[n->type->etype]))) {
+			l->type = n->type;
+			*n = *l;
+		}
+		break;
+
+	case OCOND:
+		ccom(l);
+		ccom(r);
+		if(l->op == OCONST){
+			if(vconst(l) == 0)
+				*n = *r->right;
+			else
+				*n = *r->left;
+		}
+		break;
+
+	case OREGISTER:
+	case OINDREG:
+	case OCONST:
+	case ONAME:
+		break;
+
+	case OADDR:
+		ccom(l);
+		l->etype = TVOID;
+		if(l->op == OIND) {
+			l->left->type = n->type;
+			*n = *l->left;
+			break;
+		}
+		goto common;
+
+	case OIND:
+		ccom(l);
+		if(l->op == OADDR) {
+			l->left->type = n->type;
+			*n = *l->left;
+			break;
+		}
+		goto common;
+
+	case OEQ:
+	case ONE:
+
+	case OLE:
+	case OGE:
+	case OLT:
+	case OGT:
+
+	case OLS:
+	case OHS:
+	case OLO:
+	case OHI:
+		ccom(l);
+		ccom(r);
+		if(compar(n, 0) || compar(n, 1))
+			break;
+		relcon(l, r);
+		relcon(r, l);
+		goto common;
+
+	case OASHR:
+	case OASHL:
+	case OLSHR:
+		ccom(l);
+		if(vconst(l) == 0 && !side(r)) {
+			*n = *l;
+			break;
+		}
+		ccom(r);
+		if(vconst(r) == 0) {
+			*n = *l;
+			break;
+		}
+		if(r->op == OCONST) {
+			t = n->type->width * 8;	/* bits per byte */
+			if(r->vconst >= t || r->vconst <= -t)
+				warn(n, "stupid shift: %lld", r->vconst);
+		}
+		goto common;
+
+	case OMUL:
+	case OLMUL:
+		ccom(l);
+		t = vconst(l);
+		if(t == 0 && !side(r)) {
+			*n = *l;
+			break;
+		}
+		if(t == 1) {
+			*n = *r;
+			goto loop;
+		}
+		ccom(r);
+		t = vconst(r);
+		if(t == 0 && !side(l)) {
+			*n = *r;
+			break;
+		}
+		if(t == 1) {
+			*n = *l;
+			break;
+		}
+		goto common;
+
+	case ODIV:
+	case OLDIV:
+		ccom(l);
+		if(vconst(l) == 0 && !side(r)) {
+			*n = *l;
+			break;
+		}
+		ccom(r);
+		t = vconst(r);
+		if(t == 0) {
+			diag(n, "divide check");
+			*n = *r;
+			break;
+		}
+		if(t == 1) {
+			*n = *l;
+			break;
+		}
+		goto common;
+
+	case OSUB:
+		ccom(r);
+		if(r->op == OCONST) {
+			if(typefd[r->type->etype]) {
+				n->op = OADD;
+				r->fconst = -r->fconst;
+				goto loop;
+			} else {
+				n->op = OADD;
+				r->vconst = -r->vconst;
+				goto loop;
+			}
+		}
+		ccom(l);
+		goto common;
+
+	case OXOR:
+	case OOR:
+	case OADD:
+		ccom(l);
+		if(vconst(l) == 0) {
+			*n = *r;
+			goto loop;
+		}
+		ccom(r);
+		if(vconst(r) == 0) {
+			*n = *l;
+			break;
+		}
+		goto commute;
+
+	case OAND:
+		ccom(l);
+		ccom(r);
+		if(vconst(l) == 0 && !side(r)) {
+			*n = *l;
+			break;
+		}
+		if(vconst(r) == 0 && !side(l)) {
+			*n = *r;
+			break;
+		}
+
+	commute:
+		/* look for commutative constant */
+		if(r->op == OCONST) {
+			if(l->op == n->op) {
+				if(l->left->op == OCONST) {
+					n->right = l->right;
+					l->right = r;
+					goto loop;
+				}
+				if(l->right->op == OCONST) {
+					n->right = l->left;
+					l->left = r;
+					goto loop;
+				}
+			}
+		}
+		if(l->op == OCONST) {
+			if(r->op == n->op) {
+				if(r->left->op == OCONST) {
+					n->left = r->right;
+					r->right = l;
+					goto loop;
+				}
+				if(r->right->op == OCONST) {
+					n->left = r->left;
+					r->left = l;
+					goto loop;
+				}
+			}
+		}
+		goto common;
+
+	case OANDAND:
+		ccom(l);
+		if(vconst(l) == 0) {
+			*n = *l;
+			break;
+		}
+		ccom(r);
+		goto common;
+
+	case OOROR:
+		ccom(l);
+		if(l->op == OCONST && l->vconst != 0) {
+			*n = *l;
+			n->vconst = 1;
+			break;
+		}
+		ccom(r);
+		goto common;
+
+	default:
+		if(l != Z)
+			ccom(l);
+		if(r != Z)
+			ccom(r);
+	common:
+		if(l != Z)
+		if(l->op != OCONST)
+			break;
+		if(r != Z)
+		if(r->op != OCONST)
+			break;
+		evconst(n);
+	}
+}
+
+/*	OEQ, ONE, OLE, OLS, OLT, OLO, OGE, OHS, OGT, OHI */
+static char *cmps[12] = 
+{
+	"==", "!=", "<=", "<=", "<", "<", ">=", ">=", ">", ">",
+};
+
+/* 128-bit numbers */
+typedef struct Big Big;
+struct Big
+{
+	vlong a;
+	uvlong b;
+};
+static int
+cmp(Big x, Big y)
+{
+	if(x.a != y.a){
+		if(x.a < y.a)
+			return -1;
+		return 1;
+	}
+	if(x.b != y.b){
+		if(x.b < y.b)
+			return -1;
+		return 1;
+	}
+	return 0;
+}
+static Big
+add(Big x, int y)
+{
+	uvlong ob;
+	
+	ob = x.b;
+	x.b += y;
+	if(y > 0 && x.b < ob)
+		x.a++;
+	if(y < 0 && x.b > ob)
+		x.a--;
+	return x;
+} 
+
+Big
+big(vlong a, uvlong b)
+{
+	Big x;
+
+	x.a = a;
+	x.b = b;
+	return x;
+}
+
+int
+compar(Node *n, int reverse)
+{
+	Big lo, hi, x;
+	int op;
+	char xbuf[40], cmpbuf[50];
+	Node *l, *r;
+	Type *lt, *rt;
+
+	/*
+	 * The point of this function is to diagnose comparisons 
+	 * that can never be true or that look misleading because
+	 * of the `usual arithmetic conversions'.  As an example 
+	 * of the latter, if x is a ulong, then if(x <= -1) really means
+	 * if(x <= 0xFFFFFFFF), while if(x <= -1LL) really means
+	 * what it says (but 8c compiles it wrong anyway).
+	 */
+
+	if(reverse){
+		r = n->left;
+		l = n->right;
+		op = comrel[relindex(n->op)];
+	}else{
+		l = n->left;
+		r = n->right;
+		op = n->op;
+	}
+
+	/*
+	 * Skip over left casts to find out the original expression range.
+	 */
+	while(l->op == OCAST)
+		l = l->left;
+	if(l->op == OCONST)
+		return 0;
+	lt = l->type;
+	if(l->op == ONAME && l->sym->type){
+		lt = l->sym->type;
+		if(lt->etype == TARRAY)
+			lt = lt->link;
+	}
+	if(lt == T)
+		return 0;
+	if(lt->etype == TXXX || lt->etype > TUVLONG)
+		return 0;
+	
+	/*
+	 * Skip over the right casts to find the on-screen value.
+	 */
+	if(r->op != OCONST)
+		return 0;
+	while(r->oldop == OCAST && !r->xcast)
+		r = r->left;
+	rt = r->type;
+	if(rt == T)
+		return 0;
+
+	x.b = r->vconst;
+	x.a = 0;
+	if((rt->etype&1) && r->vconst < 0)	/* signed negative */
+		x.a = ~0ULL;
+
+	if((lt->etype&1)==0){
+		/* unsigned */
+		lo = big(0, 0);
+		if(lt->width == 8)
+			hi = big(0, ~0ULL);
+		else
+			hi = big(0, (1LL<<(l->type->width*8))-1);
+	}else{
+		lo = big(~0ULL, -(1LL<<(l->type->width*8-1)));
+		hi = big(0, (1LL<<(l->type->width*8-1))-1);
+	}
+
+	switch(op){
+	case OLT:
+	case OLO:
+	case OGE:
+	case OHS:
+		if(cmp(x, lo) <= 0)
+			goto useless;
+		if(cmp(x, add(hi, 1)) >= 0)
+			goto useless;
+		break;
+	case OLE:
+	case OLS:
+	case OGT:
+	case OHI:
+		if(cmp(x, add(lo, -1)) <= 0)
+			goto useless;
+		if(cmp(x, hi) >= 0)
+			goto useless;
+		break;
+	case OEQ:
+	case ONE:
+		/*
+		 * Don't warn about comparisons if the expression
+		 * is as wide as the value: the compiler-supplied casts
+		 * will make both outcomes possible.
+		 */
+		if(lt->width >= rt->width && debug['w'] < 2)
+			return 0;
+		if(cmp(x, lo) < 0 || cmp(x, hi) > 0)
+			goto useless;
+		break;
+	}
+	return 0;
+
+useless:
+	if((x.a==0 && x.b<=9) || (x.a==~0LL && x.b >= -9ULL))
+		snprint(xbuf, sizeof xbuf, "%lld", x.b);
+	else if(x.a == 0)
+		snprint(xbuf, sizeof xbuf, "%#llux", x.b);
+	else
+		snprint(xbuf, sizeof xbuf, "%#llx", x.b);
+	if(reverse)
+		snprint(cmpbuf, sizeof cmpbuf, "%s %s %T",
+			xbuf, cmps[relindex(n->op)], lt);
+	else
+		snprint(cmpbuf, sizeof cmpbuf, "%T %s %s",
+			lt, cmps[relindex(n->op)], xbuf);
+if(debug['y']) prtree(n, "strange");
+	warn(n, "useless or misleading comparison: %s", cmpbuf);
+	return 0;
+}
+

+ 628 - 0
sys/src/libcc/com64.c

@@ -0,0 +1,628 @@
+/*
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+
+#include "cc.h"
+
+/*
+ * this is machine dependent, but it is totally
+ * common on all of the 64-bit symulating machines.
+ */
+
+#define	FNX	100	/* botch -- redefinition */
+
+Node*	nodaddv;
+Node*	nodsubv;
+Node*	nodmulv;
+Node*	noddivv;
+Node*	noddivvu;
+Node*	nodmodv;
+Node*	nodmodvu;
+Node*	nodlshv;
+Node*	nodrshav;
+Node*	nodrshlv;
+Node*	nodandv;
+Node*	nodorv;
+Node*	nodxorv;
+Node*	nodnegv;
+Node*	nodcomv;
+
+Node*	nodtestv;
+Node*	nodeqv;
+Node*	nodnev;
+Node*	nodlev;
+Node*	nodltv;
+Node*	nodgev;
+Node*	nodgtv;
+Node*	nodhiv;
+Node*	nodhsv;
+Node*	nodlov;
+Node*	nodlsv;
+
+Node*	nodf2v;
+Node*	nodd2v;
+Node*	nodp2v;
+Node*	nodsi2v;
+Node*	nodui2v;
+Node*	nodsl2v;
+Node*	nodul2v;
+Node*	nodsh2v;
+Node*	noduh2v;
+Node*	nodsc2v;
+Node*	noduc2v;
+
+Node*	nodv2f;
+Node*	nodv2d;
+Node*	nodv2ui;
+Node*	nodv2si;
+Node*	nodv2ul;
+Node*	nodv2sl;
+Node*	nodv2uh;
+Node*	nodv2sh;
+Node*	nodv2uc;
+Node*	nodv2sc;
+
+Node*	nodvpp;
+Node*	nodppv;
+Node*	nodvmm;
+Node*	nodmmv;
+
+Node*	nodvasop;
+
+char	etconv[NTYPE];	/* for _vasop */
+Init	initetconv[] =
+{
+	{TCHAR,		1,	0},
+	{TUCHAR,		2,	0},
+	{TSHORT,		3,	0},
+	{TUSHORT,	4,	0},
+	{TLONG,		5,	0},
+	{TULONG,		6,	0},
+	{TVLONG,		7,	0},
+	{TUVLONG,	8,	0},
+	{TINT,		9,	0},
+	{TUINT,		10,	0},
+	{-1,		0,	0},
+};
+
+Node*
+fvn(char *name, int type)
+{
+	Node *n;
+
+	n = new(ONAME, Z, Z);
+	n->sym = slookup(name);
+	n->sym->sig = SIGINTERN;
+	if(fntypes[type] == 0)
+		fntypes[type] = typ(TFUNC, types[type]);
+	n->type = fntypes[type];
+	n->etype = type;
+	n->class = CGLOBL;
+	n->addable = 10;
+	n->complex = 0;
+	return n;
+}
+
+void
+com64init(void)
+{
+	Init *p;
+
+	nodaddv = fvn("_addv", TVLONG);
+	nodsubv = fvn("_subv", TVLONG);
+	nodmulv = fvn("_mulv", TVLONG);
+	noddivv = fvn("_divv", TVLONG);
+	noddivvu = fvn("_divvu", TVLONG);
+	nodmodv = fvn("_modv", TVLONG);
+	nodmodvu = fvn("_modvu", TVLONG);
+	nodlshv = fvn("_lshv", TVLONG);
+	nodrshav = fvn("_rshav", TVLONG);
+	nodrshlv = fvn("_rshlv", TVLONG);
+	nodandv = fvn("_andv", TVLONG);
+	nodorv = fvn("_orv", TVLONG);
+	nodxorv = fvn("_xorv", TVLONG);
+	nodnegv = fvn("_negv", TVLONG);
+	nodcomv = fvn("_comv", TVLONG);
+
+	nodtestv = fvn("_testv", TLONG);
+	nodeqv = fvn("_eqv", TLONG);
+	nodnev = fvn("_nev", TLONG);
+	nodlev = fvn("_lev", TLONG);
+	nodltv = fvn("_ltv", TLONG);
+	nodgev = fvn("_gev", TLONG);
+	nodgtv = fvn("_gtv", TLONG);
+	nodhiv = fvn("_hiv", TLONG);
+	nodhsv = fvn("_hsv", TLONG);
+	nodlov = fvn("_lov", TLONG);
+	nodlsv = fvn("_lsv", TLONG);
+
+	nodf2v = fvn("_f2v", TVLONG);
+	nodd2v = fvn("_d2v", TVLONG);
+	nodp2v = fvn("_p2v", TVLONG);
+	nodsi2v = fvn("_si2v", TVLONG);
+	nodui2v = fvn("_ui2v", TVLONG);
+	nodsl2v = fvn("_sl2v", TVLONG);
+	nodul2v = fvn("_ul2v", TVLONG);
+	nodsh2v = fvn("_sh2v", TVLONG);
+	noduh2v = fvn("_uh2v", TVLONG);
+	nodsc2v = fvn("_sc2v", TVLONG);
+	noduc2v = fvn("_uc2v", TVLONG);
+
+	nodv2f = fvn("_v2f", TFLOAT);
+	nodv2d = fvn("_v2d", TDOUBLE);
+	nodv2sl = fvn("_v2sl", TLONG);
+	nodv2ul = fvn("_v2ul", TULONG);
+	nodv2si = fvn("_v2si", TINT);
+	nodv2ui = fvn("_v2ui", TUINT);
+	nodv2sh = fvn("_v2sh", TSHORT);
+	nodv2uh = fvn("_v2ul", TUSHORT);
+	nodv2sc = fvn("_v2sc", TCHAR);
+	nodv2uc = fvn("_v2uc", TUCHAR);
+
+	nodvpp = fvn("_vpp", TVLONG);
+	nodppv = fvn("_ppv", TVLONG);
+	nodvmm = fvn("_vmm", TVLONG);
+	nodmmv = fvn("_mmv", TVLONG);
+
+	nodvasop = fvn("_vasop", TVLONG);
+
+	for(p = initetconv; p->code >= 0; p++)
+		etconv[p->code] = p->value;
+}
+
+int
+com64(Node *n)
+{
+	Node *l, *r, *a, *t;
+	int lv, rv;
+
+	if(n->type == 0)
+		return 0;
+
+	l = n->left;
+	r = n->right;
+
+	lv = 0;
+	if(l && l->type && typev[l->type->etype])
+		lv = 1;
+	rv = 0;
+	if(r && r->type && typev[r->type->etype])
+		rv = 1;
+
+	if(lv) {
+		switch(n->op) {
+		case OEQ:
+			a = nodeqv;
+			goto setbool;
+		case ONE:
+			a = nodnev;
+			goto setbool;
+		case OLE:
+			a = nodlev;
+			goto setbool;
+		case OLT:
+			a = nodltv;
+			goto setbool;
+		case OGE:
+			a = nodgev;
+			goto setbool;
+		case OGT:
+			a = nodgtv;
+			goto setbool;
+		case OHI:
+			a = nodhiv;
+			goto setbool;
+		case OHS:
+			a = nodhsv;
+			goto setbool;
+		case OLO:
+			a = nodlov;
+			goto setbool;
+		case OLS:
+			a = nodlsv;
+			goto setbool;
+
+		case OANDAND:
+		case OOROR:
+			if(machcap(n))
+				return 1;
+
+			if(rv) {
+				r = new(OFUNC, nodtestv, r);
+				n->right = r;
+				r->complex = FNX;
+				r->op = OFUNC;
+				r->type = types[TLONG];
+			}
+
+		case OCOND:
+		case ONOT:
+			if(machcap(n))
+				return 1;
+
+			l = new(OFUNC, nodtestv, l);
+			n->left = l;
+			l->complex = FNX;
+			l->op = OFUNC;
+			l->type = types[TLONG];
+			n->complex = FNX;
+			return 1;
+		}
+	}
+
+	if(rv) {
+		if(machcap(n))
+			return 1;
+		switch(n->op) {
+		case OANDAND:
+		case OOROR:
+			r = new(OFUNC, nodtestv, r);
+			n->right = r;
+			r->complex = FNX;
+			r->op = OFUNC;
+			r->type = types[TLONG];
+			return 1;
+		case OCOND:
+			return 1;
+		}
+	}
+
+	if(typev[n->type->etype]) {
+		if(machcap(n))
+			return 1;
+		switch(n->op) {
+		default:
+			diag(n, "unknown vlong %O", n->op);
+		case OFUNC:
+			n->complex = FNX;
+		case ORETURN:
+		case OAS:
+		case OIND:
+		case OLIST:
+		case OCOMMA:
+			return 1;
+		case OADD:
+			a = nodaddv;
+			goto setbop;
+		case OSUB:
+			a = nodsubv;
+			goto setbop;
+		case OMUL:
+		case OLMUL:
+			a = nodmulv;
+			goto setbop;
+		case ODIV:
+			a = noddivv;
+			goto setbop;
+		case OLDIV:
+			a = noddivvu;
+			goto setbop;
+		case OMOD:
+			a = nodmodv;
+			goto setbop;
+		case OLMOD:
+			a = nodmodvu;
+			goto setbop;
+		case OASHL:
+			a = nodlshv;
+			goto setbop;
+		case OASHR:
+			a = nodrshav;
+			goto setbop;
+		case OLSHR:
+			a = nodrshlv;
+			goto setbop;
+		case OAND:
+			a = nodandv;
+			goto setbop;
+		case OOR:
+			a = nodorv;
+			goto setbop;
+		case OXOR:
+			a = nodxorv;
+			goto setbop;
+		case OPOSTINC:
+			a = nodvpp;
+			goto setvinc;
+		case OPOSTDEC:
+			a = nodvmm;
+			goto setvinc;
+		case OPREINC:
+			a = nodppv;
+			goto setvinc;
+		case OPREDEC:
+			a = nodmmv;
+			goto setvinc;
+		case ONEG:
+			a = nodnegv;
+			goto setfnx;
+		case OCOM:
+			a = nodcomv;
+			goto setfnx;
+		case OCAST:
+			switch(l->type->etype) {
+			case TCHAR:
+				a = nodsc2v;
+				goto setfnxl;
+			case TUCHAR:
+				a = noduc2v;
+				goto setfnxl;
+			case TSHORT:
+				a = nodsh2v;
+				goto setfnxl;
+			case TUSHORT:
+				a = noduh2v;
+				goto setfnxl;
+			case TINT:
+				a = nodsi2v;
+				goto setfnx;
+			case TUINT:
+				a = nodui2v;
+				goto setfnx;
+			case TLONG:
+				a = nodsl2v;
+				goto setfnx;
+			case TULONG:
+				a = nodul2v;
+				goto setfnx;
+			case TFLOAT:
+				a = nodf2v;
+				goto setfnx;
+			case TDOUBLE:
+				a = nodd2v;
+				goto setfnx;
+			case TIND:
+				a = nodp2v;
+				goto setfnx;
+			}
+			diag(n, "unknown %T->vlong cast", l->type);
+			return 1;
+		case OASADD:
+			a = nodaddv;
+			goto setasop;
+		case OASSUB:
+			a = nodsubv;
+			goto setasop;
+		case OASMUL:
+		case OASLMUL:
+			a = nodmulv;
+			goto setasop;
+		case OASDIV:
+			a = noddivv;
+			goto setasop;
+		case OASLDIV:
+			a = noddivvu;
+			goto setasop;
+		case OASMOD:
+			a = nodmodv;
+			goto setasop;
+		case OASLMOD:
+			a = nodmodvu;
+			goto setasop;
+		case OASASHL:
+			a = nodlshv;
+			goto setasop;
+		case OASASHR:
+			a = nodrshav;
+			goto setasop;
+		case OASLSHR:
+			a = nodrshlv;
+			goto setasop;
+		case OASAND:
+			a = nodandv;
+			goto setasop;
+		case OASOR:
+			a = nodorv;
+			goto setasop;
+		case OASXOR:
+			a = nodxorv;
+			goto setasop;
+		}
+	}
+
+	if(typefd[n->type->etype] && l && l->op == OFUNC) {
+		switch(n->op) {
+		case OASADD:
+		case OASSUB:
+		case OASMUL:
+		case OASLMUL:
+		case OASDIV:
+		case OASLDIV:
+		case OASMOD:
+		case OASLMOD:
+		case OASASHL:
+		case OASASHR:
+		case OASLSHR:
+		case OASAND:
+		case OASOR:
+		case OASXOR:
+			if(l->right && typev[l->right->etype]) {
+				diag(n, "sorry float <asop> vlong not implemented\n");
+			}
+		}
+	}
+
+	if(n->op == OCAST) {
+		if(l->type && typev[l->type->etype]) {
+			if(machcap(n))
+				return 1;
+			switch(n->type->etype) {
+			case TDOUBLE:
+				a = nodv2d;
+				goto setfnx;
+			case TFLOAT:
+				a = nodv2f;
+				goto setfnx;
+			case TLONG:
+				a = nodv2sl;
+				goto setfnx;
+			case TULONG:
+				a = nodv2ul;
+				goto setfnx;
+			case TINT:
+				a = nodv2si;
+				goto setfnx;
+			case TUINT:
+				a = nodv2ui;
+				goto setfnx;
+			case TSHORT:
+				a = nodv2sh;
+				goto setfnx;
+			case TUSHORT:
+				a = nodv2uh;
+				goto setfnx;
+			case TCHAR:
+				a = nodv2sc;
+				goto setfnx;
+			case TUCHAR:
+				a = nodv2uc;
+				goto setfnx;
+			case TIND:	// small pun here
+				a = nodv2ul;
+				goto setfnx;
+			}
+			diag(n, "unknown vlong->%T cast", n->type);
+			return 1;
+		}
+	}
+
+	return 0;
+
+setbop:
+	n->left = a;
+	n->right = new(OLIST, l, r);
+	n->complex = FNX;
+	n->op = OFUNC;
+	return 1;
+
+setfnxl:
+	l = new(OCAST, l, 0);
+	l->type = types[TLONG];
+	l->complex = l->left->complex;
+
+setfnx:
+	n->left = a;
+	n->right = l;
+	n->complex = FNX;
+	n->op = OFUNC;
+	return 1;
+
+setvinc:
+	n->left = a;
+	l = new(OADDR, l, Z);
+	l->type = typ(TIND, l->left->type);
+	l->complex = l->left->complex;
+	n->right = new(OLIST, l, r);
+	n->complex = FNX;
+	n->op = OFUNC;
+	return 1;
+
+setbool:
+	if(machcap(n))
+		return 1;
+	n->left = a;
+	n->right = new(OLIST, l, r);
+	n->complex = FNX;
+	n->op = OFUNC;
+	n->type = types[TLONG];
+	return 1;
+
+setasop:
+	if(l->op == OFUNC) {
+		l = l->right;
+		goto setasop;
+	}
+
+	t = new(OCONST, 0, 0);
+	t->vconst = etconv[l->type->etype];
+	t->type = types[TLONG];
+	t->addable = 20;
+	r = new(OLIST, t, r);
+
+	t = new(OADDR, a, 0);
+	t->type = typ(TIND, a->type);
+	r = new(OLIST, t, r);
+
+	t = new(OADDR, l, 0);
+	t->type = typ(TIND, l->type);
+	t->complex = l->complex;
+	r = new(OLIST, t, r);
+
+	n->left = nodvasop;
+	n->right = r;
+	n->complex = FNX;
+	n->op = OFUNC;
+
+	return 1;
+}
+
+void
+bool64(Node *n)
+{
+	Node *n1;
+
+	if(machcap(Z))
+		return;
+	if(typev[n->type->etype]) {
+		n1 = new(OXXX, 0, 0);
+		*n1 = *n;
+
+		n->right = n1;
+		n->left = nodtestv;
+		n->complex = FNX;
+		n->addable = 0;
+		n->op = OFUNC;
+		n->type = types[TLONG];
+	}
+}
+
+/*
+ * more machine depend stuff.
+ * this is common for 8,16,32,64 bit machines.
+ * this is common for ieee machines.
+ */
+double
+convvtof(vlong v)
+{
+	double d;
+
+	d = v;		/* BOTCH */
+	return d;
+}
+
+vlong
+convftov(double d)
+{
+	vlong v;
+
+
+	v = d;		/* BOTCH */
+	return v;
+}
+
+double
+convftox(double d, int et)
+{
+
+	if(!typefd[et])
+		diag(Z, "bad type in castftox %s", tnames[et]);
+	return d;
+}
+
+vlong
+convvtox(vlong c, int et)
+{
+	int n;
+
+	n = 8 * ewidth[et];
+	c &= MASK(n);
+	if(!typeu[et])
+		if(c & SIGN(n))
+			c |= ~MASK(n);
+	return c;
+}

+ 79 - 0
sys/src/libcc/compat

@@ -0,0 +1,79 @@
+
+int
+myaccess(char *f)
+{
+	return access(f, AEXIST);
+}
+
+void*
+mysbrk(ulong size)
+{
+	return sbrk(size);
+}
+
+int
+mycreat(char *n, int p)
+{
+
+	return create(n, 1, p);
+}
+
+int
+mywait(int *s)
+{
+	int p;
+	Waitmsg *w;
+
+	if((w = wait()) == nil)
+		return -1;
+	else{
+		p = w->pid;
+		*s = 0;
+		if(w->msg[0])
+			*s = 1;
+		free(w);
+		return p;
+	}
+}
+
+int
+mydup(int f1, int f2)
+{
+	return dup(f1,f2);
+}
+
+int
+mypipe(int *fd)
+{
+	return pipe(fd);
+}
+
+int
+systemtype(int sys)
+{
+	return sys & Plan9;
+}
+
+int
+pathchar(void)
+{
+	return '/';
+}
+
+char*
+mygetwd(char *path, int len)
+{
+	return getwd(path, len);
+}
+
+int
+myexec(char *path, char *argv[])
+{
+	return exec(path, argv);
+}
+
+int
+myfork(void)
+{
+	return fork();
+}

+ 56 - 0
sys/src/libcc/compat.c

@@ -0,0 +1,56 @@
+/*
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+
+#include	"cc.h"
+#include	"compat"
+
+/*
+ * fake mallocs
+ */
+void*
+malloc(ulong n)
+{
+	return alloc(n);
+}
+
+void*
+calloc(uint32_t m, size_t n)
+{
+	return alloc(m*n);
+}
+
+void*
+realloc(void* v, ulong u)
+{
+	fprint(2, "realloc called\n");
+	abort();
+	return 0;
+}
+
+void
+free(void* v)
+{
+}
+
+/* needed when profiling */
+void*
+mallocz(uint32_t size, int clr)
+{
+	void *v;
+
+	v = alloc(size);
+	if(clr && v != nil)
+		memset(v, 0, size);
+	return v;
+}
+
+void
+setmalloctag(void* v, uintptr_t u)
+{
+}

+ 1647 - 0
sys/src/libcc/dcl.c

@@ -0,0 +1,1647 @@
+/*
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+
+#include "cc.h"
+
+Node*
+dodecl(void (*f)(int,Type*,Sym*), int c, Type *t, Node *n)
+{
+	Sym *s;
+	Node *n1;
+	long v;
+
+	nearln = lineno;
+	lastfield = 0;
+
+loop:
+	if(n != Z)
+	switch(n->op) {
+	default:
+		diag(n, "unknown declarator: %O", n->op);
+		break;
+
+	case OARRAY:
+		t = typ(TARRAY, t);
+		t->width = 0;
+		n1 = n->right;
+		n = n->left;
+		if(n1 != Z) {
+			complex(n1);
+			v = -1;
+			if(n1->op == OCONST)
+				v = n1->vconst;
+			if(v <= 0) {
+				diag(n, "array size must be a positive constant");
+				v = 1;
+			}
+			t->width = v * t->link->width;
+		}
+		goto loop;
+
+	case OIND:
+		t = typ(TIND, t);
+		t->garb = n->garb;
+		n = n->left;
+		goto loop;
+
+	case OFUNC:
+		t = typ(TFUNC, t);
+		t->down = fnproto(n);
+		n = n->left;
+		goto loop;
+
+	case OBIT:
+		n1 = n->right;
+		complex(n1);
+		lastfield = -1;
+		if(n1->op == OCONST)
+			lastfield = n1->vconst;
+		if(lastfield < 0) {
+			diag(n, "field width must be non-negative constant");
+			lastfield = 1;
+		}
+		if(lastfield == 0) {
+			lastbit = 0;
+			firstbit = 1;
+			if(n->left != Z) {
+				diag(n, "zero width named field");
+				lastfield = 1;
+			}
+		}
+		if(!typei[t->etype]) {
+			diag(n, "field type must be int-like");
+			t = types[TINT];
+			lastfield = 1;
+		}
+		if(lastfield > tfield->width*8) {
+			diag(n, "field width larger than field unit");
+			lastfield = 1;
+		}
+		lastbit += lastfield;
+		if(lastbit > tfield->width*8) {
+			lastbit = lastfield;
+			firstbit = 1;
+		}
+		n = n->left;
+		goto loop;
+
+	case ONAME:
+		if(f == NODECL)
+			break;
+		s = n->sym;
+		(*f)(c, t, s);
+		if(s->class == CLOCAL)
+			s = mkstatic(s);
+		firstbit = 0;
+		n->sym = s;
+		n->type = s->type;
+		n->xoffset = s->offset;
+		n->class = s->class;
+		n->etype = TVOID;
+		if(n->type != T)
+			n->etype = n->type->etype;
+		if(debug['d'])
+			dbgdecl(s);
+		acidvar(s);
+		s->varlineno = lineno;
+		break;
+	}
+	lastdcl = t;
+	return n;
+}
+
+Sym*
+mkstatic(Sym *s)
+{
+	Sym *s1;
+
+	if(s->class != CLOCAL)
+		return s;
+	snprint(symb, NSYMB, "%s$%d", s->name, s->block);
+	s1 = lookup();
+	if(s1->class != CSTATIC) {
+		s1->type = s->type;
+		s1->offset = s->offset;
+		s1->block = s->block;
+		s1->class = CSTATIC;
+	}
+	return s1;
+}
+
+/*
+ * make a copy of a typedef
+ * the problem is to split out incomplete
+ * arrays so that it is in the variable
+ * rather than the typedef.
+ */
+Type*
+tcopy(Type *t)
+{
+	Type *tl, *tx;
+	int et;
+
+	if(t == T)
+		return t;
+	et = t->etype;
+	if(typesu[et])
+		return t;
+	tl = tcopy(t->link);
+	if(tl != t->link ||
+	  (et == TARRAY && t->width == 0)) {
+		tx = copytyp(t);
+		tx->link = tl;
+		return tx;
+	}
+	return t;
+}
+
+Node*
+doinit(Sym *s, Type *t, long o, Node *a)
+{
+	Node *n;
+
+	if(t == T)
+		return Z;
+	if(s->class == CEXTERN) {
+		s->class = CGLOBL;
+		if(debug['d'])
+			dbgdecl(s);
+	}
+	if(debug['i']) {
+		print("t = %T; o = %ld; n = %s\n", t, o, s->name);
+		prtree(a, "doinit value");
+	}
+
+
+	n = initlist;
+	if(a->op == OINIT)
+		a = a->left;
+	initlist = a;
+
+	a = init1(s, t, o, 0);
+	if(initlist != Z)
+		diag(initlist, "more initializers than structure: %s",
+			s->name);
+	initlist = n;
+
+	return a;
+}
+
+/*
+ * get next major operator,
+ * dont advance initlist.
+ */
+Node*
+peekinit(void)
+{
+	Node *a;
+
+	a = initlist;
+
+loop:
+	if(a == Z)
+		return a;
+	if(a->op == OLIST) {
+		a = a->left;
+		goto loop;
+	}
+	return a;
+}
+
+/*
+ * consume and return next element on
+ * initlist. expand strings.
+ */
+Node*
+nextinit(void)
+{
+	Node *a, *b, *n;
+
+	a = initlist;
+	n = Z;
+
+	if(a == Z)
+		return a;
+	if(a->op == OLIST) {
+		n = a->right;
+		a = a->left;
+	}
+	if(a->op == OUSED) {
+		a = a->left;
+		b = new(OCONST, Z, Z);
+		b->type = a->type->link;
+		if(a->op == OSTRING) {
+			b->vconst = convvtox(*a->cstring, TCHAR);
+			a->cstring++;
+		}
+		if(a->op == OLSTRING) {
+			b->vconst = convvtox(*a->rstring, TRUNE);
+			a->rstring++;
+		}
+		a->type->width -= b->type->width;
+		if(a->type->width <= 0)
+			initlist = n;
+		return b;
+	}
+	initlist = n;
+	return a;
+}
+
+int
+isstruct(Node *a, Type *t)
+{
+	Node *n;
+
+	switch(a->op) {
+	case ODOTDOT:
+		n = a->left;
+		if(n && n->type && sametype(n->type, t))
+			return 1;
+	case OSTRING:
+	case OLSTRING:
+	case OCONST:
+	case OINIT:
+	case OELEM:
+		return 0;
+	}
+
+	n = new(ODOTDOT, Z, Z);
+	*n = *a;
+
+	/*
+	 * ODOTDOT is a flag for tcom
+	 * a second tcom will not be performed
+	 */
+	a->op = ODOTDOT;
+	a->left = n;
+	a->right = Z;
+
+	if(tcom(n))
+		return 0;
+
+	if(sametype(n->type, t))
+		return 1;
+	return 0;
+}
+
+Node*
+init1(Sym *s, Type *t, long o, int exflag)
+{
+	Node *a, *l, *r, nod;
+	Type *t1;
+	long e, w, so, mw;
+
+	a = peekinit();
+	if(a == Z)
+		return Z;
+
+	if(debug['i']) {
+		print("t = %T; o = %ld; n = %s\n", t, o, s->name);
+		prtree(a, "init1 value");
+	}
+
+	if(exflag && a->op == OINIT)
+		return doinit(s, t, o, nextinit());
+
+	switch(t->etype) {
+	default:
+		diag(Z, "unknown type in initialization: %T to: %s", t, s->name);
+		return Z;
+
+	case TCHAR:
+	case TUCHAR:
+	case TINT:
+	case TUINT:
+	case TSHORT:
+	case TUSHORT:
+	case TLONG:
+	case TULONG:
+	case TVLONG:
+	case TUVLONG:
+	case TFLOAT:
+	case TDOUBLE:
+	case TIND:
+	single:
+		if(a->op == OARRAY || a->op == OELEM)
+			return Z;
+
+		a = nextinit();
+		if(a == Z)
+			return Z;
+
+		if(t->nbits)
+			diag(Z, "cannot initialize bitfields");
+		if(s->class == CAUTO) {
+			l = new(ONAME, Z, Z);
+			l->sym = s;
+			l->type = t;
+			l->etype = TVOID;
+			if(s->type)
+				l->etype = s->type->etype;
+			l->xoffset = s->offset + o;
+			l->class = s->class;
+
+			l = new(OASI, l, a);
+			return l;
+		}
+
+		complex(a);
+		if(a->type == T)
+			return Z;
+
+		if(a->op == OCONST) {
+			if(vconst(a) && t->etype == TIND && a->type && a->type->etype != TIND){
+				diag(a, "initialize pointer to an integer: %s", s->name);
+				return Z;
+			}
+			if(!sametype(a->type, t)) {
+				/* hoop jumping to save malloc */
+				if(nodcast == Z)
+					nodcast = new(OCAST, Z, Z);
+				nod = *nodcast;
+				nod.left = a;
+				nod.type = t;
+				nod.lineno = a->lineno;
+				complex(&nod);
+				if(nod.type)
+					*a = nod;
+			}
+			if(a->op != OCONST) {
+				diag(a, "initializer is not a constant: %s",
+					s->name);
+				return Z;
+			}
+			if(vconst(a) == 0)
+				return Z;
+			goto gext;
+		}
+		if(t->etype == TIND) {
+			while(a->op == OCAST) {
+				warn(a, "CAST in initialization ignored");
+				a = a->left;
+			}
+			if(!sametype(t, a->type)) {
+				diag(a, "initialization of incompatible pointers: %s\n%T and %T",
+					s->name, t, a->type);
+			}
+			if(a->op == OADDR)
+				a = a->left;
+			goto gext;
+		}
+
+		while(a->op == OCAST)
+			a = a->left;
+		if(a->op == OADDR) {
+			warn(a, "initialize pointer to an integer: %s", s->name);
+			a = a->left;
+			goto gext;
+		}
+		diag(a, "initializer is not a constant: %s", s->name);
+		return Z;
+
+	gext:
+		gextern(s, a, o, t->width);
+
+		return Z;
+
+	case TARRAY:
+		w = t->link->width;
+		if(a->op == OSTRING || a->op == OLSTRING)
+		if(typei[t->link->etype]) {
+			/*
+			 * get rid of null if sizes match exactly
+			 */
+			a = nextinit();
+			mw = t->width/w;
+			so = a->type->width/a->type->link->width;
+			if(mw && so > mw) {
+				if(so != mw+1)
+					diag(a, "string initialization larger than array");
+				a->type->width -= a->type->link->width;
+			}
+
+			/*
+			 * arrange strings to be expanded
+			 * inside OINIT braces.
+			 */
+			a = new(OUSED, a, Z);
+			return doinit(s, t, o, a);
+		}
+
+		mw = -w;
+		l = Z;
+		for(e=0;;) {
+			/*
+			 * peek ahead for element initializer
+			 */
+			a = peekinit();
+			if(a == Z)
+				break;
+			if(a->op == OELEM && t->link->etype != TSTRUCT)
+				break;
+			if(a->op == OARRAY) {
+				if(e && exflag)
+					break;
+				a = nextinit();
+				r = a->left;
+				complex(r);
+				if(r->op != OCONST) {
+					diag(r, "initializer subscript must be constant");
+					return Z;
+				}
+				e = r->vconst;
+				if(t->width != 0)
+					if(e < 0 || e*w >= t->width) {
+						diag(a, "initialization index out of range: %ld", e);
+						continue;
+					}
+			}
+
+			so = e*w;
+			if(so > mw)
+				mw = so;
+			if(t->width != 0)
+				if(mw >= t->width)
+					break;
+			r = init1(s, t->link, o+so, 1);
+			l = newlist(l, r);
+			e++;
+		}
+		if(t->width == 0)
+			t->width = mw+w;
+		return l;
+
+	case TUNION:
+	case TSTRUCT:
+		/*
+		 * peek ahead to find type of rhs.
+		 * if its a structure, then treat
+		 * this element as a variable
+		 * rather than an aggregate.
+		 */
+		if(isstruct(a, t))
+			goto single;
+
+		if(t->width <= 0) {
+			diag(Z, "incomplete structure: %s", s->name);
+			return Z;
+		}
+		l = Z;
+
+	again:
+		for(t1 = t->link; t1 != T; t1 = t1->down) {
+			if(a->op == OARRAY && t1->etype != TARRAY)
+				break;
+			if(a->op == OELEM) {
+				if(t1->sym != a->sym)
+					continue;
+				nextinit();
+			}
+			r = init1(s, t1, o+t1->offset, 1);
+			l = newlist(l, r);
+			a = peekinit();
+			if(a == Z)
+				break;
+			if(a->op == OELEM)
+				goto again;
+		}
+		if(a && a->op == OELEM)
+			diag(a, "structure element not found %F", a);
+		return l;
+	}
+}
+
+Node*
+newlist(Node *l, Node *r)
+{
+	if(r == Z)
+		return l;
+	if(l == Z)
+		return r;
+	return new(OLIST, l, r);
+}
+
+void
+sualign(Type *t)
+{
+	Type *l;
+	long o, w;
+
+	o = 0;
+	switch(t->etype) {
+
+	case TSTRUCT:
+		t->offset = 0;
+		w = 0;
+		for(l = t->link; l != T; l = l->down) {
+			if(l->nbits) {
+				if(l->shift <= 0) {
+					l->shift = -l->shift;
+					w = round(w, tfield->width);
+					o = w;
+					w += tfield->width;
+				}
+				l->offset = o;
+			} else {
+				if((l->width < 0 ||
+				   l->width == 0) && (l->down != T)){
+					if(l->sym)
+						diag(Z, "incomplete structure element: %s",
+							l->sym->name);
+					else
+						diag(Z, "incomplete structure element");
+				}
+				w = align(w, l, Ael1);
+				l->offset = w;
+				w = align(w, l, Ael2);
+			}
+		}
+		w = align(w, t, Asu2);
+		t->width = w;
+		acidtype(t);
+		pickletype(t);
+		return;
+
+	case TUNION:
+		t->offset = 0;
+		w = 0;
+		for(l = t->link; l != T; l = l->down) {
+			if(l->width <= 0){
+				if(l->sym)
+					diag(Z, "incomplete union element: %s",
+						l->sym->name);
+				else
+					diag(Z, "incomplete union element");
+			}
+			l->offset = 0;
+			l->shift = 0;
+			o = align(align(0, l, Ael1), l, Ael2);
+			if(o > w)
+				w = o;
+		}
+		w = align(w, t, Asu2);
+		t->width = w;
+		acidtype(t);
+		pickletype(t);
+		return;
+
+	default:
+		diag(Z, "unknown type in sualign: %T", t);
+		break;
+	}
+}
+
+long
+round(long v, int w)
+{
+	int r;
+
+	if(w <= 0 || w > 8) {
+		diag(Z, "rounding by %d", w);
+		w = 1;
+	}
+	r = v%w;
+	if(r)
+		v += w-r;
+	return v;
+}
+
+Type*
+ofnproto(Node *n)
+{
+	Type *tl, *tr, *t;
+
+	if(n == Z)
+		return T;
+	switch(n->op) {
+	case OLIST:
+		tl = ofnproto(n->left);
+		tr = ofnproto(n->right);
+		if(tl == T)
+			return tr;
+		tl->down = tr;
+		return tl;
+
+	case ONAME:
+		t = copytyp(n->sym->type);
+		t->down = T;
+		return t;
+	}
+	return T;
+}
+
+#define	ANSIPROTO	1
+#define	OLDPROTO	2
+
+void
+argmark(Node *n, int pass)
+{
+	Type *t;
+
+	autoffset = align(0, thisfn->link, Aarg0);
+	stkoff = 0;
+	for(; n->left != Z; n = n->left) {
+		if(n->op != OFUNC || n->left->op != ONAME)
+			continue;
+		walkparam(n->right, pass);
+		if(pass != 0 && anyproto(n->right) == OLDPROTO) {
+			t = typ(TFUNC, n->left->sym->type->link);
+			t->down = typ(TOLD, T);
+			t->down->down = ofnproto(n->right);
+			tmerge(t, n->left->sym);
+			n->left->sym->type = t;
+		}
+		break;
+	}
+	autoffset = 0;
+	stkoff = 0;
+}
+
+void
+walkparam(Node *n, int pass)
+{
+	Sym *s;
+	Node *n1;
+
+	if(n != Z && n->op == OPROTO && n->left == Z && n->type == types[TVOID])
+		return;
+
+loop:
+	if(n == Z)
+		return;
+	switch(n->op) {
+	default:
+		diag(n, "argument not a name/prototype: %O", n->op);
+		break;
+
+	case OLIST:
+		walkparam(n->left, pass);
+		n = n->right;
+		goto loop;
+
+	case OPROTO:
+		for(n1 = n; n1 != Z; n1=n1->left)
+			if(n1->op == ONAME) {
+				if(pass == 0) {
+					s = n1->sym;
+					push1(s);
+					s->offset = -1;
+					break;
+				}
+				dodecl(pdecl, CPARAM, n->type, n->left);
+				break;
+			}
+		if(n1)
+			break;
+		if(pass == 0) {
+			/*
+			 * extension:
+			 *	allow no name in argument declaration
+			diag(Z, "no name in argument declaration");
+			 */
+			break;
+		}
+		dodecl(NODECL, CPARAM, n->type, n->left);
+		pdecl(CPARAM, lastdcl, S);
+		break;
+
+	case ODOTDOT:
+		break;
+	
+	case ONAME:
+		s = n->sym;
+		if(pass == 0) {
+			push1(s);
+			s->offset = -1;
+			break;
+		}
+		if(s->offset != -1) {
+			if(autoffset == 0) {
+				firstarg = s;
+				firstargtype = s->type;
+			}
+			autoffset = align(autoffset, s->type, Aarg1);
+			s->offset = autoffset;
+			autoffset = align(autoffset, s->type, Aarg2);
+		} else
+			dodecl(pdecl, CXXX, types[TINT], n);
+		break;
+	}
+}
+
+void
+markdcl(void)
+{
+	Decl *d;
+
+	blockno++;
+	d = push();
+	d->val = DMARK;
+	d->offset = autoffset;
+	d->block = autobn;
+	autobn = blockno;
+}
+
+Node*
+revertdcl(void)
+{
+	Decl *d;
+	Sym *s;
+	Node *n, *n1;
+
+	n = Z;
+	for(;;) {
+		d = dclstack;
+		if(d == D) {
+			diag(Z, "pop off dcl stack");
+			break;
+		}
+		dclstack = d->link;
+		s = d->sym;
+		switch(d->val) {
+		case DMARK:
+			autoffset = d->offset;
+			autobn = d->block;
+			return n;
+
+		case DAUTO:
+			if(debug['d'])
+				print("revert1 \"%s\"\n", s->name);
+			if(s->aused == 0) {
+				nearln = s->varlineno;
+				if(s->class == CAUTO)
+					warn(Z, "auto declared and not used: %s", s->name);
+				if(s->class == CPARAM)
+					warn(Z, "param declared and not used: %s", s->name);
+			}
+			if(s->type && (s->type->garb & GVOLATILE)) {
+				n1 = new(ONAME, Z, Z);
+				n1->sym = s;
+				n1->type = s->type;
+				n1->etype = TVOID;
+				if(n1->type != T)
+					n1->etype = n1->type->etype;
+				n1->xoffset = s->offset;
+				n1->class = s->class;
+
+				n1 = new(OADDR, n1, Z);
+				n1 = new(OUSED, n1, Z);
+				if(n == Z)
+					n = n1;
+				else
+					n = new(OLIST, n1, n);
+			}
+			s->type = d->type;
+			s->class = d->class;
+			s->offset = d->offset;
+			s->block = d->block;
+			s->varlineno = d->varlineno;
+			s->aused = d->aused;
+			break;
+
+		case DSUE:
+			if(debug['d'])
+				print("revert2 \"%s\"\n", s->name);
+			s->suetag = d->type;
+			s->sueblock = d->block;
+			break;
+
+		case DLABEL:
+			if(debug['d'])
+				print("revert3 \"%s\"\n", s->name);
+			if(s->label && s->label->addable == 0)
+				warn(s->label, "label declared and not used \"%s\"", s->name);
+			s->label = Z;
+			break;
+		}
+	}
+	return n;
+}
+
+Type*
+fnproto(Node *n)
+{
+	int r;
+
+	r = anyproto(n->right);
+	if(r == 0 || (r & OLDPROTO)) {
+		if(r & ANSIPROTO)
+			diag(n, "mixed ansi/old function declaration: %F", n->left);
+		return T;
+	}
+	return fnproto1(n->right);
+}
+
+int
+anyproto(Node *n)
+{
+	int r;
+
+	r = 0;
+
+loop:
+	if(n == Z)
+		return r;
+	switch(n->op) {
+	case OLIST:
+		r |= anyproto(n->left);
+		n = n->right;
+		goto loop;
+
+	case ODOTDOT:
+	case OPROTO:
+		return r | ANSIPROTO;
+	}
+	return r | OLDPROTO;
+}
+
+Type*
+fnproto1(Node *n)
+{
+	Type *t;
+
+	if(n == Z)
+		return T;
+	switch(n->op) {
+	case OLIST:
+		t = fnproto1(n->left);
+		if(t != T)
+			t->down = fnproto1(n->right);
+		return t;
+
+	case OPROTO:
+		lastdcl = T;
+		dodecl(NODECL, CXXX, n->type, n->left);
+		t = typ(TXXX, T);
+		if(lastdcl != T)
+			*t = *paramconv(lastdcl, 1);
+		return t;
+
+	case ONAME:
+		diag(n, "incomplete argument prototype");
+		return typ(TINT, T);
+
+	case ODOTDOT:
+		return typ(TDOT, T);
+	}
+	diag(n, "unknown op in fnproto");
+	return T;
+}
+
+void
+dbgdecl(Sym *s)
+{
+	print("decl \"%s\": C=%s [B=%d:O=%ld] T=%T\n",
+		s->name, cnames[s->class], s->block, s->offset, s->type);
+}
+
+Decl*
+push(void)
+{
+	Decl *d;
+
+	d = alloc(sizeof(*d));
+	d->link = dclstack;
+	dclstack = d;
+	return d;
+}
+
+Decl*
+push1(Sym *s)
+{
+	Decl *d;
+
+	d = push();
+	d->sym = s;
+	d->val = DAUTO;
+	d->type = s->type;
+	d->class = s->class;
+	d->offset = s->offset;
+	d->block = s->block;
+	d->varlineno = s->varlineno;
+	d->aused = s->aused;
+	return d;
+}
+
+int
+sametype(Type *t1, Type *t2)
+{
+
+	if(t1 == t2)
+		return 1;
+	return rsametype(t1, t2, 5, 1);
+}
+
+int
+rsametype(Type *t1, Type *t2, int n, int f)
+{
+	int et;
+
+	n--;
+	for(;;) {
+		if(t1 == t2)
+			return 1;
+		if(t1 == T || t2 == T)
+			return 0;
+		if(n <= 0)
+			return 1;
+		et = t1->etype;
+		if(et != t2->etype)
+			return 0;
+		if(et == TFUNC) {
+			if(!rsametype(t1->link, t2->link, n, 0))
+				return 0;
+			t1 = t1->down;
+			t2 = t2->down;
+			while(t1 != T && t2 != T) {
+				if(t1->etype == TOLD) {
+					t1 = t1->down;
+					continue;
+				}
+				if(t2->etype == TOLD) {
+					t2 = t2->down;
+					continue;
+				}
+				while(t1 != T || t2 != T) {
+					if(!rsametype(t1, t2, n, 0))
+						return 0;
+					t1 = t1->down;
+					t2 = t2->down;
+				}
+				break;
+			}
+			return 1;
+		}
+		if(et == TARRAY)
+			if(t1->width != t2->width && t1->width != 0 && t2->width != 0)
+				return 0;
+		if(typesu[et]) {
+			if(t1->link == T)
+				snap(t1);
+			if(t2->link == T)
+				snap(t2);
+			if(t1 != t2 && t1->link == T && t2->link == T){
+				/* structs with missing or different tag names aren't considered equal */
+				if(t1->tag == nil || t2->tag == nil ||
+				   strcmp(t1->tag->name, t2->tag->name) != 0)
+					return 0;
+			}
+			t1 = t1->link;
+			t2 = t2->link;
+			for(;;) {
+				if(t1 == t2)
+					return 1;
+				if(!rsametype(t1, t2, n, 0))
+					return 0;
+				t1 = t1->down;
+				t2 = t2->down;
+			}
+		}
+		t1 = t1->link;
+		t2 = t2->link;
+		if((f || !debug['V']) && et == TIND) {
+			if(t1 != T && t1->etype == TVOID)
+				return 1;
+			if(t2 != T && t2->etype == TVOID)
+				return 1;
+		}
+	}
+}
+
+typedef struct Typetab Typetab;
+
+struct Typetab{
+	int n;
+	Type **a;
+};
+
+static int
+sigind(Type *t, Typetab *tt)
+{
+	int n;
+	Type **a, **na, **p, **e;
+
+	n = tt->n;
+	a = tt->a;
+	e = a+n;
+	/* linear search seems ok */
+	for(p = a ; p < e; p++)
+		if(sametype(*p, t))
+			return p-a;
+	if((n&15) == 0){
+		na = malloc((n+16)*sizeof(Type*));
+		memmove(na, a, n*sizeof(Type*));
+		free(a);
+		a = tt->a = na;
+	}
+	a[tt->n++] = t;
+	return -1;
+}
+
+static ulong
+signat(Type *t, Typetab *tt)
+{
+	int i;
+	Type *t1;
+	long s;
+
+	s = 0;
+	for(; t; t=t->link) {
+		s = s*thash1 + thash[t->etype];
+		if(t->garb&GINCOMPLETE)
+			return s;
+		switch(t->etype) {
+		default:
+			return s;
+		case TARRAY:
+			s = s*thash2 + 0;	/* was t->width */
+			break;
+		case TFUNC:
+			for(t1=t->down; t1; t1=t1->down)
+				s = s*thash3 + signat(t1, tt);
+			break;
+		case TSTRUCT:
+		case TUNION:
+			if((i = sigind(t, tt)) >= 0){
+				s = s*thash2 + i;
+				return s;
+			}
+			for(t1=t->link; t1; t1=t1->down)
+				s = s*thash3 + signat(t1, tt);
+			return s;
+		case TIND:
+			break;
+		}
+	}
+	return s;
+}
+
+ulong
+signature(Type *t)
+{
+	ulong s;
+	Typetab tt;
+
+	tt.n = 0;
+	tt.a = nil;
+	s = signat(t, &tt);
+	free(tt.a);
+	return s;
+}
+
+ulong
+sign(Sym *s)
+{
+	ulong v;
+	Type *t;
+
+	if(s->sig == SIGINTERN)
+		return SIGNINTERN;
+	if((t = s->type) == T)
+		return 0;
+	v = signature(t);
+	if(v == 0)
+		v = SIGNINTERN;
+	return v;
+}
+
+void
+snap(Type *t)
+{
+	if(typesu[t->etype])
+	if(t->link == T && t->tag && t->tag->suetag) {
+		t->link = t->tag->suetag->link;
+		t->width = t->tag->suetag->width;
+	}
+}
+
+Type*
+dotag(Sym *s, int et, int bn)
+{
+	Decl *d;
+
+	if(bn != 0 && bn != s->sueblock) {
+		d = push();
+		d->sym = s;
+		d->val = DSUE;
+		d->type = s->suetag;
+		d->block = s->sueblock;
+		s->suetag = T;
+	}
+	if(s->suetag == T) {
+		s->suetag = typ(et, T);
+		s->sueblock = autobn;
+	}
+	if(s->suetag->etype != et)
+		diag(Z, "tag used for more than one type: %s",
+			s->name);
+	if(s->suetag->tag == S)
+		s->suetag->tag = s;
+	return s->suetag;
+}
+
+Node*
+dcllabel(Sym *s, int f)
+{
+	Decl *d, d1;
+	Node *n;
+
+	n = s->label;
+	if(n != Z) {
+		if(f) {
+			if(n->complex)
+				diag(Z, "label reused: %s", s->name);
+			n->complex = 1;	// declared
+		} else
+			n->addable = 1;	// used
+		return n;
+	}
+
+	d = push();
+	d->sym = s;
+	d->val = DLABEL;
+	dclstack = d->link;
+
+	d1 = *firstdcl;
+	*firstdcl = *d;
+	*d = d1;
+
+	firstdcl->link = d;
+	firstdcl = d;
+
+	n = new(OXXX, Z, Z);
+	n->sym = s;
+	n->complex = f;
+	n->addable = !f;
+	s->label = n;
+
+	if(debug['d'])
+		dbgdecl(s);
+	return n;
+}
+
+Type*
+paramconv(Type *t, int f)
+{
+
+	switch(t->etype) {
+	case TARRAY:
+		t = typ(TIND, t->link);
+		t->width = types[TIND]->width;
+		break;
+
+	case TFUNC:
+		t = typ(TIND, t);
+		t->width = types[TIND]->width;
+		break;
+
+	case TFLOAT:
+		if(!f)
+			t = types[TDOUBLE];
+		break;
+
+	case TCHAR:
+	case TSHORT:
+		if(!f)
+			t = types[TINT];
+		break;
+
+	case TUCHAR:
+	case TUSHORT:
+		if(!f)
+			t = types[TUINT];
+		break;
+	}
+	return t;
+}
+
+void
+adecl(int c, Type *t, Sym *s)
+{
+
+	if(c == CSTATIC)
+		c = CLOCAL;
+	if(t->etype == TFUNC) {
+		if(c == CXXX)
+			c = CEXTERN;
+		if(c == CLOCAL)
+			c = CSTATIC;
+		if(c == CAUTO || c == CEXREG)
+			diag(Z, "function cannot be %s %s", cnames[c], s->name);
+	}
+	if(c == CXXX)
+		c = CAUTO;
+	if(s) {
+		if(s->class == CSTATIC)
+			if(c == CEXTERN || c == CGLOBL) {
+				warn(Z, "just say static: %s", s->name);
+				c = CSTATIC;
+			}
+		if(s->class == CAUTO || s->class == CPARAM || s->class == CLOCAL)
+		if(s->block == autobn)
+			diag(Z, "auto redeclaration of: %s", s->name);
+		if(c != CPARAM)
+			push1(s);
+		s->block = autobn;
+		s->offset = 0;
+		s->type = t;
+		s->class = c;
+		s->aused = 0;
+	}
+	switch(c) {
+	case CAUTO:
+		autoffset = align(autoffset, t, Aaut3);
+		stkoff = maxround(stkoff, autoffset);
+		s->offset = -autoffset;
+		break;
+
+	case CPARAM:
+		if(autoffset == 0) {
+			firstarg = s;
+			firstargtype = t;
+		}
+		autoffset = align(autoffset, t, Aarg1);
+		if(s)
+			s->offset = autoffset;
+		autoffset = align(autoffset, t, Aarg2);
+		break;
+	}
+}
+
+void
+pdecl(int c, Type *t, Sym *s)
+{
+	if(s && s->offset != -1) {
+		diag(Z, "not a parameter: %s", s->name);
+		return;
+	}
+	t = paramconv(t, c==CPARAM);
+	if(c == CXXX)
+		c = CPARAM;
+	if(c != CPARAM) {
+		diag(Z, "parameter cannot have class: %s", s->name);
+		c = CPARAM;
+	}
+	if(typesu[t->etype] && t->width <= 0)
+		diag(Z, "incomplete structure: %s", t->tag->name);
+	adecl(c, t, s);
+}
+
+void
+xdecl(int c, Type *t, Sym *s)
+{
+	long o;
+
+	o = 0;
+	switch(c) {
+	case CEXREG:
+		o = exreg(t);
+		if(o == 0)
+			c = CEXTERN;
+		if(s->class == CGLOBL)
+			c = CGLOBL;
+		break;
+
+	case CEXTERN:
+		if(s->class == CGLOBL)
+			c = CGLOBL;
+		break;
+
+	case CXXX:
+		c = CGLOBL;
+		if(s->class == CEXTERN)
+			s->class = CGLOBL;
+		break;
+
+	case CAUTO:
+		diag(Z, "overspecified class: %s %s %s", s->name, cnames[c], cnames[s->class]);
+		c = CEXTERN;
+		break;
+
+	case CTYPESTR:
+		if(!typesuv[t->etype]) {
+			diag(Z, "typestr must be struct/union: %s", s->name);
+			break;
+		}
+		dclfunct(t, s);
+		break;
+	}
+
+	if(s->class == CSTATIC)
+		if(c == CEXTERN || c == CGLOBL) {
+			warn(Z, "overspecified class: %s %s %s", s->name, cnames[c], cnames[s->class]);
+			c = CSTATIC;
+		}
+	if(s->type != T)
+		if(s->class != c || !sametype(t, s->type) || t->etype == TENUM) {
+			diag(Z, "external redeclaration of: %s", s->name);
+			Bprint(&diagbuf, "	%s %T %L\n", cnames[c], t, nearln);
+			Bprint(&diagbuf, "	%s %T %L\n", cnames[s->class], s->type, s->varlineno);
+		}
+	tmerge(t, s);
+	s->type = t;
+	s->class = c;
+	s->block = 0;
+	s->offset = o;
+}
+
+void
+tmerge(Type *t1, Sym *s)
+{
+	Type *ta, *tb, *t2;
+
+	t2 = s->type;
+/*print("merge	%T; %T\n", t1, t2); */
+	for(;;) {
+		if(t1 == T || t2 == T || t1 == t2)
+			break;
+		if(t1->etype != t2->etype)
+			break;
+		switch(t1->etype) {
+		case TFUNC:
+			ta = t1->down;
+			tb = t2->down;
+			if(ta == T) {
+				t1->down = tb;
+				break;
+			}
+			if(tb == T)
+				break;
+			while(ta != T && tb != T) {
+				if(ta == tb)
+					break;
+				/* ignore old-style flag */
+				if(ta->etype == TOLD) {
+					ta = ta->down;
+					continue;
+				}
+				if(tb->etype == TOLD) {
+					tb = tb->down;
+					continue;
+				}
+				/* checking terminated by ... */
+				if(ta->etype == TDOT && tb->etype == TDOT) {
+					ta = T;
+					tb = T;
+					break;
+				}
+				if(!sametype(ta, tb))
+					break;
+				ta = ta->down;
+				tb = tb->down;
+			}
+			if(ta != tb)
+				diag(Z, "function inconsistently declared: %s", s->name);
+
+			/* take new-style over old-style */
+			ta = t1->down;
+			tb = t2->down;
+			if(ta != T && ta->etype == TOLD)
+				if(tb != T && tb->etype != TOLD)
+					t1->down = tb;
+			break;
+
+		case TARRAY:
+			/* should we check array size change? */
+			if(t2->width > t1->width)
+				t1->width = t2->width;
+			break;
+
+		case TUNION:
+		case TSTRUCT:
+			return;
+		}
+		t1 = t1->link;
+		t2 = t2->link;
+	}
+}
+
+void
+edecl(int c, Type *t, Sym *s)
+{
+	Type *t1;
+
+	if(s == S) {
+		if(!typesu[t->etype])
+			diag(Z, "unnamed structure element must be struct/union");
+		if(c != CXXX)
+			diag(Z, "unnamed structure element cannot have class");
+	} else
+		if(c != CXXX)
+			diag(Z, "structure element cannot have class: %s", s->name);
+	t1 = t;
+	t = copytyp(t1);
+	t->sym = s;
+	t->down = T;
+	if(lastfield) {
+		t->shift = lastbit - lastfield;
+		t->nbits = lastfield;
+		if(firstbit)
+			t->shift = -t->shift;
+		if(typeu[t->etype])
+			t->etype = tufield->etype;
+		else
+			t->etype = tfield->etype;
+	}
+	if(strf == T)
+		strf = t;
+	else
+		strl->down = t;
+	strl = t;
+}
+
+/*
+ * this routine is very suspect.
+ * ansi requires the enum type to
+ * be represented as an 'int'
+ * this means that 0x81234567
+ * would be illegal. this routine
+ * makes signed and unsigned go
+ * to unsigned.
+ */
+Type*
+maxtype(Type *t1, Type *t2)
+{
+
+	if(t1 == T)
+		return t2;
+	if(t2 == T)
+		return t1;
+	if(t1->etype > t2->etype)
+		return t1;
+	return t2;
+}
+
+void
+doenum(Sym *s, Node *n)
+{
+
+	if(n) {
+		complex(n);
+		if(n->op != OCONST) {
+			diag(n, "enum not a constant: %s", s->name);
+			return;
+		}
+		en.cenum = n->type;
+		en.tenum = maxtype(en.cenum, en.tenum);
+
+		if(!typefd[en.cenum->etype])
+			en.lastenum = n->vconst;
+		else
+			en.floatenum = n->fconst;
+	}
+	if(dclstack)
+		push1(s);
+	xdecl(CXXX, types[TENUM], s);
+
+	if(en.cenum == T) {
+		en.tenum = types[TINT];
+		en.cenum = types[TINT];
+		en.lastenum = 0;
+	}
+	s->tenum = en.cenum;
+
+	if(!typefd[s->tenum->etype]) {
+		s->vconst = convvtox(en.lastenum, s->tenum->etype);
+		en.lastenum++;
+	} else {
+		s->fconst = en.floatenum;
+		en.floatenum++;
+	}
+
+	if(debug['d'])
+		dbgdecl(s);
+	acidvar(s);
+}
+
+void
+symadjust(Sym *s, Node *n, long del)
+{
+
+	switch(n->op) {
+	default:
+		if(n->left)
+			symadjust(s, n->left, del);
+		if(n->right)
+			symadjust(s, n->right, del);
+		return;
+
+	case ONAME:
+		if(n->sym == s)
+			n->xoffset -= del;
+		return;
+
+	case OCONST:
+	case OSTRING:
+	case OLSTRING:
+	case OINDREG:
+	case OREGISTER:
+		return;
+	}
+}
+
+Node*
+contig(Sym *s, Node *n, long v)
+{
+	Node *p, *r, *q, *m;
+	long w;
+	Type *zt;
+
+	if(debug['i']) {
+		print("contig v = %ld; s = %s\n", v, s->name);
+		prtree(n, "doinit value");
+	}
+
+	if(n == Z)
+		goto no;
+	w = s->type->width;
+
+	/*
+	 * nightmare: an automatic array whose size
+	 * increases when it is initialized
+	 */
+	if(v != w) {
+		if(v != 0)
+			diag(n, "automatic adjustable array: %s", s->name);
+		v = s->offset;
+		autoffset = align(autoffset, s->type, Aaut3);
+		s->offset = -autoffset;
+		stkoff = maxround(stkoff, autoffset);
+		symadjust(s, n, v - s->offset);
+	}
+	if(w <= ewidth[TIND])
+		goto no;
+	if(n->op == OAS)
+		diag(Z, "oops in contig");
+/*ZZZ this appears incorrect
+need to check if the list completely covers the data.
+if not, bail
+ */
+	if(n->op == OLIST)
+		goto no;
+	if(n->op == OASI)
+		if(n->left->type)
+		if(n->left->type->width == w)
+			goto no;
+	while(w & (ewidth[TIND]-1))
+		w++;
+/*
+ * insert the following code, where long becomes vlong if pointers are fat
+ *
+	*(long**)&X = (long*)((char*)X + sizeof(X));
+	do {
+		*(long**)&X -= 1;
+		**(long**)&X = 0;
+	} while(*(long**)&X);
+ */
+
+	for(q=n; q->op != ONAME; q=q->left)
+		;
+
+	zt = ewidth[TIND] > ewidth[TLONG]? types[TVLONG]: types[TLONG];
+
+	p = new(ONAME, Z, Z);
+	*p = *q;
+	p->type = typ(TIND, zt);
+	p->xoffset = s->offset;
+
+	r = new(ONAME, Z, Z);
+	*r = *p;
+	r = new(OPOSTDEC, r, Z);
+
+	q = new(ONAME, Z, Z);
+	*q = *p;
+	q = new(OIND, q, Z);
+
+	m = new(OCONST, Z, Z);
+	m->vconst = 0;
+	m->type = zt;
+
+	q = new(OAS, q, m);
+
+	r = new(OLIST, r, q);
+
+	q = new(ONAME, Z, Z);
+	*q = *p;
+	r = new(ODWHILE, q, r);
+
+	q = new(ONAME, Z, Z);
+	*q = *p;
+	q->type = q->type->link;
+	q->xoffset += w;
+	q = new(OADDR, q, 0);
+
+	q = new(OASI, p, q);
+	r = new(OLIST, q, r);
+
+	n = new(OLIST, r, n);
+
+no:
+	return n;
+}

+ 506 - 0
sys/src/libcc/dpchk.c

@@ -0,0 +1,506 @@
+/*
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+
+#include	"cc.h"
+#include	"y.tab.h"
+
+enum
+{
+	Fnone	= 0,
+	Fl,
+	Fvl,
+	Fignor,
+	Fstar,
+	Fadj,
+
+	Fverb	= 10,
+};
+
+typedef	struct	Tprot	Tprot;
+struct	Tprot
+{
+	Type*	type;
+	Bits	flag;
+	Tprot*	link;
+};
+
+typedef	struct	Tname	Tname;
+struct	Tname
+{
+	char*	name;
+	int	param;
+	Tname*	link;
+};
+
+static	Type*	indchar;
+static	uchar	flagbits[512];
+static	char	fmtbuf[100];
+static	int	lastadj;
+static	int	lastverb;
+static	int	nstar;
+static	Tprot*	tprot;
+static	Tname*	tname;
+
+void
+argflag(int c, int v)
+{
+
+	switch(v) {
+	case Fignor:
+	case Fstar:
+	case Fl:
+	case Fvl:
+		flagbits[c] = v;
+		break;
+	case Fverb:
+		flagbits[c] = lastverb;
+/*print("flag-v %c %d\n", c, lastadj);*/
+		lastverb++;
+		break;
+	case Fadj:
+		flagbits[c] = lastadj;
+/*print("flag-l %c %d\n", c, lastadj);*/
+		lastadj++;
+		break;
+	}
+}
+
+Bits
+getflag(char *s)
+{
+	Bits flag;
+	int f;
+	char *fmt;
+	Rune c;
+
+	fmt = fmtbuf;
+	flag = zbits;
+	nstar = 0;
+	for(;;) {
+		s += chartorune(&c, s);
+		fmt += runetochar(fmt, &c);
+		if(c == 0 || c >= nelem(flagbits))
+			break;
+		f = flagbits[c];
+		switch(f) {
+		case Fnone:
+			argflag(c, Fverb);
+			f = flagbits[c];
+			break;
+		case Fstar:
+			nstar++;
+		case Fignor:
+			continue;
+		case Fl:
+			if(bset(flag, Fl))
+				flag = bor(flag, blsh(Fvl));
+		}
+		flag = bor(flag, blsh(f));
+		if(f >= Fverb)
+			break;
+	}
+	*fmt = 0;
+	return flag;
+}
+
+void
+newprot(Sym *m, Type *t, char *s)
+{
+	Bits flag;
+	Tprot *l;
+
+	if(t == T) {
+		warn(Z, "%s: newprot: type not defined", m->name);
+		return;
+	}
+	flag = getflag(s);
+	for(l=tprot; l; l=l->link)
+		if(beq(flag, l->flag) && sametype(t, l->type))
+			return;
+	l = alloc(sizeof(*l));
+	l->type = t;
+	l->flag = flag;
+	l->link = tprot;
+	tprot = l;
+}
+
+void
+newname(char *s, int p)
+{
+	Tname *l;
+
+	for(l=tname; l; l=l->link)
+		if(strcmp(l->name, s) == 0) {
+			if(l->param != p)
+				yyerror("vargck %s already defined\n", s);
+			return;
+		}
+	l = alloc(sizeof(*l));
+	l->name = s;
+	l->param = p;
+	l->link = tname;
+	tname = l;
+}
+
+void
+arginit(void)
+{
+	int i;
+
+/* debug['F'] = 1;*/
+/* debug['w'] = 1;*/
+
+	lastadj = Fadj;
+	lastverb = Fverb;
+	indchar = typ(TIND, types[TCHAR]);
+
+	memset(flagbits, Fnone, sizeof(flagbits));
+
+	for(i='0'; i<='9'; i++)
+		argflag(i, Fignor);
+	argflag('.', Fignor);
+	argflag('#', Fignor);
+	argflag('u', Fignor);
+	argflag('h', Fignor);
+	argflag('+', Fignor);
+	argflag('-', Fignor);
+
+	argflag('*', Fstar);
+	argflag('l', Fl);
+
+	argflag('o', Fverb);
+	flagbits['x'] = flagbits['o'];
+	flagbits['X'] = flagbits['o'];
+}
+
+void
+pragvararg(void)
+{
+	Sym *s;
+	int n, c;
+	char *t;
+	Rune r;
+	Type *ty;
+
+	if(!debug['F'])
+		goto out;
+	s = getsym();
+	if(s && strcmp(s->name, "argpos") == 0)
+		goto ckpos;
+	if(s && strcmp(s->name, "type") == 0)
+		goto cktype;
+	if(s && strcmp(s->name, "flag") == 0)
+		goto ckflag;
+	yyerror("syntax in #pragma varargck");
+	goto out;
+
+ckpos:
+/*#pragma	varargck	argpos	warn	2*/
+	s = getsym();
+	if(s == S)
+		goto bad;
+	n = getnsn();
+	if(n < 0)
+		goto bad;
+	newname(s->name, n);
+	goto out;
+
+ckflag:
+/*#pragma	varargck	flag	'c'*/
+	c = getnsc();
+	if(c != '\'')
+		goto bad;
+	c = getr();
+	if(c == '\\')
+		c = getr();
+	else if(c == '\'')
+		goto bad;
+	if(c == '\n')
+		goto bad;
+	if(getc() != '\'')
+		goto bad;
+	argflag(c, Fignor);
+	goto out;
+
+cktype:
+/*#pragma	varargck	type	O	int*/
+	c = getnsc();
+	if(c != '"')
+		goto bad;
+	t = fmtbuf;
+	for(;;) {
+		r = getr();
+		if(r == ' ' || r == '\n')
+			goto bad;
+		if(r == '"')
+			break;
+		t += runetochar(t, &r);
+	}
+	*t = 0;
+	t = strdup(fmtbuf);
+	s = getsym();
+	if(s == S)
+		goto bad;
+	ty = s->type;
+	while((c = getnsc()) == '*')
+		ty = typ(TIND, ty);
+	unget(c);
+	newprot(s, ty, t);
+	goto out;
+
+bad:
+	yyerror("syntax in #pragma varargck");
+
+out:
+	while(getnsc() != '\n')
+		;
+}
+
+Node*
+nextarg(Node *n, Node **a)
+{
+	if(n == Z) {
+		*a = Z;
+		return Z;
+	}
+	if(n->op == OLIST) {
+		*a = n->left;
+		return n->right;
+	}
+	*a = n;
+	return Z;
+}
+
+void
+checkargs(Node *nn, char *s, int pos)
+{
+	Node *a, *n;
+	Bits flag;
+	Tprot *l;
+
+	if(!debug['F'])
+		return;
+	n = nn;
+	for(;;) {
+		s = strchr(s, '%');
+		if(s == 0) {
+			nextarg(n, &a);
+			if(a != Z)
+				warn(nn, "more arguments than format %T",
+					a->type);
+			return;
+		}
+		s++;
+		flag = getflag(s);
+		while(nstar > 0) {
+			n = nextarg(n, &a);
+			pos++;
+			nstar--;
+			if(a == Z) {
+				warn(nn, "more format than arguments %s",
+					fmtbuf);
+				return;
+			}
+			if(a->type == T)
+				continue;
+			if(!sametype(types[TINT], a->type) &&
+			   !sametype(types[TUINT], a->type))
+				warn(nn, "format mismatch '*' in %s %T, arg %d",
+					fmtbuf, a->type, pos);
+		}
+		for(l=tprot; l; l=l->link)
+			if(sametype(types[TVOID], l->type)) {
+				if(beq(flag, l->flag)) {
+					s++;
+					goto loop;
+				}
+			}
+
+		n = nextarg(n, &a);
+		pos++;
+		if(a == Z) {
+			warn(nn, "more format than arguments %s",
+				fmtbuf);
+			return;
+		}
+		if(a->type == 0)
+			continue;
+		for(l=tprot; l; l=l->link)
+			if(sametype(a->type, l->type)) {
+/*print("checking %T/%ulx %T/%ulx\n", a->type, flag.b[0], l->type, l->flag.b[0]);*/
+				if(beq(flag, l->flag))
+					goto loop;
+			}
+		warn(nn, "format mismatch %s %T, arg %d", fmtbuf, a->type, pos);
+	loop:;
+	}
+}
+
+void
+dpcheck(Node *n)
+{
+	char *s;
+	Node *a, *b;
+	Tname *l;
+	int i;
+
+	if(n == Z)
+		return;
+	b = n->left;
+	if(b == Z || b->op != ONAME)
+		return;
+	s = b->sym->name;
+	for(l=tname; l; l=l->link)
+		if(strcmp(s, l->name) == 0)
+			break;
+	if(l == 0)
+		return;
+
+	i = l->param;
+	b = n->right;
+	while(i > 0) {
+		b = nextarg(b, &a);
+		i--;
+	}
+	if(a == Z) {
+		warn(n, "cant find format arg");
+		return;
+	}
+	if(!sametype(indchar, a->type)) {
+		warn(n, "format arg type %T", a->type);
+		return;
+	}
+	if(a->op != OADDR || a->left->op != ONAME || a->left->sym != symstring) {
+/*		warn(n, "format arg not constant string");*/
+		return;
+	}
+	s = a->left->cstring;
+	checkargs(b, s, l->param);
+}
+
+void
+pragpack(void)
+{
+	Sym *s;
+
+	packflg = 0;
+	s = getsym();
+	if(s) {
+		packflg = atoi(s->name+1);
+		if(strcmp(s->name, "on") == 0 ||
+		   strcmp(s->name, "yes") == 0)
+			packflg = 1;
+	}
+	while(getnsc() != '\n')
+		;
+	if(debug['f']){
+		if(packflg)
+			print("%4ld: pack %d\n", lineno, packflg);
+		else
+			print("%4ld: pack off\n", lineno);
+	}
+}
+
+void
+pragfpround(void)
+{
+	Sym *s;
+
+	fproundflg = 0;
+	s = getsym();
+	if(s) {
+		fproundflg = atoi(s->name+1);
+		if(strcmp(s->name, "on") == 0 ||
+		   strcmp(s->name, "yes") == 0)
+			fproundflg = 1;
+	}
+	while(getnsc() != '\n')
+		;
+	if(debug['f']){
+		if(fproundflg)
+			print("%4ld: fproundflg %d\n", lineno, fproundflg);
+		else
+			print("%4ld: fproundflg off\n", lineno);
+	}
+}
+
+void
+pragprofile(void)
+{
+	Sym *s;
+
+	profileflg = 0;
+	s = getsym();
+	if(s) {
+		profileflg = atoi(s->name+1);
+		if(strcmp(s->name, "on") == 0 ||
+		   strcmp(s->name, "yes") == 0)
+			profileflg = 1;
+	}
+	while(getnsc() != '\n')
+		;
+	if(debug['f']){
+		if(profileflg)
+			print("%4ld: profileflg %d\n", lineno, profileflg);
+		else
+			print("%4ld: profileflg off\n", lineno);
+	}
+}
+
+void
+pragincomplete(void)
+{
+	Sym *s;
+	Type *t;
+	int istag, w, et;
+
+	istag = 0;
+	s = getsym();
+	if(s == nil)
+		goto out;
+	et = 0;
+	w = s->lexical;
+	if(w == LSTRUCT)
+		et = TSTRUCT;
+	else if(w == LUNION)
+		et = TUNION;
+	if(et != 0){
+		s = getsym();
+		if(s == nil){
+			yyerror("missing struct/union tag in pragma incomplete");
+			goto out;
+		}
+		if(s->lexical != LNAME && s->lexical != LTYPE){
+			yyerror("invalid struct/union tag: %s", s->name);
+			goto out;
+		}
+		dotag(s, et, 0);
+		istag = 1;
+	}else if(strcmp(s->name, "_off_") == 0){
+		debug['T'] = 0;
+		goto out;
+	}else if(strcmp(s->name, "_on_") == 0){
+		debug['T'] = 1;
+		goto out;
+	}
+	t = s->type;
+	if(istag)
+		t = s->suetag;
+	if(t == T)
+		yyerror("unknown type %s in pragma incomplete", s->name);
+	else if(!typesu[t->etype])
+		yyerror("not struct/union type in pragma incomplete: %s", s->name);
+	else
+		t->garb |= GINCOMPLETE;
+out:
+	while(getnsc() != '\n')
+		;
+	if(debug['f'])
+		print("%s incomplete\n", s->name);
+}

+ 409 - 0
sys/src/libcc/funct.c

@@ -0,0 +1,409 @@
+/*
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+
+#include	"cc.h"
+
+typedef	struct	Ftab	Ftab;
+struct	Ftab
+{
+	char	op;
+	char*	name;
+	char	typ;
+};
+typedef	struct	Gtab	Gtab;
+struct	Gtab
+{
+	char	etype;
+	char*	name;
+};
+
+Ftab	ftabinit[OEND];
+Gtab	gtabinit[NTYPE];
+
+int
+isfunct(Node *n)
+{
+	Type *t, *t1;
+	Funct *f;
+	Node *l;
+	Sym *s;
+	int o;
+
+	o = n->op;
+	if(n->left == Z)
+		goto no;
+	t = n->left->type;
+	if(t == T)
+		goto no;
+	f = t->funct;
+
+	switch(o) {
+	case OAS:	// put cast on rhs
+	case OASI:
+	case OASADD:
+	case OASAND:
+	case OASASHL:
+	case OASASHR:
+	case OASDIV:
+	case OASLDIV:
+	case OASLMOD:
+	case OASLMUL:
+	case OASLSHR:
+	case OASMOD:
+	case OASMUL:
+	case OASOR:
+	case OASSUB:
+	case OASXOR:
+		if(n->right == Z)
+			goto no;
+		t1 = n->right->type;
+		if(t1 == T)
+			goto no;
+		if(t1->funct == f)
+			break;
+
+		l = new(OXXX, Z, Z);
+		*l = *n->right;
+
+		n->right->left = l;
+		n->right->right = Z;
+		n->right->type = t;
+		n->right->op = OCAST;
+
+		if(!isfunct(n->right))
+			prtree(n, "isfunc !");
+		break;
+
+	case OCAST:	// t f(T) or T f(t)
+		t1 = n->type;
+		if(t1 == T)
+			goto no;
+		if(f != nil) {
+			s = f->castfr[t1->etype];
+			if(s == S)
+				goto no;
+			n->right = n->left;
+			goto build;
+		}
+		f = t1->funct;
+		if(f != nil) {
+			s = f->castto[t->etype];
+			if(s == S)
+				goto no;
+			n->right = n->left;
+			goto build;
+		}
+		goto no;
+	}
+
+	if(f == nil)
+		goto no;
+	s = f->sym[o];
+	if(s == S)
+		goto no;
+
+	/*
+	 * the answer is yes,
+	 * now we rewrite the node
+	 * and give diagnostics
+	 */
+	switch(o) {
+	default:
+		diag(n, "isfunct op missing %O\n", o);
+		goto bad;
+
+	case OADD:	// T f(T, T)
+	case OAND:
+	case OASHL:
+	case OASHR:
+	case ODIV:
+	case OLDIV:
+	case OLMOD:
+	case OLMUL:
+	case OLSHR:
+	case OMOD:
+	case OMUL:
+	case OOR:
+	case OSUB:
+	case OXOR:
+
+	case OEQ:	// int f(T, T)
+	case OGE:
+	case OGT:
+	case OHI:
+	case OHS:
+	case OLE:
+	case OLO:
+	case OLS:
+	case OLT:
+	case ONE:
+		if(n->right == Z)
+			goto bad;
+		t1 = n->right->type;
+		if(t1 == T)
+			goto bad;
+		if(t1->funct != f)
+			goto bad;
+		n->right = new(OLIST, n->left, n->right);
+		break;
+
+	case OAS:	// structure copies done by the compiler
+	case OASI:
+		goto no;
+
+	case OASADD:	// T f(T*, T)
+	case OASAND:
+	case OASASHL:
+	case OASASHR:
+	case OASDIV:
+	case OASLDIV:
+	case OASLMOD:
+	case OASLMUL:
+	case OASLSHR:
+	case OASMOD:
+	case OASMUL:
+	case OASOR:
+	case OASSUB:
+	case OASXOR:
+		if(n->right == Z)
+			goto bad;
+		t1 = n->right->type;
+		if(t1 == T)
+			goto bad;
+		if(t1->funct != f)
+			goto bad;
+		n->right = new(OLIST, new(OADDR, n->left, Z), n->right);
+		break;
+
+	case OPOS:	// T f(T)
+	case ONEG:
+	case ONOT:
+	case OCOM:
+		n->right = n->left;
+		break;
+
+
+	}
+
+build:
+	l = new(ONAME, Z, Z);
+	l->sym = s;
+	l->type = s->type;
+	l->etype = s->type->etype;
+	l->xoffset = s->offset;
+	l->class = s->class;
+	tcomo(l, 0);
+
+	n->op = OFUNC;
+	n->left = l;
+	n->type = l->type->link;
+	if(tcompat(n, T, l->type, tfunct))
+		goto bad;
+	if(tcoma(n->left, n->right, l->type->down, 1))
+		goto bad;
+	return 1;
+
+no:
+	return 0;
+
+bad:
+	diag(n, "cant rewrite typestr for op %O\n", o);
+	prtree(n, "isfunct");
+	n->type = T;
+	return 1;
+}
+
+void
+dclfunct(Type *t, Sym *s)
+{
+	Funct *f;
+	Node *n;
+	Type *f1, *f2, *f3, *f4;
+	int o, i, c;
+	char str[100];
+
+	if(t->funct)
+		return;
+
+	// recognize generated tag of dorm _%d_
+	if(t->tag == S)
+		goto bad;
+	for(i=0; (c = t->tag->name[i]); i++) {
+		if(c == '_') {
+			if(i == 0 || t->tag->name[i+1] == 0)
+				continue;
+			break;
+		}
+		if(c < '0' || c > '9')
+			break;
+	}
+	if(c == 0)
+		goto bad;
+
+	f = alloc(sizeof(*f));
+	for(o=0; o<nelem(f->sym); o++)
+		f->sym[o] = S;
+
+	t->funct = f;
+
+	f1 = typ(TFUNC, t);
+	f1->down = copytyp(t);
+	f1->down->down = t;
+
+	f2 = typ(TFUNC, types[TINT]);
+	f2->down = copytyp(t);
+	f2->down->down = t;
+
+	f3 = typ(TFUNC, t);
+	f3->down = typ(TIND, t);
+	f3->down->down = t;
+
+	f4 = typ(TFUNC, t);
+	f4->down = t;
+
+	for(i=0;; i++) {
+		o = ftabinit[i].op;
+		if(o == OXXX)
+			break;
+		sprint(str, "%s_%s_", t->tag->name, ftabinit[i].name);
+		n = new(ONAME, Z, Z);
+		n->sym = slookup(str);
+		f->sym[o] = n->sym;
+		switch(ftabinit[i].typ) {
+		default:
+			diag(Z, "dclfunct op missing %d\n", ftabinit[i].typ);
+			break;
+
+		case 1:	// T f(T,T)	+
+			dodecl(xdecl, CEXTERN, f1, n);
+			break;
+
+		case 2:	// int f(T,T)	==
+			dodecl(xdecl, CEXTERN, f2, n);
+			break;
+
+		case 3:	// void f(T*,T)	+=
+			dodecl(xdecl, CEXTERN, f3, n);
+			break;
+
+		case 4:	// T f(T)	~
+			dodecl(xdecl, CEXTERN, f4, n);
+			break;
+		}
+	}
+	for(i=0;; i++) {
+		o = gtabinit[i].etype;
+		if(o == TXXX)
+			break;
+
+		/*
+		 * OCAST types T1 _T2_T1_(T2)
+		 */
+		sprint(str, "_%s%s_", gtabinit[i].name, t->tag->name);
+		n = new(ONAME, Z, Z);
+		n->sym = slookup(str);
+		f->castto[o] = n->sym;
+
+		f1 = typ(TFUNC, t);
+		f1->down = types[o];
+		dodecl(xdecl, CEXTERN, f1, n);
+
+		sprint(str, "%s_%s_", t->tag->name, gtabinit[i].name);
+		n = new(ONAME, Z, Z);
+		n->sym = slookup(str);
+		f->castfr[o] = n->sym;
+
+		f1 = typ(TFUNC, types[o]);
+		f1->down = t;
+		dodecl(xdecl, CEXTERN, f1, n);
+	}
+	return;
+bad:
+	diag(Z, "dclfunct bad %T %s\n", t, s->name);
+}
+
+Gtab	gtabinit[NTYPE] =
+{
+	{TCHAR,		"c"},
+	{TUCHAR,		"uc"},
+	{TSHORT,		"h"},
+	{TUSHORT,	"uh"},
+	{TINT,		"i"},
+	{TUINT,		"ui"},
+	{TLONG,		"l"},
+	{TULONG,		"ul"},
+	{TVLONG,		"v"},
+	{TUVLONG,	"uv"},
+	{TFLOAT,		"f"},
+	{TDOUBLE,	"d"},
+	{TXXX}
+};
+
+Ftab	ftabinit[OEND] =
+{
+	{OADD,		"add",		1},
+	{OAND,		"and",		1},
+	{OASHL,		"ashl",		1},
+	{OASHR,		"ashr",		1},
+	{ODIV,		"div",		1},
+	{OLDIV,		"ldiv",		1},
+	{OLMOD,		"lmod",		1},
+	{OLMUL,		"lmul",		1},
+	{OLSHR,		"lshr",		1},
+	{OMOD,		"mod",		1},
+	{OMUL,		"mul",		1},
+	{OOR,		"or",		1},
+	{OSUB,		"sub",		1},
+	{OXOR,		"xor",		1},
+
+	{OEQ,		"eq",		2},
+	{OGE,		"ge",		2},
+	{OGT,		"gt",		2},
+	{OHI,		"hi",		2},
+	{OHS,		"hs",		2},
+	{OLE,		"le",		2},
+	{OLO,		"lo",		2},
+	{OLS,		"ls",		2},
+	{OLT,		"lt",		2},
+	{ONE,		"ne",		2},
+
+	{OASADD,		"asadd",	3},
+	{OASAND,		"asand",	3},
+	{OASASHL,	"asashl",	3},
+	{OASASHR,	"asashr",	3},
+	{OASDIV,		"asdiv",	3},
+	{OASLDIV,	"asldiv",	3},
+	{OASLMOD,	"aslmod",	3},
+	{OASLMUL,	"aslmul",	3},
+	{OASLSHR,	"aslshr",	3},
+	{OASMOD,		"asmod",	3},
+	{OASMUL,		"asmul",	3},
+	{OASOR,		"asor",		3},
+	{OASSUB,		"assub",	3},
+	{OASXOR,		"asxor",	3},
+
+	{OPOS,		"pos",		4},
+	{ONEG,		"neg",		4},
+	{OCOM,		"com",		4},
+	{ONOT,		"not",		4},
+
+//	{OPOSTDEC},
+//	{OPOSTINC},
+//	{OPREDEC},
+//	{OPREINC},
+
+	{OXXX},
+};
+
+//	Node*	nodtestv;
+
+//	Node*	nodvpp;
+//	Node*	nodppv;
+//	Node*	nodvmm;
+//	Node*	nodmmv;

+ 1570 - 0
sys/src/libcc/lex.c

@@ -0,0 +1,1570 @@
+/*
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+
+#include	"cc.h"
+#include	"y.tab.h"
+
+#ifndef	CPP
+#define	CPP	"/bin/cpp"
+#endif
+
+/*
+ * known debug flags
+ *	-a		acid declaration output
+ *	-A		!B
+ *	-B		non ANSI
+ *	-d		print declarations
+ *	-D name		define
+ *	-F		format specification check
+ *	-i		print initialization
+ *	-I path		include
+ *	-l		generate little-endian code
+ *	-L		print every NAME symbol
+ *	-M		constant multiplication
+ *	-m		print add/sub/mul trees
+ *	-n		print acid to file (%.c=%.acid) (with -a or -aa)
+ *	-o file		output file
+ *	-p		use standard cpp ANSI preprocessor (not on windows)
+ *	-r		print registerization
+ *	-s		print structure offsets (with -a or -aa)
+ *	-S		print assembly
+ *	-t		print type trees
+ *	-V		enable void* conversion warnings
+ *	-v		verbose printing
+ *	-w		print warnings
+ *	-X		abort on error
+ *	-.		Inhibit search for includes in source directory
+ */
+
+void
+main(int argc, char *argv[])
+{
+	char **defs, **np, *p;
+	int nproc, nout, status, i, c, ndef, maxdef;
+
+	memset(debug, 0, sizeof(debug));
+	tinit();
+	cinit();
+	ginit();
+	arginit();
+
+	profileflg = 1;	/* #pragma can turn it off */
+	tufield = simplet((1L<<tfield->etype) | BUNSIGNED);
+	maxdef = 0;
+	ndef = 0;
+	outfile = 0;
+	defs = nil;
+	setinclude(".");
+	ARGBEGIN {
+	default:
+		c = ARGC();
+		if(c >= 0 && c < sizeof(debug))
+			debug[c]++;
+		break;
+
+	case 'l':			/* for little-endian mips */
+		if(thechar != 'v'){
+			print("can only use -l with vc");
+			errorexit();
+		}
+		thechar = '0';
+		thestring = "spim";
+		break;
+
+	case 'o':
+		outfile = ARGF();
+		break;
+
+	case 'D':
+		p = ARGF();
+		if(p) {
+			if(ndef >= maxdef){
+				maxdef += 50;
+				np = alloc(maxdef * sizeof *np);
+				if(defs != nil)
+					memmove(np, defs, (maxdef - 50) * sizeof *np);
+				defs = np;
+			}
+			defs[ndef++] = p;
+			dodefine(p);
+		}
+		break;
+
+	case 'I':
+		p = ARGF();
+		if(p)
+			setinclude(p);
+		break;
+	} ARGEND
+	if(argc < 1 && outfile == 0) {
+		print("usage: %cc [-options] files\n", thechar);
+		errorexit();
+	}
+	if(argc > 1 && systemtype(Windows)){
+		print("can't compile multiple files on windows\n");
+		errorexit();
+	}
+	if(argc > 1 && !systemtype(Windows)) {
+		nproc = 1;
+		/*
+		 * if we're writing acid to standard output, don't compile
+		 * concurrently, to avoid interleaving output.
+		 */
+		if(((!debug['a'] && !debug['Z']) || debug['n']) &&
+		    (p = getenv("NPROC")) != nil)
+			nproc = atol(p);	/* */
+		c = 0;
+		nout = 0;
+		for(;;) {
+			while(nout < nproc && argc > 0) {
+				i = myfork();
+				if(i < 0) {
+					i = mywait(&status);
+					if(i < 0) {
+						print("cannot create a process\n");
+						errorexit();
+					}
+					if(status)
+						c++;
+					nout--;
+					continue;
+				}
+				if(i == 0) {
+					fprint(2, "%s:\n", *argv);
+					if (compile(*argv, defs, ndef))
+						errorexit();
+					exits(0);
+				}
+				nout++;
+				argc--;
+				argv++;
+			}
+			i = mywait(&status);
+			if(i < 0) {
+				if(c)
+					errorexit();
+				exits(0);
+			}
+			if(status)
+				c++;
+			nout--;
+		}
+	}
+
+	if(argc == 0)
+		c = compile("stdin", defs, ndef);
+	else
+		c = compile(argv[0], defs, ndef);
+
+	if(c)
+		errorexit();
+	exits(0);
+}
+
+int
+compile(char *file, char **defs, int ndef)
+{
+	char ofile[400], incfile[20];
+	char *p, **av, opt[256];
+	int i, c, fd[2];
+	static int first = 1;
+
+	strcpy(ofile, file);
+	p = utfrrune(ofile, pathchar());
+	if(p) {
+		*p++ = 0;
+		if(!debug['.'])
+			include[0] = strdup(ofile);
+	} else
+		p = ofile;
+
+	if(outfile == 0) {
+		outfile = p;
+		if(outfile) {
+			if((p = utfrrune(outfile, '.'))){
+				if(p[1] == 'c' && p[2] == 0)
+					p[0] = 0;
+			}
+			p = utfrune(outfile, 0);
+			if(debug['a'] && debug['n']){
+				strcat(p, ".acid");
+			} else if (debug['Z'] && debug['n']){
+				strcat(p, "_pickle.c");
+			} else {
+				p[0] = '.';
+				p[1] = thechar;
+				p[2] = 0;
+			}
+		} else
+			outfile = "/dev/null";
+	}
+
+	if((p = getenv("INCLUDE"))) {
+		setinclude(p);
+	} else {
+		if(systemtype(Plan9)) {
+			sprint(incfile, "/%s/include", thestring);
+			setinclude(strdup(incfile));
+			setinclude("/sys/include");
+		}
+	}
+	if (first)
+		Binit(&diagbuf, 1, OWRITE);
+	/*
+	 * if we're writing acid to standard output, don't keep scratching
+	 * outbuf.
+	 */
+	if((debug['a'] || debug['Z']) && !debug['n']) {
+		if (first) {
+			outfile = 0;
+			Binit(&outbuf, dup(1, -1), OWRITE);
+			dup(2, 1);
+		}
+	} else {
+		c = mycreat(outfile, 0664);
+		if(c < 0) {
+			diag(Z, "cannot open %s - %r", outfile);
+			outfile = 0;
+			errorexit();
+		}
+		Binit(&outbuf, c, OWRITE);
+	}
+	newio();
+	first = 0;
+
+	/* Use an ANSI preprocessor */
+	if(debug['p']) {
+		if(systemtype(Windows)) {
+			diag(Z, "-p option not supported on windows");
+			errorexit();
+		}
+		if(myaccess(file) < 0) {
+			diag(Z, "%s does not exist", file);
+			errorexit();
+		}
+		if(mypipe(fd) < 0) {
+			diag(Z, "pipe failed");
+			errorexit();
+		}
+		switch(myfork()) {
+		case -1:
+			diag(Z, "fork failed");
+			errorexit();
+		case 0:
+			close(fd[0]);
+			mydup(fd[1], 1);
+			close(fd[1]);
+			av = alloc((3 + ndef + ninclude + 2) * sizeof *av);
+			av[0] = CPP;
+			i = 1;
+			if(debug['.'])
+				av[i++] = strdup("-.");
+			/* 1999 ANSI C requires recognising // comments */
+			av[i++] = strdup("-+");
+			for(c = 0; c < ndef; c++) {
+				sprint(opt, "-D%s", defs[c]);
+				av[i++] = strdup(opt);
+			}
+			for(c = 0; c < ninclude; c++) {
+				sprint(opt, "-I%s", include[c]);
+				av[i++] = strdup(opt);
+			}
+			if(strcmp(file, "stdin") != 0)
+				av[i++] = file;
+			av[i] = 0;
+			if(debug['p'] > 1) {
+				for(c = 0; c < i; c++)
+					fprint(2, "%s ", av[c]);
+				fprint(2, "\n");
+			}
+			myexec(av[0], av);
+			fprint(2, "can't exec C preprocessor %s: %r\n", CPP);
+			errorexit();
+		default:
+			close(fd[1]);
+			newfile(file, fd[0]);
+			break;
+		}
+	} else {
+		if(strcmp(file, "stdin") == 0)
+			newfile(file, 0);
+		else
+			newfile(file, -1);
+	}
+	yyparse();
+	if(!debug['a'] && !debug['Z'])
+		gclean();
+	return nerrors;
+}
+
+void
+errorexit(void)
+{
+	if(outfile)
+		remove(outfile);
+	exits("error");
+}
+
+void
+pushio(void)
+{
+	Io *i;
+
+	i = iostack;
+	if(i == I) {
+		yyerror("botch in pushio");
+		errorexit();
+	}
+	i->p = fi.p;
+	i->c = fi.c;
+}
+
+void
+newio(void)
+{
+	Io *i;
+	static int pushdepth = 0;
+
+	i = iofree;
+	if(i == I) {
+		pushdepth++;
+		if(pushdepth > 1000) {
+			yyerror("macro/io expansion too deep");
+			errorexit();
+		}
+		i = alloc(sizeof(*i));
+	} else
+		iofree = i->link;
+	i->c = 0;
+	i->f = -1;
+	ionext = i;
+}
+
+void
+newfile(char *s, int f)
+{
+	Io *i;
+
+	if(debug['e'])
+		print("%L: %s\n", lineno, s);
+
+	i = ionext;
+	i->link = iostack;
+	iostack = i;
+	i->f = f;
+	if(f < 0)
+		i->f = open(s, 0);
+	if(i->f < 0) {
+		yyerror("%cc: %r: %s", thechar, s);
+		errorexit();
+	}
+	fi.c = 0;
+	linehist(s, 0);
+}
+
+Sym*
+slookup(char *s)
+{
+
+	strcpy(symb, s);
+	return lookup();
+}
+
+Sym*
+lookup(void)
+{
+	Sym *s;
+	ulong h;
+	char *p;
+	int c, n;
+
+	h = 0;
+	for(p=symb; *p;) {
+		h = h * 3;
+		h += *p++;
+	}
+	n = (p - symb) + 1;
+	if((long)h < 0)
+		h = ~h;
+	h %= NHASH;
+	c = symb[0];
+	for(s = hash[h]; s != S; s = s->link) {
+		if(s->name[0] != c)
+			continue;
+		if(strcmp(s->name, symb) == 0)
+			return s;
+	}
+	s = alloc(sizeof(*s));
+	s->name = alloc(n);
+	memmove(s->name, symb, n);
+
+	strcpy(s->name, symb);
+	s->link = hash[h];
+	hash[h] = s;
+	syminit(s);
+
+	return s;
+}
+
+void
+syminit(Sym *s)
+{
+	s->lexical = LNAME;
+	s->block = 0;
+	s->offset = 0;
+	s->type = T;
+	s->suetag = T;
+	s->class = CXXX;
+	s->aused = 0;
+	s->sig = SIGNONE;
+}
+
+#define	EOF	(-1)
+#define	IGN	(-2)
+#define	ESC	(Runemask+1)		/* Rune flag: a literal byte */
+#define	GETC()	((--fi.c < 0)? filbuf(): (*fi.p++ & 0xff))
+
+enum
+{
+	Numdec		= 1<<0,
+	Numlong		= 1<<1,
+	Numuns		= 1<<2,
+	Numvlong	= 1<<3,
+	Numflt		= 1<<4,
+};
+
+long
+yylex(void)
+{
+	vlong vv;
+	long c, c1, t;
+	char *cp;
+	Rune rune;
+	Sym *s;
+
+	if(peekc != IGN) {
+		c = peekc;
+		peekc = IGN;
+		goto l1;
+	}
+l0:
+	c = GETC();
+
+l1:
+	if(c >= Runeself) {
+		/*
+		 * extension --
+		 *	all multibyte runes are alpha
+		 */
+		cp = symb;
+		goto talph;
+	}
+	if(isspace(c)) {
+		if(c == '\n')
+			lineno++;
+		goto l0;
+	}
+	if(isalpha(c)) {
+		cp = symb;
+		if(c != 'L')
+			goto talph;
+		*cp++ = c;
+		c = GETC();
+		if(c == '\'') {
+			/* L'x' */
+			c = escchar('\'', 1, 0);
+			if(c == EOF)
+				c = '\'';
+			c1 = escchar('\'', 1, 0);
+			if(c1 != EOF) {
+				yyerror("missing '");
+				peekc = c1;
+			}
+			yylval.vval = convvtox(c, TRUNE);
+			return LUCONST;
+		}
+		if(c == '"') {
+			goto caselq;
+		}
+		goto talph;
+	}
+	if(isdigit(c))
+		goto tnum;
+	switch(c)
+	{
+
+	case EOF:
+		peekc = EOF;
+		return -1;
+
+	case '_':
+		cp = symb;
+		goto talph;
+
+	case '#':
+		domacro();
+		goto l0;
+
+	case '.':
+		c1 = GETC();
+		if(isdigit(c1)) {
+			cp = symb;
+			*cp++ = c;
+			c = c1;
+			c1 = 0;
+			goto casedot;
+		}
+		break;
+
+	case '"':
+		strcpy(symb, "\"<string>\"");
+		cp = alloc(0);
+		c1 = 0;
+
+		/* "..." */
+		for(;;) {
+			c = escchar('"', 0, 1);
+			if(c == EOF)
+				break;
+			if(c & ESC) {
+				cp = allocn(cp, c1, 1);
+				cp[c1++] = c;
+			} else {
+				rune = c;
+				c = runelen(rune);
+				cp = allocn(cp, c1, c);
+				runetochar(cp+c1, &rune);
+				c1 += c;
+			}
+		}
+		yylval.sval.l = c1;
+		do {
+			cp = allocn(cp, c1, 1);
+			cp[c1++] = 0;
+		} while(c1 & MAXALIGN);
+		yylval.sval.s = cp;
+		return LSTRING;
+
+	caselq:
+		/* L"..." */
+		strcpy(symb, "\"L<string>\"");
+		cp = alloc(0);
+		c1 = 0;
+		for(;;) {
+			c = escchar('"', 1, 0);
+			if(c == EOF)
+				break;
+			cp = allocn(cp, c1, sizeof(TRune));
+			*(TRune*)(cp + c1) = c;
+			c1 += sizeof(TRune);
+		}
+		yylval.sval.l = c1;
+		do {
+			cp = allocn(cp, c1, sizeof(TRune));
+			*(TRune*)(cp + c1) = 0;
+			c1 += sizeof(TRune);
+		} while(c1 & MAXALIGN);
+		yylval.sval.s = cp;
+		return LLSTRING;
+
+	case '\'':
+		/* '.' */
+		c = escchar('\'', 0, 0);
+		if(c == EOF)
+			c = '\'';
+		c1 = escchar('\'', 0, 0);
+		if(c1 != EOF) {
+			yyerror("missing '");
+			peekc = c1;
+		}
+		vv = c;
+		yylval.vval = convvtox(vv, TUCHAR);
+		if(yylval.vval != vv)
+			yyerror("overflow in character constant: 0x%lx", c);
+		else
+		if(c & 0x80){
+			nearln = lineno;
+			warn(Z, "sign-extended character constant");
+		}
+		yylval.vval = convvtox(vv, TCHAR);
+		return LCONST;
+
+	case '/':
+		c1 = GETC();
+		if(c1 == '*') {
+			for(;;) {
+				c = getr();
+				while(c == '*') {
+					c = getr();
+					if(c == '/')
+						goto l0;
+				}
+				if(c == EOF) {
+					yyerror("eof in comment");
+					errorexit();
+				}
+			}
+		}
+		if(c1 == '/') {
+			for(;;) {
+				c = getr();
+				if(c == '\n')
+					goto l0;
+				if(c == EOF) {
+					yyerror("eof in comment");
+					errorexit();
+				}
+			}
+		}
+		if(c1 == '=')
+			return LDVE;
+		break;
+
+	case '*':
+		c1 = GETC();
+		if(c1 == '=')
+			return LMLE;
+		break;
+
+	case '%':
+		c1 = GETC();
+		if(c1 == '=')
+			return LMDE;
+		break;
+
+	case '+':
+		c1 = GETC();
+		if(c1 == '+')
+			return LPP;
+		if(c1 == '=')
+			return LPE;
+		break;
+
+	case '-':
+		c1 = GETC();
+		if(c1 == '-')
+			return LMM;
+		if(c1 == '=')
+			return LME;
+		if(c1 == '>')
+			return LMG;
+		break;
+
+	case '>':
+		c1 = GETC();
+		if(c1 == '>') {
+			c = LRSH;
+			c1 = GETC();
+			if(c1 == '=')
+				return LRSHE;
+			break;
+		}
+		if(c1 == '=')
+			return LGE;
+		break;
+
+	case '<':
+		c1 = GETC();
+		if(c1 == '<') {
+			c = LLSH;
+			c1 = GETC();
+			if(c1 == '=')
+				return LLSHE;
+			break;
+		}
+		if(c1 == '=')
+			return LLE;
+		break;
+
+	case '=':
+		c1 = GETC();
+		if(c1 == '=')
+			return LEQ;
+		break;
+
+	case '!':
+		c1 = GETC();
+		if(c1 == '=')
+			return LNE;
+		break;
+
+	case '&':
+		c1 = GETC();
+		if(c1 == '&')
+			return LANDAND;
+		if(c1 == '=')
+			return LANDE;
+		break;
+
+	case '|':
+		c1 = GETC();
+		if(c1 == '|')
+			return LOROR;
+		if(c1 == '=')
+			return LORE;
+		break;
+
+	case '^':
+		c1 = GETC();
+		if(c1 == '=')
+			return LXORE;
+		break;
+
+	default:
+		return c;
+	}
+	peekc = c1;
+	return c;
+
+talph:
+	/*
+	 * cp is set to symb and some
+	 * prefix has been stored
+	 */
+	for(;;) {
+		if(c >= Runeself) {
+			for(c1=0;;) {
+				cp[c1++] = c;
+				if(fullrune(cp, c1))
+					break;
+				c = GETC();
+			}
+			cp += c1;
+			c = GETC();
+			continue;
+		}
+		if(!isalnum(c) && c != '_')
+			break;
+		*cp++ = c;
+		c = GETC();
+	}
+	*cp = 0;
+	if(debug['L'])
+		print("%L: %s\n", lineno, symb);
+	peekc = c;
+	s = lookup();
+	if(s->macro) {
+		newio();
+		cp = ionext->b;
+		macexpand(s, cp);
+		pushio();
+		ionext->link = iostack;
+		iostack = ionext;
+		fi.p = cp;
+		fi.c = strlen(cp);
+		if(peekc != IGN) {
+			cp[fi.c++] = peekc;
+			cp[fi.c] = 0;
+			peekc = IGN;
+		}
+		goto l0;
+	}
+	yylval.sym = s;
+	if(s->class == CTYPEDEF || s->class == CTYPESTR)
+		return LTYPE;
+	return s->lexical;
+
+tnum:
+	c1 = 0;
+	cp = symb;
+	if(c != '0') {
+		c1 |= Numdec;
+		for(;;) {
+			*cp++ = c;
+			c = GETC();
+			if(isdigit(c))
+				continue;
+			goto dc;
+		}
+	}
+	*cp++ = c;
+	c = GETC();
+	if(c == 'x' || c == 'X')
+		for(;;) {
+			*cp++ = c;
+			c = GETC();
+			if(isdigit(c))
+				continue;
+			if(c >= 'a' && c <= 'f')
+				continue;
+			if(c >= 'A' && c <= 'F')
+				continue;
+			if(cp == symb+2)
+				yyerror("malformed hex constant");
+			goto ncu;
+		}
+	if(c < '0' || c > '7')
+		goto dc;
+	for(;;) {
+		if(c >= '0' && c <= '7') {
+			*cp++ = c;
+			c = GETC();
+			continue;
+		}
+		goto ncu;
+	}
+
+dc:
+	if(c == '.')
+		goto casedot;
+	if(c == 'e' || c == 'E')
+		goto casee;
+
+ncu:
+	if((c == 'U' || c == 'u') && !(c1 & Numuns)) {
+		c = GETC();
+		c1 |= Numuns;
+		goto ncu;
+	}
+	if((c == 'L' || c == 'l') && !(c1 & Numvlong)) {
+		c = GETC();
+		if(c1 & Numlong)
+			c1 |= Numvlong;
+		c1 |= Numlong;
+		goto ncu;
+	}
+	*cp = 0;
+	peekc = c;
+	if(mpatov(symb, &yylval.vval))
+		yyerror("overflow in constant");
+
+	vv = yylval.vval;
+	if(c1 & Numvlong) {
+		if((c1 & Numuns) || convvtox(vv, TVLONG) < 0) {
+			c = LUVLCONST;
+			t = TUVLONG;
+			goto nret;
+		}
+		c = LVLCONST;
+		t = TVLONG;
+		goto nret;
+	}
+	if(c1 & Numlong) {
+		if((c1 & Numuns) || convvtox(vv, TLONG) < 0) {
+			c = LULCONST;
+			t = TULONG;
+			goto nret;
+		}
+		c = LLCONST;
+		t = TLONG;
+		goto nret;
+	}
+	if((c1 & Numuns) || convvtox(vv, TINT) < 0) {
+		c = LUCONST;
+		t = TUINT;
+		goto nret;
+	}
+	c = LCONST;
+	t = TINT;
+	goto nret;
+
+nret:
+	yylval.vval = convvtox(vv, t);
+	if(yylval.vval != vv){
+		nearln = lineno;
+		warn(Z, "truncated constant: %T %s", types[t], symb);
+	}
+	return c;
+
+casedot:
+	for(;;) {
+		*cp++ = c;
+		c = GETC();
+		if(!isdigit(c))
+			break;
+	}
+	if(c != 'e' && c != 'E')
+		goto caseout;
+
+casee:
+	*cp++ = 'e';
+	c = GETC();
+	if(c == '+' || c == '-') {
+		*cp++ = c;
+		c = GETC();
+	}
+	if(!isdigit(c))
+		yyerror("malformed fp constant exponent");
+	while(isdigit(c)) {
+		*cp++ = c;
+		c = GETC();
+	}
+
+caseout:
+	if(c == 'L' || c == 'l') {
+		c = GETC();
+		c1 |= Numlong;
+	} else
+	if(c == 'F' || c == 'f') {
+		c = GETC();
+		c1 |= Numflt;
+	}
+	*cp = 0;
+	peekc = c;
+	yylval.dval = strtod(symb, nil);
+	if(isInf(yylval.dval, 1) || isInf(yylval.dval, -1)) {
+		yyerror("overflow in float constant");
+		yylval.dval = 0;
+	}
+	if(c1 & Numflt)
+		return LFCONST;
+	return LDCONST;
+}
+
+/*
+ * convert a string, s, to vlong in *v
+ * return conversion overflow.
+ * required syntax is [0[x]]d*
+ */
+int
+mpatov(char *s, vlong *v)
+{
+	vlong n, nn;
+	int c;
+
+	n = 0;
+	c = *s;
+	if(c == '0')
+		goto oct;
+	while((c = *s++)) {
+		if(c >= '0' && c <= '9')
+			nn = n*10 + c-'0';
+		else
+			goto bad;
+		if(n < 0 && nn >= 0)
+			goto bad;
+		n = nn;
+	}
+	goto out;
+
+oct:
+	s++;
+	c = *s;
+	if(c == 'x' || c == 'X')
+		goto hex;
+	while((c = *s++)) {
+		if(c >= '0' || c <= '7')
+			nn = n*8 + c-'0';
+		else
+			goto bad;
+		if(n < 0 && nn >= 0)
+			goto bad;
+		n = nn;
+	}
+	goto out;
+
+hex:
+	s++;
+	while((c = *s++)) {
+		if(c >= '0' && c <= '9')
+			c += 0-'0';
+		else
+		if(c >= 'a' && c <= 'f')
+			c += 10-'a';
+		else
+		if(c >= 'A' && c <= 'F')
+			c += 10-'A';
+		else
+			goto bad;
+		nn = n*16 + c;
+		if(n < 0 && nn >= 0)
+			goto bad;
+		n = nn;
+	}
+out:
+	*v = n;
+	return 0;
+
+bad:
+	*v = ~0;
+	return 1;
+}
+
+int
+getc(void)
+{
+	int c;
+
+	if(peekc != IGN) {
+		c = peekc;
+		peekc = IGN;
+	} else
+		c = GETC();
+	if(c == '\n')
+		lineno++;
+	if(c == EOF) {
+		yyerror("End of file");
+		errorexit();
+	}
+	return c;
+}
+
+long
+getr(void)
+{
+	int c, i;
+	char str[UTFmax+1];
+	Rune rune;
+
+
+	c = getc();
+	if(c < Runeself)
+		return c;
+	i = 0;
+	str[i++] = c;
+
+loop:
+	c = getc();
+	str[i++] = c;
+	if(!fullrune(str, i))
+		goto loop;
+	c = chartorune(&rune, str);
+	if(rune == Runeerror && c == 1) {
+		nearln = lineno;
+		diag(Z, "illegal rune in string");
+		for(c=0; c<i; c++)
+			print(" %.2x", *(uchar*)(str+c));
+		print("\n");
+	}
+	return rune;
+}
+
+int
+getnsc(void)
+{
+	int c;
+
+	if(peekc != IGN) {
+		c = peekc;
+		peekc = IGN;
+	} else
+		c = GETC();
+	for(;;) {
+		if(c >= Runeself || !isspace(c))
+			return c;
+		if(c == '\n') {
+			lineno++;
+			return c;
+		}
+		c = GETC();
+	}
+}
+
+void
+unget(int c)
+{
+
+	peekc = c;
+	if(c == '\n')
+		lineno--;
+}
+
+long
+escchar(long e, int longflg, int escflg)
+{
+	long c, l;
+	int i;
+
+loop:
+	c = getr();
+	if(c == '\n') {
+		yyerror("newline in string");
+		return EOF;
+	}
+	if(c != '\\') {
+		if(c == e)
+			c = EOF;
+		return c;
+	}
+	c = getr();
+	if(c == 'x') {
+		/*
+		 * note this is not ansi,
+		 * supposed to only accept 2 hex
+		 */
+		i = 2;
+		if(longflg)
+			i = 6;
+		l = 0;
+		for(; i>0; i--) {
+			c = getc();
+			if(c >= '0' && c <= '9') {
+				l = l*16 + c-'0';
+				continue;
+			}
+			if(c >= 'a' && c <= 'f') {
+				l = l*16 + c-'a' + 10;
+				continue;
+			}
+			if(c >= 'A' && c <= 'F') {
+				l = l*16 + c-'A' + 10;
+				continue;
+			}
+			unget(c);
+			break;
+		}
+		if(escflg)
+			l |= ESC;
+		return l;
+	}
+	if(c >= '0' && c <= '7') {
+		/*
+		 * note this is not ansi,
+		 * supposed to only accept 3 oct
+		 */
+		i = 2;
+		if(longflg)
+			i = 8;
+		l = c - '0';
+		for(; i>0; i--) {
+			c = getc();
+			if(c >= '0' && c <= '7') {
+				l = l*8 + c-'0';
+				continue;
+			}
+			unget(c);
+		}
+		if(escflg)
+			l |= ESC;
+		return l;
+	}
+	switch(c)
+	{
+	case '\n':	goto loop;
+	case 'n':	return '\n';
+	case 't':	return '\t';
+	case 'b':	return '\b';
+	case 'r':	return '\r';
+	case 'f':	return '\f';
+	case 'a':	return '\a';
+	case 'v':	return '\v';
+	}
+	return c;
+}
+
+struct
+{
+	char	*name;
+	ushort	lexical;
+	ushort	type;
+} itab[] = {
+	{"auto",		LAUTO,		0},
+	{"break",	LBREAK,		0},
+	{"case",		LCASE,		0},
+	{"char",		LCHAR,		TCHAR},
+	{"const",	LCONSTNT,	0},
+	{"continue",	LCONTINUE,	0},
+	{"default",	LDEFAULT,	0},
+	{"do",		LDO,		0},
+	{"double",	LDOUBLE,	TDOUBLE},
+	{"else",		LELSE,		0},
+	{"enum",		LENUM,		0},
+	{"extern",	LEXTERN,	0},
+	{"float",	LFLOAT,		TFLOAT},
+	{"for",		LFOR,		0},
+	{"goto",		LGOTO,		0},
+	{"if",		LIF,		0},
+	{"inline",	LINLINE,	0},
+	{"int",		LINT,		TINT},
+	{"long",		LLONG,		TLONG},
+	{"register",	LREGISTER,	0},
+	{"restrict",	LRESTRICT,	0},
+	{"return",	LRETURN,	0},
+	{"SET",		LSET,		0},
+	{"short",	LSHORT,		TSHORT},
+	{"signed",	LSIGNED,	0},
+	{"signof",	LSIGNOF,	0},
+	{"sizeof",	LSIZEOF,	0},
+	{"static",	LSTATIC,	0},
+	{"struct",	LSTRUCT,	0},
+	{"switch",	LSWITCH,	0},
+	{"typedef",	LTYPEDEF,	0},
+	{"typestr",	LTYPESTR,	0},
+	{"union",	LUNION,		0},
+	{"unsigned",	LUNSIGNED,	0},
+	{"USED",		LUSED,		0},
+	{"void",		LVOID,		TVOID},
+	{"volatile",	LVOLATILE,	0},
+	{"while",	LWHILE,		0},
+	{0}
+};
+
+void
+cinit(void)
+{
+	Sym *s;
+	int i;
+	Type *t;
+
+	nerrors = 0;
+	lineno = 1;
+	iostack = I;
+	iofree = I;
+	peekc = IGN;
+	nhunk = 0;
+
+	types[TXXX] = T;
+	types[TCHAR] = typ(TCHAR, T);
+	types[TUCHAR] = typ(TUCHAR, T);
+	types[TSHORT] = typ(TSHORT, T);
+	types[TUSHORT] = typ(TUSHORT, T);
+	types[TINT] = typ(TINT, T);
+	types[TUINT] = typ(TUINT, T);
+	types[TLONG] = typ(TLONG, T);
+	types[TULONG] = typ(TULONG, T);
+	types[TVLONG] = typ(TVLONG, T);
+	types[TUVLONG] = typ(TUVLONG, T);
+	types[TFLOAT] = typ(TFLOAT, T);
+	types[TDOUBLE] = typ(TDOUBLE, T);
+	types[TVOID] = typ(TVOID, T);
+	types[TENUM] = typ(TENUM, T);
+	types[TFUNC] = typ(TFUNC, types[TINT]);
+	types[TIND] = typ(TIND, types[TVOID]);
+
+	for(i=0; i<NHASH; i++)
+		hash[i] = S;
+	for(i=0; itab[i].name; i++) {
+		s = slookup(itab[i].name);
+		s->lexical = itab[i].lexical;
+		if(itab[i].type != 0)
+			s->type = types[itab[i].type];
+	}
+	blockno = 0;
+	autobn = 0;
+	autoffset = 0;
+
+	t = typ(TARRAY, types[TCHAR]);
+	t->width = 0;
+	symstring = slookup(".string");
+	symstring->class = CSTATIC;
+	symstring->type = t;
+
+	t = typ(TARRAY, types[TCHAR]);
+	t->width = 0;
+
+	nodproto = new(OPROTO, Z, Z);
+	dclstack = D;
+
+	pathname = allocn(pathname, 0, 100);
+	if(mygetwd(pathname, 99) == 0) {
+		pathname = allocn(pathname, 100, 900);
+		if(mygetwd(pathname, 999) == 0)
+			strcpy(pathname, "/???");
+	}
+
+	fmtinstall('O', Oconv);
+	fmtinstall('T', Tconv);
+	fmtinstall('F', FNconv);
+	fmtinstall('L', Lconv);
+	fmtinstall('Q', Qconv);
+	fmtinstall('|', VBconv);
+}
+
+int
+filbuf(void)
+{
+	Io *i;
+
+loop:
+	i = iostack;
+	if(i == I)
+		return EOF;
+	if(i->f < 0)
+		goto pop;
+	fi.c = read(i->f, i->b, BUFSIZ) - 1;
+	if(fi.c < 0) {
+		close(i->f);
+		linehist(0, 0);
+		goto pop;
+	}
+	fi.p = i->b + 1;
+	return i->b[0] & 0xff;
+
+pop:
+	iostack = i->link;
+	i->link = iofree;
+	iofree = i;
+	i = iostack;
+	if(i == I)
+		return EOF;
+	fi.p = i->p;
+	fi.c = i->c;
+	if(--fi.c < 0)
+		goto loop;
+	return *fi.p++ & 0xff;
+}
+
+int
+Oconv(Fmt *fp)
+{
+	int a;
+
+	a = va_arg(fp->args, int);
+	if(a < OXXX || a > OEND)
+		return fmtprint(fp, "***badO %d***", a);
+
+	return fmtstrcpy(fp, onames[a]);
+}
+
+int
+Lconv(Fmt *fp)
+{
+	char str[STRINGSZ], s[STRINGSZ];
+	Hist *h;
+	struct
+	{
+		Hist*	incl;	/* start of this include file */
+		long	idel;	/* delta line number to apply to include */
+		Hist*	line;	/* start of this #line directive */
+		long	ldel;	/* delta line number to apply to #line */
+	} a[HISTSZ];
+	long l, d;
+	int i, n;
+
+	l = va_arg(fp->args, long);
+	n = 0;
+	for(h = hist; h != H; h = h->link) {
+		if(l < h->line)
+			break;
+		if(h->name) {
+			if(h->offset != 0) {		/* #line directive, not #pragma */
+				if(n > 0 && n < HISTSZ && h->offset >= 0) {
+					a[n-1].line = h;
+					a[n-1].ldel = h->line - h->offset + 1;
+				}
+			} else {
+				if(n < HISTSZ) {	/* beginning of file */
+					a[n].incl = h;
+					a[n].idel = h->line;
+					a[n].line = 0;
+				}
+				n++;
+			}
+			continue;
+		}
+		n--;
+		if(n > 0 && n < HISTSZ) {
+			d = h->line - a[n].incl->line;
+			a[n-1].ldel += d;
+			a[n-1].idel += d;
+		}
+	}
+	if(n > HISTSZ)
+		n = HISTSZ;
+	str[0] = 0;
+	for(i=n-1; i>=0; i--) {
+		if(i != n-1) {
+			if(fp->flags & ~(FmtWidth|FmtPrec))	/* BUG ROB - was f3 */
+				break;
+			strcat(str, " ");
+		}
+		if(a[i].line)
+			snprint(s, STRINGSZ, "%s:%ld[%s:%ld]",
+				a[i].line->name, l-a[i].ldel+1,
+				a[i].incl->name, l-a[i].idel+1);
+		else
+			snprint(s, STRINGSZ, "%s:%ld",
+				a[i].incl->name, l-a[i].idel+1);
+		if(strlen(s)+strlen(str) >= STRINGSZ-10)
+			break;
+		strcat(str, s);
+		l = a[i].incl->line - 1;	/* now print out start of this file */
+	}
+	if(n == 0)
+		strcat(str, "<eof>");
+	return fmtstrcpy(fp, str);
+}
+
+int
+Tconv(Fmt *fp)
+{
+	char str[STRINGSZ+20], s[STRINGSZ+20];
+	Type *t, *t1;
+	int et;
+	long n;
+
+	str[0] = 0;
+	for(t = va_arg(fp->args, Type*); t != T; t = t->link) {
+		et = t->etype;
+		if(str[0])
+			strcat(str, " ");
+		if(t->garb&~GINCOMPLETE) {
+			sprint(s, "%s ", gnames[t->garb&~GINCOMPLETE]);
+			if(strlen(str) + strlen(s) < STRINGSZ)
+				strcat(str, s);
+		}
+		sprint(s, "%s", tnames[et]);
+		if(strlen(str) + strlen(s) < STRINGSZ)
+			strcat(str, s);
+		if(et == TFUNC && (t1 = t->down)) {
+			sprint(s, "(%T", t1);
+			if(strlen(str) + strlen(s) < STRINGSZ)
+				strcat(str, s);
+			while((t1 = t1->down)) {
+				sprint(s, ", %T", t1);
+				if(strlen(str) + strlen(s) < STRINGSZ)
+					strcat(str, s);
+			}
+			if(strlen(str) + strlen(s) < STRINGSZ)
+				strcat(str, ")");
+		}
+		if(et == TARRAY) {
+			n = t->width;
+			if(t->link && t->link->width)
+				n /= t->link->width;
+			sprint(s, "[%ld]", n);
+			if(strlen(str) + strlen(s) < STRINGSZ)
+				strcat(str, s);
+		}
+		if(t->nbits) {
+			sprint(s, " %d:%d", t->shift, t->nbits);
+			if(strlen(str) + strlen(s) < STRINGSZ)
+				strcat(str, s);
+		}
+		if(typesu[et]) {
+			if(t->tag) {
+				strcat(str, " ");
+				if(strlen(str) + strlen(t->tag->name) < STRINGSZ)
+					strcat(str, t->tag->name);
+			} else
+				strcat(str, " {}");
+			break;
+		}
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+FNconv(Fmt *fp)
+{
+	char *str;
+	Node *n;
+
+	n = va_arg(fp->args, Node*);
+	str = "<indirect>";
+	if(n != Z && (n->op == ONAME || n->op == ODOT || n->op == OELEM))
+		str = n->sym->name;
+	return fmtstrcpy(fp, str);
+}
+
+int
+Qconv(Fmt *fp)
+{
+	char str[STRINGSZ+20], *s;
+	long b;
+	int i;
+
+	str[0] = 0;
+	for(b = va_arg(fp->args, long); b;) {
+		i = bitno(b);
+		if(str[0])
+			strcat(str, " ");
+		s = qnames[i];
+		if(strlen(str) + strlen(s) >= STRINGSZ)
+			break;
+		strcat(str, s);
+		b &= ~(1L << i);
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+VBconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	int i, n, t, pc;
+
+	n = va_arg(fp->args, int);
+	pc = 0;	/* BUG: was printcol */
+	i = 0;
+	while(pc < n) {
+		t = (pc+4) & ~3;
+		if(t <= n) {
+			str[i++] = '\t';
+			pc = t;
+			continue;
+		}
+		str[i++] = ' ';
+		pc++;
+	}
+	str[i] = 0;
+
+	return fmtstrcpy(fp, str);
+}
+
+/*
+ * real allocs
+ */
+void*
+alloc(long n)
+{
+	void *p;
+
+	while((uintptr)hunk & MAXALIGN) {
+		hunk++;
+		nhunk--;
+	}
+	while(nhunk < n)
+		gethunk();
+	p = hunk;
+	nhunk -= n;
+	hunk += n;
+	return p;
+}
+
+void*
+allocn(void *p, long on, long n)
+{
+	void *q;
+
+	q = (uchar*)p + on;
+	if(q != hunk || nhunk < n) {
+		while(nhunk < on+n)
+			gethunk();
+		memmove(hunk, p, on);
+		p = hunk;
+		hunk += on;
+		nhunk -= on;
+	}
+	hunk += n;
+	nhunk -= n;
+	return p;
+}
+
+void
+setinclude(char *p)
+{
+	int i;
+	char *e, **np;
+
+	while(*p != 0) {
+		e = strchr(p, ' ');
+		if(e != 0)
+			*e = '\0';
+
+		for(i=0; i < ninclude; i++)
+			if(strcmp(p, include[i]) == 0)
+				break;
+
+		if(i >= ninclude){
+			if(i >= maxinclude){
+				maxinclude += 20;
+				np = alloc(maxinclude * sizeof *np);
+				if(include != nil)
+					memmove(np, include, (maxinclude - 20) * sizeof *np);
+				include = np;
+			}
+			include[ninclude++] = p;
+		}
+
+		if(e == 0)
+			break;
+		p = e+1;
+	}
+}

+ 686 - 0
sys/src/libcc/lexbody

@@ -0,0 +1,686 @@
+/*
+ * common code for all the assemblers
+ */
+
+void
+pragpack(void)
+{
+	while(getnsc() != '\n')
+		;
+}
+
+void
+pragvararg(void)
+{
+	while(getnsc() != '\n')
+		;
+}
+
+void
+pragfpround(void)
+{
+	while(getnsc() != '\n')
+		;
+}
+
+void
+pragprofile(void)
+{
+	while(getnsc() != '\n')
+		;
+}
+
+void
+pragincomplete(void)
+{
+	while(getnsc() != '\n')
+		;
+}
+
+/*
+ * real allocs
+ */
+void*
+alloc(long n)
+{
+	void *p;
+
+	while((uintptr)hunk & MAXALIGN) {
+		hunk++;
+		nhunk--;
+	}
+	while(nhunk < n)
+		gethunk();
+	p = hunk;
+	nhunk -= n;
+	hunk += n;
+	return p;
+}
+
+void*
+allocn(void *p, long on, long n)
+{
+	void *q;
+
+	q = (uchar*)p + on;
+	if(q != hunk || nhunk < n) {
+		while(nhunk < on+n)
+			gethunk();
+		memmove(hunk, p, on);
+		p = hunk;
+		hunk += on;
+		nhunk -= on;
+	}
+	hunk += n;
+	nhunk -= n;
+	return p;
+}
+
+void
+setinclude(char *p)
+{
+	int i;
+
+	if(p == 0)
+		return;
+	for(i=1; i < ninclude; i++)
+		if(strcmp(p, include[i]) == 0)
+			return;
+
+	if(ninclude >= nelem(include)) {
+		yyerror("ninclude too small %d", nelem(include));
+		exits("ninclude");
+	}
+	include[ninclude++] = p;
+}
+
+void
+errorexit(void)
+{
+
+	if(outfile)
+		remove(outfile);
+	exits("error");
+}
+
+void
+pushio(void)
+{
+	Io *i;
+
+	i = iostack;
+	if(i == I) {
+		yyerror("botch in pushio");
+		errorexit();
+	}
+	i->p = fi.p;
+	i->c = fi.c;
+}
+
+void
+newio(void)
+{
+	Io *i;
+	static int pushdepth = 0;
+
+	i = iofree;
+	if(i == I) {
+		pushdepth++;
+		if(pushdepth > 1000) {
+			yyerror("macro/io expansion too deep");
+			errorexit();
+		}
+		i = alloc(sizeof(*i));
+	} else
+		iofree = i->link;
+	i->c = 0;
+	i->f = -1;
+	ionext = i;
+}
+
+void
+newfile(char *s, int f)
+{
+	Io *i;
+
+	i = ionext;
+	i->link = iostack;
+	iostack = i;
+	i->f = f;
+	if(f < 0)
+		i->f = open(s, 0);
+	if(i->f < 0) {
+		yyerror("%ca: %r: %s", thechar, s);
+		errorexit();
+	}
+	fi.c = 0;
+	linehist(s, 0);
+}
+
+Sym*
+slookup(char *s)
+{
+
+	strcpy(symb, s);
+	return lookup();
+}
+
+Sym*
+lookup(void)
+{
+	Sym *s;
+	long h;
+	char *p;
+	int c, l;
+
+	h = 0;
+	for(p=symb; c = *p; p++)
+		h = h+h+h + c;
+	l = (p - symb) + 1;
+	if(h < 0)
+		h = ~h;
+	h %= NHASH;
+	c = symb[0];
+	for(s = hash[h]; s != S; s = s->link) {
+		if(s->name[0] != c)
+			continue;
+		if(memcmp(s->name, symb, l) == 0)
+			return s;
+	}
+	s = alloc(sizeof(*s));
+	s->name = alloc(l);
+	memmove(s->name, symb, l);
+
+	s->link = hash[h];
+	hash[h] = s;
+	syminit(s);
+	return s;
+}
+
+long
+yylex(void)
+{
+	int c, c1;
+	char *cp;
+	Sym *s;
+
+	c = peekc;
+	if(c != IGN) {
+		peekc = IGN;
+		goto l1;
+	}
+l0:
+	c = GETC();
+
+l1:
+	if(c == EOF) {
+		peekc = EOF;
+		return -1;
+	}
+	if(isspace(c)) {
+		if(c == '\n') {
+			lineno++;
+			return ';';
+		}
+		goto l0;
+	}
+	if(isalpha(c))
+		goto talph;
+	if(isdigit(c))
+		goto tnum;
+	switch(c)
+	{
+	case '\n':
+		lineno++;
+		return ';';
+
+	case '#':
+		domacro();
+		goto l0;
+
+	case '.':
+		c = GETC();
+		if(isalpha(c)) {
+			cp = symb;
+			*cp++ = '.';
+			goto aloop;
+		}
+		if(isdigit(c)) {
+			cp = symb;
+			*cp++ = '.';
+			goto casedot;
+		}
+		peekc = c;
+		return '.';
+
+	talph:
+	case '_':
+	case '@':
+		cp = symb;
+
+	aloop:
+		*cp++ = c;
+		c = GETC();
+		if(isalpha(c) || isdigit(c) || c == '_' || c == '$')
+			goto aloop;
+		*cp = 0;
+		peekc = c;
+		s = lookup();
+		if(s->macro) {
+			newio();
+			cp = ionext->b;
+			macexpand(s, cp);
+			pushio();
+			ionext->link = iostack;
+			iostack = ionext;
+			fi.p = cp;
+			fi.c = strlen(cp);
+			if(peekc != IGN) {
+				cp[fi.c++] = peekc;
+				cp[fi.c] = 0;
+				peekc = IGN;
+			}
+			goto l0;
+		}
+		if(s->type == 0)
+			s->type = LNAME;
+		if(s->type == LNAME ||
+		   s->type == LVAR ||
+		   s->type == LLAB) {
+			yylval.sym = s;
+			return s->type;
+		}
+		yylval.lval = s->value;
+		return s->type;
+
+	tnum:
+		cp = symb;
+		if(c != '0')
+			goto dc;
+		*cp++ = c;
+		c = GETC();
+		c1 = 3;
+		if(c == 'x' || c == 'X') {
+			c1 = 4;
+			c = GETC();
+		} else
+		if(c < '0' || c > '7')
+			goto dc;
+		yylval.lval = 0;
+		for(;;) {
+			if(c >= '0' && c <= '9') {
+				if(c > '7' && c1 == 3)
+					break;
+				yylval.lval <<= c1;
+				yylval.lval += c - '0';
+				c = GETC();
+				continue;
+			}
+			if(c1 == 3)
+				break;
+			if(c >= 'A' && c <= 'F')
+				c += 'a' - 'A';
+			if(c >= 'a' && c <= 'f') {
+				yylval.lval <<= c1;
+				yylval.lval += c - 'a' + 10;
+				c = GETC();
+				continue;
+			}
+			break;
+		}
+		goto ncu;
+
+	dc:
+		for(;;) {
+			if(!isdigit(c))
+				break;
+			*cp++ = c;
+			c = GETC();
+		}
+		if(c == '.')
+			goto casedot;
+		if(c == 'e' || c == 'E')
+			goto casee;
+		*cp = 0;
+		if(sizeof(yylval.lval) == sizeof(vlong))
+			yylval.lval = strtoll(symb, nil, 10);
+		else
+			yylval.lval = strtol(symb, nil, 10);
+
+	ncu:
+		while(c == 'U' || c == 'u' || c == 'l' || c == 'L')
+			c = GETC();
+		peekc = c;
+		return LCONST;
+
+	casedot:
+		for(;;) {
+			*cp++ = c;
+			c = GETC();
+			if(!isdigit(c))
+				break;
+		}
+		if(c == 'e' || c == 'E')
+			goto casee;
+		goto caseout;
+
+	casee:
+		*cp++ = 'e';
+		c = GETC();
+		if(c == '+' || c == '-') {
+			*cp++ = c;
+			c = GETC();
+		}
+		while(isdigit(c)) {
+			*cp++ = c;
+			c = GETC();
+		}
+
+	caseout:
+		*cp = 0;
+		peekc = c;
+		if(FPCHIP) {
+			yylval.dval = atof(symb);
+			return LFCONST;
+		}
+		yyerror("assembler cannot interpret fp constants");
+		yylval.lval = 1L;
+		return LCONST;
+
+	case '"':
+		memcpy(yylval.sval, nullgen.sval, sizeof(yylval.sval));
+		cp = yylval.sval;
+		c1 = 0;
+		for(;;) {
+			c = escchar('"');
+			if(c == EOF)
+				break;
+			if(c1 < sizeof(yylval.sval))
+				*cp++ = c;
+			c1++;
+		}
+		if(c1 > sizeof(yylval.sval))
+			yyerror("string constant too long");
+		return LSCONST;
+
+	case '\'':
+		c = escchar('\'');
+		if(c == EOF)
+			c = '\'';
+		if(escchar('\'') != EOF)
+			yyerror("missing '");
+		yylval.lval = c;
+		return LCONST;
+
+	case '/':
+		c1 = GETC();
+		if(c1 == '/') {
+			for(;;) {
+				c = GETC();
+				if(c == '\n')
+					goto l1;
+				if(c == EOF) {
+					yyerror("eof in comment");
+					errorexit();
+				}
+			}
+		}
+		if(c1 == '*') {
+			for(;;) {
+				c = GETC();
+				while(c == '*') {
+					c = GETC();
+					if(c == '/')
+						goto l0;
+				}
+				if(c == EOF) {
+					yyerror("eof in comment");
+					errorexit();
+				}
+				if(c == '\n')
+					lineno++;
+			}
+		}
+		break;
+
+	default:
+		return c;
+	}
+	peekc = c1;
+	return c;
+}
+
+int
+getc(void)
+{
+	int c;
+
+	c = peekc;
+	if(c != IGN) {
+		peekc = IGN;
+		return c;
+	}
+	c = GETC();
+	if(c == '\n')
+		lineno++;
+	if(c == EOF) {
+		yyerror("End of file");
+		errorexit();
+	}
+	return c;
+}
+
+int
+getnsc(void)
+{
+	int c;
+
+	for(;;) {
+		c = getc();
+		if(!isspace(c) || c == '\n')
+			return c;
+	}
+}
+
+void
+unget(int c)
+{
+
+	peekc = c;
+	if(c == '\n')
+		lineno--;
+}
+
+int
+escchar(int e)
+{
+	int c, l;
+
+loop:
+	c = getc();
+	if(c == '\n') {
+		yyerror("newline in string");
+		return EOF;
+	}
+	if(c != '\\') {
+		if(c == e)
+			return EOF;
+		return c;
+	}
+	c = getc();
+	if(c >= '0' && c <= '7') {
+		l = c - '0';
+		c = getc();
+		if(c >= '0' && c <= '7') {
+			l = l*8 + c-'0';
+			c = getc();
+			if(c >= '0' && c <= '7') {
+				l = l*8 + c-'0';
+				return l;
+			}
+		}
+		peekc = c;
+		return l;
+	}
+	switch(c)
+	{
+	case '\n':	goto loop;
+	case 'n':	return '\n';
+	case 't':	return '\t';
+	case 'b':	return '\b';
+	case 'r':	return '\r';
+	case 'f':	return '\f';
+	case 'a':	return 0x07;
+	case 'v':	return 0x0b;
+	case 'z':	return 0x00;
+	}
+	return c;
+}
+
+void
+pinit(char *f)
+{
+	int i;
+	Sym *s;
+
+	lineno = 1;
+	newio();
+	newfile(f, -1);
+	pc = 0;
+	peekc = IGN;
+	sym = 1;
+	for(i=0; i<NSYM; i++) {
+		h[i].type = 0;
+		h[i].sym = S;
+	}
+	for(i=0; i<NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link)
+			s->macro = 0;
+}
+
+int
+filbuf(void)
+{
+	Io *i;
+
+loop:
+	i = iostack;
+	if(i == I)
+		return EOF;
+	if(i->f < 0)
+		goto pop;
+	fi.c = read(i->f, i->b, BUFSIZ) - 1;
+	if(fi.c < 0) {
+		close(i->f);
+		linehist(0, 0);
+		goto pop;
+	}
+	fi.p = i->b + 1;
+	return i->b[0];
+
+pop:
+	iostack = i->link;
+	i->link = iofree;
+	iofree = i;
+	i = iostack;
+	if(i == I)
+		return EOF;
+	fi.p = i->p;
+	fi.c = i->c;
+	if(--fi.c < 0)
+		goto loop;
+	return *fi.p++;
+}
+
+void
+yyerror(char *a, ...)
+{
+	char buf[200];
+	va_list arg;
+
+	/*
+	 * hack to intercept message from yaccpar
+	 */
+	if(strcmp(a, "syntax error") == 0) {
+		yyerror("syntax error, last name: %s", symb);
+		return;
+	}
+	prfile(lineno);
+	va_start(arg, a);
+	vseprint(buf, buf+sizeof(buf), a, arg);
+	va_end(arg);
+	print("%s\n", buf);
+	nerrors++;
+	if(nerrors > 10) {
+		print("too many errors\n");
+		errorexit();
+	}
+}
+
+void
+prfile(long l)
+{
+	int i, n;
+	Hist a[HISTSZ], *h;
+	long d;
+
+	n = 0;
+	for(h = hist; h != H; h = h->link) {
+		if(l < h->line)
+			break;
+		if(h->name) {
+			if(h->offset == 0) {
+				if(n >= 0 && n < HISTSZ)
+					a[n] = *h;
+				n++;
+				continue;
+			}
+			if(n > 0 && n < HISTSZ)
+				if(a[n-1].offset == 0) {
+					a[n] = *h;
+					n++;
+				} else
+					a[n-1] = *h;
+			continue;
+		}
+		n--;
+		if(n >= 0 && n < HISTSZ) {
+			d = h->line - a[n].line;
+			for(i=0; i<n; i++)
+				a[i].line += d;
+		}
+	}
+	if(n > HISTSZ)
+		n = HISTSZ;
+	for(i=0; i<n; i++)
+		print("%s:%ld ", a[i].name, (long)(l-a[i].line+a[i].offset+1));
+}
+
+void
+ieeedtod(Ieee *ieee, double native)
+{
+	double fr, ho, f;
+	int exp;
+
+	if(native < 0) {
+		ieeedtod(ieee, -native);
+		ieee->h |= 0x80000000L;
+		return;
+	}
+	if(native == 0) {
+		ieee->l = 0;
+		ieee->h = 0;
+		return;
+	}
+	fr = frexp(native, &exp);
+	f = 2097152L;		/* shouldnt use fp constants here */
+	fr = modf(fr*f, &ho);
+	ieee->h = ho;
+	ieee->h &= 0xfffffL;
+	ieee->h |= (exp+1022L) << 20;
+	f = 65536L;
+	fr = modf(fr*f, &ho);
+	ieee->l = ho;
+	ieee->l <<= 16;
+	ieee->l |= (long)(fr*f);
+}

+ 12 - 0
sys/src/libcc/mac.c

@@ -0,0 +1,12 @@
+/*
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+
+#include	"cc.h"
+
+#include	"macbody"

+ 857 - 0
sys/src/libcc/macbody

@@ -0,0 +1,857 @@
+#define VARMAC 0x80
+
+long
+getnsn(void)
+{
+	long n;
+	int c;
+
+	c = getnsc();
+	if(c < '0' || c > '9')
+		return -1;
+	n = 0;
+	while(c >= '0' && c <= '9') {
+		n = n*10 + c-'0';
+		c = getc();
+	}
+	unget(c);
+	return n;
+}
+
+Sym*
+getsym(void)
+{
+	int c;
+	char *cp;
+
+	c = getnsc();
+	if(!isalpha(c) && c != '_' && c < Runeself) {
+		unget(c);
+		return S;
+	}
+	for(cp = symb;;) {
+		if(cp <= symb+NSYMB-4)
+			*cp++ = c;
+		c = getc();
+		if(isalnum(c) || c == '_' || c >= Runeself)
+			continue;
+		unget(c);
+		break;
+	}
+	*cp = 0;
+	if(cp > symb+NSYMB-4)
+		yyerror("symbol too large: %s", symb);
+	return lookup();
+}
+
+Sym*
+getsymdots(int *dots)
+{
+	int c;
+	Sym *s;
+
+	s = getsym();
+	if(s != S)
+		return s;
+
+	c = getnsc();
+	if(c != '.'){
+		unget(c);
+		return S;
+	}
+	if(getc() != '.' || getc() != '.')
+		yyerror("bad dots in macro");
+	*dots = 1;
+	return slookup("__VA_ARGS__");
+}
+
+int
+getcom(void)
+{
+	int c;
+
+	for(;;) {
+		c = getnsc();
+		if(c != '/')
+			break;
+		c = getc();
+		if(c == '/') {
+			while(c != '\n')
+				c = getc();
+			break;
+		}
+		if(c != '*')
+			break;
+		c = getc();
+		for(;;) {
+			if(c == '*') {
+				c = getc();
+				if(c != '/')
+					continue;
+				c = getc();
+				break;
+			}
+			if(c == '\n') {
+				yyerror("comment across newline");
+				break;
+			}
+			c = getc();
+		}
+		if(c == '\n')
+			break;
+	}
+	return c;
+}
+
+void
+dodefine(char *cp)
+{
+	Sym *s;
+	char *p;
+	long l;
+
+	strcpy(symb, cp);
+	p = strchr(symb, '=');
+	if(p) {
+		*p++ = 0;
+		s = lookup();
+		l = strlen(p) + 2;	/* +1 null, +1 nargs */
+		while(l & 3)
+			l++;
+		while(nhunk < l)
+			gethunk();
+		*hunk = 0;
+		strcpy(hunk+1, p);
+		s->macro = hunk;
+		hunk += l;
+		nhunk -= l;
+	} else {
+		s = lookup();
+		s->macro = "\0001";	/* \000 is nargs */
+	}
+	if(debug['m'])
+		print("#define (-D) %s %s\n", s->name, s->macro+1);
+}
+
+struct
+{
+	char	*macname;
+	void	(*macf)(void);
+} mactab[] =
+{
+	{"ifdef",	0},	/* macif(0) */
+	{"ifndef",	0},	/* macif(1) */
+	{"else",		0},	/* macif(2) */
+
+	{"line",		maclin},
+	{"define",	macdef},
+	{"include",	macinc},
+	{"undef",	macund},
+
+	{"pragma",	macprag},
+	{"endif",	macend},
+	{0}
+};
+
+void
+domacro(void)
+{
+	int i;
+	Sym *s;
+
+	s = getsym();
+	if(s == S)
+		s = slookup("endif");
+	for(i=0; mactab[i].macname; i++)
+		if(strcmp(s->name, mactab[i].macname) == 0) {
+			if(mactab[i].macf)
+				(*mactab[i].macf)();
+			else
+				macif(i);
+			return;
+		}
+	yyerror("unknown #: %s", s->name);
+	macend();
+}
+
+void
+macund(void)
+{
+	Sym *s;
+
+	s = getsym();
+	macend();
+	if(s == S) {
+		yyerror("syntax in #undef");
+		return;
+	}
+	s->macro = 0;
+}
+
+#define	NARG	25
+void
+macdef(void)
+{
+	Sym *s, *a;
+	char *args[NARG], *np, *base;
+	int n, i, c, len, dots;
+	int ischr;
+
+	s = getsym();
+	if(s == S)
+		goto bad;
+	if(s->macro)
+		yyerror("macro redefined: %s", s->name);
+	c = getc();
+	n = -1;
+	dots = 0;
+	if(c == '(') {
+		n++;
+		c = getnsc();
+		if(c != ')') {
+			unget(c);
+			for(;;) {
+				a = getsymdots(&dots);
+				if(a == S)
+					goto bad;
+				if(n >= NARG) {
+					yyerror("too many arguments in #define: %s", s->name);
+					goto bad;
+				}
+				args[n++] = a->name;
+				c = getnsc();
+				if(c == ')')
+					break;
+				if(c != ',' || dots)
+					goto bad;
+			}
+		}
+		c = getc();
+	}
+	if(isspace(c))
+		if(c != '\n')
+			c = getnsc();
+	base = hunk;
+	len = 1;
+	ischr = 0;
+	for(;;) {
+		if(isalpha(c) || c == '_') {
+			np = symb;
+			*np++ = c;
+			c = getc();
+			while(isalnum(c) || c == '_') {
+				*np++ = c;
+				c = getc();
+			}
+			*np = 0;
+			for(i=0; i<n; i++)
+				if(strcmp(symb, args[i]) == 0)
+					break;
+			if(i >= n) {
+				i = strlen(symb);
+				base = allocn(base, len, i);
+				memcpy(base+len, symb, i);
+				len += i;
+				continue;
+			}
+			base = allocn(base, len, 2);
+			base[len++] = '#';
+			base[len++] = 'a' + i;
+			continue;
+		}
+		if(ischr){
+			if(c == '\\'){ 
+				base = allocn(base, len, 1);
+				base[len++] = c;
+				c = getc();
+			}else if(c == ischr)
+				ischr = 0;
+		}else{
+			if(c == '"' || c == '\''){
+				base = allocn(base, len, 1);
+				base[len++] = c;
+				ischr = c;
+				c = getc();
+				continue;
+			}
+			if(c == '/') {
+				c = getc();
+				if(c == '/'){
+					c = getc();
+					for(;;) {
+						if(c == '\n')
+							break;
+						c = getc();
+					}
+					continue;
+				}
+				if(c == '*'){
+					c = getc();
+					for(;;) {
+						if(c == '*') {
+							c = getc();
+							if(c != '/')
+								continue;
+							c = getc();
+							break;
+						}
+						if(c == '\n') {
+							yyerror("comment and newline in define: %s", s->name);
+							break;
+						}
+						c = getc();
+					}
+					continue;
+				}
+				base = allocn(base, len, 1);
+				base[len++] = '/';
+				continue;
+			}
+		}
+		if(c == '\\') {
+			c = getc();
+			if(c == '\n') {
+				c = getc();
+				continue;
+			}
+			else if(c == '\r') {
+				c = getc();
+				if(c == '\n') {
+					c = getc();
+					continue;
+				}
+			}
+			base = allocn(base, len, 1);
+			base[len++] = '\\';
+			continue;
+		}
+		if(c == '\n')
+			break;
+		if(c == '#')
+		if(n > 0) {
+			base = allocn(base, len, 1);
+			base[len++] = c;
+		}
+		base = allocn(base, len, 1);
+		base[len++] = c;
+		c = ((--fi.c < 0)? filbuf(): (*fi.p++ & 0xff));
+		if(c == '\n')
+			lineno++;
+		if(c == -1) {
+			yyerror("eof in a macro: %s", s->name);
+			break;
+		}
+	}
+	do {
+		base = allocn(base, len, 1);
+		base[len++] = 0;
+	} while(len & 3);
+
+	*base = n+1;
+	if(dots)
+		*base |= VARMAC;
+	s->macro = base;
+	if(debug['m'])
+		print("#define %s %s\n", s->name, s->macro+1);
+	return;
+
+bad:
+	if(s == S)
+		yyerror("syntax in #define");
+	else
+		yyerror("syntax in #define: %s", s->name);
+	macend();
+}
+
+void
+macexpand(Sym *s, char *b)
+{
+	char buf[2000];
+	int n, l, c, nargs;
+	char *arg[NARG], *cp, *ob, *ecp, dots;
+
+	ob = b;
+	if(*s->macro == 0) {
+		strcpy(b, s->macro+1);
+		if(debug['m'])
+			print("#expand %s %s\n", s->name, ob);
+		return;
+	}
+	
+	nargs = (char)(*s->macro & ~VARMAC) - 1;
+	dots = *s->macro & VARMAC;
+
+	c = getnsc();
+	if(c != '(')
+		goto bad;
+	n = 0;
+	c = getc();
+	if(c != ')') {
+		unget(c);
+		l = 0;
+		cp = buf;
+		ecp = cp + sizeof(buf)-4;
+		arg[n++] = cp;
+		for(;;) {
+			if(cp >= ecp)
+				goto toobig;
+			c = getc();
+			if(c == '"')
+				for(;;) {
+					if(cp >= ecp)
+						goto toobig;
+					*cp++ = c;
+					c = getc();
+					if(c == '\\') {
+						*cp++ = c;
+						c = getc();
+						continue;
+					}
+					if(c == '\n')
+						goto bad;
+					if(c == '"')
+						break;
+				}
+			if(c == '\'')
+				for(;;) {
+					if(cp >= ecp)
+						goto toobig;
+					*cp++ = c;
+					c = getc();
+					if(c == '\\') {
+						*cp++ = c;
+						c = getc();
+						continue;
+					}
+					if(c == '\n')
+						goto bad;
+					if(c == '\'')
+						break;
+				}
+			if(c == '/') {
+				c = getc();
+				switch(c) {
+				case '*':
+					for(;;) {
+						c = getc();
+						if(c == '*') {
+							c = getc();
+							if(c == '/')
+								break;
+						}
+					}
+					*cp++ = ' ';
+					continue;
+				case '/':
+					while((c = getc()) != '\n')
+						;
+					break;
+				default:
+					unget(c);
+					c = '/';
+				}
+			}
+			if(l == 0) {
+				if(c == ',') {
+					if(n == nargs && dots) {
+						*cp++ = ',';
+						continue;
+					}
+					*cp++ = 0;
+					arg[n++] = cp;
+					if(n > nargs)
+						break;
+					continue;
+				}
+				if(c == ')')
+					break;
+			}
+			if(c == '\n')
+				c = ' ';
+			*cp++ = c;
+			if(c == '(')
+				l++;
+			if(c == ')')
+				l--;
+		}
+		*cp = 0;
+	}
+	if(n != nargs) {
+		yyerror("argument mismatch expanding: %s", s->name);
+		*b = 0;
+		return;
+	}
+	cp = s->macro+1;
+	for(;;) {
+		c = *cp++;
+		if(c == '\n')
+			c = ' ';
+		if(c != '#') {
+			*b++ = c;
+			if(c == 0)
+				break;
+			continue;
+		}
+		c = *cp++;
+		if(c == 0)
+			goto bad;
+		if(c == '#') {
+			*b++ = c;
+			continue;
+		}
+		c -= 'a';
+		if(c < 0 || c >= n)
+			continue;
+		strcpy(b, arg[c]);
+		b += strlen(arg[c]);
+	}
+	*b = 0;
+	if(debug['m'])
+		print("#expand %s %s\n", s->name, ob);
+	return;
+
+bad:
+	yyerror("syntax in macro expansion: %s", s->name);
+	*b = 0;
+	return;
+
+toobig:
+	yyerror("too much text in macro expansion: %s", s->name);
+	*b = 0;
+}
+
+void
+macinc(void)
+{
+	int c0, c, i, f;
+	char str[STRINGSZ], *hp;
+
+	c0 = getnsc();
+	if(c0 != '"') {
+		c = c0;
+		if(c0 != '<')
+			goto bad;
+		c0 = '>';
+	}
+	for(hp = str;;) {
+		c = getc();
+		if(c == c0)
+			break;
+		if(c == '\n')
+			goto bad;
+		*hp++ = c;
+	}
+	*hp = 0;
+
+	c = getcom();
+	if(c != '\n')
+		goto bad;
+
+	f = -1;
+	for(i=0; i<ninclude; i++) {
+		if(i == 0 && c0 == '>')
+			continue;
+		strcpy(symb, include[i]);
+		strcat(symb, "/");
+		if(strcmp(symb, "./") == 0)
+			symb[0] = 0;
+		strcat(symb, str);
+		f = open(symb, 0);
+		if(f >= 0)
+			break;
+	}
+	if(f < 0)
+		strcpy(symb, str);
+	c = strlen(symb) + 1;
+	while(c & 3)
+		c++;
+	while(nhunk < c)
+		gethunk();
+	hp = hunk;
+	memcpy(hunk, symb, c);
+	nhunk -= c;
+	hunk += c;
+	newio();
+	pushio();
+	newfile(hp, f);
+	return;
+
+bad:
+	unget(c);
+	yyerror("syntax in #include");
+	macend();
+}
+
+void
+maclin(void)
+{
+	char *cp;
+	int c;
+	long n;
+
+	n = getnsn();
+	c = getc();
+	if(n < 0)
+		goto bad;
+
+	for(;;) {
+		if(c == ' ' || c == '\t') {
+			c = getc();
+			continue;
+		}
+		if(c == '"')
+			break;
+		if(c == '\n') {
+			strcpy(symb, "<noname>");
+			goto nn;
+		}
+		goto bad;
+	}
+	cp = symb;
+	for(;;) {
+		c = getc();
+		if(c == '"')
+			break;
+		*cp++ = c;
+	}
+	*cp = 0;
+	c = getcom();
+	if(c != '\n')
+		goto bad;
+
+nn:
+	c = strlen(symb) + 1;
+	while(c & 3)
+		c++;
+	while(nhunk < c)
+		gethunk();
+	cp = hunk;
+	memcpy(hunk, symb, c);
+	nhunk -= c;
+	hunk += c;
+	linehist(cp, n);
+	return;
+
+bad:
+	unget(c);
+	yyerror("syntax in #line");
+	macend();
+}
+
+void
+macif(int f)
+{
+	int c, l, bol;
+	Sym *s;
+
+	if(f == 2)
+		goto skip;
+	s = getsym();
+	if(s == S)
+		goto bad;
+	if(getcom() != '\n')
+		goto bad;
+	if((s->macro != 0) ^ f)
+		return;
+
+skip:
+	bol = 1;
+	l = 0;
+	for(;;) {
+		c = getc();
+		if(c != '#') {
+			if(!isspace(c))
+				bol = 0;
+			if(c == '\n')
+				bol = 1;
+			continue;
+		}
+		if(!bol)
+			continue;
+		s = getsym();
+		if(s == S)
+			continue;
+		if(strcmp(s->name, "endif") == 0) {
+			if(l) {
+				l--;
+				continue;
+			}
+			macend();
+			return;
+		}
+		if(strcmp(s->name, "ifdef") == 0 || strcmp(s->name, "ifndef") == 0) {
+			l++;
+			continue;
+		}
+		if(l == 0 && f != 2 && strcmp(s->name, "else") == 0) {
+			macend();
+			return;
+		}
+	}
+
+bad:
+	yyerror("syntax in #if(n)def");
+	macend();
+}
+
+void
+macprag(void)
+{
+	Sym *s;
+	int c0, c;
+	char *hp;
+	Hist *h;
+
+	s = getsym();
+
+	if(s && strcmp(s->name, "lib") == 0)
+		goto praglib;
+	if(s && strcmp(s->name, "pack") == 0) {
+		pragpack();
+		return;
+	}
+	if(s && strcmp(s->name, "fpround") == 0) {
+		pragfpround();
+		return;
+	}
+	if(s && strcmp(s->name, "profile") == 0) {
+		pragprofile();
+		return;
+	}
+	if(s && strcmp(s->name, "varargck") == 0) {
+		pragvararg();
+		return;
+	}
+	if(s && strcmp(s->name, "incomplete") == 0) {
+		pragincomplete();
+		return;
+	}
+	while(getnsc() != '\n')
+		;
+	return;
+
+praglib:
+	c0 = getnsc();
+	if(c0 != '"') {
+		c = c0;
+		if(c0 != '<')
+			goto bad;
+		c0 = '>';
+	}
+	for(hp = symb;;) {
+		c = getc();
+		if(c == c0)
+			break;
+		if(c == '\n')
+			goto bad;
+		*hp++ = c;
+	}
+	*hp = 0;
+	c = getcom();
+	if(c != '\n')
+		goto bad;
+
+	/*
+	 * put pragma-line in as a funny history 
+	 */
+	c = strlen(symb) + 1;
+	while(c & 3)
+		c++;
+	while(nhunk < c)
+		gethunk();
+	hp = hunk;
+	memcpy(hunk, symb, c);
+	nhunk -= c;
+	hunk += c;
+
+	h = alloc(sizeof(Hist));
+	h->name = hp;
+	h->line = lineno;
+	h->offset = -1;
+	h->link = H;
+	if(ehist == H) {
+		hist = h;
+		ehist = h;
+		return;
+	}
+	ehist->link = h;
+	ehist = h;
+	return;
+
+bad:
+	unget(c);
+	yyerror("syntax in #pragma lib");
+	macend();
+}
+
+void
+macend(void)
+{
+	int c;
+
+	for(;;) {
+		c = getnsc();
+		if(c < 0 || c == '\n')
+			return;
+	}
+}
+
+void
+linehist(char *f, int offset)
+{
+	Hist *h;
+
+	/*
+	 * overwrite the last #line directive if
+	 * no alloc has happened since the last one
+	 */
+	if(newflag == 0 && ehist != H && offset != 0 && ehist->offset != 0)
+		if(f && ehist->name && strcmp(f, ehist->name) == 0) {
+			ehist->line = lineno;
+			ehist->offset = offset;
+			return;
+		}
+
+	if(debug['f']){
+		if(f) {
+			if(offset)
+				print("%4ld: %s (#line %d)\n", lineno, f, offset);
+			else
+				print("%4ld: %s\n", lineno, f);
+		} else
+			print("%4ld: <pop>\n", lineno);
+	}
+	newflag = 0;
+
+	h = alloc(sizeof(Hist));
+	h->name = f;
+	h->line = lineno;
+	h->offset = offset;
+	h->link = H;
+	if(ehist == H) {
+		hist = h;
+		ehist = h;
+		return;
+	}
+	ehist->link = h;
+	ehist = h;
+}
+
+void
+gethunk(void)
+{
+	char *h;
+	long nh;
+
+	nh = NHUNK;
+	if(thunk >= 10L*NHUNK)
+		nh = 10L*NHUNK;
+	h = (char*)mysbrk(nh);
+	if(h == (char*)-1) {
+		yyerror("out of memory");
+		errorexit();
+	}
+	hunk = h;
+	nhunk = nh;
+	thunk += nh;
+}

+ 17 - 0
sys/src/libcc/omachcap.c

@@ -0,0 +1,17 @@
+/*
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+
+#include	"cc.h"
+
+/* default, like old cc */
+int
+machcap(Node* n)
+{
+	return 0;
+}

+ 600 - 0
sys/src/libcc/pgen.c

@@ -0,0 +1,600 @@
+/*
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+
+#include "gc.h"
+
+void
+codgen(Node *n, Node *nn)
+{
+	Prog *sp;
+	Node *n1, nod, nod1;
+
+	cursafe = 0;
+	curarg = 0;
+	maxargsafe = 0;
+	hasdoubled = 0;
+
+	/*
+	 * isolate name
+	 */
+	for(n1 = nn;; n1 = n1->left) {
+		if(n1 == Z) {
+			diag(nn, "cant find function name");
+			return;
+		}
+		if(n1->op == ONAME)
+			break;
+	}
+	nearln = nn->lineno;
+	gpseudo(ATEXT, n1->sym, nodconst(stkoff));
+	sp = p;
+
+	if(typecmplx[thisfn->link->etype]) {
+		if(nodret == nil) {
+			nodret = new(ONAME, Z, Z);
+			nodret->sym = slookup(".ret");
+			nodret->class = CPARAM;
+			nodret->type = types[TIND];
+			nodret->etype = TIND;
+			nodret = new(OIND, nodret, Z);
+		}
+		n1 = nodret->left;
+		if(n1->type == T || n1->type->link != thisfn->link) {
+			n1->type = typ(TIND, thisfn->link);
+			n1->etype = n1->type->etype;
+			nodret = new(OIND, n1, Z);
+			complex(nodret);
+		}
+	}
+
+	/*
+	 * isolate first argument
+	 */
+	if(REGARG >= 0) {	
+		if(typecmplx[thisfn->link->etype]) {
+			nod1 = *nodret->left;
+			nodreg(&nod, &nod1, REGARG);
+			gmove(&nod, &nod1);
+		} else
+		if(firstarg && typeword[firstargtype->etype]) {
+			nod1 = znode;
+			nod1.op = ONAME;
+			nod1.sym = firstarg;
+			nod1.type = firstargtype;
+			nod1.class = CPARAM;
+			nod1.xoffset = align(0, firstargtype, Aarg1);
+			nod1.etype = firstargtype->etype;
+			xcom(&nod1);
+			nodreg(&nod, &nod1, REGARG);
+			gmove(&nod, &nod1);
+		}
+	}
+
+	canreach = 1;
+	warnreach = 1;
+	gen(n);
+	if(canreach && thisfn->link->etype != TVOID){
+		if(debug['B'])
+			warn(Z, "no return at end of function: %s", n1->sym->name);
+		else
+			diag(Z, "no return at end of function: %s", n1->sym->name);
+	}
+	noretval(3);
+	gbranch(ORETURN);
+
+	if(!debug['N'] || debug['R'] || debug['P'])
+		regopt(sp);
+	
+	if(thechar=='6' || thechar=='7' || thechar=='9' || hasdoubled)	/* [sic] */
+		maxargsafe = round(maxargsafe, 8);
+	sp->to.offset += maxargsafe;
+}
+
+void
+supgen(Node *n)
+{
+	int owarn;
+	long spc;
+	Prog *sp;
+
+	if(n == Z)
+		return;
+	suppress++;
+	owarn = warnreach;
+	warnreach = 0;
+	spc = pc;
+	sp = lastp;
+	gen(n);
+	lastp = sp;
+	pc = spc;
+	sp->link = nil;
+	suppress--;
+	warnreach = owarn;
+}
+
+Node*
+uncomma(Node *n)
+{
+	while(n != Z && n->op == OCOMMA) {
+		cgen(n->left, Z);
+		n = n->right;
+	}
+	return n;
+}
+
+void
+gen(Node *n)
+{
+	Node *l, nod, rn;
+	Prog *sp, *spc, *spb;
+	Case *cn;
+	long sbc, scc;
+	int snbreak, sncontin;
+	int f, o, oldreach;
+
+loop:
+	if(n == Z)
+		return;
+	nearln = n->lineno;
+	o = n->op;
+	if(debug['G'])
+		if(o != OLIST)
+			print("%L %O\n", nearln, o);
+
+	if(!canreach) {
+		switch(o) {
+		case OLABEL:
+		case OCASE:
+		case OLIST:
+		case OCOMMA:
+		case OBREAK:
+		case OFOR:
+		case OWHILE:
+		case ODWHILE:
+			/* all handled specially - see switch body below */
+			break;
+		default:
+			if(warnreach) {
+				warn(n, "unreachable code %O", o);
+				warnreach = 0;
+			}
+		}
+	}
+
+	switch(o) {
+
+	default:
+		complex(n);
+		cgen(n, Z);
+		break;
+
+	case OLIST:
+	case OCOMMA:
+		gen(n->left);
+
+	rloop:
+		n = n->right;
+		goto loop;
+
+	case ORETURN:
+		canreach = 0;
+		warnreach = !suppress;
+		complex(n);
+		if(n->type == T)
+			break;
+		l = uncomma(n->left);
+		if(l == Z) {
+			noretval(3);
+			gbranch(ORETURN);
+			break;
+		}
+		if(typecmplx[n->type->etype]) {
+			nod = znode;
+			nod.op = OAS;
+			nod.left = nodret;
+			nod.right = l;
+			nod.type = n->type;
+			nod.complex = l->complex;
+			cgen(&nod, Z);
+			noretval(3);
+			gbranch(ORETURN);
+			break;
+		}
+		if(newvlongcode && !typefd[n->type->etype]){
+			regret(&rn, n);
+			regfree(&rn);
+			nod = znode;
+			nod.op = OAS;
+			nod.left = &rn;
+			nod.right = l;
+			nod.type = n->type;
+			nod.complex = l->complex;
+			cgen(&nod, Z);
+			noretval(2);
+			gbranch(ORETURN);
+			break;
+		}
+		regret(&nod, n);
+		cgen(l, &nod);
+		regfree(&nod);
+		if(typefd[n->type->etype])
+			noretval(1);
+		else
+			noretval(2);
+		gbranch(ORETURN);
+		break;
+
+	case OLABEL:
+		canreach = 1;
+		l = n->left;
+		if(l) {
+			l->pc = pc;
+			if(l->label)
+				patch(l->label, pc);
+		}
+		gbranch(OGOTO);	/* prevent self reference in reg */
+		patch(p, pc);
+		goto rloop;
+
+	case OGOTO:
+		canreach = 0;
+		warnreach = !suppress;
+		n = n->left;
+		if(n == Z)
+			return;
+		if(n->complex == 0) {
+			diag(Z, "label undefined: %s", n->sym->name);
+			return;
+		}
+		if(suppress)
+			return;
+		gbranch(OGOTO);
+		if(n->pc) {
+			patch(p, n->pc);
+			return;
+		}
+		if(n->label)
+			patch(n->label, pc-1);
+		n->label = p;
+		return;
+
+	case OCASE:
+		canreach = 1;
+		l = n->left;
+		if(cases == C)
+			diag(n, "case/default outside a switch");
+		if(l == Z) {
+			casf();
+			cases->val = 0;
+			cases->def = 1;
+			cases->label = pc;
+			cases->isv = 0;
+			goto rloop;
+		}
+		complex(l);
+		if(l->type == T)
+			goto rloop;
+		if(l->op != OCONST || !typeswitch[l->type->etype]) {
+			diag(n, "case expression must be integer constant");
+			goto rloop;
+		}
+		casf();
+		cases->val = l->vconst;
+		cases->def = 0;
+		cases->label = pc;
+		cases->isv = typev[l->type->etype];
+		goto rloop;
+
+	case OSWITCH:
+		l = n->left;
+		complex(l);
+		if(l->type == T)
+			break;
+		if(!typeswitch[l->type->etype]) {
+			diag(n, "switch expression must be integer");
+			break;
+		}
+
+		gbranch(OGOTO);		/* entry */
+		sp = p;
+
+		cn = cases;
+		cases = C;
+		casf();
+
+		sbc = breakpc;
+		breakpc = pc;
+		snbreak = nbreak;
+		nbreak = 0;
+		gbranch(OGOTO);
+		spb = p;
+
+		gen(n->right);		/* body */
+		if(canreach){
+			gbranch(OGOTO);
+			patch(p, breakpc);
+			nbreak++;
+		}
+
+		patch(sp, pc);
+		regalloc(&nod, l, Z);
+		/* always signed */
+		if(typev[l->type->etype])
+			nod.type = types[TVLONG];
+		else
+			nod.type = types[TLONG];
+		cgen(l, &nod);
+		doswit(&nod);
+		regfree(&nod);
+		patch(spb, pc);
+
+		cases = cn;
+		breakpc = sbc;
+		canreach = nbreak!=0;
+		if(canreach == 0)
+			warnreach = !suppress;
+		nbreak = snbreak;
+		break;
+
+	case OWHILE:
+	case ODWHILE:
+		l = n->left;
+		gbranch(OGOTO);		/* entry */
+		sp = p;
+
+		scc = continpc;
+		continpc = pc;
+		gbranch(OGOTO);
+		spc = p;
+
+		sbc = breakpc;
+		breakpc = pc;
+		snbreak = nbreak;
+		nbreak = 0;
+		gbranch(OGOTO);
+		spb = p;
+
+		patch(spc, pc);
+		if(n->op == OWHILE)
+			patch(sp, pc);
+		bcomplex(l, Z);		/* test */
+		patch(p, breakpc);
+		if(l->op != OCONST || vconst(l) == 0)
+			nbreak++;
+
+		if(n->op == ODWHILE)
+			patch(sp, pc);
+		gen(n->right);		/* body */
+		gbranch(OGOTO);
+		patch(p, continpc);
+
+		patch(spb, pc);
+		continpc = scc;
+		breakpc = sbc;
+		canreach = nbreak!=0;
+		if(canreach == 0)
+			warnreach = !suppress;
+		nbreak = snbreak;
+		break;
+
+	case OFOR:
+		l = n->left;
+		if(!canreach && l->right->left && warnreach) {
+			warn(n, "unreachable code FOR");
+			warnreach = 0;
+		}
+		gen(l->right->left);	/* init */
+		gbranch(OGOTO);		/* entry */
+		sp = p;
+
+		/* 
+		 * if there are no incoming labels in the 
+		 * body and the top's not reachable, warn
+		 */
+		if(!canreach && warnreach && deadheads(n)) {
+			warn(n, "unreachable code %O", o);
+			warnreach = 0;
+		}
+
+		scc = continpc;
+		continpc = pc;
+		gbranch(OGOTO);
+		spc = p;
+
+		sbc = breakpc;
+		breakpc = pc;
+		snbreak = nbreak;
+		nbreak = 0;
+		sncontin = ncontin;
+		ncontin = 0;
+		gbranch(OGOTO);
+		spb = p;
+
+		patch(spc, pc);
+		gen(l->right->right);	/* inc */
+		patch(sp, pc);	
+		if(l->left != Z) {	/* test */
+			bcomplex(l->left, Z);
+			patch(p, breakpc);
+			if(l->left->op != OCONST || vconst(l->left) == 0)
+				nbreak++;
+		}
+		canreach = 1;
+		gen(n->right);		/* body */
+		if(canreach){
+			gbranch(OGOTO);
+			patch(p, continpc);
+			ncontin++;
+		}
+		if(!ncontin && l->right->right && warnreach) {
+			warn(l->right->right, "unreachable FOR inc");
+			warnreach = 0;
+		}
+
+		patch(spb, pc);
+		continpc = scc;
+		breakpc = sbc;
+		canreach = nbreak!=0;
+		if(canreach == 0)
+			warnreach = !suppress;
+		nbreak = snbreak;
+		ncontin = sncontin;
+		break;
+
+	case OCONTINUE:
+		if(continpc < 0) {
+			diag(n, "continue not in a loop");
+			break;
+		}
+		gbranch(OGOTO);
+		patch(p, continpc);
+		ncontin++;
+		canreach = 0;
+		warnreach = !suppress;
+		break;
+
+	case OBREAK:
+		if(breakpc < 0) {
+			diag(n, "break not in a loop");
+			break;
+		}
+		/*
+		 * Don't complain about unreachable break statements.
+		 * There are breaks hidden in yacc's output and some people
+		 * write return; break; in their switch statements out of habit.
+		 * However, don't confuse the analysis by inserting an 
+		 * unreachable reference to breakpc either.
+		 */
+		if(!canreach)
+			break;
+		gbranch(OGOTO);
+		patch(p, breakpc);
+		nbreak++;
+		canreach = 0;
+		warnreach = !suppress;
+		break;
+
+	case OIF:
+		l = n->left;
+		if(bcomplex(l, n->right)) {
+			if(typefd[l->type->etype])
+				f = !l->fconst;
+			else
+				f = !l->vconst;
+			if(debug['c'])
+				print("%L const if %s\n", nearln, f ? "false" : "true");
+			if(f) {
+				canreach = 1;
+				supgen(n->right->left);
+				oldreach = canreach;
+				canreach = 1;
+				gen(n->right->right);
+				/*
+				 * treat constant ifs as regular ifs for 
+				 * reachability warnings.
+				 */
+				if(!canreach && oldreach && debug['w'] < 2)
+					warnreach = 0;
+			}
+			else {
+				canreach = 1;
+				gen(n->right->left);
+				oldreach = canreach;
+				canreach = 1;
+				supgen(n->right->right);
+				/*
+				 * treat constant ifs as regular ifs for 
+				 * reachability warnings.
+				 */
+				if(!oldreach && canreach && debug['w'] < 2)
+					warnreach = 0;
+				canreach = oldreach;
+			}
+		}
+		else {
+			sp = p;
+			canreach = 1;
+			if(n->right->left != Z)
+				gen(n->right->left);
+			oldreach = canreach;
+			canreach = 1;
+			if(n->right->right != Z) {
+				gbranch(OGOTO);
+				patch(sp, pc);
+				sp = p;
+				gen(n->right->right);
+			}
+			patch(sp, pc);
+			canreach = canreach || oldreach;
+			if(canreach == 0)
+				warnreach = !suppress;
+		}
+		break;
+
+	case OSET:
+	case OUSED:
+		usedset(n->left, o);
+		break;
+	}
+}
+
+void
+usedset(Node *n, int o)
+{
+	if(n->op == OLIST) {
+		usedset(n->left, o);
+		usedset(n->right, o);
+		return;
+	}
+	complex(n);
+	switch(n->op) {
+	case OADDR:	/* volatile */
+		gins(ANOP, n, Z);
+		break;
+	case ONAME:
+		if(o == OSET)
+			gins(ANOP, Z, n);
+		else
+			gins(ANOP, n, Z);
+		break;
+	}
+}
+
+int
+bcomplex(Node *n, Node *c)
+{
+	Node *b, nod;
+
+
+	complex(n);
+	if(n->type != T)
+	if(tcompat(n, T, n->type, tnot))
+		n->type = T;
+	if(n->type == T) {
+		gbranch(OGOTO);
+		return 0;
+	}
+	if(c != Z && n->op == OCONST && deadheads(c))
+		return 1;
+	/* this is not quite right yet, so ignore it for now */
+	if(0 && newvlongcode && typev[n->type->etype] && machcap(Z)) {
+		b = &nod;
+		b->op = ONE;
+		b->left = n;
+		b->right = new(0, Z, Z);
+		*b->right = *nodconst(0);
+		b->right->type = n->type;
+		b->type = types[TLONG];
+		cgen(b, Z);
+		return 0;
+	}
+	bool64(n);
+	boolgen(n, 1, Z);
+	return 0;
+}

+ 277 - 0
sys/src/libcc/pickle.c

@@ -0,0 +1,277 @@
+/*
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+
+#include "cc.h"
+
+static char *kwd[] =
+{
+	"$adt", "$aggr", "$append", "$complex", "$defn",
+	"$delete", "$do", "$else", "$eval", "$head", "$if",
+	"$local", "$loop", "$return", "$tail", "$then",
+	"$union", "$whatis", "$while",
+};
+static char picklestr[] = "\tpickle(s, un, ";
+
+static char*
+pmap(char *s)
+{
+	int i, bot, top, new;
+
+	bot = 0;
+	top = bot + nelem(kwd) - 1;
+	while(bot <= top){
+		new = bot + (top - bot)/2;
+		i = strcmp(kwd[new]+1, s);
+		if(i == 0)
+			return kwd[new];
+
+		if(i < 0)
+			bot = new + 1;
+		else
+			top = new - 1;
+	}
+	return s;
+}
+
+Sym*
+picklesue(Type *t)
+{
+	int h;
+	Sym *s;
+
+	if(t != T)
+	for(h=0; h<nelem(hash); h++)
+		for(s = hash[h]; s != S; s = s->link)
+			if(s->suetag && s->suetag->link == t)
+				return s;
+	return 0;
+}
+
+Sym*
+picklefun(Type *t)
+{
+	int h;
+	Sym *s;
+
+	for(h=0; h<nelem(hash); h++)
+		for(s = hash[h]; s != S; s = s->link)
+			if(s->type == t)
+				return s;
+	return 0;
+}
+
+char	picklechar[NTYPE];
+Init	picklecinit[] =
+{
+	{TCHAR,		'C',	0},
+	{TUCHAR,		'b',	0},
+	{TSHORT,		'd',	0},
+	{TUSHORT,		'u',	0},
+	{TLONG,		'D',	0},
+	{TULONG,		'U',	0},
+	{TVLONG,		'V',	0},
+	{TUVLONG,	'W',	0},
+	{TFLOAT,		'f',	0},
+	{TDOUBLE,		'F',	0},
+	{TARRAY,		'a',	0},
+	{TIND,		'X',	0},
+	{-1,		0,	0},
+};
+
+static void
+pickleinit(void)
+{
+	Init *p;
+
+	for(p=picklecinit; p->code >= 0; p++)
+		picklechar[p->code] = p->value;
+
+	picklechar[TINT] = picklechar[TLONG];
+	picklechar[TUINT] = picklechar[TULONG];
+	if(types[TINT]->width != types[TLONG]->width) {
+		picklechar[TINT] = picklechar[TSHORT];
+		picklechar[TUINT] = picklechar[TUSHORT];
+		if(types[TINT]->width != types[TSHORT]->width)
+			warn(Z, "picklemember int not long or short");
+	}
+	
+}
+
+void
+picklemember(Type *t, long off)
+{
+	Sym *s, *s1;
+	static int picklecharinit = 0;
+
+	if(picklecharinit == 0) {
+		pickleinit();
+		picklecharinit = 1;
+	}
+	s = t->sym;
+	switch(t->etype) {
+	default:
+		Bprint(&outbuf, "	T%d\n", t->etype);
+		break;
+
+	case TIND:
+		if(s == S)
+			Bprint(&outbuf,
+				"%s\"p\", (char*)addr+%ld+_i*%ld);\n",
+				picklestr, t->offset+off, t->width);
+		else
+			Bprint(&outbuf,
+				"%s\"p\", &addr->%s);\n",
+				picklestr, pmap(s->name));
+		break;
+
+	case TINT:
+	case TUINT:
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TLONG:
+	case TULONG:
+	case TVLONG:
+	case TUVLONG:
+	case TFLOAT:
+	case TDOUBLE:
+		if(s == S)
+			Bprint(&outbuf, "%s\"%c\", (char*)addr+%ld+_i*%ld);\n",
+				picklestr, picklechar[t->etype], t->offset+off, t->width);
+		else
+			Bprint(&outbuf, "%s\"%c\", &addr->%s);\n",
+				picklestr, picklechar[t->etype], pmap(s->name));
+		break;
+	case TARRAY:
+		Bprint(&outbuf, "\tfor(_i = 0; _i < %ld; _i++) {\n\t",
+			t->width/t->link->width);
+		picklemember(t->link, t->offset+off);
+		Bprint(&outbuf, "\t}\n\t_i = 0;\n\tUSED(_i);\n");
+		break;
+
+	case TSTRUCT:
+	case TUNION:
+		s1 = picklesue(t->link);
+		if(s1 == S)
+			break;
+		if(s == S) {
+			Bprint(&outbuf, "\tpickle_%s(s, un, (%s*)((char*)addr+%ld+_i*%ld));\n",
+				pmap(s1->name), pmap(s1->name), t->offset+off, t->width);
+		} else {
+			Bprint(&outbuf, "\tpickle_%s(s, un, &addr->%s);\n",
+				pmap(s1->name), pmap(s->name));
+		}
+		break;
+	}
+}
+
+void
+pickletype(Type *t)
+{
+	Sym *s;
+	Type *l;
+	Io *i;
+	int n;
+	char *an;
+
+	if(!debug['P'])
+		return;
+	if(debug['P'] > 1) {
+		n = 0;
+		for(i=iostack; i; i=i->link)
+			n++;
+		if(n > 1)
+			return;
+	}
+	s = picklesue(t->link);
+	if(s == S)
+		return;
+	switch(t->etype) {
+	default:
+		Bprint(&outbuf, "T%d\n", t->etype);
+		return;
+
+	case TUNION:
+	case TSTRUCT:
+		if(debug['s'])
+			goto asmstr;
+		an = pmap(s->name);
+
+		Bprint(&outbuf, "void\npickle_%s(void *s, int un, %s *addr)\n{\n\tint _i = 0;\n\n\tUSED(_i);\n", an, an);
+		for(l = t->link; l != T; l = l->down)
+			picklemember(l, 0);
+		Bprint(&outbuf, "}\n\n");
+		break;
+	asmstr:
+		if(s == S)
+			break;
+		for(l = t->link; l != T; l = l->down)
+			if(l->sym != S)
+				Bprint(&outbuf, "#define\t%s.%s\t%ld\n",
+					s->name,
+					l->sym->name,
+					l->offset);
+		break;
+	}
+}
+
+void
+picklevar(Sym *s)
+{
+	int n;
+	Io *i;
+	Type *t;
+	Sym *s1, *s2;
+
+	if(!debug['P'] || debug['s'])
+		return;
+	if(debug['P'] > 1) {
+		n = 0;
+		for(i=iostack; i; i=i->link)
+			n++;
+		if(n > 1)
+			return;
+	}
+	t = s->type;
+	while(t && t->etype == TIND)
+		t = t->link;
+	if(t == T)
+		return;
+	if(t->etype == TENUM) {
+		Bprint(&outbuf, "%s = ", pmap(s->name));
+		if(!typefd[t->etype])
+			Bprint(&outbuf, "%lld;\n", s->vconst);
+		else
+			Bprint(&outbuf, "%f\n;", s->fconst);
+		return;
+	}
+	if(!typesu[t->etype])
+		return;
+	s1 = picklesue(t->link);
+	if(s1 == S)
+		return;
+	switch(s->class) {
+	case CAUTO:
+	case CPARAM:
+		s2 = picklefun(thisfn);
+		if(s2)
+			Bprint(&outbuf, "complex %s %s:%s;\n",
+				pmap(s1->name), pmap(s2->name), pmap(s->name));
+		break;
+	
+	case CSTATIC:
+	case CEXTERN:
+	case CGLOBL:
+	case CLOCAL:
+		Bprint(&outbuf, "complex %s %s;\n",
+			pmap(s1->name), pmap(s->name));
+		break;
+	}
+}

+ 208 - 0
sys/src/libcc/pswt.c

@@ -0,0 +1,208 @@
+/*
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+
+#include "gc.h"
+
+int
+swcmp(const void *a1, const void *a2)
+{
+	C1 *p1, *p2;
+
+	p1 = (C1*)a1;
+	p2 = (C1*)a2;
+	if(p1->val < p2->val)
+		return -1;
+	return p1->val > p2->val;
+}
+
+void
+doswit(Node *n)
+{
+	Case *c;
+	C1 *q, *iq, *iqh, *iql;
+	long def, nc, i, j, isv, nh;
+	Prog *hsb;
+	Node *vr[2];
+	int dup;
+
+	def = 0;
+	nc = 0;
+	isv = 0;
+	for(c = cases; c->link != C; c = c->link) {
+		if(c->def) {
+			if(def)
+				diag(n, "more than one default in switch");
+			def = c->label;
+			continue;
+		}
+		isv |= c->isv;
+		nc++;
+	}
+	if(typev[n->type->etype])
+		isv = 1;
+	else if(isv){
+		warn(n, "32-bit switch expression with 64-bit case constant");
+		isv = 0;
+	}
+
+	iq = alloc(nc*sizeof(C1));
+	q = iq;
+	for(c = cases; c->link != C; c = c->link) {
+		if(c->def)
+			continue;
+		if(c->isv && !isv)
+			continue;	/* can never match */
+		q->label = c->label;
+		if(isv)
+			q->val = c->val;
+		else
+			q->val = (long)c->val;	/* cast ensures correct value for 32-bit switch on 64-bit architecture */
+		q++;
+	}
+	qsort(iq, nc, sizeof(C1), swcmp);
+	if(debug['K'])
+	for(i=0; i<nc; i++)
+		print("case %2ld: = %.8llux\n", i, (vlong)iq[i].val);
+	dup = 0;
+	for(i=0; i<nc-1; i++)
+		if(iq[i].val == iq[i+1].val) {
+			diag(n, "duplicate cases in switch %lld", (vlong)iq[i].val);
+			dup = 1;
+		}
+	if(dup)
+		return;
+	if(def == 0) {
+		def = breakpc;
+		nbreak++;
+	}
+	if(!isv || ewidth[TIND] > ewidth[TLONG] || n->op == OREGISTER) {
+		swit1(iq, nc, def, n);
+		return;
+	}
+
+	/*
+	 * 64-bit case on 32-bit machine:
+	 * switch on high-order words, and
+	 * in each of those, switch on low-order words
+	 */
+	if(n->op != OREGPAIR)
+		fatal(n, "internal: expected register pair");
+	if(thechar == '8'){	/* TO DO: need an enquiry function */
+		vr[0] = n->left;	/* low */
+		vr[1] = n->right;	/* high */
+	}else{
+		vr[0] = n->right;
+		vr[1] = n->left;
+	}
+	vr[0]->type = types[TLONG];
+	vr[1]->type = types[TLONG];
+	gbranch(OGOTO);
+	hsb = p;
+	iqh = alloc(nc*sizeof(C1));
+	iql = alloc(nc*sizeof(C1));
+	nh = 0;
+	for(i=0; i<nc;){
+		iqh[nh].val = iq[i].val >> 32;
+		q = iql;
+		/* iq is sorted, so equal top halves are adjacent */
+		for(j = i; j < nc; j++){
+			if((iq[j].val>>32) != iqh[nh].val)
+				break;
+			q->val = (long)iq[j].val;
+			q->label = iq[j].label;
+			q++;
+		}
+		qsort(iql,  q-iql, sizeof(C1), swcmp);
+if(0){for(int k=0; k<(q-iql); k++)print("nh=%ld k=%d h=%#llux l=%#llux lab=%ld\n", nh, k, (vlong)iqh[nh].val,  (vlong)iql[k].val, iql[k].label);}
+		iqh[nh].label = pc;
+		nh++;
+		swit1(iql, q-iql, def, vr[0]);
+		i = j;
+	}
+	patch(hsb, pc);
+if(0){for(int k=0; k<nh; k++)print("k*=%d h=%#llux lab=%ld\n", k, (vlong)iqh[k].val,  iqh[k].label);}
+	swit1(iqh, nh, def, vr[1]);
+}
+
+void
+casf(void)
+{
+	Case *c;
+
+	c = alloc(sizeof(*c));
+	c->link = cases;
+	cases = c;
+}
+
+long
+outlstring(TRune *s, long n)
+{
+	char buf[sizeof(TRune)];
+	uint c;
+	int i;
+	long r;
+
+	if(suppress)
+		return nstring;
+	while(nstring & (sizeof(TRune)-1))
+		outstring("", 1);
+	r = nstring;
+	while(n > 0) {
+		c = *s++;
+		if(align(0, types[TCHAR], Aarg1)) {
+			for(i = 0; i < sizeof(TRune); i++)
+				buf[i] = c>>(8*(sizeof(TRune) - i - 1));
+		} else {
+			for(i = 0; i < sizeof(TRune); i++)
+				buf[i] = c>>(8*i);
+		}
+		outstring(buf, sizeof(TRune));
+		n -= sizeof(TRune);
+	}
+	return r;
+}
+
+void
+nullwarn(Node *l, Node *r)
+{
+	warn(Z, "result of operation not used");
+	if(l != Z)
+		cgen(l, Z);
+	if(r != Z)
+		cgen(r, Z);
+}
+
+void
+ieeedtod(Ieee *ieee, double native)
+{
+	double fr, ho, f;
+	int exp;
+
+	if(native < 0) {
+		ieeedtod(ieee, -native);
+		ieee->h |= 0x80000000L;
+		return;
+	}
+	if(native == 0) {
+		ieee->l = 0;
+		ieee->h = 0;
+		return;
+	}
+	fr = frexp(native, &exp);
+	f = 2097152L;		/* shouldnt use fp constants here */
+	fr = modf(fr*f, &ho);
+	ieee->h = ho;
+	ieee->h &= 0xfffffL;
+	ieee->h |= (exp+1022L) << 20;
+	f = 65536L;
+	fr = modf(fr*f, &ho);
+	ieee->l = ho;
+	ieee->l <<= 16;
+	ieee->l |= (long)(fr*f);
+}

+ 615 - 0
sys/src/libcc/scon.c

@@ -0,0 +1,615 @@
+/*
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+
+#include "cc.h"
+
+static Node*
+acast(Type *t, Node *n)
+{
+	if(n->type->etype != t->etype || n->op == OBIT) {
+		n = new1(OCAST, n, Z);
+		if(nocast(n->left->type, t))
+			*n = *n->left;
+		n->type = t;
+	}
+	return n;
+}
+
+
+void
+evconst(Node *n)
+{
+	Node *l, *r;
+	int et, isf;
+	vlong v;
+	double d;
+
+	if(n == Z || n->type == T)
+		return;
+
+	et = n->type->etype;
+	isf = typefd[et];
+
+	l = n->left;
+	r = n->right;
+
+	d = 0;
+	v = 0;
+
+	switch(n->op) {
+	default:
+		return;
+
+	case ONEG:
+		if(isf)
+			d = -l->fconst;
+		else
+			v = -l->vconst;
+		break;
+
+	case OCOM:
+		v = ~l->vconst;
+		break;
+
+	case OCAST:
+		if(et == TVOID)
+			return;
+		et = l->type->etype;
+		if(isf) {
+			if(typefd[et])
+				d = l->fconst;
+			else
+				d = l->vconst;
+		} else {
+			if(typefd[et])
+				v = l->fconst;
+			else
+				v = convvtox(l->vconst, n->type->etype);
+		}
+		break;
+
+	case OCONST:
+		break;
+
+	case OADD:
+		if(isf)
+			d = l->fconst + r->fconst;
+		else {
+			v = l->vconst + r->vconst;
+		}
+		break;
+
+	case OSUB:
+		if(isf)
+			d = l->fconst - r->fconst;
+		else
+			v = l->vconst - r->vconst;
+		break;
+
+	case OMUL:
+		if(isf)
+			d = l->fconst * r->fconst;
+		else {
+			v = l->vconst * r->vconst;
+		}
+		break;
+
+	case OLMUL:
+		v = (uvlong)l->vconst * (uvlong)r->vconst;
+		break;
+
+
+	case ODIV:
+		if(vconst(r) == 0) {
+			warn(n, "divide by zero");
+			return;
+		}
+		if(isf)
+			d = l->fconst / r->fconst;
+		else
+			v = l->vconst / r->vconst;
+		break;
+
+	case OLDIV:
+		if(vconst(r) == 0) {
+			warn(n, "divide by zero");
+			return;
+		}
+		v = (uvlong)l->vconst / (uvlong)r->vconst;
+		break;
+
+	case OMOD:
+		if(vconst(r) == 0) {
+			warn(n, "modulo by zero");
+			return;
+		}
+		v = l->vconst % r->vconst;
+		break;
+
+	case OLMOD:
+		if(vconst(r) == 0) {
+			warn(n, "modulo by zero");
+			return;
+		}
+		v = (uvlong)l->vconst % (uvlong)r->vconst;
+		break;
+
+	case OAND:
+		v = l->vconst & r->vconst;
+		break;
+
+	case OOR:
+		v = l->vconst | r->vconst;
+		break;
+
+	case OXOR:
+		v = l->vconst ^ r->vconst;
+		break;
+
+	case OLSHR:
+		v = (uvlong)l->vconst >> r->vconst;
+		break;
+
+	case OASHR:
+		v = l->vconst >> r->vconst;
+		break;
+
+	case OASHL:
+		v = l->vconst << r->vconst;
+		break;
+
+	case OLO:
+		v = (uvlong)l->vconst < (uvlong)r->vconst;
+		break;
+
+	case OLT:
+		if(typefd[l->type->etype])
+			v = l->fconst < r->fconst;
+		else
+			v = l->vconst < r->vconst;
+		break;
+
+	case OHI:
+		v = (uvlong)l->vconst > (uvlong)r->vconst;
+		break;
+
+	case OGT:
+		if(typefd[l->type->etype])
+			v = l->fconst > r->fconst;
+		else
+			v = l->vconst > r->vconst;
+		break;
+
+	case OLS:
+		v = (uvlong)l->vconst <= (uvlong)r->vconst;
+		break;
+
+	case OLE:
+		if(typefd[l->type->etype])
+			v = l->fconst <= r->fconst;
+		else
+			v = l->vconst <= r->vconst;
+		break;
+
+	case OHS:
+		v = (uvlong)l->vconst >= (uvlong)r->vconst;
+		break;
+
+	case OGE:
+		if(typefd[l->type->etype])
+			v = l->fconst >= r->fconst;
+		else
+			v = l->vconst >= r->vconst;
+		break;
+
+	case OEQ:
+		if(typefd[l->type->etype])
+			v = l->fconst == r->fconst;
+		else
+			v = l->vconst == r->vconst;
+		break;
+
+	case ONE:
+		if(typefd[l->type->etype])
+			v = l->fconst != r->fconst;
+		else
+			v = l->vconst != r->vconst;
+		break;
+
+	case ONOT:
+		if(typefd[l->type->etype])
+			v = !l->fconst;
+		else
+			v = !l->vconst;
+		break;
+
+	case OANDAND:
+		if(typefd[l->type->etype])
+			v = l->fconst && r->fconst;
+		else
+			v = l->vconst && r->vconst;
+		break;
+
+	case OOROR:
+		if(typefd[l->type->etype])
+			v = l->fconst || r->fconst;
+		else
+			v = l->vconst || r->vconst;
+		break;
+	}
+	if(isf) {
+		n->fconst = d;
+	} else {
+		n->vconst = convvtox(v, n->type->etype);
+	}
+	n->oldop = n->op;
+	n->op = OCONST;
+}
+
+void
+acom(Node *n)
+{
+	Type *t;
+	Node *l, *r;
+	int i;
+
+	switch(n->op)
+	{
+
+	case ONAME:
+	case OCONST:
+	case OSTRING:
+	case OINDREG:
+	case OREGISTER:
+		return;
+
+	case ONEG:
+		l = n->left;
+		if(addo(n) && addo(l))
+			break;
+		acom(l);
+		return;
+
+	case OADD:
+	case OSUB:
+	case OMUL:
+		l = n->left;
+		r = n->right;
+		if(addo(n)) {
+			if(addo(r))
+				break;
+			if(addo(l))
+				break;
+		}
+		acom(l);
+		acom(r);
+		return;
+
+	default:
+		l = n->left;
+		r = n->right;
+		if(l != Z)
+			acom(l);
+		if(r != Z)
+			acom(r);
+		return;
+	}
+
+	/* bust terms out */
+	t = n->type;
+	term[0].mult = 0;
+	term[0].node = Z;
+	nterm = 1;
+	acom1(1, n);
+	if(debug['m'])
+	for(i=0; i<nterm; i++) {
+		print("%d %3lld ", i, term[i].mult);
+		prtree1(term[i].node, 1, 0);
+	}
+	if(nterm < NTERM)
+		acom2(n, t);
+	n->type = t;
+}
+
+int
+acomcmp1(const void *a1, const void *a2)
+{
+	vlong c1, c2;
+	Term *t1, *t2;
+
+	t1 = (Term*)a1;
+	t2 = (Term*)a2;
+	c1 = t1->mult;
+	if(c1 < 0)
+		c1 = -c1;
+	c2 = t2->mult;
+	if(c2 < 0)
+		c2 = -c2;
+	if(c1 > c2)
+		return 1;
+	if(c1 < c2)
+		return -1;
+	c1 = 1;
+	if(t1->mult < 0)
+		c1 = 0;
+	c2 = 1;
+	if(t2->mult < 0)
+		c2 = 0;
+	if(c2 -= c1)
+		return c2;
+	if(t2 > t1)
+		return 1;
+	return -1;
+}
+
+int
+acomcmp2(const void *a1, const void *a2)
+{
+	vlong c1, c2;
+	Term *t1, *t2;
+
+	t1 = (Term*)a1;
+	t2 = (Term*)a2;
+	c1 = t1->mult;
+	c2 = t2->mult;
+	if(c1 > c2)
+		return 1;
+	if(c1 < c2)
+		return -1;
+	if(t2 > t1)
+		return 1;
+	return -1;
+}
+
+void
+acom2(Node *n, Type *t)
+{
+	Node *l, *r;
+	Term trm[NTERM];
+	int et, nt, i, j;
+	vlong c1, c2;
+
+	/*
+	 * copy into automatic
+	 */
+	c2 = 0;
+	nt = nterm;
+	for(i=0; i<nt; i++)
+		trm[i] = term[i];
+	/*
+	 * recur on subtrees
+	 */
+	j = 0;
+	for(i=1; i<nt; i++) {
+		c1 = trm[i].mult;
+		if(c1 == 0)
+			continue;
+		l = trm[i].node;
+		if(l != Z) {
+			j = 1;
+			acom(l);
+		}
+	}
+	c1 = trm[0].mult;
+	if(j == 0) {
+		n->oldop = n->op;
+		n->op = OCONST;
+		n->vconst = c1;
+		return;
+	}
+	et = t->etype;
+
+	/*
+	 * prepare constant term,
+	 * combine it with an addressing term
+	 */
+	if(c1 != 0) {
+		l = new1(OCONST, Z, Z);
+		l->type = t;
+		l->vconst = c1;
+		trm[0].mult = 1;
+		for(i=1; i<nt; i++) {
+			if(trm[i].mult != 1)
+				continue;
+			r = trm[i].node;
+			if(r->op != OADDR)
+				continue;
+			r->type = t;
+			l = new1(OADD, r, l);
+			l->type = t;
+			trm[i].mult = 0;
+			break;
+		}
+		trm[0].node = l;
+	}
+	/*
+	 * look for factorable terms
+	 * c1*i + c1*c2*j -> c1*(i + c2*j)
+	 */
+	qsort(trm+1, nt-1, sizeof(trm[0]), acomcmp1);
+	for(i=nt-1; i>=0; i--) {
+		c1 = trm[i].mult;
+		if(c1 < 0)
+			c1 = -c1;
+		if(c1 <= 1)
+			continue;
+		for(j=i+1; j<nt; j++) {
+			c2 = trm[j].mult;
+			if(c2 < 0)
+				c2 = -c2;
+			if(c2 <= 1)
+				continue;
+			if(c2 % c1)
+				continue;
+			r = trm[j].node;
+			if(r->type->etype != et)
+				r = acast(t, r);
+			c2 = trm[j].mult/trm[i].mult;
+			if(c2 != 1 && c2 != -1) {
+				r = new1(OMUL, r, new(OCONST, Z, Z));
+				r->type = t;
+				r->right->type = t;
+				r->right->vconst = c2;
+			}
+			l = trm[i].node;
+			if(l->type->etype != et)
+				l = acast(t, l);
+			r = new1(OADD, l, r);
+			r->type = t;
+			if(c2 == -1)
+				r->op = OSUB;
+			trm[i].node = r;
+			trm[j].mult = 0;
+		}
+	}
+	if(debug['m']) {
+		print("\n");
+		for(i=0; i<nt; i++) {
+			print("%d %3lld ", i, trm[i].mult);
+			prtree1(trm[i].node, 1, 0);
+		}
+	}
+
+	/*
+	 * put it all back together
+	 */
+	qsort(trm+1, nt-1, sizeof(trm[0]), acomcmp2);
+	l = Z;
+	for(i=nt-1; i>=0; i--) {
+		c1 = trm[i].mult;
+		if(c1 == 0)
+			continue;
+		r = trm[i].node;
+		if(r->type->etype != et || r->op == OBIT)
+			r = acast(t, r);
+		if(c1 != 1 && c1 != -1) {
+			r = new1(OMUL, r, new(OCONST, Z, Z));
+			r->type = t;
+			r->right->type = t;
+			if(c1 < 0) {
+				r->right->vconst = -c1;
+				c1 = -1;
+			} else {
+				r->right->vconst = c1;
+				c1 = 1;
+			}
+		}
+		if(l == Z) {
+			l = r;
+			c2 = c1;
+			continue;
+		}
+		if(c1 < 0)
+			if(c2 < 0)
+				l = new1(OADD, l, r);
+			else
+				l = new1(OSUB, l, r);
+		else
+			if(c2 < 0) {
+				l = new1(OSUB, r, l);
+				c2 = 1;
+			} else
+				l = new1(OADD, l, r);
+		l->type = t;
+	}
+	if(c2 < 0) {
+		r = new1(OCONST, 0, 0);
+		r->vconst = 0;
+		r->type = t;
+		l = new1(OSUB, r, l);
+		l->type = t;
+	}
+	*n = *l;
+}
+
+void
+acom1(vlong v, Node *n)
+{
+	Node *l, *r;
+
+	if(v == 0 || nterm >= NTERM)
+		return;
+	if(!addo(n)) {
+		if(n->op == OCONST)
+		if(!typefd[n->type->etype]) {
+			term[0].mult += v*n->vconst;
+			return;
+		}
+		term[nterm].mult = v;
+		term[nterm].node = n;
+		nterm++;
+		return;
+	}
+	switch(n->op) {
+
+	case OCAST:
+		acom1(v, n->left);
+		break;
+
+	case ONEG:
+		acom1(-v, n->left);
+		break;
+
+	case OADD:
+		acom1(v, n->left);
+		acom1(v, n->right);
+		break;
+
+	case OSUB:
+		acom1(v, n->left);
+		acom1(-v, n->right);
+		break;
+
+	case OMUL:
+		l = n->left;
+		r = n->right;
+		if(l->op == OCONST)
+		if(!typefd[n->type->etype]) {
+			acom1(v*l->vconst, r);
+			break;
+		}
+		if(r->op == OCONST)
+		if(!typefd[n->type->etype]) {
+			acom1(v*r->vconst, l);
+			break;
+		}
+		break;
+
+	default:
+		diag(n, "not addo");
+	}
+}
+
+int
+addo(Node *n)
+{
+
+	if(n != Z)
+	if(!typefd[n->type->etype])
+	if(!typev[n->type->etype] || ewidth[TVLONG] == ewidth[TIND])
+	switch(n->op) {
+
+	case OCAST:
+		if(nilcast(n->left->type, n->type))
+			return 1;
+		break;
+
+	case ONEG:
+	case OADD:
+	case OSUB:
+		return 1;
+
+	case OMUL:
+		if(n->left->op == OCONST)
+			return 1;
+		if(n->right->op == OCONST)
+			return 1;
+	}
+	return 0;
+}

+ 2042 - 0
sys/src/libcc/sub.c

@@ -0,0 +1,2042 @@
+/*
+ * This file is part of the UCB release of Plan 9. It is subject to the license
+ * terms in the LICENSE file found in the top-level directory of this
+ * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
+ * part of the UCB release of Plan 9, including this file, may be copied,
+ * modified, propagated, or distributed except according to the terms contained
+ * in the LICENSE file.
+ */
+
+#include	"cc.h"
+
+Node*
+new(int t, Node *l, Node *r)
+{
+	Node *n;
+
+	n = alloc(sizeof(*n));
+	n->op = t;
+	n->left = l;
+	n->right = r;
+	if(l && t != OGOTO)
+		n->lineno = l->lineno;
+	else if(r)
+		n->lineno = r->lineno;
+	else
+		n->lineno = lineno;
+	newflag = 1;
+	return n;
+}
+
+Node*
+new1(int o, Node *l, Node *r)
+{
+	Node *n;
+
+	n = new(o, l, r);
+	n->lineno = nearln;
+	return n;
+}
+
+void
+prtree(Node *n, char *s)
+{
+
+	print(" == %s ==\n", s);
+	prtree1(n, 0, 0);
+	print("\n");
+}
+
+void
+prtree1(Node *n, int d, int f)
+{
+	int i;
+
+	if(f)
+	for(i=0; i<d; i++)
+		print("   ");
+	if(n == Z) {
+		print("Z\n");
+		return;
+	}
+	if(n->op == OLIST) {
+		prtree1(n->left, d, 0);
+		prtree1(n->right, d, 1);
+		return;
+	}
+	d++;
+	print("%O", n->op);
+	i = 3;
+	switch(n->op)
+	{
+	case ONAME:
+		print(" \"%F\"", n);
+		print(" %ld", n->xoffset);
+		i = 0;
+		break;
+
+	case OINDREG:
+		print(" %ld(R%d)", n->xoffset, n->reg);
+		i = 0;
+		break;
+
+	case OREGISTER:
+		if(n->xoffset)
+			print(" %ld+R%d", n->xoffset, n->reg);
+		else
+			print(" R%d", n->reg);
+		i = 0;
+		break;
+
+	case OSTRING:
+		print(" \"%s\"", n->cstring);
+		i = 0;
+		break;
+
+	case OLSTRING:
+		if(sizeof(TRune) == sizeof(Rune))
+			print(" \"%S\"", (Rune*)n->rstring);
+		else
+			print(" \"...\"");
+		i = 0;
+		break;
+
+	case ODOT:
+	case OELEM:
+		print(" \"%F\"", n);
+		break;
+
+	case OCONST:
+		if(typefd[n->type->etype])
+			print(" \"%.8e\"", n->fconst);
+		else
+			print(" \"%lld\"", n->vconst);
+		i = 0;
+		break;
+	}
+	if(n->addable != 0)
+		print(" <%d>", n->addable);
+	if(n->type != T)
+		print(" %T", n->type);
+	if(n->complex != 0)
+		print(" (%d)", n->complex);
+	print(" %L\n", n->lineno);
+	if(i & 2)
+		prtree1(n->left, d, 1);
+	if(i & 1)
+		prtree1(n->right, d, 1);
+}
+
+Type*
+typ(int et, Type *d)
+{
+	Type *t;
+
+	t = alloc(sizeof(*t));
+	t->etype = et;
+	t->link = d;
+	t->down = T;
+	t->sym = S;
+	t->width = ewidth[et];
+	t->offset = 0;
+	t->shift = 0;
+	t->nbits = 0;
+	t->garb = 0;
+	return t;
+}
+
+Type*
+copytyp(Type *t)
+{
+	Type *nt;
+
+	nt = typ(TXXX, T);
+	*nt = *t;
+	return nt;
+}
+
+Type*
+garbt(Type *t, long b)
+{
+	Type *t1;
+
+	if(b & BGARB) {
+		t1 = copytyp(t);
+		t1->garb = simpleg(b);
+		return t1;
+	}
+	return t;
+}
+
+int
+simpleg(long b)
+{
+
+	b &= BGARB;
+	switch(b) {
+	case BCONSTNT:
+		return GCONSTNT;
+	case BVOLATILE:
+		return GVOLATILE;
+	case BVOLATILE|BCONSTNT:
+		return GCONSTNT|GVOLATILE;
+	}
+	return GXXX;
+}
+
+int
+simplec(long b)
+{
+
+	b &= BCLASS;
+	switch(b) {
+	case 0:
+	case BREGISTER:
+		return CXXX;
+	case BAUTO:
+	case BAUTO|BREGISTER:
+		return CAUTO;
+	case BEXTERN:
+		return CEXTERN;
+	case BEXTERN|BREGISTER:
+		return CEXREG;
+	case BSTATIC:
+		return CSTATIC;
+	case BTYPEDEF:
+		return CTYPEDEF;
+	case BTYPESTR:
+		return CTYPESTR;
+	}
+	diag(Z, "illegal combination of classes %Q", b);
+	return CXXX;
+}
+
+Type*
+simplet(long b)
+{
+
+	b &= ~BCLASS & ~BGARB;
+	switch(b) {
+	case BCHAR:
+	case BCHAR|BSIGNED:
+		return types[TCHAR];
+
+	case BCHAR|BUNSIGNED:
+		return types[TUCHAR];
+
+	case BSHORT:
+	case BSHORT|BINT:
+	case BSHORT|BSIGNED:
+	case BSHORT|BINT|BSIGNED:
+		return types[TSHORT];
+
+	case BUNSIGNED|BSHORT:
+	case BUNSIGNED|BSHORT|BINT:
+		return types[TUSHORT];
+
+	case 0:
+	case BINT:
+	case BINT|BSIGNED:
+	case BSIGNED:
+		return types[TINT];
+
+	case BUNSIGNED:
+	case BUNSIGNED|BINT:
+		return types[TUINT];
+
+	case BLONG:
+	case BLONG|BINT:
+	case BLONG|BSIGNED:
+	case BLONG|BINT|BSIGNED:
+		return types[TLONG];
+
+	case BUNSIGNED|BLONG:
+	case BUNSIGNED|BLONG|BINT:
+		return types[TULONG];
+
+	case BVLONG|BLONG:
+	case BVLONG|BLONG|BINT:
+	case BVLONG|BLONG|BSIGNED:
+	case BVLONG|BLONG|BINT|BSIGNED:
+		return types[TVLONG];
+
+	case BVLONG|BLONG|BUNSIGNED:
+	case BVLONG|BLONG|BINT|BUNSIGNED:
+		return types[TUVLONG];
+
+	case BFLOAT:
+		return types[TFLOAT];
+
+	case BDOUBLE:
+	case BDOUBLE|BLONG:
+	case BFLOAT|BLONG:
+		return types[TDOUBLE];
+
+	case BVOID:
+		return types[TVOID];
+	}
+
+	diag(Z, "illegal combination of types %Q", b);
+	return types[TINT];
+}
+
+int
+stcompat(Node *n, Type *t1, Type *t2, long ttab[])
+{
+	int i;
+	ulong b;
+
+	i = 0;
+	if(t2 != T)
+		i = t2->etype;
+	b = 1L << i;
+	i = 0;
+	if(t1 != T)
+		i = t1->etype;
+	if(b & ttab[i]) {
+		if(ttab == tasign)
+			if(b == BSTRUCT || b == BUNION)
+				if(!sametype(t1, t2))
+					return 1;
+		if(n->op != OCAST)
+		 	if(b == BIND && i == TIND)
+				if(!sametype(t1, t2))
+					return 1;
+		return 0;
+	}
+	return 1;
+}
+
+int
+tcompat(Node *n, Type *t1, Type *t2, long ttab[])
+{
+
+	if(stcompat(n, t1, t2, ttab)) {
+		if(t1 == T)
+			diag(n, "incompatible type: \"%T\" for op \"%O\"",
+				t2, n->op);
+		else
+			diag(n, "incompatible types: \"%T\" and \"%T\" for op \"%O\"",
+				t1, t2, n->op);
+		return 1;
+	}
+	return 0;
+}
+
+void
+makedot(Node *n, Type *t, long o)
+{
+	Node *n1, *n2;
+
+	if(t->nbits) {
+		n1 = new(OXXX, Z, Z);
+		*n1 = *n;
+		n->op = OBIT;
+		n->left = n1;
+		n->right = Z;
+		n->type = t;
+		n->addable = n1->left->addable;
+		n = n1;
+	}
+	n->addable = n->left->addable;
+	if(n->addable == 0) {
+		n1 = new1(OCONST, Z, Z);
+		n1->vconst = o;
+		n1->type = types[TLONG];
+		n->right = n1;
+		n->type = t;
+		return;
+	}
+	n->left->type = t;
+	if(o == 0) {
+		*n = *n->left;
+		return;
+	}
+	n->type = t;
+	n1 = new1(OCONST, Z, Z);
+	n1->vconst = o;
+	t = typ(TIND, t);
+	t->width = types[TIND]->width;
+	n1->type = t;
+
+	n2 = new1(OADDR, n->left, Z);
+	n2->type = t;
+
+	n1 = new1(OADD, n1, n2);
+	n1->type = t;
+
+	n->op = OIND;
+	n->left = n1;
+	n->right = Z;
+}
+
+Type*
+dotsearch(Sym *s, Type *t, Node *n, long *off)
+{
+	Type *t1, *xt, *rt;
+
+	xt = T;
+
+	/*
+	 * look it up by name
+	 */
+	for(t1 = t; t1 != T; t1 = t1->down)
+		if(t1->sym == s) {
+			if(xt != T)
+				goto ambig;
+			xt = t1;
+		}
+
+	/*
+	 * look it up by type
+	 */
+	if(s->class == CTYPEDEF || s->class == CTYPESTR)
+		for(t1 = t; t1 != T; t1 = t1->down)
+			if(t1->sym == S && typesu[t1->etype])
+				if(sametype(s->type, t1)) {
+					if(xt != T)
+						goto ambig;
+					xt = t1;
+				}
+	if(xt != T) {
+		*off = xt->offset;
+		return xt;
+	}
+
+	/*
+	 * look it up in unnamed substructures
+	 */
+	for(t1 = t; t1 != T; t1 = t1->down)
+		if(t1->sym == S && typesu[t1->etype]){
+			rt = dotsearch(s, t1->link, n, off);
+			if(rt != T) {
+				if(xt != T)
+					goto ambig;
+				xt = rt;
+				*off += t1->offset;
+			}
+		}
+	return xt;
+
+ambig:
+	diag(n, "ambiguous structure element: %s", s->name);
+	return xt;
+}
+
+long
+dotoffset(Type *st, Type *lt, Node *n)
+{
+	Type *t;
+	Sym *g;
+	long o, o1;
+
+	o = -1;
+	/*
+	 * first try matching at the top level
+	 * for matching tag names
+	 */
+	g = st->tag;
+	if(g != S)
+		for(t=lt->link; t!=T; t=t->down)
+			if(t->sym == S)
+				if(g == t->tag) {
+					if(o >= 0)
+						goto ambig;
+					o = t->offset;
+				}
+	if(o >= 0)
+		return o;
+
+	/*
+	 * second try matching at the top level
+	 * for similar types
+	 */
+	for(t=lt->link; t!=T; t=t->down)
+		if(t->sym == S)
+			if(sametype(st, t)) {
+				if(o >= 0)
+					goto ambig;
+				o = t->offset;
+			}
+	if(o >= 0)
+		return o;
+
+	/*
+	 * last try matching sub-levels
+	 */
+	for(t=lt->link; t!=T; t=t->down)
+		if(t->sym == S)
+		if(typesu[t->etype]) {
+			o1 = dotoffset(st, t, n);
+			if(o1 >= 0) {
+				if(o >= 0)
+					goto ambig;
+				o = o1 + t->offset;
+			}
+		}
+	return o;
+
+ambig:
+	diag(n, "ambiguous unnamed structure element");
+	return o;
+}
+
+/*
+ * look into tree for floating point constant expressions
+ */
+int
+allfloat(Node *n, int flag)
+{
+
+	if(n != Z) {
+		if(n->type->etype != TDOUBLE)
+			return 1;
+		switch(n->op) {
+		case OCONST:
+			if(flag)
+				n->type = types[TFLOAT];
+			return 1;
+		case OADD:	/* no need to get more exotic than this */
+		case OSUB:
+		case OMUL:
+		case ODIV:
+			if(!allfloat(n->right, flag))
+				break;
+		case OCAST:
+			if(!allfloat(n->left, flag))
+				break;
+			if(flag)
+				n->type = types[TFLOAT];
+			return 1;
+		}
+	}
+	return 0;
+}
+
+void
+constas(Node *n, Type *il, Type *ir)
+{
+	Type *l, *r;
+
+	l = il;
+	r = ir;
+
+	if(l == T)
+		return;
+	if(l->garb & GCONSTNT) {
+		warn(n, "assignment to a constant type (%T)", il);
+		return;
+	}
+	if(r == T)
+		return;
+	for(;;) {
+		if(l->etype != TIND || r->etype != TIND)
+			break;
+		l = l->link;
+		r = r->link;
+		if(l == T || r == T)
+			break;
+		if(r->garb & GCONSTNT)
+			if(!(l->garb & GCONSTNT)) {
+				warn(n, "assignment of a constant pointer type (%T)", ir);
+				break;
+			}
+	}
+}
+
+void
+typeext1(Type *st, Node *l)
+{
+	if(st->etype == TFLOAT && allfloat(l, 0))
+		allfloat(l, 1);
+}
+
+void
+typeext(Type *st, Node *l)
+{
+	Type *lt;
+	Node *n1, *n2;
+	long o;
+
+	lt = l->type;
+	if(lt == T)
+		return;
+	if(st->etype == TIND && vconst(l) == 0) {
+		l->type = st;
+		l->vconst = 0;
+		return;
+	}
+	typeext1(st, l);
+
+	/*
+	 * extension of C
+	 * if assign of struct containing unnamed sub-struct
+	 * to type of sub-struct, insert the DOT.
+	 * if assign of *struct containing unnamed substruct
+	 * to type of *sub-struct, insert the add-offset
+	 */
+	if(typesu[st->etype] && typesu[lt->etype]) {
+		o = dotoffset(st, lt, l);
+		if(o >= 0) {
+			n1 = new1(OXXX, Z, Z);
+			*n1 = *l;
+			l->op = ODOT;
+			l->left = n1;
+			l->right = Z;
+			makedot(l, st, o);
+		}
+		return;
+	}
+	if(st->etype == TIND && typesu[st->link->etype])
+	if(lt->etype == TIND && typesu[lt->link->etype]) {
+		o = dotoffset(st->link, lt->link, l);
+		if(o >= 0) {
+			l->type = st;
+			if(o == 0)
+				return;
+			n1 = new1(OXXX, Z, Z);
+			*n1 = *l;
+			n2 = new1(OCONST, Z, Z);
+			n2->vconst = o;
+			n2->type = st;
+			l->op = OADD;
+			l->left = n1;
+			l->right = n2;
+		}
+		return;
+	}
+}
+
+/*
+ * a cast that generates no code
+ * (same size move)
+ */
+int
+nocast(Type *t1, Type *t2)
+{
+	int i, b;
+
+	if(t1->nbits)
+		return 0;
+	i = 0;
+	if(t2 != T)
+		i = t2->etype;
+	b = 1<<i;
+	i = 0;
+	if(t1 != T)
+		i = t1->etype;
+	if(b & ncast[i])
+		return 1;
+	return 0;
+}
+
+/*
+ * a cast that has a noop semantic
+ * (small to large, convert)
+ */
+int
+nilcast(Type *t1, Type *t2)
+{
+	int et1, et2;
+
+	if(t1 == T)
+		return 0;
+	if(t1->nbits)
+		return 0;
+	if(t2 == T)
+		return 0;
+	et1 = t1->etype;
+	et2 = t2->etype;
+	if(et1 == et2)
+		return 1;
+	if(typefd[et1] && typefd[et2]) {
+		if(ewidth[et1] < ewidth[et2])
+			return 1;
+		return 0;
+	}
+	if(typechlp[et1] && typechlp[et2]) {
+		if(ewidth[et1] < ewidth[et2])
+			return 1;
+		return 0;
+	}
+	return 0;
+}
+
+/*
+ * "the usual arithmetic conversions are performed"
+ */
+void
+arith(Node *n, int f)
+{
+	Type *t1, *t2;
+	int i, j, k;
+	Node *n1;
+	long w;
+
+	t1 = n->left->type;
+	if(n->right == Z)
+		t2 = t1;
+	else
+		t2 = n->right->type;
+	i = TXXX;
+	if(t1 != T)
+		i = t1->etype;
+	j = TXXX;
+	if(t2 != T)
+		j = t2->etype;
+	k = tab[i][j];
+	if(k == TIND) {
+		if(i == TIND)
+			n->type = t1;
+		else
+		if(j == TIND)
+			n->type = t2;
+	} else {
+		/* convert up to at least int */
+		if(f == 1)
+		while(k < TINT)
+			k += 2;
+		n->type = types[k];
+	}
+	if(n->op == OSUB)
+	if(i == TIND && j == TIND) {
+		w = n->right->type->link->width;
+		if(w < 1 || n->left->type->link == T || n->left->type->link->width < 1)
+			goto bad;
+		n->type = types[ewidth[TIND] <= ewidth[TLONG]? TLONG: TVLONG];
+		if(1 && ewidth[TIND] > ewidth[TLONG]){
+			n1 = new1(OXXX, Z, Z);
+			*n1 = *n;
+			n->op = OCAST;
+			n->left = n1;
+			n->right = Z;
+			n->type = types[TLONG];
+		}
+		if(w > 1) {
+			n1 = new1(OXXX, Z, Z);
+			*n1 = *n;
+			n->op = ODIV;
+			n->left = n1;
+			n1 = new1(OCONST, Z, Z);
+			n1->vconst = w;
+			n1->type = n->type;
+			n->right = n1;
+			w = vlog(n1);
+			if(w >= 0) {
+				n->op = OASHR;
+				n1->vconst = w;
+			}
+		}
+		return;
+	}
+	if(!sametype(n->type, n->left->type)) {
+		n->left = new1(OCAST, n->left, Z);
+		n->left->type = n->type;
+		if(n->type->etype == TIND) {
+			w = n->type->link->width;
+			if(w < 1) {
+				snap(n->type->link);
+				w = n->type->link->width;
+				if(w < 1)
+					goto bad;
+			}
+			if(w > 1) {
+				n1 = new1(OCONST, Z, Z);
+				n1->vconst = w;
+				n1->type = n->type;
+				n->left = new1(OMUL, n->left, n1);
+				n->left->type = n->type;
+			}
+		}
+	}
+	if(n->right != Z)
+	if(!sametype(n->type, n->right->type)) {
+		n->right = new1(OCAST, n->right, Z);
+		n->right->type = n->type;
+		if(n->type->etype == TIND) {
+			w = n->type->link->width;
+			if(w < 1) {
+				snap(n->type->link);
+				w = n->type->link->width;
+				if(w < 1)
+					goto bad;
+			}
+			if(w != 1) {
+				n1 = new1(OCONST, Z, Z);
+				n1->vconst = w;
+				n1->type = n->type;
+				n->right = new1(OMUL, n->right, n1);
+				n->right->type = n->type;
+			}
+		}
+	}
+	return;
+bad:
+	diag(n, "pointer addition not fully declared: %T", n->type->link);
+}
+
+/*
+ * try to rewrite shift & mask
+ */
+void
+simplifyshift(Node *n)
+{
+	ulong c3;
+	int o, s1, s2, c1, c2;
+
+	if(!typechlp[n->type->etype])
+		return;
+	switch(n->op) {
+	default:
+		return;
+	case OASHL:
+		s1 = 0;
+		break;
+	case OLSHR:
+		s1 = 1;
+		break;
+	case OASHR:
+		s1 = 2;
+		break;
+	}
+	if(n->right->op != OCONST)
+		return;
+	if(n->left->op != OAND)
+		return;
+	if(n->left->right->op != OCONST)
+		return;
+	switch(n->left->left->op) {
+	default:
+		return;
+	case OASHL:
+		s2 = 0;
+		break;
+	case OLSHR:
+		s2 = 1;
+		break;
+	case OASHR:
+		s2 = 2;
+		break;
+	}
+	if(n->left->left->right->op != OCONST)
+		return;
+
+	c1 = n->right->vconst;
+	c2 = n->left->left->right->vconst;
+	c3 = n->left->right->vconst;
+
+/*
+	if(debug['h'])
+		print("%.3o %ld %ld %d #%.lux\n",
+			(s1<<3)|s2, c1, c2, topbit(c3), c3);
+*/
+
+	o = n->op;
+	switch((s1<<3)|s2) {
+	case 000:	/* (((e <<u c2) & c3) <<u c1) */
+		c3 >>= c2;
+		c1 += c2;
+		if(c1 >= 32)
+			break;
+		goto rewrite1;
+
+	case 002:	/* (((e >>s c2) & c3) <<u c1) */
+		if(topbit(c3) >= (32-c2))
+			break;
+	case 001:	/* (((e >>u c2) & c3) <<u c1) */
+		if(c1 > c2) {
+			c3 <<= c2;
+			c1 -= c2;
+			o = OASHL;
+			goto rewrite1;
+		}
+		c3 <<= c1;
+		if(c1 == c2)
+			goto rewrite0;
+		c1 = c2-c1;
+		o = OLSHR;
+		goto rewrite2;
+
+	case 022:	/* (((e >>s c2) & c3) >>s c1) */
+		if(c2 <= 0)
+			break;
+	case 012:	/* (((e >>s c2) & c3) >>u c1) */
+		if(topbit(c3) >= (32-c2))
+			break;
+		goto s11;
+	case 021:	/* (((e >>u c2) & c3) >>s c1) */
+		if(topbit(c3) >= 31 && c2 <= 0)
+			break;
+		goto s11;
+	case 011:	/* (((e >>u c2) & c3) >>u c1) */
+	s11:
+		c3 <<= c2;
+		c1 += c2;
+		if(c1 >= 32)
+			break;
+		o = OLSHR;
+		goto rewrite1;
+
+	case 020:	/* (((e <<u c2) & c3) >>s c1) */
+		if(topbit(c3) >= 31)
+			break;
+	case 010:	/* (((e <<u c2) & c3) >>u c1) */
+		c3 >>= c1;
+		if(c1 == c2)
+			goto rewrite0;
+		if(c1 > c2) {
+			c1 -= c2;
+			goto rewrite2;
+		}
+		c1 = c2 - c1;
+		o = OASHL;
+		goto rewrite2;
+	}
+	return;
+
+rewrite0:	/* get rid of both shifts */
+if(debug['<'])prtree(n, "rewrite0");
+	*n = *n->left;
+	n->left = n->left->left;
+	n->right->vconst = c3;
+	return;
+rewrite1:	/* get rid of lower shift */
+if(debug['<'])prtree(n, "rewrite1");
+	n->left->left = n->left->left->left;
+	n->left->right->vconst = c3;
+	n->right->vconst = c1;
+	n->op = o;
+	return;
+rewrite2:	/* get rid of upper shift */
+if(debug['<'])prtree(n, "rewrite2");
+	*n = *n->left;
+	n->right->vconst = c3;
+	n->left->right->vconst = c1;
+	n->left->op = o;
+}
+
+int
+side(Node *n)
+{
+
+loop:
+	if(n != Z)
+	switch(n->op) {
+	case OCAST:
+	case ONOT:
+	case OADDR:
+	case OIND:
+		n = n->left;
+		goto loop;
+
+	case OCOND:
+		if(side(n->left))
+			break;
+		n = n->right;
+
+	case OEQ:
+	case ONE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OLE:
+	case OADD:
+	case OSUB:
+	case OMUL:
+	case OLMUL:
+	case ODIV:
+	case OLDIV:
+	case OLSHR:
+	case OASHL:
+	case OASHR:
+	case OAND:
+	case OOR:
+	case OXOR:
+	case OMOD:
+	case OLMOD:
+	case OANDAND:
+	case OOROR:
+	case OCOMMA:
+	case ODOT:
+		if(side(n->left))
+			break;
+		n = n->right;
+		goto loop;
+
+	case OSIGN:
+	case OSIZE:
+	case OCONST:
+	case OSTRING:
+	case OLSTRING:
+	case ONAME:
+		return 0;
+	}
+	return 1;
+}
+
+int
+vconst(Node *n)
+{
+	int i;
+
+	if(n == Z)
+		goto no;
+	if(n->op != OCONST)
+		goto no;
+	if(n->type == T)
+		goto no;
+	switch(n->type->etype)
+	{
+	case TFLOAT:
+	case TDOUBLE:
+		i = 100;
+		if(n->fconst > i || n->fconst < -i)
+			goto no;
+		i = n->fconst;
+		if(i != n->fconst)
+			goto no;
+		return i;
+
+	case TVLONG:
+	case TUVLONG:
+		i = n->vconst;
+		if(i != n->vconst)
+			goto no;
+		return i;
+
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TIND:
+		i = n->vconst;
+		if(i != n->vconst)
+			goto no;
+		return i;
+	}
+no:
+	return -159;	/* first uninteresting constant */
+}
+
+/*
+ * return log(n) if n is a power of 2 constant
+ */
+int
+log2(uvlong v)
+{
+	int s, i;
+	uvlong m;
+
+	s = 0;
+	m = MASK(8*sizeof(uvlong));
+	for(i=32; i; i>>=1) {
+		m >>= i;
+		if(!(v & m)) {
+			v >>= i;
+			s += i;
+		}
+	}
+	if(v == 1)
+		return s;
+	return -1;
+}
+
+int
+vlog(Node *n)
+{
+	if(n->op != OCONST)
+		goto bad;
+	if(typefd[n->type->etype])
+		goto bad;
+
+	return log2(n->vconst);
+
+bad:
+	return -1;
+}
+
+int
+topbit(ulong v)
+{
+	int i;
+
+	for(i = -1; v; i++)
+		v >>= 1;
+	return i;
+}
+
+/*
+ * try to cast a constant down
+ * rather than cast a variable up
+ * example:
+ *	if(c == 'a')
+ */
+void
+relcon(Node *l, Node *r)
+{
+	vlong v;
+
+	if(l->op != OCONST)
+		return;
+	if(r->op != OCAST)
+		return;
+	if(!nilcast(r->left->type, r->type))
+		return;
+	switch(r->type->etype) {
+	default:
+		return;
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+		v = convvtox(l->vconst, r->type->etype);
+		if(v != l->vconst)
+			return;
+		break;
+	}
+	l->type = r->left->type;
+	*r = *r->left;
+}
+
+int
+relindex(int o)
+{
+
+	switch(o) {
+	default:
+		diag(Z, "bad in relindex: %O", o);
+	case OEQ: return 0;
+	case ONE: return 1;
+	case OLE: return 2;
+	case OLS: return 3;
+	case OLT: return 4;
+	case OLO: return 5;
+	case OGE: return 6;
+	case OHS: return 7;
+	case OGT: return 8;
+	case OHI: return 9;
+	}
+}
+
+Node*
+invert(Node *n)
+{
+	Node *i;
+
+	if(n == Z || n->op != OLIST)
+		return n;
+	i = n;
+	for(n = n->left; n != Z; n = n->left) {
+		if(n->op != OLIST)
+			break;
+		i->left = n->right;
+		n->right = i;
+		i = n;
+	}
+	i->left = n;
+	return i;
+}
+
+int
+bitno(long b)
+{
+	int i;
+
+	for(i=0; i<32; i++)
+		if(b & (1L<<i))
+			return i;
+	diag(Z, "bad in bitno");
+	return 0;
+}
+
+long
+typebitor(long a, long b)
+{
+	long c;
+
+	c = a | b;
+	if(a & b){
+		if((a & b) == BLONG)
+			c |= BVLONG;		/* long long => vlong */
+		else
+			warn(Z, "once is enough: %Q", a & b);
+	}
+	return c;
+}
+
+void
+diag(Node *n, char *fmt, ...)
+{
+	char buf[STRINGSZ];
+	va_list arg;
+
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	Bprint(&diagbuf, "%L %s\n", (n==Z)? nearln: n->lineno, buf);
+
+	if(debug['X']){
+		Bflush(&diagbuf);
+		abort();
+	}
+	if(n != Z)
+	if(debug['v'])
+		prtree(n, "diagnostic");
+
+	nerrors++;
+	if(nerrors > 10) {
+		Bprint(&diagbuf, "too many errors\n");
+		errorexit();
+	}
+}
+
+void
+warn(Node *n, char *fmt, ...)
+{
+	char buf[STRINGSZ];
+	va_list arg;
+
+	if(debug['w'] || debug['W']) {
+		va_start(arg, fmt);
+		vseprint(buf, buf+sizeof(buf), fmt, arg);
+		va_end(arg);
+		if(debug['W']) {
+			diag(n, "%s", buf);
+			return;
+		}
+		Bprint(&diagbuf, "warning: %L %s\n", (n==Z)? nearln: n->lineno, buf);
+
+		if(n != Z)
+		if(debug['v'])
+			prtree(n, "warning");
+	}
+}
+
+void
+yyerror(char *fmt, ...)
+{
+	char buf[STRINGSZ];
+	va_list arg;
+
+	/*
+	 * hack to intercept message from yaccpar
+	 */
+	if(strcmp(fmt, "syntax error") == 0) {
+		yyerror("syntax error, last name: %s", symb);
+		return;
+	}
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	Bprint(&diagbuf, "%L %s\n", lineno, buf);
+	nerrors++;
+	if(nerrors > 10) {
+		Bprint(&diagbuf, "too many errors\n");
+		errorexit();
+	}
+}
+
+void
+fatal(Node *n, char *fmt, ...)
+{
+	char buf[STRINGSZ];
+	va_list arg;
+
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	Bprint(&diagbuf, "%L %s\n", (n==Z)? nearln: n->lineno, buf);
+
+	if(debug['X']){
+		Bflush(&diagbuf);
+		abort();
+	}
+	if(n != Z)
+	if(debug['v'])
+		prtree(n, "diagnostic");
+
+	nerrors++;
+	errorexit();
+}
+
+ulong	thash1	= 0x2edab8c9;
+ulong	thash2	= 0x1dc74fb8;
+ulong	thash3	= 0x1f241331;
+ulong	thash[NALLTYPES];
+Init	thashinit[] =
+{
+	{TXXX,		0x17527bbd,	0},
+	{TCHAR,		0x5cedd32b,	0},
+	{TUCHAR,		0x552c4454,	0},
+	{TSHORT,		0x63040b4b,	0},
+	{TUSHORT,	0x32a45878,	0},
+	{TINT,		0x4151d5bd,	0},
+	{TUINT,		0x5ae707d6,	0},
+	{TLONG,		0x5ef20f47,	0},
+	{TULONG,		0x36d8eb8f,	0},
+	{TVLONG,		0x6e5e9590,	0},
+	{TUVLONG,	0x75910105,	0},
+	{TFLOAT,		0x25fd7af1,	0},
+	{TDOUBLE,	0x7c40a1b2,	0},
+	{TIND,		0x1b832357,	0},
+	{TFUNC,		0x6babc9cb,	0},
+	{TARRAY,		0x7c50986d,	0},
+	{TVOID,		0x44112eff,	0},
+	{TSTRUCT,	0x7c2da3bf,	0},
+	{TUNION,		0x3eb25e98,	0},
+	{TENUM,		0x44b54f61,	0},
+	{TFILE,		0x19242ac3,	0},
+	{TOLD,		0x22b15988,	0},
+	{TDOT,		0x0204f6b3,	0},
+	{-1,		0,		0},
+};
+
+char*	bnames[NALIGN];
+Init	bnamesinit[] =
+{
+	{Axxx,	0,	"Axxx"},
+	{Ael1,	0,	"el1"},
+	{Ael2,	0,	"el2"},
+	{Asu2,	0,	"su2"},
+	{Aarg0,	0,	"arg0"},
+	{Aarg1,	0,	"arg1"},
+	{Aarg2,	0,	"arg2"},
+	{Aaut3,	0,	"aut3"},
+	{-1,	0,	0},
+};
+
+char*	tnames[NALLTYPES];
+Init	tnamesinit[] =
+{
+	{TXXX,		0,	"TXXX"},
+	{TCHAR,		0,	"CHAR"},
+	{TUCHAR,		0,	"UCHAR"},
+	{TSHORT,		0,	"SHORT"},
+	{TUSHORT,	0,	"USHORT"},
+	{TINT,		0,	"INT"},
+	{TUINT,		0,	"UINT"},
+	{TLONG,		0,	"LONG"},
+	{TULONG,		0,	"ULONG"},
+	{TVLONG,		0,	"VLONG"},
+	{TUVLONG,	0,	"UVLONG"},
+	{TFLOAT,		0,	"FLOAT"},
+	{TDOUBLE,	0,	"DOUBLE"},
+	{TIND,		0,	"IND"},
+	{TFUNC,		0,	"FUNC"},
+	{TARRAY,		0,	"ARRAY"},
+	{TVOID,		0,	"VOID"},
+	{TSTRUCT,	0,	"STRUCT"},
+	{TUNION,		0,	"UNION"},
+	{TENUM,		0,	"ENUM"},
+	{TFILE,		0,	"FILE"},
+	{TOLD,		0,	"OLD"},
+	{TDOT,		0,	"DOT"},
+	{-1,		0,	0},
+};
+
+char*	gnames[NGTYPES];
+Init	gnamesinit[] =
+{
+	{GXXX,			0,	"GXXX"},
+	{GCONSTNT,		0,	"CONST"},
+	{GVOLATILE,		0,	"VOLATILE"},
+	{GVOLATILE|GCONSTNT,	0,	"CONST-VOLATILE"},
+	{-1,			0,	0},
+};
+
+char*	qnames[NALLTYPES];
+Init	qnamesinit[] =
+{
+	{TXXX,		0,	"TXXX"},
+	{TCHAR,		0,	"CHAR"},
+	{TUCHAR,		0,	"UCHAR"},
+	{TSHORT,		0,	"SHORT"},
+	{TUSHORT,	0,	"USHORT"},
+	{TINT,		0,	"INT"},
+	{TUINT,		0,	"UINT"},
+	{TLONG,		0,	"LONG"},
+	{TULONG,		0,	"ULONG"},
+	{TVLONG,		0,	"VLONG"},
+	{TUVLONG,	0,	"UVLONG"},
+	{TFLOAT,		0,	"FLOAT"},
+	{TDOUBLE,	0,	"DOUBLE"},
+	{TIND,		0,	"IND"},
+	{TFUNC,		0,	"FUNC"},
+	{TARRAY,		0,	"ARRAY"},
+	{TVOID,		0,	"VOID"},
+	{TSTRUCT,	0,	"STRUCT"},
+	{TUNION,		0,	"UNION"},
+	{TENUM,		0,	"ENUM"},
+
+	{TAUTO,		0,	"AUTO"},
+	{TEXTERN,	0,	"EXTERN"},
+	{TSTATIC,	0,	"STATIC"},
+	{TTYPEDEF,	0,	"TYPEDEF"},
+	{TTYPESTR,	0,	"TYPESTR"},
+	{TREGISTER,	0,	"REGISTER"},
+	{TCONSTNT,	0,	"CONSTNT"},
+	{TVOLATILE,	0,	"VOLATILE"},
+	{TUNSIGNED,	0,	"UNSIGNED"},
+	{TSIGNED,	0,	"SIGNED"},
+	{TDOT,		0,	"DOT"},
+	{TFILE,		0,	"FILE"},
+	{TOLD,		0,	"OLD"},
+	{-1,		0,	0},
+};
+char*	cnames[NCTYPES];
+Init	cnamesinit[] =
+{
+	{CXXX,		0,	"CXXX"},
+	{CAUTO,		0,	"AUTO"},
+	{CEXTERN,	0,	"EXTERN"},
+	{CGLOBL,		0,	"GLOBL"},
+	{CSTATIC,	0,	"STATIC"},
+	{CLOCAL,		0,	"LOCAL"},
+	{CTYPEDEF,	0,	"TYPEDEF"},
+	{CTYPESTR,	0,	"TYPESTR"},
+	{CPARAM,		0,	"PARAM"},
+	{CSELEM,		0,	"SELEM"},
+	{CLABEL,		0,	"LABEL"},
+	{CEXREG,		0,	"EXREG"},
+	{-1,		0,	0},
+};
+
+char*	onames[OEND+1];
+Init	onamesinit[] =
+{
+	{OXXX,		0,	"OXXX"},
+	{OADD,		0,	"ADD"},
+	{OADDR,		0,	"ADDR"},
+	{OAND,		0,	"AND"},
+	{OANDAND,	0,	"ANDAND"},
+	{OARRAY,		0,	"ARRAY"},
+	{OAS,		0,	"AS"},
+	{OASI,		0,	"ASI"},
+	{OASADD,		0,	"ASADD"},
+	{OASAND,		0,	"ASAND"},
+	{OASASHL,	0,	"ASASHL"},
+	{OASASHR,	0,	"ASASHR"},
+	{OASDIV,		0,	"ASDIV"},
+	{OASHL,		0,	"ASHL"},
+	{OASHR,		0,	"ASHR"},
+	{OASLDIV,	0,	"ASLDIV"},
+	{OASLMOD,	0,	"ASLMOD"},
+	{OASLMUL,	0,	"ASLMUL"},
+	{OASLSHR,	0,	"ASLSHR"},
+	{OASMOD,		0,	"ASMOD"},
+	{OASMUL,		0,	"ASMUL"},
+	{OASOR,		0,	"ASOR"},
+	{OASSUB,		0,	"ASSUB"},
+	{OASXOR,		0,	"ASXOR"},
+	{OBIT,		0,	"BIT"},
+	{OBREAK,		0,	"BREAK"},
+	{OCASE,		0,	"CASE"},
+	{OCAST,		0,	"CAST"},
+	{OCOMMA,		0,	"COMMA"},
+	{OCOND,		0,	"COND"},
+	{OCONST,		0,	"CONST"},
+	{OCONTINUE,	0,	"CONTINUE"},
+	{ODIV,		0,	"DIV"},
+	{ODOT,		0,	"DOT"},
+	{ODOTDOT,	0,	"DOTDOT"},
+	{ODWHILE,	0,	"DWHILE"},
+	{OENUM,		0,	"ENUM"},
+	{OEQ,		0,	"EQ"},
+	{OFOR,		0,	"FOR"},
+	{OFUNC,		0,	"FUNC"},
+	{OGE,		0,	"GE"},
+	{OGOTO,		0,	"GOTO"},
+	{OGT,		0,	"GT"},
+	{OHI,		0,	"HI"},
+	{OHS,		0,	"HS"},
+	{OIF,		0,	"IF"},
+	{OIND,		0,	"IND"},
+	{OINDREG,	0,	"INDREG"},
+	{OINIT,		0,	"INIT"},
+	{OLABEL,		0,	"LABEL"},
+	{OLDIV,		0,	"LDIV"},
+	{OLE,		0,	"LE"},
+	{OLIST,		0,	"LIST"},
+	{OLMOD,		0,	"LMOD"},
+	{OLMUL,		0,	"LMUL"},
+	{OLO,		0,	"LO"},
+	{OLS,		0,	"LS"},
+	{OLSHR,		0,	"LSHR"},
+	{OLT,		0,	"LT"},
+	{OMOD,		0,	"MOD"},
+	{OMUL,		0,	"MUL"},
+	{ONAME,		0,	"NAME"},
+	{ONE,		0,	"NE"},
+	{ONOT,		0,	"NOT"},
+	{OOR,		0,	"OR"},
+	{OOROR,		0,	"OROR"},
+	{OPOSTDEC,	0,	"POSTDEC"},
+	{OPOSTINC,	0,	"POSTINC"},
+	{OPREDEC,	0,	"PREDEC"},
+	{OPREINC,	0,	"PREINC"},
+	{OPROTO,		0,	"PROTO"},
+	{OREGISTER,	0,	"REGISTER"},
+	{ORETURN,	0,	"RETURN"},
+	{OSET,		0,	"SET"},
+	{OSIGN,		0,	"SIGN"},
+	{OSIZE,		0,	"SIZE"},
+	{OSTRING,	0,	"STRING"},
+	{OLSTRING,	0,	"LSTRING"},
+	{OSTRUCT,	0,	"STRUCT"},
+	{OSUB,		0,	"SUB"},
+	{OSWITCH,	0,	"SWITCH"},
+	{OUNION,		0,	"UNION"},
+	{OUSED,		0,	"USED"},
+	{OWHILE,		0,	"WHILE"},
+	{OXOR,		0,	"XOR"},
+	{OPOS,		0,	"POS"},
+	{ONEG,		0,	"NEG"},
+	{OCOM,		0,	"COM"},
+	{OELEM,		0,	"ELEM"},
+	{OTST,		0,	"TST"},
+	{OINDEX,		0,	"INDEX"},
+	{OFAS,		0,	"FAS"},
+	{OREGPAIR,	0,	"REGPAIR"},
+	{OEXREG,		0,	"EXREG"},
+	{OEND,		0,	"END"},
+	{-1,		0,	0},
+};
+
+/*	OEQ, ONE, OLE, OLS, OLT, OLO, OGE, OHS, OGT, OHI */
+char	comrel[12] =
+{
+	ONE, OEQ, OGT, OHI, OGE, OHS, OLT, OLO, OLE, OLS,
+};
+char	invrel[12] =
+{
+	OEQ, ONE, OGE, OHS, OGT, OHI, OLE, OLS, OLT, OLO,
+};
+char	logrel[12] =
+{
+	OEQ, ONE, OLS, OLS, OLO, OLO, OHS, OHS, OHI, OHI,
+};
+
+char	typei[NTYPE];
+int	typeiinit[] =
+{
+	TCHAR, TUCHAR, TSHORT, TUSHORT, TINT, TUINT, TLONG, TULONG, TVLONG, TUVLONG, -1,
+};
+char	typeu[NTYPE];
+int	typeuinit[] =
+{
+	TUCHAR, TUSHORT, TUINT, TULONG, TUVLONG, TIND, -1,
+};
+
+char	typesuv[NTYPE];
+int	typesuvinit[] =
+{
+	TVLONG, TUVLONG, TSTRUCT, TUNION, -1,
+};
+
+char	typeilp[NTYPE];
+int	typeilpinit[] =
+{
+	TINT, TUINT, TLONG, TULONG, TIND, -1
+};
+
+char	typechl[NTYPE];
+char	typechlv[NTYPE];
+char typechlvp[NTYPE];
+int	typechlinit[] =
+{
+	TCHAR, TUCHAR, TSHORT, TUSHORT, TINT, TUINT, TLONG, TULONG, -1,
+};
+
+char	typechlp[NTYPE];
+int	typechlpinit[] =
+{
+	TCHAR, TUCHAR, TSHORT, TUSHORT, TINT, TUINT, TLONG, TULONG, TIND, -1,
+};
+
+char	typechlpfd[NTYPE];
+int	typechlpfdinit[] =
+{
+	TCHAR, TUCHAR, TSHORT, TUSHORT, TINT, TUINT, TLONG, TULONG, TFLOAT, TDOUBLE, TIND, -1,
+};
+
+char	typec[NTYPE];
+int	typecinit[] =
+{
+	TCHAR, TUCHAR, -1
+};
+
+char	typeh[NTYPE];
+int	typehinit[] =
+{
+	TSHORT, TUSHORT, -1,
+};
+
+char	typeil[NTYPE];
+int	typeilinit[] =
+{
+	TINT, TUINT, TLONG, TULONG, -1,
+};
+
+char	typev[NTYPE];
+int	typevinit[] =
+{
+	TVLONG,	TUVLONG, -1,
+};
+
+char	typefd[NTYPE];
+int	typefdinit[] =
+{
+	TFLOAT, TDOUBLE, -1,
+};
+
+char	typeaf[NTYPE];
+int	typeafinit[] =
+{
+	TFUNC, TARRAY, -1,
+};
+
+char	typesu[NTYPE];
+int	typesuinit[] =
+{
+	TSTRUCT, TUNION, -1,
+};
+
+long	tasign[NTYPE];
+Init	tasigninit[] =
+{
+	{TCHAR,		BNUMBER,	0},
+	{TUCHAR,		BNUMBER,	0},
+	{TSHORT,		BNUMBER,	0},
+	{TUSHORT,	BNUMBER,	0},
+	{TINT,		BNUMBER,	0},
+	{TUINT,		BNUMBER,	0},
+	{TLONG,		BNUMBER,	0},
+	{TULONG,		BNUMBER,	0},
+	{TVLONG,		BNUMBER,	0},
+	{TUVLONG,	BNUMBER,	0},
+	{TFLOAT,		BNUMBER,	0},
+	{TDOUBLE,	BNUMBER,	0},
+	{TIND,		BIND,		0},
+	{TSTRUCT,	BSTRUCT,	0},
+	{TUNION,		BUNION,		0},
+	{-1,		0,		0},
+};
+
+long	tasadd[NTYPE];
+Init	tasaddinit[] =
+{
+	{TCHAR,		BNUMBER,	0},
+	{TUCHAR,		BNUMBER,	0},
+	{TSHORT,		BNUMBER,	0},
+	{TUSHORT,	BNUMBER,	0},
+	{TINT,		BNUMBER,	0},
+	{TUINT,		BNUMBER,	0},
+	{TLONG,		BNUMBER,	0},
+	{TULONG,		BNUMBER,	0},
+	{TVLONG,		BNUMBER,	0},
+	{TUVLONG,	BNUMBER,	0},
+	{TFLOAT,		BNUMBER,	0},
+	{TDOUBLE,	BNUMBER,	0},
+	{TIND,		BINTEGER,	0},
+	{-1,		0,		0},
+};
+
+long	tcast[NTYPE];
+Init	tcastinit[] =
+{
+	{TCHAR,		BNUMBER|BIND|BVOID,	0},
+	{TUCHAR,		BNUMBER|BIND|BVOID,	0},
+	{TSHORT,		BNUMBER|BIND|BVOID,	0},
+	{TUSHORT,	BNUMBER|BIND|BVOID,	0},
+	{TINT,		BNUMBER|BIND|BVOID,	0},
+	{TUINT,		BNUMBER|BIND|BVOID,	0},
+	{TLONG,		BNUMBER|BIND|BVOID,	0},
+	{TULONG,		BNUMBER|BIND|BVOID,	0},
+	{TVLONG,		BNUMBER|BIND|BVOID,	0},
+	{TUVLONG,	BNUMBER|BIND|BVOID,	0},
+	{TFLOAT,		BNUMBER|BVOID,		0},
+	{TDOUBLE,	BNUMBER|BVOID,		0},
+	{TIND,		BINTEGER|BIND|BVOID,	0},
+	{TVOID,		BVOID,			0},
+	{TSTRUCT,	BSTRUCT|BVOID,		0},
+	{TUNION,		BUNION|BVOID,		0},
+	{-1,		0,			0},
+};
+
+long	tadd[NTYPE];
+Init	taddinit[] =
+{
+	{TCHAR,		BNUMBER|BIND,	0},
+	{TUCHAR,		BNUMBER|BIND,	0},
+	{TSHORT,		BNUMBER|BIND,	0},
+	{TUSHORT,	BNUMBER|BIND,	0},
+	{TINT,		BNUMBER|BIND,	0},
+	{TUINT,		BNUMBER|BIND,	0},
+	{TLONG,		BNUMBER|BIND,	0},
+	{TULONG,		BNUMBER|BIND,	0},
+	{TVLONG,		BNUMBER|BIND,	0},
+	{TUVLONG,	BNUMBER|BIND,	0},
+	{TFLOAT,		BNUMBER,	0},
+	{TDOUBLE,	BNUMBER,	0},
+	{TIND,		BINTEGER,	0},
+	{-1,		0,		0},
+};
+
+long	tsub[NTYPE];
+Init	tsubinit[] =
+{
+	{TCHAR,		BNUMBER,	0},
+	{TUCHAR,		BNUMBER,	0},
+	{TSHORT,		BNUMBER,	0},
+	{TUSHORT,	BNUMBER,	0},
+	{TINT,		BNUMBER,	0},
+	{TUINT,		BNUMBER,	0},
+	{TLONG,		BNUMBER,	0},
+	{TULONG,		BNUMBER,	0},
+	{TVLONG,		BNUMBER,	0},
+	{TUVLONG,	BNUMBER,	0},
+	{TFLOAT,		BNUMBER,	0},
+	{TDOUBLE,	BNUMBER,	0},
+	{TIND,		BINTEGER|BIND,	0},
+	{-1,		0,		0},
+};
+
+long	tmul[NTYPE];
+Init	tmulinit[] =
+{
+	{TCHAR,		BNUMBER,	0},
+	{TUCHAR,		BNUMBER,	0},
+	{TSHORT,		BNUMBER,	0},
+	{TUSHORT,	BNUMBER,	0},
+	{TINT,		BNUMBER,	0},
+	{TUINT,		BNUMBER,	0},
+	{TLONG,		BNUMBER,	0},
+	{TULONG,		BNUMBER,	0},
+	{TVLONG,		BNUMBER,	0},
+	{TUVLONG,	BNUMBER,	0},
+	{TFLOAT,		BNUMBER,	0},
+	{TDOUBLE,	BNUMBER,	0},
+	{-1,		0,		0},
+};
+
+long	tand[NTYPE];
+Init	tandinit[] =
+{
+	{TCHAR,		BINTEGER,	0},
+	{TUCHAR,		BINTEGER,	0},
+	{TSHORT,		BINTEGER,	0},
+	{TUSHORT,	BINTEGER,	0},
+	{TINT,		BNUMBER,	0},
+	{TUINT,		BNUMBER,	0},
+	{TLONG,		BINTEGER,	0},
+	{TULONG,		BINTEGER,	0},
+	{TVLONG,		BINTEGER,	0},
+	{TUVLONG,	BINTEGER,	0},
+	{-1,		0,		0},
+};
+
+long	trel[NTYPE];
+Init	trelinit[] =
+{
+	{TCHAR,		BNUMBER,	0},
+	{TUCHAR,		BNUMBER,	0},
+	{TSHORT,		BNUMBER,	0},
+	{TUSHORT,	BNUMBER,	0},
+	{TINT,		BNUMBER,	0},
+	{TUINT,		BNUMBER,	0},
+	{TLONG,		BNUMBER,	0},
+	{TULONG,		BNUMBER,	0},
+	{TVLONG,		BNUMBER,	0},
+	{TUVLONG,	BNUMBER,	0},
+	{TFLOAT,		BNUMBER,	0},
+	{TDOUBLE,	BNUMBER,	0},
+	{TIND,		BIND,		0},
+	{-1,		0,		0},
+};
+
+long	tfunct[1] =
+{
+	BFUNC,
+};
+
+long	tindir[1] =
+{
+	BIND,
+};
+
+long	tdot[1] =
+{
+	BSTRUCT|BUNION,
+};
+
+long	tnot[1] =
+{
+	BNUMBER|BIND,
+};
+
+long	targ[1] =
+{
+	BNUMBER|BIND|BSTRUCT|BUNION,
+};
+
+char	tab[NTYPE][NTYPE] =
+{
+/*TXXX*/	{ 0,
+		},
+
+/*TCHAR*/	{ 0,	TCHAR, TUCHAR, TSHORT, TUSHORT, TINT, TUINT, TLONG,
+			TULONG, TVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TUCHAR*/	{ 0,	TUCHAR, TUCHAR, TUSHORT, TUSHORT, TUINT, TUINT, TULONG,
+			TULONG, TUVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TSHORT*/	{ 0,	TSHORT, TUSHORT, TSHORT, TUSHORT, TINT, TUINT, TLONG,
+			TULONG, TVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TUSHORT*/	{ 0,	TUSHORT, TUSHORT, TUSHORT, TUSHORT, TUINT, TUINT, TULONG,
+			TULONG, TUVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TINT*/	{ 0,	TINT, TUINT, TINT, TUINT, TINT, TUINT, TLONG,
+			TULONG, TVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TUINT*/	{ 0,	TUINT, TUINT, TUINT, TUINT, TUINT, TUINT, TULONG,
+			TULONG, TUVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TLONG*/	{ 0,	TLONG, TULONG, TLONG, TULONG, TLONG, TULONG, TLONG,
+			TULONG, TVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TULONG*/	{ 0,	TULONG, TULONG, TULONG, TULONG, TULONG, TULONG, TULONG,
+			TULONG, TUVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TVLONG*/	{ 0,	TVLONG, TUVLONG, TVLONG, TUVLONG, TVLONG, TUVLONG, TVLONG,
+			TUVLONG, TVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TUVLONG*/	{ 0,	TUVLONG, TUVLONG, TUVLONG, TUVLONG, TUVLONG, TUVLONG, TUVLONG,
+			TUVLONG, TUVLONG, TUVLONG, TFLOAT, TDOUBLE, TIND,
+		},
+/*TFLOAT*/	{ 0,	TFLOAT, TFLOAT, TFLOAT, TFLOAT, TFLOAT, TFLOAT, TFLOAT,
+			TFLOAT, TFLOAT, TFLOAT, TFLOAT, TDOUBLE, TIND,
+		},
+/*TDOUBLE*/	{ 0,	TDOUBLE, TDOUBLE, TDOUBLE, TDOUBLE, TDOUBLE, TDOUBLE, TDOUBLE,
+			TDOUBLE, TDOUBLE, TDOUBLE, TFLOAT, TDOUBLE, TIND,
+		},
+/*TIND*/	{ 0,	TIND, TIND, TIND, TIND, TIND, TIND, TIND,
+			 TIND, TIND, TIND, TIND, TIND, TIND,
+		},
+};
+
+void
+urk(char *name, int max, int i)
+{
+	if(i >= max) {
+		fprint(2, "bad tinit: %s %d>=%d\n", name, i, max);
+		exits("init");
+	}
+}
+
+void
+tinit(void)
+{
+	int *ip;
+	Init *p;
+
+	for(p=thashinit; p->code >= 0; p++) {
+		urk("thash", nelem(thash), p->code);
+		thash[p->code] = p->value;
+	}
+	for(p=bnamesinit; p->code >= 0; p++) {
+		urk("bnames", nelem(bnames), p->code);
+		bnames[p->code] = p->s;
+	}
+	for(p=tnamesinit; p->code >= 0; p++) {
+		urk("tnames", nelem(tnames), p->code);
+		tnames[p->code] = p->s;
+	}
+	for(p=gnamesinit; p->code >= 0; p++) {
+		urk("gnames", nelem(gnames), p->code);
+		gnames[p->code] = p->s;
+	}
+	for(p=qnamesinit; p->code >= 0; p++) {
+		urk("qnames", nelem(qnames), p->code);
+		qnames[p->code] = p->s;
+	}
+	for(p=cnamesinit; p->code >= 0; p++) {
+		urk("cnames", nelem(cnames), p->code);
+		cnames[p->code] = p->s;
+	}
+	for(p=onamesinit; p->code >= 0; p++) {
+		urk("onames", nelem(onames), p->code);
+		onames[p->code] = p->s;
+	}
+	for(ip=typeiinit; *ip>=0; ip++) {
+		urk("typei", nelem(typei), *ip);
+		typei[*ip] = 1;
+	}
+	for(ip=typeuinit; *ip>=0; ip++) {
+		urk("typeu", nelem(typeu), *ip);
+		typeu[*ip] = 1;
+	}
+	for(ip=typesuvinit; *ip>=0; ip++) {
+		urk("typesuv", nelem(typesuv), *ip);
+		typesuv[*ip] = 1;
+	}
+	for(ip=typeilpinit; *ip>=0; ip++) {
+		urk("typeilp", nelem(typeilp), *ip);
+		typeilp[*ip] = 1;
+	}
+	for(ip=typechlinit; *ip>=0; ip++) {
+		urk("typechl", nelem(typechl), *ip);
+		typechl[*ip] = 1;
+		typechlv[*ip] = 1;
+		typechlvp[*ip] = 1;
+	}
+	for(ip=typechlpinit; *ip>=0; ip++) {
+		urk("typechlp", nelem(typechlp), *ip);
+		typechlp[*ip] = 1;
+		typechlvp[*ip] = 1;
+	}
+	for(ip=typechlpfdinit; *ip>=0; ip++) {
+		urk("typechlpfd", nelem(typechlpfd), *ip);
+		typechlpfd[*ip] = 1;
+	}
+	for(ip=typecinit; *ip>=0; ip++) {
+		urk("typec", nelem(typec), *ip);
+		typec[*ip] = 1;
+	}
+	for(ip=typehinit; *ip>=0; ip++) {
+		urk("typeh", nelem(typeh), *ip);
+		typeh[*ip] = 1;
+	}
+	for(ip=typeilinit; *ip>=0; ip++) {
+		urk("typeil", nelem(typeil), *ip);
+		typeil[*ip] = 1;
+	}
+	for(ip=typevinit; *ip>=0; ip++) {
+		urk("typev", nelem(typev), *ip);
+		typev[*ip] = 1;
+		typechlv[*ip] = 1;
+		typechlvp[*ip] = 1;
+	}
+	for(ip=typefdinit; *ip>=0; ip++) {
+		urk("typefd", nelem(typefd), *ip);
+		typefd[*ip] = 1;
+	}
+	for(ip=typeafinit; *ip>=0; ip++) {
+		urk("typeaf", nelem(typeaf), *ip);
+		typeaf[*ip] = 1;
+	}
+	for(ip=typesuinit; *ip >= 0; ip++) {
+		urk("typesu", nelem(typesu), *ip);
+		typesu[*ip] = 1;
+	}
+	for(p=tasigninit; p->code >= 0; p++) {
+		urk("tasign", nelem(tasign), p->code);
+		tasign[p->code] = p->value;
+	}
+	for(p=tasaddinit; p->code >= 0; p++) {
+		urk("tasadd", nelem(tasadd), p->code);
+		tasadd[p->code] = p->value;
+	}
+	for(p=tcastinit; p->code >= 0; p++) {
+		urk("tcast", nelem(tcast), p->code);
+		tcast[p->code] = p->value;
+	}
+	for(p=taddinit; p->code >= 0; p++) {
+		urk("tadd", nelem(tadd), p->code);
+		tadd[p->code] = p->value;
+	}
+	for(p=tsubinit; p->code >= 0; p++) {
+		urk("tsub", nelem(tsub), p->code);
+		tsub[p->code] = p->value;
+	}
+	for(p=tmulinit; p->code >= 0; p++) {
+		urk("tmul", nelem(tmul), p->code);
+		tmul[p->code] = p->value;
+	}
+	for(p=tandinit; p->code >= 0; p++) {
+		urk("tand", nelem(tand), p->code);
+		tand[p->code] = p->value;
+	}
+	for(p=trelinit; p->code >= 0; p++) {
+		urk("trel", nelem(trel), p->code);
+		trel[p->code] = p->value;
+	}
+	
+	/* 32-bit defaults */
+	typeword = typechlp;
+	typeswitch = typechl;
+	typecmplx = typesuv;
+}
+
+/*
+ * return 1 if it is impossible to jump into the middle of n.
+ */
+static int
+deadhead(Node *n, int caseok)
+{
+loop:
+	if(n == Z)
+		return 1;
+	switch(n->op) {
+	case OLIST:
+		if(!deadhead(n->left, caseok))
+			return 0;
+	rloop:
+		n = n->right;
+		goto loop;
+
+	case ORETURN:
+		break;
+
+	case OLABEL:
+		return 0;
+
+	case OGOTO:
+		break;
+
+	case OCASE:
+		if(!caseok)
+			return 0;
+		goto rloop;
+
+	case OSWITCH:
+		return deadhead(n->right, 1);
+
+	case OWHILE:
+	case ODWHILE:
+		goto rloop;
+
+	case OFOR:
+		goto rloop;
+
+	case OCONTINUE:
+		break;
+
+	case OBREAK:
+		break;
+
+	case OIF:
+		return deadhead(n->right->left, caseok) && deadhead(n->right->right, caseok);
+
+	case OSET:
+	case OUSED:
+		break;
+	}
+	return 1;
+}
+
+int
+deadheads(Node *c)
+{
+	return deadhead(c->left, 0) && deadhead(c->right, 0);
+}
+
+int
+mixedasop(Type *l, Type *r)
+{
+	return !typefd[l->etype] && typefd[r->etype];
+}

+ 1 - 0
sys/src/libs.json

@@ -12,6 +12,7 @@
 			"/sys/src/libavl/libavl.json",
 			"/sys/src/libbin/libbin.json",
 			"/sys/src/libbio/libbio.json",
+			"/sys/src/libcc/build.json",
 			"/sys/src/libcomplete/libcomplete.json",
 			"/sys/src/libcontrol/libcontrol.json",
 			"/sys/src/libdisk",