shithub: riscv

Download patch

ref: b29d5ac7b1f713e41285ad4b2ce1b23313d8088e
parent: e897df33e638dcc3768b8207146e2a4195dd3c4b
author: cinap_lenrek <[email protected]>
date: Mon Apr 8 09:45:49 EDT 2019

add arm64 c compiler and assembler (thanks charles forsyth)

this is the the initial sync of charles forsyths plan9 c
compiler suite from http://bitbucket.org/plan9-from-bell-labs/9-cc
at changeset version 54:65fb8bb56c59

--- /dev/null
+++ b/sys/src/cmd/7a/a.h
@@ -1,0 +1,185 @@
+/*
+ * arm64
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "../7c/7.out.h"
+
+typedef vlong int64;
+
+#ifndef	EXTERN
+#define	EXTERN	extern
+#endif
+
+typedef	struct	Sym	Sym;
+typedef	struct	Gen	Gen;
+typedef	struct	Io	Io;
+typedef	struct	Hist	Hist;
+
+#define	MAXALIGN	7
+#define	FPCHIP		1
+#define	NSYMB		8192
+#define	BUFSIZ		8192
+#define	HISTSZ		20
+#define	NINCLUDE	10
+#define	NHUNK		10000
+#define	EOF		(-1)
+#define	IGN		(-2)
+#define	GETC()		((--fi.c < 0)? filbuf(): *fi.p++ & 0xff)
+#define	NHASH		503
+#define	STRINGSZ	200
+#define	NMACRO		10
+
+struct	Sym
+{
+	Sym*	link;
+	char*	macro;
+	long	value;
+	ushort	type;
+	char	*name;
+	char	sym;
+};
+#define	S	((Sym*)0)
+
+EXTERN	struct
+{
+	char*	p;
+	int	c;
+} fi;
+
+struct	Io
+{
+	Io*	link;
+	char	b[BUFSIZ];
+	char*	p;
+	short	c;
+	short	f;
+};
+#define	I	((Io*)0)
+
+EXTERN	struct
+{
+	Sym*	sym;
+	short	type;
+} h[NSYM];
+
+struct	Gen
+{
+	Sym*	sym;
+	int64	offset;
+	short	type;
+	short	reg;
+	short	xreg;
+	short	name;
+	short	ext;
+	double	dval;
+	char	sval[NSNAME];
+};
+
+struct	Hist
+{
+	Hist*	link;
+	char*	name;
+	long	line;
+	long	offset;
+};
+#define	H	((Hist*)0)
+
+enum
+{
+	CLAST,
+	CMACARG,
+	CMACRO,
+	CPREPROC,
+};
+
+EXTERN	char	debug[256];
+EXTERN	Sym*	hash[NHASH];
+EXTERN	char*	Dlist[30];
+EXTERN	int	nDlist;
+EXTERN	Hist*	ehist;
+EXTERN	int	newflag;
+EXTERN	Hist*	hist;
+EXTERN	char*	hunk;
+EXTERN	char*	include[NINCLUDE];
+EXTERN	Io*	iofree;
+EXTERN	Io*	ionext;
+EXTERN	Io*	iostack;
+EXTERN	long	lineno;
+EXTERN	int	nerrors;
+EXTERN	long	nhunk;
+EXTERN	int	ninclude;
+EXTERN	Gen	nullgen;
+EXTERN	char*	outfile;
+EXTERN	int	pass;
+EXTERN	char*	pathname;
+EXTERN	long	pc;
+EXTERN	int	peekc;
+EXTERN	int	sym;
+EXTERN	char	symb[NSYMB];
+EXTERN	int	thechar;
+EXTERN	char*	thestring;
+EXTERN	long	thunk;
+EXTERN	Biobuf	obuf;
+
+void*	alloc(long);
+void*	allocn(void*, long, long);
+void	errorexit(void);
+void	pushio(void);
+void	newio(void);
+void	newfile(char*, int);
+Sym*	slookup(char*);
+Sym*	lookup(void);
+void	syminit(Sym*);
+long	yylex(void);
+int	getc(void);
+int	getnsc(void);
+void	unget(int);
+int	escchar(int);
+void	cinit(void);
+void	pinit(char*);
+void	cclean(void);
+void	outcode(int, Gen*, int, Gen*);
+void	outcodec(int, int, Gen*, int, Gen*);
+void	outcode4(int, Gen*, int, Gen*, Gen*);
+void	zname(char*, int, int);
+void	zaddr(Gen*, int);
+void	ieeedtod(Ieee*, double);
+int	filbuf(void);
+Sym*	getsym(void);
+void	domacro(void);
+void	macund(void);
+void	macdef(void);
+void	macexpand(Sym*, char*);
+void	macinc(void);
+void	maclin(void);
+void	macprag(void);
+void	macif(int);
+void	macend(void);
+void	outhist(void);
+void	dodefine(char*);
+void	prfile(long);
+void	linehist(char*, int);
+void	gethunk(void);
+void	yyerror(char*, ...);
+int	yyparse(void);
+void	setinclude(char*);
+int	assemble(char*);
+
+/*
+ *	system-dependent stuff from ../cc/compat.c
+ */
+
+enum				/* keep in synch with ../cc/cc.h */
+{
+	Plan9	= 1<<0,
+	Unix	= 1<<1,
+	Windows	= 1<<2,
+};
+int	mywait(int*);
+int	mycreat(char*, int);
+int	systemtype(int);
+int	pathchar(void);
+int	myfork(void);
+void*	mysbrk(ulong);
--- /dev/null
+++ b/sys/src/cmd/7a/a.y
@@ -1,0 +1,878 @@
+%{
+#include "a.h"
+%}
+%union
+{
+	Sym	*sym;
+	vlong	lval;
+	double	dval;
+	char	sval[NSNAME];
+	Gen	gen;
+}
+%left	'|'
+%left	'^'
+%left	'&'
+%left	'<' '>'
+%left	'+' '-'
+%left	'*' '/' '%'
+%token	<lval>	LTYPE0 LTYPE1 LTYPE2 LTYPE3 LTYPE4 LTYPE5
+%token	<lval>	LTYPE6 LTYPE7 LTYPE8 LTYPE9 LTYPEA
+%token	<lval>	LTYPEB LTYPEC LTYPED LTYPEE LTYPEF
+%token	<lval>	LTYPEG LTYPEH LTYPEI LTYPEJ LTYPEK
+%token	<lval>	LTYPEL LTYPEM LTYPEN LTYPEO LTYPEP LTYPEQ
+%token	<lval>	LTYPER LTYPES LTYPET LTYPEU LTYPEV LTYPEW LTYPEX LTYPEY LTYPEZ
+%token	<lval>	LMOVK LDMB LSTXR
+%token	<lval>	LCONST LSP LSB LFP LPC
+%token	<lval>	LR LREG LF LFREG LV LVREG LC LCREG LFCR LFCSEL
+%token	<lval>	LCOND LS LAT LEXT LSPR LSPREG LVTYPE
+%token	<dval>	LFCONST
+%token	<sval>	LSCONST
+%token	<sym>	LNAME LLAB LVAR
+%type	<lval>	con expr pointer offset sreg spreg
+%type	<lval>	scon indexreg vset vreglist
+%type	<gen>	gen rel reg freg vreg shift fcon frcon extreg vlane vgen
+%type	<gen>	imm ximm name oreg nireg ioreg imsr spr cond sysarg
+%%
+prog:
+|	prog line
+
+line:
+	LLAB ':'
+	{
+		if($1->value != pc)
+			yyerror("redeclaration of %s", $1->name);
+		$1->value = pc;
+	}
+	line
+|	LNAME ':'
+	{
+		$1->type = LLAB;
+		$1->value = pc;
+	}
+	line
+|	LNAME '=' expr ';'
+	{
+		$1->type = LVAR;
+		$1->value = $3;
+	}
+|	LVAR '=' expr ';'
+	{
+		if($1->value != $3)
+			yyerror("redeclaration of %s", $1->name);
+		$1->value = $3;
+	}
+|	';'
+|	inst ';'
+|	error ';'
+
+inst:
+/*
+ * ERET
+ */
+	LTYPE0 comma
+	{
+		outcode($1, &nullgen, NREG, &nullgen);
+	}
+/*
+ * ADD
+ */
+|	LTYPE1 imsr ',' spreg ',' reg
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LTYPE1 imsr ',' spreg ','
+	{
+		outcode($1, &$2, $4, &nullgen);
+	}
+|	LTYPE1 imsr ',' reg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * CLS
+ */
+|	LTYPE2 imsr ',' reg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * MOV
+ */
+|	LTYPE3 gen ',' gen
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * MOVK
+ */
+|	LMOVK imm ',' reg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LMOVK imm '<' '<' con ',' reg
+	{
+		Gen g;
+		g = nullgen;
+		g.type = D_CONST;
+		g.offset = $5;
+		outcode4($1, &$2, NREG, &g, &$7);
+	}
+/*
+ * B/BL
+ */
+|	LTYPE4 comma rel
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LTYPE4 comma nireg
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+/*
+ * BEQ
+ */
+|	LTYPE5 comma rel
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+/*
+ * SVC
+ */
+|	LTYPE6 comma gen
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LTYPE6
+	{
+		outcode($1, &nullgen, NREG, &nullgen);
+	}
+/*
+ * CMP
+ */
+|	LTYPE7 imsr ',' spreg comma
+	{
+		outcode($1, &$2, $4, &nullgen);
+	}
+/*
+ * CBZ
+ */
+|	LTYPE8 reg ',' rel
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * CSET
+ */
+|	LTYPER cond ',' reg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * CSEL/CINC/CNEG/CINV
+ */
+|	LTYPES cond ',' reg ',' reg ',' reg
+	{
+		outcode4($1, &$2, $6.reg, &$4, &$8);
+	}
+|	LTYPES cond ',' reg ',' reg
+	{
+		outcode($1, &$2, $4.reg, &$6);
+	}
+/*
+ * TBZ
+ */
+|	LTYPET imm ',' reg ',' rel
+	{
+		outcode($1, &$2, $4.reg, &$6);
+	}
+/*
+ * CCMN
+ */
+|	LTYPEU cond ',' imsr ',' reg ',' imm comma
+	{
+		outcode4($1, &$2, $6.reg, &$4, &$8);
+	}
+/*
+ * ADR
+ */
+|	LTYPEV rel ',' reg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LTYPEV '$' name ',' reg
+	{
+		outcode($1, &$3, NREG, &$5);
+	}
+/*
+ * BFM/BFI
+ */
+|	LTYPEY imm ',' imm ',' spreg ',' reg
+	{
+		outcode4($1, &$2, $6, &$4, &$8);
+	}
+/*
+ * EXTR
+ */
+|	LTYPEP imm ',' reg ',' spreg ',' reg
+	{
+		outcode4($1, &$2, $6, &$4, &$8);
+	}
+/*
+ * RET/RETURN
+ */
+|	LTYPEA comma
+	{
+		outcode($1, &nullgen, NREG, &nullgen);
+	}
+|	LTYPEA reg
+	{
+		outcode($1, &nullgen, NREG, &$2);
+	}
+/*
+ * NOP
+ */
+|	LTYPEQ comma
+	{
+		outcode($1, &nullgen, NREG, &nullgen);
+	}
+|	LTYPEQ reg comma
+	{
+		outcode($1, &$2, NREG, &nullgen);
+	}
+|	LTYPEQ freg comma
+	{
+		outcode($1, &$2, NREG, &nullgen);
+	}
+|	LTYPEQ ',' reg
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LTYPEQ ',' freg
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+/*
+ * TEXT/GLOBL
+ */
+|	LTYPEB name ',' imm
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LTYPEB name ',' con ',' imm
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+/*
+ * DATA
+ */
+|	LTYPEC name '/' con ',' ximm
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+/*
+ * CASE
+ */
+|	LTYPED reg ',' reg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * word
+ */
+|	LTYPEH comma ximm
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+/*
+ * floating-point
+ */
+|	LTYPEI freg ',' freg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * FADDD
+ */
+|	LTYPEK frcon ',' freg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LTYPEK frcon ',' freg ',' freg
+	{
+		outcode($1, &$2, $4.reg, &$6);
+	}
+/*
+ * FCMP
+ */
+|	LTYPEL frcon ',' freg comma
+	{
+		outcode($1, &$2, $4.reg, &nullgen);
+	}
+/*
+ * FCCMP
+ */
+|	LTYPEF cond ',' freg ',' freg ',' imm comma
+	{
+		outcode4($1, &$2, $6.reg, &$4, &$8);
+	}
+/*
+ * FMULA
+ */
+|	LTYPE9 freg ',' freg ', ' freg ',' freg comma
+	{
+		outcode4($1, &$2, $4.reg, &$6, &$8);
+	}
+/*
+ * FCSEL
+ */
+|	LFCSEL cond ',' freg ',' freg ',' freg
+	{
+		outcode4($1, &$2, $6.reg, &$4, &$8);
+	}
+/*
+ * SIMD
+ */
+|	LTYPEW vgen ',' vgen
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LTYPEW vgen ',' vgen ',' vgen
+	{
+		outcode($1, &$2, $4.reg, &$6);
+	}
+/*
+ * MOVP/MOVNP
+ */
+|	LTYPEJ gen ',' sreg ',' gen
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+/*
+ * MADD Rn,Rm,Ra,Rd
+ */
+|	LTYPEM reg ',' reg ',' sreg ',' reg
+	{
+		outcode4($1, &$2, $6, &$4, &$8);
+	}
+/*
+ * SYS/SYSL
+ */
+|	LTYPEN sysarg
+	{
+		outcode($1, &$2, NREG, &nullgen);
+	}
+|	LTYPEN reg ',' sysarg
+	{
+		outcode($1, &$4, $2.reg, &nullgen);
+	}
+|	LTYPEO sysarg ',' reg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * DMB, HINT
+ */
+|	LDMB imm
+	{
+		outcode($1, &$2, NREG, &nullgen);
+	}
+/*
+ * STXR
+ */
+|	LSTXR reg ',' gen ',' sreg
+	{
+		outcode($1, &$2, $6, &$4);
+	}
+/*
+ * END
+ */
+|	LTYPEE comma
+	{
+		outcode($1, &nullgen, NREG, &nullgen);
+	}
+
+cond:
+	LCOND
+	{
+		$$ = nullgen;
+		$$.type = D_COND;
+		$$.reg = $1;
+	}
+
+comma:
+|	',' comma
+
+sysarg:
+	con ',' con ',' con ',' con
+	{
+		$$ = nullgen;
+		$$.type = D_CONST;
+		$$.offset = SYSARG4($1, $3, $5, $7);
+	}
+|	imm
+
+rel:
+	con '(' LPC ')'
+	{
+		$$ = nullgen;
+		$$.type = D_BRANCH;
+		$$.offset = $1 + pc;
+	}
+|	LNAME offset
+	{
+		$$ = nullgen;
+		if(pass == 2)
+			yyerror("undefined label: %s", $1->name);
+		$$.type = D_BRANCH;
+		$$.sym = $1;
+		$$.offset = $2;
+	}
+|	LLAB offset
+	{
+		$$ = nullgen;
+		$$.type = D_BRANCH;
+		$$.sym = $1;
+		$$.offset = $1->value + $2;
+	}
+
+ximm:	'$' con
+	{
+		$$ = nullgen;
+		$$.type = D_CONST;
+		$$.offset = $2;
+	}
+|	'$' oreg
+	{
+		$$ = $2;
+		$$.type = D_CONST;
+	}
+|	'$' '*' '$' oreg
+	{
+		$$ = $4;
+		$$.type = D_OCONST;
+	}
+|	'$' LSCONST
+	{
+		$$ = nullgen;
+		$$.type = D_SCONST;
+		memmove($$.sval, $2, sizeof($$.sval));
+	}
+|	fcon
+
+fcon:
+	'$' LFCONST
+	{
+		$$ = nullgen;
+		$$.type = D_FCONST;
+		$$.dval = $2;
+	}
+|	'$' '-' LFCONST
+	{
+		$$ = nullgen;
+		$$.type = D_FCONST;
+		$$.dval = -$3;
+	}
+
+gen:
+	reg
+|	ximm
+|	LFCR
+	{
+		$$ = nullgen;
+		$$.type = D_SPR;
+		$$.offset = $1;
+	}
+|	con
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.offset = $1;
+	}
+|	oreg
+|	freg
+|	vreg
+|	spr
+
+nireg:
+	'(' sreg ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.reg = $2;
+		$$.offset = 0;
+	}
+|	name
+	{
+		$$ = $1;
+		if($1.name != D_EXTERN && $1.name != D_STATIC) {
+		}
+	}
+
+oreg:
+	name
+|	name '(' sreg ')'
+	{
+		$$ = $1;
+		$$.type = D_OREG;
+		$$.reg = $3;
+	}
+|	ioreg
+
+ioreg:
+	'(' sreg ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.reg = $2;
+		$$.offset = 0;
+	}
+|	con '(' sreg ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.reg = $3;
+		$$.offset = $1;
+	}
+|	con '(' sreg ')' '!'
+	{
+		$$ = nullgen;
+		$$.type = D_XPRE;
+		$$.reg = $3;
+		$$.offset = $1;
+	}
+|	'(' sreg ')' con '!'
+	{
+		$$.type = D_XPOST;
+		$$.offset = $2;
+	}
+|	'(' sreg ')' '(' indexreg ')'
+	{
+		$$ = nullgen;
+		$$.type = D_ROFF;
+		$$.reg = $2;
+		$$.xreg = $5 & 0x1f;
+		$$.offset = $5;
+	}
+|	'(' sreg ')' '[' indexreg ']'
+	{
+		$$ = nullgen;
+		$$.type = D_ROFF;
+		$$.reg = $2;
+		$$.xreg = $5 & 0x1f;
+		$$.offset = $5 | (1<<16);
+	}
+
+imsr:
+	imm
+|	shift
+|	extreg
+
+imm:	'$' con
+	{
+		$$ = nullgen;
+		$$.type = D_CONST;
+		$$.offset = $2;
+	}
+
+reg:
+	sreg
+	{
+		$$ = nullgen;
+		$$.type = D_REG;
+		$$.reg = $1;
+	}
+|	LSP
+	{
+		$$ = nullgen;
+		$$.type = D_SP;
+		$$.reg = REGSP;
+	}
+
+shift:
+	sreg '<' '<' scon
+	{
+		$$ = nullgen;
+		$$.type = D_SHIFT;
+		$$.offset = ($1 << 16) | ($4 << 10) | (0 << 22);
+	}
+|	sreg '>' '>' scon
+	{
+		$$ = nullgen;
+		$$.type = D_SHIFT;
+		$$.offset = (($1&0x1F) << 16) | ($4 << 10) | (1 << 22);
+	}
+|	sreg '-' '>' scon
+	{
+		$$ = nullgen;
+		$$.type = D_SHIFT;
+		$$.offset = ($1 << 16) | ($4 << 10) | (2 << 22);
+	}
+|	sreg LAT '>' scon
+	{
+		$$ = nullgen;
+		$$.type = D_SHIFT;
+		$$.offset = ($1 << 16) | ($4 << 10) | (3 << 22);
+	}
+
+extreg:
+	sreg
+	{
+		$$ = nullgen;
+		$$.type = D_REG;
+		$$.reg = $1;
+	}
+|	sreg LEXT
+	{
+		$$ = nullgen;
+		$$.type = D_EXTREG;
+		$$.reg = $1;
+		$$.offset = ($1 << 16) | ($2 << 13);
+	}
+|	sreg LEXT '<' '<' con
+	{
+		if($5 < 0 || $5 > 4)
+			yyerror("shift value out of range");
+		$$ = nullgen;
+		$$.type = D_EXTREG;
+		$$.reg = $1;
+		$$.offset = ($1 << 16) | ($2 << 13) | ($5 << 10);
+	}
+
+indexreg:
+	sreg
+	{
+		$$ = (3 << 8) | $1;
+	}
+|	sreg LEXT
+	{
+		$$ = ($2 << 8) | $1;
+	}
+
+scon:
+	con
+	{
+		if($$ < 0 || $$ >= 64)
+			yyerror("shift value out of range");
+		$$ = $1&0x3F;
+	}
+
+sreg:
+	LREG
+|	LR '(' expr ')'
+	{
+		if($3 < 0 || $3 >= NREG)
+			print("register value out of range\n");
+		$$ = $3;
+	}
+
+spreg:
+	sreg
+|	LSP
+	{
+		$$ = REGSP;
+	}
+
+spr:
+	LSPREG
+	{
+		$$ = nullgen;
+		$$.type = D_SPR;
+		$$.offset = $1;
+	}
+|	LSPR '(' con ')'
+	{
+		$$ = nullgen;
+		$$.type = $1;
+		$$.offset = $3;
+	}
+
+frcon:
+	freg
+|	fcon
+
+freg:
+	LFREG
+	{
+		$$ = nullgen;
+		$$.type = D_FREG;
+		$$.reg = $1;
+	}
+|	LF '(' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_FREG;
+		$$.reg = $3;
+	}
+
+vgen:
+	oreg
+|	vreg
+|	vlane
+|	vset
+	{
+		$$ = nullgen;
+		$$.type = D_VSET;
+		$$.offset = $1;
+	}
+
+vlane:
+	vreg '[' con ']'
+	{
+		$$.type = D_VLANE;
+		$$.offset = $3;
+	}
+|	vset '[' con ']'
+	{
+		$$.type = D_VLANE;
+		$$.offset = $3;
+	}
+
+vset:
+	'{' vreglist '}'
+	{
+		$$ = $2;
+	}
+
+vreglist:
+	vreg
+	{
+		$$ = 1 << $1.reg;
+	}
+|	vreg '-' vreg
+	{
+		int i;
+		$$=0;
+		for(i=$1.reg; i<=$3.reg; i++)
+			$$ |= 1<<i;
+		for(i=$3.reg; i<=$1.reg; i++)
+			$$ |= 1<<i;
+	}
+|	vreg comma vreglist
+	{
+		$$ = (1<<$1.reg) | $3;
+	}
+
+vreg:
+	LVREG
+	{
+		$$ = nullgen;
+		$$.type = D_VREG;
+		$$.reg = $1;
+		/* TO DO: slice */
+	}
+|	LV '(' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_VREG;
+		$$.reg = $3;
+	}
+
+name:
+	con '(' pointer ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.name = $3;
+		$$.sym = S;
+		$$.offset = $1;
+	}
+|	LNAME offset '(' pointer ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.name = $4;
+		$$.sym = $1;
+		$$.offset = $2;
+	}
+|	LNAME '<' '>' offset '(' LSB ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.name = D_STATIC;
+		$$.sym = $1;
+		$$.offset = $4;
+	}
+
+offset:
+	{
+		$$ = 0;
+	}
+|	'+' con
+	{
+		$$ = $2;
+	}
+|	'-' con
+	{
+		$$ = -$2;
+	}
+
+pointer:
+	LSB
+|	LSP
+|	LFP
+
+con:
+	LCONST
+|	LVAR
+	{
+		$$ = $1->value;
+	}
+|	'-' con
+	{
+		$$ = -$2;
+	}
+|	'+' con
+	{
+		$$ = $2;
+	}
+|	'~' con
+	{
+		$$ = ~$2;
+	}
+|	'(' expr ')'
+	{
+		$$ = $2;
+	}
+
+expr:
+	con
+|	expr '+' expr
+	{
+		$$ = $1 + $3;
+	}
+|	expr '-' expr
+	{
+		$$ = $1 - $3;
+	}
+|	expr '*' expr
+	{
+		$$ = $1 * $3;
+	}
+|	expr '/' expr
+	{
+		$$ = $1 / $3;
+	}
+|	expr '%' expr
+	{
+		$$ = $1 % $3;
+	}
+|	expr '<' '<' expr
+	{
+		$$ = $1 << $4;
+	}
+|	expr '>' '>' expr
+	{
+		$$ = $1 >> $4;
+	}
+|	expr '&' expr
+	{
+		$$ = $1 & $3;
+	}
+|	expr '^' expr
+	{
+		$$ = $1 ^ $3;
+	}
+|	expr '|' expr
+	{
+		$$ = $1 | $3;
+	}
--- /dev/null
+++ b/sys/src/cmd/7a/lex.c
@@ -1,0 +1,1041 @@
+#define	EXTERN
+#include "a.h"
+#include "y.tab.h"
+#include <ctype.h>
+
+void
+main(int argc, char *argv[])
+{
+	char *p;
+	int nout, nproc, status, i, c;
+
+	thechar = '7';
+	thestring = "arm64";
+	memset(debug, 0, sizeof(debug));
+	cinit();
+	outfile = 0;
+	include[ninclude++] = ".";
+	ARGBEGIN {
+	default:
+		c = ARGC();
+		if(c >= 0 || c < sizeof(debug))
+			debug[c] = 1;
+		break;
+
+	case 'o':
+		outfile = ARGF();
+		break;
+
+	case 'D':
+		p = ARGF();
+		if(p)
+			Dlist[nDlist++] = p;
+		break;
+
+	case 'I':
+		p = ARGF();
+		setinclude(p);
+		break;
+	} ARGEND
+	if(*argv == 0) {
+		print("usage: %ca [-options] file.s\n", thechar);
+		errorexit();
+	}
+	if(argc > 1 && systemtype(Windows)){
+		print("can't assemble multiple files on windows\n");
+		errorexit();
+	}
+	if(argc > 1 && !systemtype(Windows)) {
+		nproc = 1;
+		if(p = getenv("NPROC"))
+			nproc = atol(p);	/* */
+		c = 0;
+		nout = 0;
+		for(;;) {
+			while(nout < nproc && argc > 0) {
+				i = myfork();
+				if(i < 0) {
+					i = mywait(&status);
+					if(i < 0)
+						errorexit();
+					if(status)
+						c++;
+					nout--;
+					continue;
+				}
+				if(i == 0) {
+					print("%s:\n", *argv);
+					if(assemble(*argv))
+						errorexit();
+					exits(0);
+				}
+				nout++;
+				argc--;
+				argv++;
+			}
+			i = mywait(&status);
+			if(i < 0) {
+				if(c)
+					errorexit();
+				exits(0);
+			}
+			if(status)
+				c++;
+			nout--;
+		}
+	}
+	if(assemble(argv[0]))
+		errorexit();
+	exits(0);
+}
+
+int
+assemble(char *file)
+{
+	char ofile[100], incfile[20], *p;
+	int i, of;
+
+	strcpy(ofile, file);
+	p = utfrrune(ofile, pathchar());
+	if(p) {
+		include[0] = ofile;
+		*p++ = 0;
+	} else
+		p = ofile;
+	if(outfile == 0) {
+		outfile = p;
+		if(outfile){
+			p = utfrrune(outfile, '.');
+			if(p)
+				if(p[1] == 's' && p[2] == 0)
+					p[0] = 0;
+			p = utfrune(outfile, 0);
+			p[0] = '.';
+			p[1] = thechar;
+			p[2] = 0;
+		} else
+			outfile = "/dev/null";
+	}
+	p = getenv("INCLUDE");
+	if(p) {
+		setinclude(p);
+	} else {
+		if(systemtype(Plan9)) {
+			sprint(incfile,"/%s/include", thestring);
+			setinclude(strdup(incfile));
+		}
+	}
+
+	of = mycreat(outfile, 0664);
+	if(of < 0) {
+		yyerror("%ca: cannot create %s", thechar, outfile);
+		errorexit();
+	}
+	Binit(&obuf, of, OWRITE);
+
+	pass = 1;
+	pinit(file);
+	for(i=0; i<nDlist; i++)
+		dodefine(Dlist[i]);
+	yyparse();
+	if(nerrors) {
+		cclean();
+		return nerrors;
+	}
+
+	pass = 2;
+	outhist();
+	pinit(file);
+	for(i=0; i<nDlist; i++)
+		dodefine(Dlist[i]);
+	yyparse();
+	cclean();
+	return nerrors;
+}
+
+struct
+{
+	char	*name;
+	ushort	type;
+	ulong	value;
+} itab[] =
+{
+	"SP",		LSP,	D_AUTO,
+	"SB",		LSB,	D_EXTERN,
+	"FP",		LFP,	D_PARAM,
+	"PC",		LPC,	D_BRANCH,
+
+	"R",		LR,	0,
+	"R0",		LREG,	0,
+	"R1",		LREG,	1,
+	"R2",		LREG,	2,
+	"R3",		LREG,	3,
+	"R4",		LREG,	4,
+	"R5",		LREG,	5,
+	"R6",		LREG,	6,
+	"R7",		LREG,	7,
+	"R8",		LREG,	8,
+	"R9",		LREG,	9,
+	"R10",		LREG,	10,
+	"R11",		LREG,	11,
+	"R12",		LREG,	12,
+	"R13",		LREG,	13,
+	"R14",		LREG,	14,
+	"R15",		LREG,	15,
+	"R16",		LREG,	16,
+	"R17",		LREG,	17,
+	"R18",		LREG,	18,
+	"R19",		LREG,	19,
+	"R20",		LREG,	20,
+	"R21",		LREG,	21,
+	"R22",		LREG,	22,
+	"R23",		LREG,	23,
+	"R24",		LREG,	24,
+	"R25",		LREG,	25,
+	"R26",		LREG,	26,
+	"R27",		LREG,	27,
+	"R28",		LREG,	28,
+	"R29",		LREG,	29,
+	"R30",		LREG,	30,
+	"LR",			LREG,	30,
+	"ZR",			LREG,	31,
+
+	"RARG",		LREG,	REGARG,
+	"RARG0",	LREG,	REGARG,
+	"RSP",	LREG,	31,
+
+	"F",		LF,	0,
+
+	"F0",		LFREG,	0,
+	"F1",		LFREG,	1,
+	"F2",		LFREG,	2,
+	"F3",		LFREG,	3,
+	"F4",		LFREG,	4,
+	"F5",		LFREG,	5,
+	"F6",		LFREG,	6,
+	"F7",		LFREG,	7,
+	"F8",		LFREG,	8,
+	"F9",		LFREG,	9,
+	"F10",	LFREG,	10,
+	"F11",	LFREG,	11,
+	"F12",	LFREG,	12,
+	"F13",	LFREG,	13,
+	"F14",	LFREG,	14,
+	"F15",	LFREG,	15,
+	"F16",	LFREG,	16,
+	"F17",	LFREG,	17,
+	"F18",	LFREG,	18,
+	"F19",	LFREG,	19,
+	"F20",	LFREG,	20,
+	"F21",	LFREG,	21,
+	"F22",	LFREG,	22,
+	"F23",	LFREG,	23,
+	"F24",	LFREG,	24,
+	"F25",	LFREG,	25,
+	"F26",	LFREG,	26,
+	"F27",	LFREG,	27,
+	"F28",	LFREG,	28,
+	"F29",	LFREG,	29,
+	"F30",	LFREG,	30,
+	"F31",	LFREG,	31,
+
+	"V",		LV,	0,
+
+	"V0",		LVREG,	0,
+	"V1",		LVREG,	1,
+	"V2",		LVREG,	2,
+	"V3",		LVREG,	3,
+	"V4",		LVREG,	4,
+	"V5",		LVREG,	5,
+	"V6",		LVREG,	6,
+	"V7",		LVREG,	7,
+	"V8",		LVREG,	8,
+	"V9",		LVREG,	9,
+	"V10",	LVREG,	10,
+	"V11",	LVREG,	11,
+	"V12",	LVREG,	12,
+	"V13",	LVREG,	13,
+	"V14",	LVREG,	14,
+	"V15",	LVREG,	15,
+	"V16",	LVREG,	16,
+	"V17",	LVREG,	17,
+	"V18",	LVREG,	18,
+	"V19",	LVREG,	19,
+	"V20",	LVREG,	20,
+	"V21",	LVREG,	21,
+	"V22",	LVREG,	22,
+	"V23",	LVREG,	23,
+	"V24",	LVREG,	24,
+	"V25",	LVREG,	25,
+	"V26",	LVREG,	26,
+	"V27",	LVREG,	27,
+	"V28",	LVREG,	28,
+	"V29",	LVREG,	29,
+	"V30",	LVREG,	30,
+	"V31",	LVREG,	31,
+
+	"FPSR",		LFCR,	D_FPSR,
+	"FPCR",		LFCR,	D_FPCR,
+
+	"SPR",		LSPR,	D_SPR,
+	"NZCV",		LSPREG,	D_NZCV,
+	"ELR_EL1",	LSPREG,	D_ELR_EL1,
+	"ELR_EL2",	LSPREG,	D_ELR_EL2,
+//	"ELR_EL3",	LSPREG,	D_ELR_EL3,
+//	"LR_EL0",	LSPREG,	D_LR_EL0,
+	"DAIF",	LSPREG,	D_DAIF,
+	"CurrentEL",	LSPREG,	D_CurrentEL,
+	"SP_EL0",	LSPREG,	D_SP_EL0,
+//	"SP_EL1",	LSPREG,	D_SP_EL1,
+//	"SP_EL2",	LSPREG,	D_SP_EL2,
+	"SPSel",	LSPREG,	D_SPSel,
+//	"SPSR_abt",	LSPREG,	D_SPSR_abt,	
+	"SPSR_EL1",	LSPREG,	D_SPSR_EL1,
+	"SPSR_EL2",	LSPREG,	D_SPSR_EL2,
+//	"SPSR_EL3",	LSPREG,	D_SPSR_EL3,
+//	"SPSR_fiq",	LSPREG,	D_SPSR_fiq,
+//	"SPSR_ieq",	LSPREG,	D_SPSR_ieq,
+//	"SPSR_und",	LSPREG,	D_SPSR_und,
+	"DAIFSet",	LSPREG,	D_DAIFSet,
+	"DAIFClr",	LSPREG,	D_DAIFClr,
+
+	"EQ",		LCOND,	0,
+	"NE",		LCOND,	1,
+	"CS",		LCOND,	2,
+	"HS",		LCOND,	2,
+	"CC",	LCOND,	3,
+	"LO",		LCOND,	3,
+	"MI",		LCOND,	4,
+	"PL",		LCOND,	5,
+	"VS",		LCOND,	6,
+	"VC",	LCOND,	7,
+	"HI",		LCOND,	8,
+	"LS",		LCOND,	9,
+	"GE",		LCOND,	10,
+	"LT",		LCOND,	11,
+	"GT",		LCOND,	12,
+	"LE",		LCOND,	13,
+	"AL",		LCOND,	14,
+
+	".UXTB",	LEXT,	0,
+	".UXTH",	LEXT,	1,
+	".UXTW",	LEXT,	2,
+	".UXTX",	LEXT,	3,
+	".SXTB",	LEXT,	4,
+	".SXTH",	LEXT,	5,
+	".SXTW",	LEXT,	6,
+	".SXTX",	LEXT,	7,
+
+	".UB",	LEXT,	0,
+	".UH",	LEXT,	1,
+	".UW",	LEXT,	2,
+	".UX",	LEXT,	3,
+	".SB",	LEXT,	4,
+	".SH",	LEXT,	5,
+	".SW",	LEXT,	6,
+	".SX",	LEXT,	7,
+
+	"@",		LAT,	0,
+
+	"ADC",	LTYPE1,	AADC,
+	"ADCS",	LTYPE1,	AADCS,
+	"ADCSW",	LTYPE1,	AADCSW,
+	"ADCW",	LTYPE1,	AADCW,
+	"ADD",	LTYPE1,	AADD,
+	"ADDS",	LTYPE1,	AADDS,
+	"ADDSW",	LTYPE1,	AADDSW,
+	"ADDW",	LTYPE1,	AADDW,
+	"ADR",	LTYPEV,	AADR,
+	"ADRP",	LTYPEV,	AADRP,
+	"AND",	LTYPE1,	AAND,
+	"ANDS",	LTYPE1,	AANDS,
+	"ANDSW",	LTYPE1,	AANDSW,
+	"ANDW",	LTYPE1,	AANDW,
+	"ASR",	LTYPE1,	AASR,
+	"ASRW",	LTYPE1,	AASRW,
+	"AT",	LTYPEN,	AAT,
+	"BFI",	LTYPEY,	ABFI,
+	"BFIW",	LTYPEY,	ABFIW,
+	"BFM",	LTYPEY,	ABFM,
+	"BFMW",	LTYPEY,	ABFMW,
+	"BFXIL",	LTYPEY,	ABFXIL,
+	"BFXILW",	LTYPEY,	ABFXILW,
+	"BIC",	LTYPE1,	ABIC,
+	"BICS",	LTYPE1,	ABICS,
+	"BICSW",	LTYPE1,	ABICSW,
+	"BICW",	LTYPE1,	ABICW,
+	"BRK",	LTYPE6,	ABRK,
+	"CBNZ",	LTYPE8,	ACBNZ,
+	"CBNZW",	LTYPE8,	ACBNZW,
+	"CBZ",	LTYPE8,	ACBZ,
+	"CBZW",	LTYPE8,	ACBZW,
+	"CCMN",	LTYPEU,	ACCMN,
+	"CCMNW",	LTYPEU,	ACCMNW,
+	"CCMP",	LTYPEU,	ACCMP,
+	"CCMPW",	LTYPEU,	ACCMPW,
+	"CINC",	LTYPES,	ACINC,
+	"CINCW",	LTYPES,	ACINCW,
+	"CINV",	LTYPES,	ACINV,
+	"CINVW",	LTYPES,	ACINVW,
+	"CLREX",	LTYPE6,	ACLREX,
+	"CLS",	LTYPE2,	ACLS,
+	"CLSW",	LTYPE2,	ACLSW,
+	"CLZ",	LTYPE2,	ACLZ,
+	"CLZW",	LTYPE2,	ACLZW,
+	"CMN",	LTYPE7,	ACMN,
+	"CMNW",	LTYPE7,	ACMNW,
+	"CMP",	LTYPE7,	ACMP,
+	"CMPW",	LTYPE7,	ACMPW,
+	"CNEG",	LTYPES,	ACNEG,
+	"CNEGW",	LTYPES,	ACNEGW,
+	"CRC32B",	LTYPE1,	ACRC32B,
+	"CRC32CB",	LTYPE1,	ACRC32CB,
+	"CRC32CH",	LTYPE1,	ACRC32CH,
+	"CRC32CW",	LTYPE1,	ACRC32CW,
+	"CRC32CX",	LTYPE1,	ACRC32CX,
+	"CRC32H",	LTYPE1,	ACRC32H,
+	"CRC32W",	LTYPE1,	ACRC32W,
+	"CRC32X",	LTYPE1,	ACRC32X,
+	"CSEL",	LTYPES,	ACSEL,
+	"CSELW",	LTYPES,	ACSELW,
+	"CSET",	LTYPER,	ACSET,
+	"CSETM",	LTYPER,	ACSETM,
+	"CSETMW",	LTYPER,	ACSETMW,
+	"CSETW",	LTYPER,	ACSETW,
+	"CSINC",	LTYPES,	ACSINC,
+	"CSINCW",	LTYPES,	ACSINCW,
+	"CSINV",	LTYPES,	ACSINV,
+	"CSINVW",	LTYPES,	ACSINVW,
+	"CSNEG",	LTYPES,	ACSNEG,
+	"CSNEGW",	LTYPES,	ACSNEGW,
+	"DC",	LTYPEN,	ADC,
+	"DCPS1",	LTYPE6,	ADCPS1,
+	"DCPS2",	LTYPE6,	ADCPS2,
+	"DCPS3",	LTYPE6,	ADCPS3,
+	"DMB",	LDMB,	ADMB,
+	"DRPS",	LTYPE6,	ADRPS,
+	"DSB",	LDMB,	ADSB,
+	"EON",	LTYPE1,	AEON,
+	"EONW",	LTYPE1,	AEONW,
+	"EOR",	LTYPE1,	AEOR,
+	"EORW",	LTYPE1,	AEORW,
+	"ERET",	LTYPE0,	AERET,
+	"EXTR",	LTYPEP,	AEXTR,
+	"EXTRW",	LTYPEP,	AEXTRW,
+	"HINT",	LDMB,	AHINT,
+	"HLT",	LTYPE6,	AHLT,
+	"HVC",	LTYPE6,	AHVC,
+	"IC",	LTYPEN,	AIC,
+	"ISB",	LDMB,	AISB,
+	"LSL",	LTYPE1,	ALSL,
+	"LSLW",	LTYPE1,	ALSLW,
+	"LSR",	LTYPE1,	ALSR,
+	"LSRW",	LTYPE1,	ALSRW,
+	"MADD",	LTYPEM,	AMADD,
+	"MADDW",	LTYPEM,	AMADDW,
+	"MNEG",	LTYPE1,	AMNEG,
+	"MNEGW",	LTYPE1,	AMNEGW,
+	"MRS",	LTYPE3,	AMRS,
+	"MSR",	LTYPE3,	AMSR,
+	"MSUB",	LTYPEM,	AMSUB,
+	"MSUBW",	LTYPEM,	AMSUBW,
+	"MUL",	LTYPE1,	AMUL,
+	"MULW",	LTYPE1,	AMULW,
+	"MVN",	LTYPE1,	AMVN,
+	"MVNW",	LTYPE1,	AMVNW,
+	"NEG",	LTYPE1,	ANEG,
+	"NEGS",	LTYPE1,	ANEGS,
+	"NEGSW",	LTYPE1,	ANEGSW,
+	"NEGW",	LTYPE1,	ANEGW,
+	"NGC",	LTYPE2,	ANGC,
+	"NGCS",	LTYPE2,	ANGCS,
+	"NGCSW",	LTYPE2,	ANGCSW,
+	"NGCW",	LTYPE2,	ANGCW,
+	"ORN",	LTYPE1,	AORN,
+	"ORNW",	LTYPE1,	AORNW,
+	"ORR",	LTYPE1,	AORR,
+	"ORRW",	LTYPE1,	AORRW,
+	"PRFM",	LTYPE1,	APRFM,
+	"PRFUM",	LTYPE1,	APRFUM,
+	"RBIT",	LTYPE2,	ARBIT,
+	"RBITW",	LTYPE2,	ARBITW,
+	"REM",	LTYPE1, AREM,
+	"REMW",	LTYPE1,	AREMW,
+	"RET",	LTYPEA,	ARET,
+	"REV",	LTYPE2,	AREV,
+	"REV16",	LTYPE2,	AREV16,
+	"REV16W",	LTYPE2,	AREV16W,
+	"REV32",	LTYPE2,	AREV32,
+	"REVW",	LTYPE2,	AREVW,
+	"ROR",	LTYPE1,	AROR,
+	"RORW",	LTYPE1,	ARORW,
+	"SBC",	LTYPE1,	ASBC,
+	"SBCS",	LTYPE1,	ASBCS,
+	"SBCSW",	LTYPE1,	ASBCSW,
+	"SBCW",	LTYPE1,	ASBCW,
+	"SBFIZ",	LTYPEY,	ASBFIZ,
+	"SBFIZW",	LTYPEY,	ASBFIZW,
+	"SBFM",	LTYPEY,	ASBFM,
+	"SBFMW",	LTYPEY,	ASBFMW,
+	"SBFX",	LTYPEY,	ASBFX,
+	"SBFXW",	LTYPEY,	ASBFXW,
+	"SDIV",	LTYPE1,	ASDIV,
+	"SDIVW",	LTYPE1,	ASDIVW,
+	"SEV",	LTYPE0,	ASEV,
+	"SEVL",	LTYPE0,	ASEVL,
+	"SMADDL",	LTYPEM,	ASMADDL,
+	"SMC",	LTYPE6,	ASMC,
+	"SMNEGL",	LTYPE1,	ASMNEGL,
+	"SMSUBL",	LTYPEM,	ASMSUBL,
+	"SMULH",	LTYPE1,	ASMULH,
+	"SMULL",	LTYPE1,	ASMULL,
+	"STLR",	LSTXR,	ASTLR,
+	"STLRB",	LSTXR,	ASTLRB,
+	"STLRH",	LSTXR,	ASTLRH,
+	"STLRW",	LSTXR,	ASTLRW,
+	"STLXP",	LSTXR,	ASTLXP,
+	"STLXR",	LSTXR,	ASTLXR,
+	"STLXRB",	LSTXR,	ASTLXRB,
+	"STLXRH",	LSTXR,	ASTLXRH,
+	"STLXRW",	LSTXR,	ASTLXRW,
+	"STXR",	LSTXR,	ASTXR,
+	"STXRB",	LSTXR,	ASTXRB,
+	"STXRH",	LSTXR,	ASTXRH,
+	"STXP",	LSTXR,	ASTXP,
+	"STXPW",	LSTXR,	ASTXPW,
+	"STXRW",	LSTXR,	ASTXRW,
+	"SUB",	LTYPE1,	ASUB,
+	"SUBS",	LTYPE1,	ASUBS,
+	"SUBSW",	LTYPE1,	ASUBSW,
+	"SUBW",	LTYPE1,	ASUBW,
+	"SVC",	LTYPE6,	ASVC,
+	"SXTB",	LTYPE2,	ASXTB,
+	"SXTBW",	LTYPE2,	ASXTBW,
+	"SXTH",	LTYPE2,	ASXTH,
+	"SXTHW",	LTYPE2,	ASXTHW,
+	"SXTW",	LTYPE2,	ASXTW,
+	"SYS",	LTYPEN,	ASYS,
+	"SYSL",	LTYPEO,	ASYSL,
+	"TBNZ",	LTYPET,	ATBNZ,
+	"TBZ",	LTYPET,	ATBZ,
+	"TLBI",	LTYPEN,	ATLBI,
+	"TST",	LTYPE7,	ATST,
+	"TSTW",	LTYPE7,	ATSTW,
+	"UBFIZ",	LTYPEY,	AUBFIZ,
+	"UBFIZW",	LTYPEY,	AUBFIZW,
+	"UBFM",	LTYPEY,	AUBFM,
+	"UBFMW",	LTYPEY,	AUBFMW,
+	"UBFX",	LTYPEY,	AUBFX,
+	"UBFXW",	LTYPEY,	AUBFXW,
+	"UDIV",	LTYPE1,	AUDIV,
+	"UDIVW",	LTYPE1,	AUDIVW,
+	"UMADDL",	LTYPEM,	AUMADDL,
+	"UMNEGL",	LTYPE1,	AUMNEGL,
+	"UMSUBL",	LTYPEM,	AUMSUBL,
+	"UMULH",	LTYPE1,	AUMULH,
+	"UMULL",	LTYPE1,	AUMULL,
+	"UREM",		LTYPE1,	AUREM,
+	"UREMW",	LTYPE1,	AUREMW,
+	"UXTB",	LTYPE2,	AUXTB,
+	"UXTH",	LTYPE2,	AUXTH,
+	"UXTBW",	LTYPE2,	AUXTBW,
+	"UXTHW",	LTYPE2,	AUXTHW,
+	"UXTW",		LTYPE2,	AUXTW,
+	"WFE",	LTYPE0,	AWFE,
+	"WFI",	LTYPE0,	AWFI,
+	"YIELD",	LTYPE0,	AYIELD,
+
+	"LDXR",	LTYPE3,	ALDXR,
+	"LDXRB",	LTYPE3,	ALDXRB,
+	"LDXRH",	LTYPE3,	ALDXRH,
+	"LDXRW",	LTYPE3,	ALDXRW,
+
+	"LDAR",	LTYPE3,	ALDAR,
+	"LDARB",	LTYPE3,	ALDARB,
+	"LDARH",	LTYPE3,	ALDARH,
+	"LDARW",	LTYPE3,	ALDARW,
+
+	"LDXP",	LTYPE3,	ALDXP,
+	"LDXPW",	LTYPE3,	ALDXPW,
+	"LDAXP",	LTYPE3,	ALDAXP,
+	"LDAXPW",	LTYPE3,	ALDAXPW,
+
+	"LDAXR",	LTYPE3,	ALDAXR,
+	"LDAXRB",	LTYPE3,	ALDAXRB,
+	"LDAXRH",	LTYPE3,	ALDAXRH,
+	"LDAXRW",	LTYPE3,	ALDAXRW,
+
+	"MOVK",	LMOVK,	AMOVK,
+	"MOVKW",	LMOVK,	AMOVKW,
+	"MOVN",	LMOVK,	AMOVN,
+	"MOVNW",	LMOVK,	AMOVNW,
+	"MOVZ",	LMOVK,	AMOVZ,
+	"MOVZW",	LMOVK,	AMOVZW,
+
+	"MOVB",		LTYPE3, AMOVB,
+	"MOVBU",	LTYPE3, AMOVBU,
+	"MOVH",		LTYPE3, AMOVH,
+	"MOVHU",	LTYPE3, AMOVHU,
+	"MOVW",		LTYPE3, AMOVW,
+	"MOVWU",		LTYPE3, AMOVWU,
+	"MOV",		LTYPE3, AMOV,
+
+	"MOVP",	LTYPEJ,	AMOVP,
+	"MOVPD",	LTYPEJ,	AMOVPD,
+	"MOVPQ",	LTYPEJ,	AMOVPQ,
+	"MOVPS",	LTYPEJ,	AMOVPS,
+	"MOVPSW",	LTYPEJ,	AMOVPSW,
+	"MOVPW",	LTYPEJ,	AMOVPW,
+
+	"MOVNP",	LTYPEJ,	AMOVNP,
+	"MOVNPW",	LTYPEJ,	AMOVNPW,
+
+	"FMOVD",		LTYPE3, AFMOVD,
+	"FMOVS",		LTYPE3, AFMOVS,
+
+	"SCVTFD",	LTYPE3,	ASCVTFD,
+	"SCVTFS",	LTYPE3,	ASCVTFS,
+	"SCVTFWD",	LTYPE3,	ASCVTFWD,
+	"SCVTFWS",	LTYPE3,	ASCVTFWS,
+	"UCVTFD",	LTYPE3,	AUCVTFD,
+	"UCVTFS",	LTYPE3,	AUCVTFS,
+	"UCVTFWD",	LTYPE3,	AUCVTFWD,
+	"UCVTFWS",	LTYPE3,	AUCVTFWS,
+
+	"FCVTSD",	LTYPE3,	AFCVTSD,
+	"FCVTDS",	LTYPE3,	AFCVTDS,
+	"FCVTZSD",	LTYPE3,	AFCVTZSD,
+	"FCVTZSDW",	LTYPE3,	AFCVTZSDW,
+	"FCVTZSS",	LTYPE3,	AFCVTZSS,
+	"FCVTZSSW",	LTYPE3,	AFCVTZSSW,
+	"FCVTZUD",	LTYPE3,	AFCVTZUD,
+	"FCVTZUDW",	LTYPE3,	AFCVTZUDW,
+	"FCVTZUS",	LTYPE3,	AFCVTZUS,
+	"FCVTZUSW",	LTYPE3,	AFCVTZUSW,
+
+	"FCMPS",		LTYPEL, AFCMPS,
+	"FCMPD",		LTYPEL, AFCMPD,
+	"FCMPES",		LTYPEL, AFCMPES,
+	"FCMPED",	LTYPEL, AFCMPED,
+	"FCCMPS",	LTYPEF, AFCCMPS,
+	"FCCMPD",	LTYPEF, AFCCMPD,
+	"FCCMPES",	LTYPEF, AFCCMPES,
+	"FCCMPED",	LTYPEF, AFCCMPED,
+	"FADDS",		LTYPEK,	AFADDS,
+	"FADDD",		LTYPEK,	AFADDD,
+	"FSUBS",		LTYPEK,	AFSUBS,
+	"FSUBD",		LTYPEK,	AFSUBD,
+	"FMULS",		LTYPEK,	AFMULS,
+	"FMULD",		LTYPEK,	AFMULD,
+	"FDIVS",		LTYPEK,	AFDIVS,
+	"FDIVD",		LTYPEK,	AFDIVD,
+
+	"FCSELS",	LFCSEL,	AFCSELS,
+	"FCSELD",	LFCSEL,	AFCSELD,
+	"FMAXS",	LTYPEK,	AFMAXS,
+	"FMINS",	LTYPEK,	AFMINS,
+	"FMAXD",	LTYPEK,	AFMAXD,
+	"FMIND",	LTYPEK,	AFMIND,
+	"FMAXNMS",	LTYPEK,	AFMAXNMS,
+	"FMAXNMD",	LTYPEK,	AFMAXNMD,
+	"FMINNMS",	LTYPEK,	AFMINNMS,
+	"FMINNMD",	LTYPEK,	AFMINNMD,
+	"FNMULS",	LTYPEK,	AFNMULS,
+	"FNMULD",	LTYPEK,	AFNMULD,
+	"FRINTNS",	LTYPE3,	AFRINTNS,
+	"FRINTND",	LTYPE3,	AFRINTND,
+	"FRINTPS",	LTYPE3,	AFRINTPS,
+	"FRINTPD",	LTYPE3,	AFRINTPD,
+	"FRINTMS",	LTYPE3,	AFRINTMS,
+	"FRINTMD",	LTYPE3,	AFRINTMD,
+	"FRINTZS",	LTYPE3,	AFRINTZS,
+	"FRINTZD",	LTYPE3,	AFRINTZD,
+	"FRINTAS",	LTYPE3,	AFRINTAS,
+	"FRINTAD",	LTYPE3,	AFRINTAD,
+	"FRINTXS",	LTYPE3,	AFRINTXS,
+	"FRINTXD",	LTYPE3,	AFRINTXD,
+	"FRINTIS",	LTYPE3,	AFRINTIS,
+	"FRINTID",	LTYPE3,	AFRINTID,
+	"FMADDS",	LTYPEM,	AFMADDS,
+	"FMADDD",	LTYPEM,	AFMADDD,
+	"FMSUBS",	LTYPEM,	AFMSUBS,
+	"FMSUBD",	LTYPEM,	AFMSUBD,
+	"FNMADDS",	LTYPEM,	AFNMADDS,
+	"FNMADDD",	LTYPEM,	AFNMADDD,
+	"FNMSUBS",	LTYPEM,	AFNMSUBS,
+	"FNMSUBD",	LTYPEM,	AFNMSUBD,
+
+	"FABSS",	LTYPE3, 	AFABSS,
+	"FABSD",	LTYPE3,	AFABSD,
+	"FNEGS",	LTYPE3,	AFNEGS,
+	"FNEGD",	LTYPE3,	AFNEGD,
+	"FSQRTS",	LTYPE3,	AFSQRTS,
+	"FSQRTD",	LTYPE3,	AFSQRTD,
+	"FCVTDH",	LTYPE3,	AFCVTDH,
+	"FCVTHS",	LTYPE3,	AFCVTHS,
+	"FCVTHD",	LTYPE3,	AFCVTHD,
+	"FCVTSH",	LTYPE3,	AFCVTSH,
+
+	"AESD",	LTYPEW,	AAESD,
+	"AESE",	LTYPEW,	AAESE,
+	"AESIMC",	LTYPEW,	AAESIMC,
+	"AESMC",	LTYPEW,	AAESMC,
+	"SHA1C",	LTYPEW,	ASHA1C,
+	"SHA1H",	LTYPEW,	ASHA1H,
+	"SHA1M",	LTYPEW,	ASHA1M,
+	"SHA1P",	LTYPEW,	ASHA1P,
+	"SHA1SU0",	LTYPEW,	ASHA1SU0,
+	"SHA1SU1",	LTYPEW,	ASHA1SU1,
+	"SHA256H",	LTYPEW,	ASHA256H,
+	"SHA256H2",	LTYPEW,	ASHA256H2,
+	"SHA256SU0",	LTYPEW,	ASHA256SU0,
+	"SHA256SU1",	LTYPEW,	ASHA256SU1,
+
+	"B",		LTYPE4, AB,
+	"BL",		LTYPE4, ABL,
+
+	"BEQ",		LTYPE5,	ABEQ,
+	"BNE",		LTYPE5,	ABNE,
+	"BCS",		LTYPE5,	ABCS,
+	"BHS",		LTYPE5,	ABHS,
+	"BCC",		LTYPE5,	ABCC,
+	"BLO",		LTYPE5,	ABLO,
+	"BMI",		LTYPE5,	ABMI,
+	"BPL",		LTYPE5,	ABPL,
+	"BVS",		LTYPE5,	ABVS,
+	"BVC",		LTYPE5,	ABVC,
+	"BHI",		LTYPE5,	ABHI,
+	"BLS",		LTYPE5,	ABLS,
+	"BGE",		LTYPE5,	ABGE,
+	"BLT",		LTYPE5,	ABLT,
+	"BGT",		LTYPE5,	ABGT,
+	"BLE",		LTYPE5,	ABLE,
+	"BCASE",	LTYPE5,	ABCASE,
+
+	"TEXT",		LTYPEB, ATEXT,
+	"GLOBL",	LTYPEB, AGLOBL,
+	"DATA",		LTYPEC, ADATA,
+	"CASE",		LTYPED, ACASE,
+	"END",		LTYPEE, AEND,
+	"WORD",		LTYPEH, AWORD,
+	"DWORD",		LTYPEH, ADWORD,
+	"NOP",		LTYPEQ, ANOP,
+	"RETURN",	LTYPEA,	ARETURN,
+	0
+};
+
+void
+cinit(void)
+{
+	Sym *s;
+	int i;
+
+	nullgen.sym = S;
+	nullgen.offset = 0;
+	nullgen.type = D_NONE;
+	nullgen.name = D_NONE;
+	nullgen.reg = NREG;
+	nullgen.xreg = NREG;
+	if(FPCHIP)
+		nullgen.dval = 0;
+	for(i=0; i<sizeof(nullgen.sval); i++)
+		nullgen.sval[i] = 0;
+
+	nerrors = 0;
+	iostack = I;
+	iofree = I;
+	peekc = IGN;
+	nhunk = 0;
+	for(i=0; i<NHASH; i++)
+		hash[i] = S;
+	for(i=0; itab[i].name; i++) {
+		s = slookup(itab[i].name);
+		if(s->value != 0)
+			yyerror("internal: duplicate %s", s->name);
+		s->type = itab[i].type;
+		s->value = itab[i].value;
+	}
+
+	pathname = allocn(pathname, 0, 100);
+	if(getwd(pathname, 99) == 0) {
+		pathname = allocn(pathname, 100, 900);
+		if(getwd(pathname, 999) == 0)
+			strcpy(pathname, "/???");
+	}
+}
+
+void
+syminit(Sym *s)
+{
+
+	s->type = LNAME;
+	s->value = 0;
+}
+
+void
+cclean(void)
+{
+
+	outcode(AEND, &nullgen, NREG, &nullgen);
+	Bflush(&obuf);
+}
+
+void
+zname(char *n, int t, int s)
+{
+
+	Bputc(&obuf, ANAME);
+	Bputc(&obuf, ANAME>>8);
+	Bputc(&obuf, t);	/* type */
+	Bputc(&obuf, s);	/* sym */
+	while(*n) {
+		Bputc(&obuf, *n);
+		n++;
+	}
+	Bputc(&obuf, 0);
+}
+
+void
+zaddr(Gen *a, int s)
+{
+	long l;
+	int i;
+	char *n;
+	Ieee e;
+
+	if(a->type == D_CONST){
+		l = a->offset;
+		if((vlong)l != a->offset)
+			a->type = D_DCONST;
+	}
+	Bputc(&obuf, a->type);
+	Bputc(&obuf, a->reg);
+	Bputc(&obuf, s);
+	Bputc(&obuf, a->name);
+	switch(a->type) {
+	default:
+		print("unknown type %d\n", a->type);
+		exits("arg");
+
+	case D_NONE:
+	case D_REG:
+	case D_SP:
+	case D_FREG:
+	case D_VREG:
+	case D_COND:
+		break;
+
+	case D_DCONST:
+		l = a->offset;
+		Bputc(&obuf, l);
+		Bputc(&obuf, l>>8);
+		Bputc(&obuf, l>>16);
+		Bputc(&obuf, l>>24);
+		l = a->offset>>32;
+		Bputc(&obuf, l);
+		Bputc(&obuf, l>>8);
+		Bputc(&obuf, l>>16);
+		Bputc(&obuf, l>>24);
+		break;
+
+	case D_OREG:
+	case D_XPRE:
+	case D_XPOST:
+	case D_CONST:
+	case D_BRANCH:
+	case D_SHIFT:
+	case D_EXTREG:
+	case D_ROFF:
+	case D_SPR:
+		l = a->offset;
+		Bputc(&obuf, l);
+		Bputc(&obuf, l>>8);
+		Bputc(&obuf, l>>16);
+		Bputc(&obuf, l>>24);
+		break;
+
+	case D_SCONST:
+		n = a->sval;
+		for(i=0; i<NSNAME; i++) {
+			Bputc(&obuf, *n);
+			n++;
+		}
+		break;
+
+	case D_FCONST:
+		ieeedtod(&e, a->dval);
+		Bputc(&obuf, e.l);
+		Bputc(&obuf, e.l>>8);
+		Bputc(&obuf, e.l>>16);
+		Bputc(&obuf, e.l>>24);
+		Bputc(&obuf, e.h);
+		Bputc(&obuf, e.h>>8);
+		Bputc(&obuf, e.h>>16);
+		Bputc(&obuf, e.h>>24);
+		break;
+	}
+}
+
+static int
+outsim(Gen *g)
+{
+	Sym *s;
+	int sno, t;
+
+	s = g->sym;
+	if(s == S)
+		return 0;
+	sno = s->sym;
+	if(sno < 0 || sno >= NSYM)
+		sno = 0;
+	t = g->name;
+	if(h[sno].type == t && h[sno].sym == s)
+		return sno;
+	zname(s->name, t, sym);
+	s->sym = sym;
+	h[sym].sym = s;
+	h[sym].type = t;
+	sno = sym;
+	sym++;
+	if(sym >= NSYM)
+		sym = 1;
+	return sno;
+}
+
+void
+outcode(int a, Gen *g1, int reg, Gen *g2)
+{
+	int sf, st;
+
+	if(a != AGLOBL && a != ADATA)
+		pc++;
+	if(pass == 1)
+		return;
+	if(g1->xreg != NREG) {
+		if(reg != NREG || g2->xreg != NREG)
+			yyerror("bad addressing modes");
+		reg = g1->xreg;
+	} else if(g2->xreg != NREG) {
+		if(reg != NREG)
+			yyerror("bad addressing modes");
+		reg = g2->xreg;
+	}
+	do{
+		sf = outsim(g1);
+		st = outsim(g2);
+	} while(sf != 0 && st == sf);
+	Bputc(&obuf, a);
+	Bputc(&obuf, a>>8);
+	Bputc(&obuf, reg);
+	Bputc(&obuf, lineno);
+	Bputc(&obuf, lineno>>8);
+	Bputc(&obuf, lineno>>16);
+	Bputc(&obuf, lineno>>24);
+	zaddr(g1, sf);
+	zaddr(g2, st);
+}
+
+void
+outcode4(int a, Gen *g1, int reg, Gen *g2, Gen *g3)
+{
+	int s1, s2, s3, flag;
+
+	if(a != AGLOBL && a != ADATA)
+		pc++;
+	if(pass == 1)
+		return;
+	do{
+		s1 = outsim(g1);
+		s2 = outsim(g2);
+		s3 = outsim(g3);
+	} while(s1 && (s2 && s1 == s2 || s3 && s1 == s3) || s2 && (s3 && s2 == s3));
+	flag = 0;
+	if(g2->type != D_NONE)
+		flag = 0x40;	/* flags extra operand */
+	Bputc(&obuf, a);
+	Bputc(&obuf, a>>8);
+	Bputc(&obuf, reg | flag);
+	Bputc(&obuf, lineno);
+	Bputc(&obuf, lineno>>8);
+	Bputc(&obuf, lineno>>16);
+	Bputc(&obuf, lineno>>24);
+	zaddr(g1, s1);
+	if(flag)
+		zaddr(g2, s2);
+	zaddr(g3, s3);
+}
+
+void
+outhist(void)
+{
+	Gen g;
+	Hist *h;
+	char *p, *q, *op, c;
+	int n;
+
+	g = nullgen;
+	c = pathchar();
+	for(h = hist; h != H; h = h->link) {
+		p = h->name;
+		op = 0;
+		/* on windows skip drive specifier in pathname */
+		if(systemtype(Windows) && p && p[1] == ':'){
+			p += 2;
+			c = *p;
+		}
+		if(p && p[0] != c && h->offset == 0 && pathname){
+			/* on windows skip drive specifier in pathname */
+			if(systemtype(Windows) && pathname[1] == ':') {
+				op = p;
+				p = pathname+2;
+				c = *p;
+			} else if(pathname[0] == c){
+				op = p;
+				p = pathname;
+			}
+		}
+		while(p) {
+			q = strchr(p, c);
+			if(q) {
+				n = q-p;
+				if(n == 0){
+					n = 1;	/* leading "/" */
+					*p = '/';	/* don't emit "\" on windows */
+				}
+				q++;
+			} else {
+				n = strlen(p);
+				q = 0;
+			}
+			if(n) {
+				Bputc(&obuf, ANAME);
+				Bputc(&obuf, ANAME>>8);
+				Bputc(&obuf, D_FILE);	/* type */
+				Bputc(&obuf, 1);	/* sym */
+				Bputc(&obuf, '<');
+				Bwrite(&obuf, p, n);
+				Bputc(&obuf, 0);
+			}
+			p = q;
+			if(p == 0 && op) {
+				p = op;
+				op = 0;
+			}
+		}
+		g.offset = h->offset;
+
+		Bputc(&obuf, AHISTORY);
+		Bputc(&obuf, AHISTORY>>8);
+		Bputc(&obuf, 0);
+		Bputc(&obuf, h->line);
+		Bputc(&obuf, h->line>>8);
+		Bputc(&obuf, h->line>>16);
+		Bputc(&obuf, h->line>>24);
+		zaddr(&nullgen, 0);
+		zaddr(&g, 0);
+	}
+}
+
+#include "../cc/lexbody"
+#include "../cc/macbody"
+#include "../cc/compat"
--- /dev/null
+++ b/sys/src/cmd/7a/mkfile
@@ -1,0 +1,19 @@
+</$objtype/mkfile
+
+TARG=7a
+OFILES=\
+	y.tab.$O\
+	lex.$O\
+
+HFILES=\
+	../7c/7.out.h\
+	y.tab.h\
+	a.h\
+
+YFILES=a.y\
+
+BIN=/$objtype/bin
+< /sys/src/cmd/mkone
+YFLAGS=-D1 -d
+
+lex.$O:	../cc/macbody ../cc/lexbody
--- /dev/null
+++ b/sys/src/cmd/7c/7.out.h
@@ -1,0 +1,517 @@
+/*
+ * arm64
+ */
+
+#define	NSNAME		8
+#define	NSYM		50
+#define	NREG		32
+
+#define NOPROF		(1<<0)
+#define DUPOK		(1<<1)
+
+#define	REGRET		0
+#define	REGARG		0
+/* R1 to R7 are potential parameter/return registers */
+#define	REGIRL		8	/* indirect result location (TO DO) */
+/* compiler allocates R9 up as temps */
+/* compiler allocates register variables R10 up */
+#define	REGMIN		9
+#define	REGMAX		15
+#define	REGIP0		16
+#define	REGIP1		17
+#define	REGTMP		REGIP1
+/* compiler allocates external registers R27 down */
+#define	REGEXT		27
+#define	REGSB		28
+#define	REGFP		29
+#define	REGLINK		30
+#define	REGSP		31
+#define	REGZERO		31
+
+#define	NFREG		32
+#define	FREGRET		0
+#define	FREGMIN		7
+#define	FREGEXT		15
+
+/* compiler allocates register variables F0 up */
+/* compiler allocates external registers F15 down */
+
+enum	as
+{
+	AXXX,
+
+	AADC,
+	AADCS,
+	AADCSW,
+	AADCW,
+	AADD,
+	AADDS,
+	AADDSW,
+	AADDW,
+	AADR,
+	AADRP,
+	AAND,
+	AANDS,
+	AANDSW,
+	AANDW,
+	AASR,
+	AASRW,
+	AAT,
+	AB,
+	ABFI,
+	ABFIW,
+	ABFM,
+	ABFMW,
+	ABFXIL,
+	ABFXILW,
+	ABIC,
+	ABICS,
+	ABICSW,
+	ABICW,
+	ABL,
+	ABRK,
+	ACBNZ,
+	ACBNZW,
+	ACBZ,
+	ACBZW,
+	ACCMN,
+	ACCMNW,
+	ACCMP,
+	ACCMPW,
+	ACINC,
+	ACINCW,
+	ACINV,
+	ACINVW,
+	ACLREX,
+	ACLS,
+	ACLSW,
+	ACLZ,
+	ACLZW,
+	ACMN,
+	ACMNW,
+	ACMP,
+	ACMPW,
+	ACNEG,
+	ACNEGW,
+	ACRC32B,
+	ACRC32CB,
+	ACRC32CH,
+	ACRC32CW,
+	ACRC32CX,
+	ACRC32H,
+	ACRC32W,
+	ACRC32X,
+	ACSEL,
+	ACSELW,
+	ACSET,
+	ACSETM,
+	ACSETMW,
+	ACSETW,
+	ACSINC,
+	ACSINCW,
+	ACSINV,
+	ACSINVW,
+	ACSNEG,
+	ACSNEGW,
+	ADC,
+	ADCPS1,
+	ADCPS2,
+	ADCPS3,
+	ADMB,
+	ADRPS,
+	ADSB,
+	AEON,
+	AEONW,
+	AEOR,
+	AEORW,
+	AERET,
+	AEXTR,
+	AEXTRW,
+	AHINT,
+	AHLT,
+	AHVC,
+	AIC,
+	AISB,
+	ALDAR,
+	ALDARB,
+	ALDARH,
+	ALDARW,
+	ALDAXP,
+	ALDAXPW,
+	ALDAXR,
+	ALDAXRB,
+	ALDAXRH,
+	ALDAXRW,
+	ALDXR,
+	ALDXRB,
+	ALDXRH,
+	ALDXRW,
+	ALDXP,
+	ALDXPW,
+	ALSL,
+	ALSLW,
+	ALSR,
+	ALSRW,
+	AMADD,
+	AMADDW,
+	AMNEG,
+	AMNEGW,
+	AMOVK,
+	AMOVKW,
+	AMOVN,
+	AMOVNW,
+	AMOVZ,
+	AMOVZW,
+	AMRS,
+	AMSR,
+	AMSUB,
+	AMSUBW,
+	AMUL,
+	AMULW,
+	AMVN,
+	AMVNW,
+	ANEG,
+	ANEGS,
+	ANEGSW,
+	ANEGW,
+	ANGC,
+	ANGCS,
+	ANGCSW,
+	ANGCW,
+	ANOP,
+	AORN,
+	AORNW,
+	AORR,
+	AORRW,
+	APRFM,
+	APRFUM,
+	ARBIT,
+	ARBITW,
+	AREM,
+	AREMW,
+	ARET,
+	AREV,
+	AREV16,
+	AREV16W,
+	AREV32,
+	AREVW,
+	AROR,
+	ARORW,
+	ASBC,
+	ASBCS,
+	ASBCSW,
+	ASBCW,
+	ASBFIZ,
+	ASBFIZW,
+	ASBFM,
+	ASBFMW,
+	ASBFX,
+	ASBFXW,
+	ASDIV,
+	ASDIVW,
+	ASEV,
+	ASEVL,
+	ASMADDL,
+	ASMC,
+	ASMNEGL,
+	ASMSUBL,
+	ASMULH,
+	ASMULL,
+	ASTXR,
+	ASTXRB,
+	ASTXRH,
+	ASTXP,
+	ASTXPW,
+	ASTXRW,
+	ASTLP,
+	ASTLPW,
+	ASTLR,
+	ASTLRB,
+	ASTLRH,
+	ASTLRW,
+	ASTLXP,
+	ASTLXPW,
+	ASTLXR,
+	ASTLXRB,
+	ASTLXRH,
+	ASTLXRW,
+	ASUB,
+	ASUBS,
+	ASUBSW,
+	ASUBW,
+	ASVC,
+	ASXTB,
+	ASXTBW,
+	ASXTH,
+	ASXTHW,
+	ASXTW,
+	ASYS,
+	ASYSL,
+	ATBNZ,
+	ATBZ,
+	ATLBI,
+	ATST,
+	ATSTW,
+	AUBFIZ,
+	AUBFIZW,
+	AUBFM,
+	AUBFMW,
+	AUBFX,
+	AUBFXW,
+	AUDIV,
+	AUDIVW,
+	AUMADDL,
+	AUMNEGL,
+	AUMSUBL,
+	AUMULH,
+	AUMULL,
+	AUREM,
+	AUREMW,
+	AUXTB,
+	AUXTH,
+	AUXTW,
+	AUXTBW,
+	AUXTHW,
+	AWFE,
+	AWFI,
+	AYIELD,
+
+	AMOVB,
+	AMOVBU,
+	AMOVH,
+	AMOVHU,
+	AMOVW,
+	AMOVWU,
+	AMOV,
+	AMOVNP,
+	AMOVNPW,
+	AMOVP,
+	AMOVPD,
+	AMOVPQ,
+	AMOVPS,
+	AMOVPSW,
+	AMOVPW,
+
+/* 
+ * Do not reorder or fragment the conditional branch 
+ * opcodes, or the predication code will break 
+ */ 
+	ABEQ,
+	ABNE,
+	ABCS,
+	ABHS,
+	ABCC,
+	ABLO,
+	ABMI,
+	ABPL,
+	ABVS,
+	ABVC,
+	ABHI,
+	ABLS,
+	ABGE,
+	ABLT,
+	ABGT,
+	ABLE,
+
+	AFABSD,
+	AFABSS,
+	AFADDD,
+	AFADDS,
+	AFCCMPD,
+	AFCCMPED,
+	AFCCMPS,
+	AFCCMPES,
+	AFCMPD,
+	AFCMPED,
+	AFCMPES,
+	AFCMPS,
+	AFCVTSD,
+	AFCVTDS,
+	AFCVTZSD,
+	AFCVTZSDW,
+	AFCVTZSS,
+	AFCVTZSSW,
+	AFCVTZUD,
+	AFCVTZUDW,
+	AFCVTZUS,
+	AFCVTZUSW,
+	AFDIVD,
+	AFDIVS,
+	AFMOVD,
+	AFMOVS,
+	AFMULD,
+	AFMULS,
+	AFNEGD,
+	AFNEGS,
+	AFSQRTD,
+	AFSQRTS,
+	AFSUBD,
+	AFSUBS,
+	ASCVTFD,
+	ASCVTFS,
+	ASCVTFWD,
+	ASCVTFWS,
+	AUCVTFD,
+	AUCVTFS,
+	AUCVTFWD,
+	AUCVTFWS,
+
+	ATEXT,
+	ADATA,
+	AGLOBL,
+	AHISTORY,
+	ANAME,
+	AWORD,
+	ADYNT,
+	AINIT,
+	ABCASE,
+	ACASE,
+	ADWORD,
+	ASIGNAME,
+	AGOK,
+	ARETURN,
+	AEND,
+
+	AFCSELS,
+	AFCSELD,
+	AFMAXS,
+	AFMINS,
+	AFMAXD,
+	AFMIND,
+	AFMAXNMS,
+	AFMAXNMD,
+	AFNMULS,
+	AFNMULD,
+	AFRINTNS,
+	AFRINTND,
+	AFRINTPS,
+	AFRINTPD,
+	AFRINTMS,
+	AFRINTMD,
+	AFRINTZS,
+	AFRINTZD,
+	AFRINTAS,
+	AFRINTAD,
+	AFRINTXS,
+	AFRINTXD,
+	AFRINTIS,
+	AFRINTID,
+	AFMADDS,
+	AFMADDD,
+	AFMSUBS,
+	AFMSUBD,
+	AFNMADDS,
+	AFNMADDD,
+	AFNMSUBS,
+	AFNMSUBD,
+	AFMINNMS,
+	AFMINNMD,
+	AFCVTDH,
+	AFCVTHS,
+	AFCVTHD,
+	AFCVTSH,
+
+	AAESD,
+	AAESE,
+	AAESIMC,
+	AAESMC,
+	ASHA1C,
+	ASHA1H,
+	ASHA1M,
+	ASHA1P,
+	ASHA1SU0,
+	ASHA1SU1,
+	ASHA256H,
+	ASHA256H2,
+	ASHA256SU0,
+	ASHA256SU1,
+	
+	ALAST,
+};
+
+/* form offset parameter to SYS; special register number */
+#define	SYSARG5(op0,op1,Cn,Cm,op2)	((op0)<<19|(op1)<<16|(Cn)<<12|(Cm)<<8|(op2)<<5)
+#define	SYSARG4(op1,Cn,Cm,op2)	SYSARG5(0,op1,Cn,Cm,op2)
+
+/* type/name */
+enum
+{
+	D_GOK	= 0,
+	D_NONE,
+
+/* name */
+	D_EXTERN,
+	D_STATIC,
+	D_AUTO,
+	D_PARAM,
+
+/* type */
+	D_BRANCH,
+	D_OREG,		/* offset(reg) */
+	D_XPRE,		/* offset(reg)! - pre-indexed */
+	D_XPOST,		/* (reg)offset! - post-indexed */
+	D_CONST,	/* 32-bit constant */
+	D_DCONST,	/* 64-bit constant */
+	D_FCONST,	/* floating-point constant */
+	D_SCONST,	/* short string constant */
+	D_REG,		/* Rn = Wn or Xn depending on op */
+	D_SP,		/* distinguish REGSP from REGZERO */
+	D_FREG,		/* Fn = Sn or Dn depending on op */
+	D_VREG,		/* Vn = SIMD register */
+	D_SPR,		/* special processor register */
+	D_FILE,
+	D_OCONST,	/* absolute address constant (unused) */
+	D_FILE1,
+
+	D_SHIFT,		/* Rm{, ashift #imm} */
+	D_PAIR,		/* pair of gprs */
+	D_ADDR,		/* address constant (dynamic loading) */
+	D_ADRP,		/* pc-relative addressing, page */
+	D_ADRLO,	/* low-order 12 bits of external reference */
+	D_EXTREG,	/* Rm{, ext #imm} */
+	D_ROFF,		/* register offset Rn+ext(Rm)<<s */
+	D_COND,		/* condition EQ, NE, etc */
+	D_VLANE,		/* Vn lane */
+	D_VSET,		/* set of Vn */
+
+	/* offset iff type is D_SPR */
+	D_DAIF	= SYSARG5(3,3,4,2,1),
+	D_NZCV	= SYSARG5(3,3,4,2,0),
+	D_FPSR	= SYSARG5(3,3,4,4,1),
+	D_FPCR	= SYSARG5(3,3,4,4,0),
+	D_SPSR_EL1 = SYSARG5(3,0,4,0,0),
+	D_ELR_EL1 = SYSARG5(3,0,4,0,1),
+	D_SPSR_EL2 = SYSARG5(3,4,4,0,0),
+	D_ELR_EL2 = SYSARG5(3,4,4,0,1),
+//	D_SPSR_EL3 = SYSARG5(3,x,4,x,x),
+//	D_ELR_EL3 = SYSARG5(3,x,4,x,x),
+//	D_LR_EL0 = SYSARG5(3,x,4,x,x),
+	D_CurrentEL = SYSARG5(3,0,4,2,2),
+	D_SP_EL0 = SYSARG5(3,0,4,1,0),
+//	D_SP_EL1 = SYSARG5(3,x,4,x,x),
+//	D_SP_EL2 = SYSARG5(3,x,4,x,x),
+	D_SPSel	= SYSARG5(3,0,4,2,0),
+//	D_SPSR_abt  = SYSARG5(3,x,4,x,x),
+//	D_SPSR_fiq = SYSARG5(3,x,4,x,x),
+//	D_SPSR_ieq = SYSARG5(3,x,4,x,x),
+//	D_SPSR_und = SYSARG5(3,x,4,x,x),
+	D_DAIFSet = (1<<30)|0,
+	D_DAIFClr = (1<<30)|1
+};
+
+/*
+ * this is the ranlib header
+ */
+#define	SYMDEF	"__.SYMDEF"
+
+/*
+ * this is the simulated IEEE floating point
+ */
+typedef	struct	Ieee	Ieee;
+struct	Ieee
+{
+	long	l;	/* contains ls-man	0xffffffff */
+	long	h;	/* contains sign	0x80000000
+				    exp		0x7ff00000
+				    ms-man	0x000fffff */
+};
--- /dev/null
+++ b/sys/src/cmd/7c/cgen.c
@@ -1,0 +1,1256 @@
+#include "gc.h"
+
+void
+cgen(Node *n, Node *nn)
+{
+	cgenrel(n, nn, 0);
+}
+
+void
+cgenrel(Node *n, Node *nn, int inrel)
+{
+	Node *l, *r;
+	Prog *p1;
+	Node nod, nod1, nod2, nod3, nod4;
+	int o, t;
+	long v, curs;
+
+	if(debug['g']) {
+		prtree(nn, "cgen lhs");
+		prtree(n, "cgen");
+	}
+	if(n == Z || n->type == T)
+		return;
+	if(typesu[n->type->etype]) {
+		sugen(n, nn, n->type->width);
+		return;
+	}
+	l = n->left;
+	r = n->right;
+	o = n->op;
+	if(n->addable >= INDEXED) {
+		if(nn == Z) {
+			switch(o) {
+			default:
+				nullwarn(Z, Z);
+				break;
+			case OINDEX:
+				nullwarn(l, r);
+				break;
+			}
+			return;
+		}
+		gmove(n, nn);
+		return;
+	}
+	curs = cursafe;
+
+	if(n->complex >= FNX)
+	if(l->complex >= FNX)
+	if(r != Z && r->complex >= FNX)
+	switch(o) {
+	default:
+		if(cond(o) && typesu[l->type->etype])
+			break;
+
+		regret(&nod, r);
+		cgen(r, &nod);
+
+		regsalloc(&nod1, r);
+		gopcode(OAS, &nod, Z, &nod1);
+
+		regfree(&nod);
+		nod = *n;
+		nod.right = &nod1;
+		cgen(&nod, nn);
+		return;
+
+	case OFUNC:
+	case OCOMMA:
+	case OANDAND:
+	case OOROR:
+	case OCOND:
+	case ODOT:
+		break;
+	}
+
+	switch(o) {
+	default:
+		diag(n, "unknown op in cgen: %O", o);
+		break;
+
+	case ONEG:
+	case OCOM:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		regalloc(&nod, l, nn);
+		cgen(l, &nod);
+		gopcode(o, &nod, Z, &nod);
+		gmove(&nod, nn);
+		regfree(&nod);
+		break;
+
+	case OAS:
+		if(l->op == OBIT)
+			goto bitas;
+		if(l->addable >= INDEXED && l->complex < FNX) {
+			if(nn != Z || r->addable < INDEXED) {	/* || hardconst(r) */
+				if(r->complex >= FNX && nn == Z)
+					regret(&nod, r);
+				else
+					regalloc(&nod, r, nn);
+				cgen(r, &nod);
+				gmove(&nod, l);
+				if(nn != Z)
+					gmove(&nod, nn);
+				regfree(&nod);
+			} else
+				gmove(r, l);
+			break;
+		}
+		if(l->complex >= r->complex) {
+			/* TO DO: see 6c for OINDEX && immconst(r) */
+			reglcgen(&nod1, l, Z);
+			if(r->addable >= INDEXED) {	/* && !hardconst(r) */
+				gmove(r, &nod1);
+				if(nn != Z)
+					gmove(r, nn);
+				regfree(&nod1);
+				break;
+			}
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+		} else {
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+			reglcgen(&nod1, l, Z);
+		}
+		gmove(&nod, &nod1);
+		regfree(&nod);
+		regfree(&nod1);
+		break;
+
+	bitas:
+		n = l->left;
+		regalloc(&nod, r, nn);
+		if(l->complex >= r->complex) {
+			reglcgen(&nod1, n, Z);
+			cgen(r, &nod);
+		} else {
+			cgen(r, &nod);
+			reglcgen(&nod1, n, Z);
+		}
+		regalloc(&nod2, n, Z);
+		gopcode(OAS, &nod1, Z, &nod2);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+
+	case OBIT:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		bitload(n, &nod, Z, Z, nn);
+		gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		break;
+
+	case ODIV:
+	case OMOD:
+		if(nn != Z)
+		if((t = vlog(r)) >= 0) {
+			/* signed div/mod by constant power of 2 */
+			cgen(l, nn);
+			gopcode(OGE, nodconst(0), nn, Z);
+			p1 = p;
+			if(o == ODIV) {
+				gopcode(OADD, nodconst((1<<t)-1), Z, nn);
+				patch(p1, pc);
+				gopcode(OASHR, nodconst(t), Z, nn);
+			} else {
+				gopcode(ONEG, nn, Z, nn);
+				gopcode(OAND, nodconst((1<<t)-1), Z, nn);
+				gopcode(ONEG, nn, Z, nn);
+				gbranch(OGOTO);
+				patch(p1, pc);
+				p1 = p;
+				gopcode(OAND, nodconst((1<<t)-1), Z, nn);
+				patch(p1, pc);
+			}
+			break;
+		}
+		goto muldiv;
+
+	case OXOR:
+#ifdef NOTYET
+		if(nn != Z)
+		if(r->op == OCONST && r->vconst == -1){
+			cgen(l, nn);
+			gopcode(OCOM, nn, Z, nn);
+			break;
+		}
+#endif
+
+	case OSUB:
+	case OADD:
+	case OAND:
+	case OOR:
+	case OLSHR:
+	case OASHL:
+	case OASHR:
+		/*
+		 * immediate operands
+		 */
+		if(nn != Z)
+		if(r->op == OCONST)
+		if(!typefd[n->type->etype]) {
+			cgen(l, nn);
+			if(r->vconst == 0)
+			if(o != OAND)
+				break;
+			if(nn != Z)
+				gopcode(o, r, Z, nn);
+			break;
+		}
+
+	case OLMUL:
+	case OLDIV:
+	case OLMOD:
+	case OMUL:
+	muldiv:
+		if(nn == Z) {
+			nullwarn(l, r);
+			break;
+		}
+		if(o == OMUL || o == OLMUL) {
+			if(mulcon(n, nn))
+				break;
+		}
+		if(l->complex >= r->complex) {
+			regalloc(&nod, l, nn);
+			cgen(l, &nod);
+			regalloc(&nod1, l, Z);		/* note: l used for type, so shifts work! */
+			cgen(r, &nod1);
+			gopcode(o, &nod1, Z, &nod);
+		} else {
+			regalloc(&nod, l, nn);		/* note: l used for type, so shifts work! */
+			cgen(r, &nod);
+			regalloc(&nod1, l, Z);
+			cgen(l, &nod1);
+			gopcode(o, &nod, &nod1, &nod);
+		}
+		gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		regfree(&nod1);
+		break;
+
+	case OASLSHR:
+	case OASASHL:
+	case OASASHR:
+	case OASAND:
+	case OASADD:
+	case OASSUB:
+	case OASXOR:
+	case OASOR:
+		if(l->op == OBIT)
+			goto asbitop;
+		if(r->op == OCONST)
+		if(!typefd[r->type->etype])
+		if(!typefd[n->type->etype]) {
+			if(l->addable < INDEXED)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+			regalloc(&nod, l, nn);		/* note: l used for type, so shifts work! */
+			gopcode(OAS, &nod2, Z, &nod);
+			gopcode(o, r, Z, &nod);
+			gopcode(OAS, &nod, Z, &nod2);
+	
+			regfree(&nod);
+			if(l->addable < INDEXED)
+				regfree(&nod2);
+			break;
+		}
+
+	case OASLMUL:
+	case OASLDIV:
+	case OASLMOD:
+	case OASMUL:
+	case OASDIV:
+	case OASMOD:
+		if(l->op == OBIT)
+			goto asbitop;
+		if(l->complex >= r->complex) {
+			if(l->addable < INDEXED)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+			regalloc(&nod, n, nn);
+			cgen(r, &nod);
+		} else {
+			regalloc(&nod, n, nn);
+			cgen(r, &nod);
+			if(l->addable < INDEXED)
+				reglcgen(&nod2, l, Z);
+			else
+				nod2 = *l;
+		}
+		regalloc(&nod1, n, Z);
+		gopcode(OAS, &nod2, Z, &nod1);
+		if(nod1.type->etype != nod.type->etype){
+			regalloc(&nod3, &nod, Z);
+			gmove(&nod1, &nod3);
+			regfree(&nod1);
+			nod1 = nod3;
+		}
+		gopcode(o, &nod, &nod1, &nod);
+		gmove(&nod, &nod2);
+		if(nn != Z)
+			gmove(&nod, nn);
+		regfree(&nod);
+		regfree(&nod1);
+		if(l->addable < INDEXED)
+			regfree(&nod2);
+		break;
+
+	asbitop:
+		regalloc(&nod4, n, nn);
+		regalloc(&nod3, r, Z);
+		if(l->complex >= r->complex) {
+			bitload(l, &nod, &nod1, &nod2, &nod4);
+			cgen(r, &nod3);
+		} else {
+			cgen(r, &nod3);
+			bitload(l, &nod, &nod1, &nod2, &nod4);
+		}
+		gmove(&nod, &nod4);
+		gopcode(o, &nod3, Z, &nod4);
+		regfree(&nod3);
+		gmove(&nod4, &nod);
+		regfree(&nod4);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+
+	case OADDR:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		lcgen(l, nn);
+		break;
+
+	case OFUNC:
+		l = uncomma(l);
+		if(l->complex >= FNX) {
+			if(l->op != OIND)
+				diag(n, "bad function call");
+
+			regret(&nod, l->left);
+			cgen(l->left, &nod);
+			regsalloc(&nod1, l->left);
+			gopcode(OAS, &nod, Z, &nod1);
+			regfree(&nod);
+
+			nod = *n;
+			nod.left = &nod2;
+			nod2 = *l;
+			nod2.left = &nod1;
+			nod2.complex = 1;
+			cgen(&nod, nn);
+
+			return;
+		}
+		if(REGARG >= 0)
+			o = reg[REGARG];
+		gargs(r, &nod, &nod1);
+		if(l->addable < INDEXED) {
+			reglcgen(&nod, l, Z);
+			gopcode(OFUNC, Z, Z, &nod);
+			regfree(&nod);
+		} else
+			gopcode(OFUNC, Z, Z, l);
+		if(REGARG >= 0)
+			if(o != reg[REGARG])
+				reg[REGARG]--;
+		if(nn != Z) {
+			regret(&nod, n);
+			gopcode(OAS, &nod, Z, nn);
+			regfree(&nod);
+		}
+		break;
+
+	case OIND:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		regialloc(&nod, n, nn);
+		r = l;
+		while(r->op == OADD)
+			r = r->right;
+		if(sconst(r) && (v = r->vconst+nod.xoffset) >= -4096 && v < 4096) {
+			v = r->vconst;
+			r->vconst = 0;
+			cgen(l, &nod);
+			nod.xoffset += v;
+			r->vconst = v;
+		} else
+			cgen(l, &nod);
+		regind(&nod, n);
+		gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		break;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OLO:
+	case OLS:
+	case OHI:
+	case OHS:
+		if(nn == Z) {
+			nullwarn(l, r);
+			break;
+		}
+		boolgen(n, 1, nn);
+		break;
+
+	case OANDAND:
+	case OOROR:
+		boolgen(n, 1, nn);
+		if(nn == Z)
+			patch(p, pc);
+		break;
+
+	case ONOT:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		boolgen(n, 1, nn);
+		break;
+
+	case OCOMMA:
+		cgen(l, Z);
+		cgen(r, nn);
+		break;
+
+	case OCAST:
+		if(nn == Z) {
+			nullwarn(l, Z);
+			break;
+		}
+		/*
+		 * convert from types l->n->nn
+		 */
+		if(nocast(l->type, n->type) && nocast(n->type, nn->type)) {
+			/* both null, gen l->nn */
+			cgen(l, nn);
+			break;
+		}
+		if(ewidth[n->type->etype] < ewidth[l->type->etype]){
+			if(l->type->etype == TIND && typechlp[n->type->etype])
+				warn(n, "conversion of pointer to shorter integer");
+		}else if(0){
+			if(nocast(n->type, nn->type) || castup(n->type, nn->type)){
+				if(typefd[l->type->etype] != typefd[nn->type->etype])
+					regalloc(&nod, l, nn);
+				else
+					regalloc(&nod, nn, nn);
+				cgen(l, &nod);
+				gmove(&nod, nn);
+				regfree(&nod);
+				break;
+			}
+		}
+		regalloc(&nod, l, nn);
+		cgen(l, &nod);
+		regalloc(&nod1, n, &nod);
+		if(inrel)
+			gmover(&nod, &nod1);
+		else
+			gopcode(OAS, &nod, Z, &nod1);
+		gopcode(OAS, &nod1, Z, nn);
+		regfree(&nod1);
+		regfree(&nod);
+		break;
+
+	case ODOT:
+		sugen(l, nodrat, l->type->width);
+		if(nn != Z) {
+			warn(n, "non-interruptable temporary");
+			nod = *nodrat;
+			if(!r || r->op != OCONST) {
+				diag(n, "DOT and no offset");
+				break;
+			}
+			nod.xoffset += (long)r->vconst;
+			nod.type = n->type;
+			cgen(&nod, nn);
+		}
+		break;
+
+	case OCOND:
+		bcgen(l, 1);
+		p1 = p;
+		cgen(r->left, nn);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		cgen(r->right, nn);
+		patch(p1, pc);
+		break;
+
+	case OPOSTINC:
+	case OPOSTDEC:
+		v = 1;
+		if(l->type->etype == TIND)
+			v = l->type->link->width;
+		if(o == OPOSTDEC)
+			v = -v;
+		if(l->op == OBIT)
+			goto bitinc;
+		if(nn == Z)
+			goto pre;
+
+		if(l->addable < INDEXED)
+			reglcgen(&nod2, l, Z);
+		else
+			nod2 = *l;
+
+		regalloc(&nod, l, nn);
+		gopcode(OAS, &nod2, Z, &nod);
+		regalloc(&nod1, l, Z);
+		if(typefd[l->type->etype]) {
+			regalloc(&nod3, l, Z);
+			if(v < 0) {
+				gopcode(OAS, nodfconst(-v), Z, &nod3);
+				gopcode(OSUB, &nod3, &nod, &nod1);
+			} else {
+				gopcode(OAS, nodfconst(v), Z, &nod3);
+				gopcode(OADD, &nod3, &nod, &nod1);
+			}
+			regfree(&nod3);
+		} else
+			gopcode(OADD, nodconst(v), &nod, &nod1);
+		gopcode(OAS, &nod1, Z, &nod2);
+
+		regfree(&nod);
+		regfree(&nod1);
+		if(l->addable < INDEXED)
+			regfree(&nod2);
+		break;
+
+	case OPREINC:
+	case OPREDEC:
+		v = 1;
+		if(l->type->etype == TIND)
+			v = l->type->link->width;
+		if(o == OPREDEC)
+			v = -v;
+		if(l->op == OBIT)
+			goto bitinc;
+
+	pre:
+		if(l->addable < INDEXED)
+			reglcgen(&nod2, l, Z);
+		else
+			nod2 = *l;
+
+		regalloc(&nod, l, nn);
+		gopcode(OAS, &nod2, Z, &nod);
+		if(typefd[l->type->etype]) {
+			regalloc(&nod3, l, Z);
+			if(v < 0) {
+				gopcode(OAS, nodfconst(-v), Z, &nod3);
+				gopcode(OSUB, &nod3, Z, &nod);
+			} else {
+				gopcode(OAS, nodfconst(v), Z, &nod3);
+				gopcode(OADD, &nod3, Z, &nod);
+			}
+			regfree(&nod3);
+		} else
+			gopcode(OADD, nodconst(v), Z, &nod);
+		gopcode(OAS, &nod, Z, &nod2);
+		if(nn && l->op == ONAME)	/* in x=++i, emit USED(i) */
+			gins(ANOP, l, Z);
+
+		regfree(&nod);
+		if(l->addable < INDEXED)
+			regfree(&nod2);
+		break;
+
+	bitinc:
+		if(nn != Z && (o == OPOSTINC || o == OPOSTDEC)) {
+			bitload(l, &nod, &nod1, &nod2, Z);
+			gopcode(OAS, &nod, Z, nn);
+			gopcode(OADD, nodconst(v), Z, &nod);
+			bitstore(l, &nod, &nod1, &nod2, Z);
+			break;
+		}
+		bitload(l, &nod, &nod1, &nod2, nn);
+		gopcode(OADD, nodconst(v), Z, &nod);
+		bitstore(l, &nod, &nod1, &nod2, nn);
+		break;
+	}
+	cursafe = curs;
+	return;
+}
+
+void
+reglcgen(Node *t, Node *n, Node *nn)
+{
+	Node *r;
+	long v;
+
+	regialloc(t, n, nn);
+	if(n->op == OIND) {
+		r = n->left;
+		while(r->op == OADD)
+			r = r->right;
+		if(sconst(r) && (v = r->vconst+t->xoffset) >= -4096 && v < 4096) {
+			v = r->vconst;
+			r->vconst = 0;
+			lcgen(n, t);
+			t->xoffset += v;
+			r->vconst = v;
+			regind(t, n);
+			return;
+		}
+	} else if(n->op == OINDREG) {
+		if((v = n->xoffset) >= -4096 && v < 4096) {
+			n->op = OREGISTER;
+			cgen(n, t);
+			t->xoffset += v;
+			n->op = OINDREG;
+			regind(t, n);
+			return;
+		}
+	}
+	lcgen(n, t);
+	regind(t, n);
+}
+
+void
+lcgen(Node *n, Node *nn)
+{
+	Prog *p1;
+	Node nod;
+
+	if(debug['g']) {
+		prtree(nn, "lcgen lhs");
+		prtree(n, "lcgen");
+	}
+	if(n == Z || n->type == T)
+		return;
+	if(nn == Z) {
+		nn = &nod;
+		regalloc(&nod, n, Z);
+	}
+	switch(n->op) {
+	default:
+		if(n->addable < INDEXED) {
+			diag(n, "unknown op in lcgen: %O", n->op);
+			break;
+		}
+		nod = *n;
+		nod.op = OADDR;
+		nod.left = n;
+		nod.right = Z;
+		nod.type = types[TIND];
+		gopcode(OAS, &nod, Z, nn);
+		break;
+
+	case OCOMMA:
+		cgen(n->left, n->left);
+		lcgen(n->right, nn);
+		break;
+
+	case OIND:
+		cgen(n->left, nn);
+		break;
+
+	case OCOND:
+		bcgen(n->left, 1);
+		p1 = p;
+		lcgen(n->right->left, nn);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		lcgen(n->right->right, nn);
+		patch(p1, pc);
+		break;
+	}
+}
+
+void
+bcgen(Node *n, int true)
+{
+
+	if(n->type == T)
+		gbranch(OGOTO);
+	else
+		boolgen(n, true, Z);
+}
+
+void
+boolgen(Node *n, int true, Node *nn)
+{
+	int o;
+	Prog *p1, *p2;
+	Node *l, *r, nod, nod1;
+	long curs;
+
+	if(debug['g']) {
+		prtree(nn, "boolgen lhs");
+		prtree(n, "boolgen");
+	}
+	curs = cursafe;
+	l = n->left;
+	r = n->right;
+	switch(n->op) {
+
+	default:
+		regalloc(&nod, n, nn);
+		cgen(n, &nod);
+		o = ONE;
+		if(true)
+			o = OEQ;
+		if(typefd[n->type->etype]) {
+			gopcode(true ? o | BTRUE : o, nodfconst(0), &nod, Z);
+		} else
+			gopcode(o, nodconst(0), &nod, Z);
+		regfree(&nod);
+		goto com;
+
+	case OCONST:
+		o = vconst(n);
+		if(!true)
+			o = !o;
+		gbranch(OGOTO);
+		if(o) {
+			p1 = p;
+			gbranch(OGOTO);
+			patch(p1, pc);
+		}
+		goto com;
+
+	case OCOMMA:
+		cgen(l, Z);
+		boolgen(r, true, nn);
+		break;
+
+	case ONOT:
+		boolgen(l, !true, nn);
+		break;
+
+	case OCOND:
+		bcgen(l, 1);
+		p1 = p;
+		bcgen(r->left, true);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		bcgen(r->right, !true);
+		patch(p2, pc);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		patch(p2, pc);
+		goto com;
+
+	case OANDAND:
+		if(!true)
+			goto caseor;
+
+	caseand:
+		bcgen(l, true);
+		p1 = p;
+		bcgen(r, !true);
+		p2 = p;
+		patch(p1, pc);
+		gbranch(OGOTO);
+		patch(p2, pc);
+		goto com;
+
+	case OOROR:
+		if(!true)
+			goto caseand;
+
+	caseor:
+		bcgen(l, !true);
+		p1 = p;
+		bcgen(r, !true);
+		p2 = p;
+		gbranch(OGOTO);
+		patch(p1, pc);
+		patch(p2, pc);
+		goto com;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OHI:
+	case OHS:
+	case OLO:
+	case OLS:
+		o = n->op;
+		if(true)
+			o = comrel[relindex(o)];
+		if(l->complex >= FNX && r->complex >= FNX) {
+			regret(&nod, r);
+			cgenrel(r, &nod, 1);
+			regsalloc(&nod1, r);
+			gopcode(OAS, &nod, Z, &nod1);
+			regfree(&nod);
+			nod = *n;
+			nod.right = &nod1;
+			boolgen(&nod, true, nn);
+			break;
+		}
+		if(sconst(l)) {
+			regalloc(&nod, r, nn);
+			cgenrel(r, &nod, 1);
+			o = invrel[relindex(o)];
+			gopcode(true ? o | BTRUE : o, l, &nod, Z);
+			regfree(&nod);
+			goto com;
+		}
+		if(sconst(r)) {
+			regalloc(&nod, l, nn);
+			cgenrel(l, &nod, 1);
+			gopcode(true ? o | BTRUE : o, r, &nod, Z);
+			regfree(&nod);
+			goto com;
+		}
+		if(l->complex >= r->complex) {
+			regalloc(&nod1, l, nn);
+			cgenrel(l, &nod1, 1);
+			regalloc(&nod, r, Z);
+			cgenrel(r, &nod, 1);
+		} else {
+			regalloc(&nod, r, nn);
+			cgenrel(r, &nod, 1);
+			regalloc(&nod1, l, Z);
+			cgenrel(l, &nod1, 1);
+		}
+		gopcode(true ? o | BTRUE : o, &nod, &nod1, Z);
+		regfree(&nod);
+		regfree(&nod1);
+
+	com:
+		if(nn != Z) {
+			p1 = p;
+			gopcode(OAS, nodconst(1), Z, nn);
+			gbranch(OGOTO);
+			p2 = p;
+			patch(p1, pc);
+			gopcode(OAS, nodconst(0), Z, nn);
+			patch(p2, pc);
+		}
+		break;
+	}
+	cursafe = curs;
+}
+
+void
+sugen(Node *n, Node *nn, long w)
+{
+	Prog *p1;
+	Node nod0, nod1, nod2, nod3, nod4, *l, *r;
+	Type *t;
+	long pc1;
+	int i, m, c;
+
+	if(n == Z || n->type == T)
+		return;
+	if(debug['g']) {
+		prtree(nn, "sugen lhs");
+		prtree(n, "sugen");
+	}
+	if(nn == nodrat)
+		if(w > nrathole)
+			nrathole = w;
+	switch(n->op) {
+	case OIND:
+		if(nn == Z) {
+			nullwarn(n->left, Z);
+			break;
+		}
+
+	default:
+		goto copy;
+
+	case OCONST:
+		if(n->type && typev[n->type->etype]) {
+			if(nn == Z) {
+				nullwarn(n->left, Z);
+				break;
+			}
+
+			t = nn->type;
+			nn->type = types[TLONG];
+			reglcgen(&nod1, nn, Z);
+			nn->type = t;
+
+			if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+				gopcode(OAS, nod32const(n->vconst>>32), Z, &nod1);
+			else
+				gopcode(OAS, nod32const(n->vconst), Z, &nod1);
+			nod1.xoffset += SZ_LONG;
+			if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+				gopcode(OAS, nod32const(n->vconst), Z, &nod1);
+			else
+				gopcode(OAS, nod32const(n->vconst>>32), Z, &nod1);
+
+			regfree(&nod1);
+			break;
+		}
+		goto copy;
+
+	case ODOT:
+		l = n->left;
+		sugen(l, nodrat, l->type->width);
+		if(nn != Z) {
+			warn(n, "non-interruptable temporary");
+			nod1 = *nodrat;
+			r = n->right;
+			if(!r || r->op != OCONST) {
+				diag(n, "DOT and no offset");
+				break;
+			}
+			nod1.xoffset += (long)r->vconst;
+			nod1.type = n->type;
+			sugen(&nod1, nn, w);
+		}
+		break;
+
+	case OSTRUCT:
+		/*
+		 * rewrite so lhs has no side effects
+		 */
+		if(nn != Z && side(nn)) {
+			nod1 = *n;
+			nod1.type = typ(TIND, n->type);
+			regret(&nod2, &nod1);
+			lcgen(nn, &nod2);
+			regsalloc(&nod0, &nod1);
+			gopcode(OAS, &nod2, Z, &nod0);
+			regfree(&nod2);
+
+			nod1 = *n;
+			nod1.op = OIND;
+			nod1.left = &nod0;
+			nod1.right = Z;
+			nod1.complex = 1;
+
+			sugen(n, &nod1, w);
+			return;
+		}
+
+		r = n->left;
+		for(t = n->type->link; t != T; t = t->down) {
+			l = r;
+			if(r->op == OLIST) {
+				l = r->left;
+				r = r->right;
+			}
+			if(nn == Z) {
+				cgen(l, nn);
+				continue;
+			}
+			/*
+			 * hand craft *(&nn + o) = l
+			 */
+			nod0 = znode;
+			nod0.op = OAS;
+			nod0.type = t;
+			nod0.left = &nod1;
+			nod0.right = l;
+
+			nod1 = znode;
+			nod1.op = OIND;
+			nod1.type = t;
+			nod1.left = &nod2;
+
+			nod2 = znode;
+			nod2.op = OADD;
+			nod2.type = typ(TIND, t);
+			nod2.left = &nod3;
+			nod2.right = &nod4;
+
+			nod3 = znode;
+			nod3.op = OADDR;
+			nod3.type = nod2.type;
+			nod3.left = nn;
+
+			nod4 = znode;
+			nod4.op = OCONST;
+			nod4.type = nod2.type;
+			nod4.vconst = t->offset;
+
+			ccom(&nod0);
+			acom(&nod0);
+			xcom(&nod0);
+			nod0.addable = 0;
+
+			cgen(&nod0, Z);
+		}
+		break;
+
+	case OAS:
+		if(nn == Z) {
+			if(n->addable < INDEXED)
+				sugen(n->right, n->left, w);
+			break;
+		}
+		sugen(n->right, nodrat, w);
+		warn(n, "non-interruptable temporary");
+		sugen(nodrat, n->left, w);
+		sugen(nodrat, nn, w);
+		break;
+
+	case OFUNC:
+		if(nn == Z) {
+			sugen(n, nodrat, w);
+			break;
+		}
+		if(nn->op != OIND) {
+			nn = new1(OADDR, nn, Z);
+			nn->type = types[TIND];
+			nn->addable = 0;
+		} else
+			nn = nn->left;
+		n = new(OFUNC, n->left, new(OLIST, nn, n->right));
+		n->type = types[TVOID];
+		n->left->type = types[TVOID];
+		cgen(n, Z);
+		break;
+
+	case OCOND:
+		bcgen(n->left, 1);
+		p1 = p;
+		sugen(n->right->left, nn, w);
+		gbranch(OGOTO);
+		patch(p1, pc);
+		p1 = p;
+		sugen(n->right->right, nn, w);
+		patch(p1, pc);
+		break;
+
+	case OCOMMA:
+		cgen(n->left, Z);
+		sugen(n->right, nn, w);
+		break;
+	}
+	return;
+
+copy:
+	if(nn == Z)
+		return;
+	if(n->complex >= FNX && nn->complex >= FNX) {
+		t = nn->type;
+		nn->type = types[TLONG];
+		regialloc(&nod1, nn, Z);
+		lcgen(nn, &nod1);
+		regsalloc(&nod2, nn);
+		nn->type = t;
+
+		gopcode(OAS, &nod1, Z, &nod2);
+		regfree(&nod1);
+
+		nod2.type = typ(TIND, t);
+
+		nod1 = nod2;
+		nod1.op = OIND;
+		nod1.left = &nod2;
+		nod1.right = Z;
+		nod1.complex = 1;
+		nod1.type = t;
+
+		sugen(n, &nod1, w);
+		return;
+	}
+
+	/* TO DO: use AMOV/VLONG when possible */
+	if(n->complex > nn->complex) {
+		t = n->type;
+		n->type = types[TLONG];
+		reglcgen(&nod1, n, Z);
+		n->type = t;
+
+		t = nn->type;
+		nn->type = types[TLONG];
+		reglcgen(&nod2, nn, Z);
+		nn->type = t;
+	} else {
+		t = nn->type;
+		nn->type = types[TLONG];
+		reglcgen(&nod2, nn, Z);
+		nn->type = t;
+
+		t = n->type;
+		n->type = types[TLONG];
+		reglcgen(&nod1, n, Z);
+		n->type = t;
+	}
+
+	w /= SZ_LONG;
+	if(w <= 5) {
+		layout(&nod1, &nod2, w, 0, Z);
+		goto out;
+	}
+
+	/*
+	 * minimize space for unrolling loop
+	 * 3,4,5 times. (6 or more is never minimum)
+	 * if small structure, try 2 also.
+	 */
+	c = 0; /* set */
+	m = 100;
+	i = 3;
+	if(w <= 15)
+		i = 2;
+	for(; i<=5; i++)
+		if(i + w%i <= m) {
+			c = i;
+			m = c + w%c;
+		}
+
+	regalloc(&nod3, &regnode, Z);
+	layout(&nod1, &nod2, w%c, w/c, &nod3);
+	
+	pc1 = pc;
+	layout(&nod1, &nod2, c, 0, Z);
+
+	gopcode(OSUB, nodconst(1L), Z, &nod3);
+	nod1.op = OREGISTER;
+	t = nod1.type;
+	nod1.type = types[TIND];
+	gopcode(OADD, nodconst(c*SZ_LONG), Z, &nod1);
+	nod1.type = t;
+	nod2.op = OREGISTER;
+	t = nod2.type;
+	nod2.type = types[TIND];
+	gopcode(OADD, nodconst(c*SZ_LONG), Z, &nod2);
+	nod2.type = t;
+	
+	gopcode(OGT, nodconst(0), &nod3, Z);
+	patch(p, pc1);
+
+	regfree(&nod3);
+out:
+	regfree(&nod1);
+	regfree(&nod2);
+}
+
+void
+layout(Node *f, Node *t, int c, int cv, Node *cn)
+{
+	Node t1, t2;
+
+	while(c > 3) {
+		layout(f, t, 2, 0, Z);
+		c -= 2;
+	}
+
+	regalloc(&t1, &regnode, Z);
+	regalloc(&t2, &regnode, Z);
+	if(c > 0) {
+		gopcode(OAS, f, Z, &t1);
+		f->xoffset += SZ_LONG;
+	}
+	if(cn != Z)
+		gopcode(OAS, nodconst(cv), Z, cn);
+	if(c > 1) {
+		gopcode(OAS, f, Z, &t2);
+		f->xoffset += SZ_LONG;
+	}
+	if(c > 0) {
+		gopcode(OAS, &t1, Z, t);
+		t->xoffset += SZ_LONG;
+	}
+	if(c > 2) {
+		gopcode(OAS, f, Z, &t1);
+		f->xoffset += SZ_LONG;
+	}
+	if(c > 1) {
+		gopcode(OAS, &t2, Z, t);
+		t->xoffset += SZ_LONG;
+	}
+	if(c > 2) {
+		gopcode(OAS, &t1, Z, t);
+		t->xoffset += SZ_LONG;
+	}
+	regfree(&t1);
+	regfree(&t2);
+}
+
+/*
+ * if a constant and vlong, doesn't fit as 32-bit signed immediate
+ */
+int
+hardconst(Node *n)
+{
+	return n->op == OCONST && !sconst(n);
+}
+
+/*
+ * casting up to t2 covers an intermediate cast to t1
+ */
+int
+castup(Type *t1, Type *t2)
+{
+	int ft;
+
+	if(!nilcast(t1, t2))
+		return 0;
+	/* known to be small to large */
+	ft = t1->etype;
+	switch(t2->etype){
+	case TINT:
+	case TLONG:
+		return ft == TLONG || ft == TINT || ft == TSHORT || ft == TCHAR;
+	case TUINT:
+	case TULONG:
+		return ft == TULONG || ft == TUINT || ft == TUSHORT || ft == TUCHAR;
+	case TVLONG:
+		return ft == TLONG || ft == TINT || ft == TSHORT;
+	case TUVLONG:
+		return ft == TULONG || ft == TUINT || ft == TUSHORT;
+	}
+	return 0;
+}
+
+int
+cond(int op)
+{
+	switch(op) {
+	case OANDAND:
+	case OOROR:
+	case ONOT:
+		return 1;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OHI:
+	case OHS:
+	case OLO:
+	case OLS:
+		return 1;
+	}
+	return 0;
+}
--- /dev/null
+++ b/sys/src/cmd/7c/gc.h
@@ -1,0 +1,358 @@
+#include	"../cc/cc.h"
+#include	"../7c/7.out.h"
+
+/*
+ * 7c/arm64
+ * Arm64
+ */
+#define	SZ_CHAR		1
+#define	SZ_SHORT	2
+#define	SZ_INT		4
+#define	SZ_LONG		4
+#define	SZ_IND		8
+#define	SZ_FLOAT	4
+#define	SZ_VLONG	8
+#define	SZ_DOUBLE	8
+#define	FNX		100
+#define	BTRUE		0x1000
+
+typedef	struct	Adr	Adr;
+typedef	struct	Prog	Prog;
+typedef	struct	Case	Case;
+typedef	struct	C1	C1;
+typedef	struct	Multab	Multab;
+typedef	struct	Hintab	Hintab;
+typedef	struct	Var	Var;
+typedef	struct	Reg	Reg;
+typedef	struct	Rgn	Rgn;
+
+
+struct	Adr
+{
+	vlong	offset;
+	double	dval;
+	char	sval[NSNAME];
+	Ieee	ieee;
+
+	Sym*	sym;
+	char	type;
+	char	reg;
+	char	name;
+	char	etype;
+};
+#define	A	((Adr*)0)
+
+#define	INDEXED	9
+struct	Prog
+{
+	Adr	from;
+	Adr	to;
+	Prog*	link;
+	long	lineno;
+	ushort	as;
+	char	reg;
+	uchar	scond;
+};
+#define	P	((Prog*)0)
+
+struct	Case
+{
+	Case*	link;
+	vlong	val;
+	long	label;
+	char	def;
+	char isv;
+};
+#define	C	((Case*)0)
+
+struct	C1
+{
+	vlong	val;
+	long	label;
+};
+
+struct	Multab
+{
+	long	val;
+	char	code[20];
+};
+
+struct	Hintab
+{
+	ushort	val;
+	char	hint[10];
+};
+
+struct	Var
+{
+	vlong	offset;
+	Sym*	sym;
+	char	name;
+	char	etype;
+};
+
+struct	Reg
+{
+	long	pc;
+	long	rpo;		/* reverse post ordering */
+
+	Bits	set;
+	Bits	use1;
+	Bits	use2;
+
+	Bits	refbehind;
+	Bits	refahead;
+	Bits	calbehind;
+	Bits	calahead;
+	Bits	regdiff;
+	Bits	act;
+
+	long	regu;
+	long	loop;		/* could be shorter */
+
+	
+	Reg*	log5;
+	long	active;
+
+	Reg*	p1;
+	Reg*	p2;
+	Reg*	p2link;
+	Reg*	s1;
+	Reg*	s2;
+	Reg*	link;
+	Prog*	prog;
+};
+#define	R	((Reg*)0)
+
+#define	NRGN	1000		/* was 600; raised for paranoia.c */
+struct	Rgn
+{
+	Reg*	enter;
+	short	cost;
+	short	varno;
+	short	regno;
+};
+
+EXTERN	long	breakpc;
+EXTERN	long	nbreak;
+EXTERN	Case*	cases;
+EXTERN	Node	constnode;
+EXTERN	Node	fconstnode;
+EXTERN	Node	vconstnode;
+EXTERN	long	continpc;
+EXTERN	long	curarg;
+EXTERN	long	cursafe;
+EXTERN	Prog*	firstp;
+EXTERN	Prog*	lastp;
+EXTERN	long	maxargsafe;
+EXTERN	int	mnstring;
+EXTERN	Multab	multab[20];
+EXTERN	int	hintabsize;
+EXTERN	Node*	nodrat;
+EXTERN	Node*	nodret;
+EXTERN	Node*	nodsafe;
+EXTERN	long	nrathole;
+EXTERN	long	nstring;
+EXTERN	Prog*	p;
+EXTERN	long	pc;
+EXTERN	Node	regnode;
+EXTERN	Node	qregnode;
+EXTERN	char	string[NSNAME];
+EXTERN	Sym*	symrathole;
+EXTERN	Node	znode;
+EXTERN	Prog	zprog;
+EXTERN	char	reg[NREG+NFREG];
+EXTERN	long	exregoffset;
+EXTERN	long	exfregoffset;
+EXTERN	int	suppress;
+EXTERN	uchar	typechlpv[NTYPE];
+
+#define	BLOAD(r)	band(bnot(r->refbehind), r->refahead)
+#define	BSTORE(r)	band(bnot(r->calbehind), r->calahead)
+#define	LOAD(r)		(~r->refbehind.b[z] & r->refahead.b[z])
+#define	STORE(r)	(~r->calbehind.b[z] & r->calahead.b[z])
+
+#define	bset(a,n)	((a).b[(n)/32]&(1L<<(n)%32))
+
+#define	CLOAD	4
+#define	CREF	5
+#define	CINF	1000
+#define	LOOP	3
+
+EXTERN	Rgn	region[NRGN];
+EXTERN	Rgn*	rgp;
+EXTERN	int	nregion;
+EXTERN	int	nvar;
+
+EXTERN	Bits	externs;
+EXTERN	Bits	params;
+EXTERN	Bits	consts;
+EXTERN	Bits	addrs;
+
+EXTERN	long	regbits;
+EXTERN	long	exregbits;
+
+EXTERN	int	change;
+
+EXTERN	Reg*	firstr;
+EXTERN	Reg*	lastr;
+EXTERN	Reg	zreg;
+EXTERN	Reg*	freer;
+EXTERN	Var	var[NVAR];
+EXTERN	long*	idom;
+EXTERN	Reg**	rpo2r;
+EXTERN	long	maxnr;
+
+extern	char*	anames[];
+extern	Hintab	hintab[];
+
+/*
+ * sgen.c
+ */
+void	codgen(Node*, Node*);
+void	gen(Node*);
+void	noretval(int);
+void	usedset(Node*, int);
+void	xcom(Node*);
+int	bcomplex(Node*, Node*);
+
+/*
+ * cgen.c
+ */
+int	castup(Type*, Type*);
+void	cgen(Node*, Node*);
+void	cgenrel(Node*, Node*, int);
+int	cond(int);
+int	hardconst(Node*);
+void	reglcgen(Node*, Node*, Node*);
+void	layout(Node*, Node*, int, int, Node*);
+void	lcgen(Node*, Node*);
+void	bcgen(Node*, int);
+void	boolgen(Node*, int, Node*);
+void	sugen(Node*, Node*, long);
+void	layout(Node*, Node*, int, int, Node*);
+
+/*
+ * txt.c
+ */
+void	ginit(void);
+void	gclean(void);
+void	nextpc(void);
+void	gargs(Node*, Node*, Node*);
+void	garg1(Node*, Node*, Node*, int, Node**);
+Node*	nodconst(long);
+Node*	nod32const(vlong);
+Node*	nodfconst(double);
+void	nodreg(Node*, Node*, int);
+void	regret(Node*, Node*);
+void	regalloc(Node*, Node*, Node*);
+void	regfree(Node*);
+void	regialloc(Node*, Node*, Node*);
+void	regsalloc(Node*, Node*);
+void	regaalloc1(Node*, Node*);
+void	regaalloc(Node*, Node*);
+void	regind(Node*, Node*);
+void	gprep(Node*, Node*);
+void	raddr(Node*, Prog*);
+void	naddr(Node*, Adr*);
+void	gmovm(Node*, Node*, int);
+void	gmove(Node*, Node*);
+void	gmover(Node*, Node*);
+void	gins(int a, Node*, Node*);
+void	gopcode(int, Node*, Node*, Node*);
+int	samaddr(Node*, Node*);
+void	gbranch(int);
+void	patch(Prog*, long);
+int	sconst(Node*);
+int	sval(long);
+void	gpseudo(int, Sym*, Node*);
+int	usableoffset(Node*, vlong, Node*);
+
+/*
+ * swt.c
+ */
+int	swcmp(void*, void*);
+void	doswit(Node*);
+void	swit1(C1*, int, long, Node*);
+void	swit2(C1*, int, long, Node*, Node*);
+void	casf(void);
+void	bitload(Node*, Node*, Node*, Node*, Node*);
+void	bitstore(Node*, Node*, Node*, Node*, Node*);
+long	outstring(char*, long);
+int	mulcon(Node*, Node*);
+Multab*	mulcon0(long);
+void	nullwarn(Node*, Node*);
+void	gextern(Sym*, Node*, long, long);
+void	outcode(void);
+void	ieeedtod(Ieee*, double);
+
+/*
+ * list
+ */
+void	listinit(void);
+int	Pconv(Fmt*);
+int	Aconv(Fmt*);
+int	Dconv(Fmt*);
+int	Sconv(Fmt*);
+int	Nconv(Fmt*);
+int	Bconv(Fmt*);
+int	Rconv(Fmt*);
+
+/*
+ * reg.c
+ */
+Reg*	rega(void);
+int	rcmp(void*, void*);
+void	regopt(Prog*);
+void	addmove(Reg*, int, int, int);
+Bits	mkvar(Adr*, int);
+void	prop(Reg*, Bits, Bits);
+void	loopit(Reg*, long);
+void	synch(Reg*, Bits);
+ulong	allreg(ulong, Rgn*);
+void	paint1(Reg*, int);
+ulong	paint2(Reg*, int);
+void	paint3(Reg*, int, long, int);
+void	addreg(Adr*, int);
+
+/*
+ * peep.c
+ */
+void	peep(void);
+void	excise(Reg*);
+Reg*	uniqp(Reg*);
+Reg*	uniqs(Reg*);
+int	regtyp(Adr*);
+int	regzer(Adr*);
+int	anyvar(Adr*);
+int	subprop(Reg*);
+int	copyprop(Reg*);
+int	shiftprop(Reg*);
+void	constprop(Adr*, Adr*, Reg*);
+int	copy1(Adr*, Adr*, Reg*, int);
+int	copyu(Prog*, Adr*, Adr*);
+
+int	copyas(Adr*, Adr*);
+int	copyau(Adr*, Adr*);
+int	copyau1(Prog*, Adr*);
+int	copysub(Adr*, Adr*, Adr*, int);
+int	copysub1(Prog*, Adr*, Adr*, int);
+
+long	RtoB(int);
+long	FtoB(int);
+int	BtoR(long);
+int	BtoF(long);
+
+void	predicate(void); 
+int	isbranch(Prog *); 
+int	predicable(Prog *p); 
+int	modifiescpsr(Prog *p); 
+
+#pragma	varargck	type	"A"	int
+#pragma	varargck	type	"A"	uint
+#pragma	varargck	type	"B"	Bits
+#pragma	varargck	type	"D"	Adr*
+#pragma	varargck	type	"N"	Adr*
+#pragma	varargck	type	"R"	Adr*
+#pragma	varargck	type	"P"	Prog*
+#pragma	varargck	type	"S"	char*
--- /dev/null
+++ b/sys/src/cmd/7c/list.c
@@ -1,0 +1,329 @@
+#define	EXTERN
+#include "gc.h"
+
+void
+listinit(void)
+{
+
+	fmtinstall('A', Aconv);
+	fmtinstall('P', Pconv);
+	fmtinstall('S', Sconv);
+	fmtinstall('N', Nconv);
+	fmtinstall('B', Bconv);
+	fmtinstall('D', Dconv);
+	fmtinstall('R', Rconv);
+}
+
+int
+Bconv(Fmt *fp)
+{
+	char str[STRINGSZ], ss[STRINGSZ], *s;
+	Bits bits;
+	int i;
+
+	str[0] = 0;
+	bits = va_arg(fp->args, Bits);
+	while(bany(&bits)) {
+		i = bnum(bits);
+		if(str[0])
+			strcat(str, " ");
+		if(var[i].sym == S) {
+			snprint(ss, sizeof(ss), "$%lld", var[i].offset);
+			s = ss;
+		} else
+			s = var[i].sym->name;
+		if(strlen(str) + strlen(s) + 1 >= STRINGSZ)
+			break;
+		strcat(str, s);
+		bits.b[i/32] &= ~(1L << (i%32));
+	}
+	return fmtstrcpy(fp, str);
+}
+
+static char *conds[] = {
+	".EQ", ".NE", ".CS", ".CC", 
+	".MI", ".PL", ".VS", ".VC", 
+	".HI", ".LS", ".GE", ".LT", 
+	".GT", ".LE", "", ".NV",
+};
+
+int
+Pconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Prog *p;
+	int a;
+
+	p = va_arg(fp->args, Prog*);
+	a = p->as;
+	if(a == ADATA)
+		snprint(str, sizeof(str), "	%A	%D/%d,%D", a, &p->from, p->reg, &p->to);
+	else
+	if(p->as == ATEXT)
+		snprint(str, sizeof(str), "	%A	%D,%d,%D", a, &p->from, p->reg, &p->to);
+	else
+	if(p->reg == NREG)
+		snprint(str, sizeof(str), "	%A	%D,%D", a, &p->from, &p->to);
+	else
+	if(p->from.type != D_FREG)
+		snprint(str, sizeof(str), "	%A	%D,R%d,%D", a, &p->from, p->reg, &p->to);
+	else
+		snprint(str, sizeof(str), "	%A	%D,F%d,%D", a, &p->from, p->reg, &p->to);
+	return fmtstrcpy(fp, str);
+}
+
+int
+Aconv(Fmt *fp)
+{
+	char *s;
+	int a;
+
+	a = va_arg(fp->args, int);
+	s = "???";
+	if(a >= AXXX && a < ALAST)
+		s = anames[a];
+	return fmtstrcpy(fp, s);
+}
+
+int
+Dconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+	char *op;
+	int v;
+	static char *extop[] = {".UB", ".UH", ".UW", ".UX", ".SB", ".SH", ".SW", ".SX"};
+
+	a = va_arg(fp->args, Adr*);
+	switch(a->type) {
+
+	default:
+		snprint(str, sizeof(str), "GOK-type(%d)", a->type);
+		break;
+
+	case D_NONE:
+		str[0] = 0;
+		if(a->name != D_NONE || a->reg != NREG || a->sym != S)
+			snprint(str, sizeof(str), "%N(R%d)(NONE)", a, a->reg);
+		break;
+
+	case D_CONST:
+		if(a->reg != NREG)
+			snprint(str, sizeof(str), "$%N(R%d)", a, a->reg);
+		else
+			snprint(str, sizeof(str), "$%N", a);
+		break;
+
+	case D_SHIFT:
+		v = a->offset;
+		op = "<<>>->@>" + (((v>>5) & 3) << 1);
+		if(v & (1<<4))
+			snprint(str, sizeof(str), "R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15);
+		else
+			snprint(str, sizeof(str), "R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31);
+		if(a->reg != NREG)
+			sprint(str+strlen(str), "(R%d)", a->reg);
+		break;
+
+	case D_OREG:
+		if(a->reg != NREG)
+			snprint(str, sizeof(str), "%N(R%d)", a, a->reg);
+		else
+			snprint(str, sizeof(str), "%N", a);
+		break;
+
+	case D_XPRE:
+		if(a->reg != NREG)
+			snprint(str, sizeof(str), "%N(R%d)!", a, a->reg);
+		else
+			snprint(str, sizeof(str), "%N!", a);
+		break;
+
+	case D_XPOST:
+		if(a->reg != NREG)
+			snprint(str, sizeof(str), "(R%d)%N!", a->reg, a);
+		else
+			snprint(str, sizeof(str), "%N!", a);
+		break;
+
+	case D_EXTREG:
+		v = a->offset;
+		if(v & (7<<10))
+			snprint(str, sizeof(str), "R%d%s<<%d", (v>>16)&31, extop[(v>>13)&7], (v>>10)&7);
+		else
+			snprint(str, sizeof(str), "R%d%s", (v>>16)&31, extop[(v>>13)&7]);
+		break;
+
+	case D_REG:
+		snprint(str, sizeof(str), "R%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			snprint(str, sizeof(str), "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_SP:
+		if(a->name != D_NONE || a->sym != S)
+			snprint(str, sizeof(str), "%N(R%d)(REG)", a, a->reg);
+		else
+			strcpy(str, "SP");
+		break;
+
+	case D_FREG:
+		snprint(str, sizeof(str), "F%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			snprint(str, sizeof(str), "%N(R%d)(REG)", a, a->reg);
+		break;
+
+
+	case D_SPR:
+		switch((ulong)a->offset){
+		case D_FPSR:
+			sprint(str, "FPSR");
+			break;
+		case D_FPCR:
+			sprint(str, "FPCR");
+			break;
+		case D_NZCV:
+			sprint(str, "NZCV");
+			break;
+		default:
+			sprint(str, "SPR(%#llux)", a->offset);
+			break;
+		}
+		if(a->name != D_NONE || a->sym != S)
+			snprint(str, sizeof(str), "%N(SPR(%lld))(REG)", a, a->offset);
+		break;
+
+	case D_BRANCH:
+		snprint(str, sizeof(str), "%lld(PC)", a->offset-pc);
+		break;
+
+	case D_FCONST:
+		snprint(str, sizeof(str), "$%.17e", a->dval);
+		break;
+
+	case D_SCONST:
+		snprint(str, sizeof(str), "$\"%S\"", a->sval);
+		break;
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Rconv(Fmt *fp)
+{
+	char str[STRINGSZ], *p, *e;
+	Adr *a;
+	int i, v;
+
+	a = va_arg(fp->args, Adr*);
+	snprint(str, sizeof(str), "GOK-reglist");
+	switch(a->type) {
+	case D_CONST:
+		if(a->reg != NREG)
+			break;
+		if(a->sym != S)
+			break;
+		v = a->offset;
+		p = str;
+		e = str+sizeof(str);
+		for(i=0; i<NREG; i++) {
+			if(v & (1<<i)) {
+				if(p == str)
+					p = seprint(p, e, "[R%d", i);
+				else
+					p = seprint(p, e, ",R%d", i);
+			}
+		}
+		seprint(p, e, "]");
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Sconv(Fmt *fp)
+{
+	int i, c;
+	char str[STRINGSZ], *p, *a;
+
+	a = va_arg(fp->args, char*);
+	p = str;
+	for(i=0; i<NSNAME; i++) {
+		c = a[i] & 0xff;
+		if(c >= 'a' && c <= 'z' ||
+		   c >= 'A' && c <= 'Z' ||
+		   c >= '0' && c <= '9' ||
+		   c == ' ' || c == '%') {
+			*p++ = c;
+			continue;
+		}
+		*p++ = '\\';
+		switch(c) {
+		case 0:
+			*p++ = 'z';
+			continue;
+		case '\\':
+		case '"':
+			*p++ = c;
+			continue;
+		case '\n':
+			*p++ = 'n';
+			continue;
+		case '\t':
+			*p++ = 't';
+			continue;
+		case '\r':
+			*p++ = 'r';
+			continue;
+		case '\f':
+			*p++ = 'f';
+			continue;
+		}
+		*p++ = (c>>6) + '0';
+		*p++ = ((c>>3) & 7) + '0';
+		*p++ = (c & 7) + '0';
+	}
+	*p = 0;
+	return fmtstrcpy(fp, str);
+}
+
+int
+Nconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+	Sym *s;
+
+	a = va_arg(fp->args, Adr*);
+	s = a->sym;
+	if(s == S) {
+		snprint(str, sizeof(str), "%lld", a->offset);
+		goto out;
+	}
+	switch(a->name) {
+	default:
+		snprint(str, sizeof(str), "GOK-name(%d)", a->name);
+		break;
+
+	case D_NONE:
+		snprint(str, sizeof(str), "%lld", a->offset);
+		break;
+
+	case D_EXTERN:
+		snprint(str, sizeof(str), "%s+%lld(SB)", s->name, a->offset);
+		break;
+
+	case D_STATIC:
+		snprint(str, sizeof(str), "%s<>+%lld(SB)", s->name, a->offset);
+		break;
+
+	case D_AUTO:
+		snprint(str, sizeof(str), "%s-%lld(SP)", s->name, -a->offset);
+		break;
+
+	case D_PARAM:
+		snprint(str, sizeof(str), "%s+%lld(FP)", s->name, a->offset);
+		break;
+	}
+out:
+	return fmtstrcpy(fp, str);
+}
--- /dev/null
+++ b/sys/src/cmd/7c/machcap.c
@@ -1,0 +1,81 @@
+#include "gc.h"
+
+int
+machcap(Node *n)
+{
+
+	if(n == Z)
+		return 1;	/* test */
+
+	switch(n->op) {
+	case OMUL:
+	case OLMUL:
+	case OASMUL:
+	case OASLMUL:
+		if(typechlv[n->type->etype])
+			return 1;
+		break;
+
+	case OADD:
+	case OAND:
+	case OOR:
+	case OSUB:
+	case OXOR:
+	case OASHL:
+	case OLSHR:
+	case OASHR:
+		if(typechlv[n->left->type->etype])
+			return 1;
+		break;
+
+	case OCAST:
+		return 1;
+
+	case OCOND:
+	case OCOMMA:
+	case OLIST:
+	case OANDAND:
+	case OOROR:
+	case ONOT:
+		return 1;
+
+	case OASADD:
+	case OASSUB:
+	case OASAND:
+	case OASOR:
+	case OASXOR:
+		return 1;
+
+	case OASASHL:
+	case OASASHR:
+	case OASLSHR:
+		return 1;
+
+	case OPOSTINC:
+	case OPOSTDEC:
+	case OPREINC:
+	case OPREDEC:
+		return 1;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OGT:
+	case OLT:
+	case OGE:
+	case OHI:
+	case OHS:
+	case OLO:
+	case OLS:
+		return 1;
+
+	case ONEG:
+		if(typechlv[n->left->type->etype])
+			return 1;
+		break;
+
+	case OCOM:
+		break;
+	}
+	return 0;
+}
--- /dev/null
+++ b/sys/src/cmd/7c/mkenam
@@ -1,0 +1,15 @@
+ed - ../7c/7.out.h <<'!'
+v/^	A/d
+,s/^	A/	"/
+g/ .*$/s///
+,s/,*$/",/
+1i
+char*	anames[] =
+{
+.
+$a
+};
+.
+w enam.c
+Q
+!
--- /dev/null
+++ b/sys/src/cmd/7c/mkfile
@@ -1,0 +1,41 @@
+</$objtype/mkfile
+
+TARG=7c
+OFILES=\
+	cgen.$O\
+	enam.$O\
+	list.$O\
+	machcap.$O\
+	mul.$O\
+	peep.$O\
+	pgen.$O\
+	pswt.$O\
+	reg.$O\
+	sgen.$O\
+	swt.$O\
+	txt.$O\
+
+HFILES=\
+	gc.h\
+	7.out.h\
+	../cc/cc.h\
+
+LIB=../cc/cc.a$O
+
+BIN=/$objtype/bin
+</sys/src/cmd/mkone
+
+$LIB:	../cc/cc.h
+	cd ../cc
+	mk install
+
+%.$O: ../cc/%.c
+	$CC $CFLAGS ../cc/$stem.c
+
+t:V:	$O.out
+	$O.out -S t
+	$LD -o t.out t.$O
+	t.out
+
+enam.c: 7.out.h
+	rc mkenam
--- /dev/null
+++ b/sys/src/cmd/7c/mul.c
@@ -1,0 +1,609 @@
+#include "gc.h"
+
+/*
+ * code sequences for multiply by constant.
+ * [a-l][0-3]
+ *	lsl	$(A-'a'),r0,r1
+ * [+][0-7]
+ *	add	r0,r1,r2
+ * [-][0-7]
+ *	sub	r0,r1,r2
+ */
+
+static  int	maxmulops = 3;	/* max # of ops to replace mul with */
+static	int	multabp;
+static	long	mulval;
+static	char*	mulcp;
+static	long	valmax;
+static	int	shmax;
+
+static int	docode(char *hp, char *cp, int r0, int r1);
+static int	gen1(int len);
+static int	gen2(int len, long r1);
+static int	gen3(int len, long r0, long r1, int flag);
+enum
+{
+	SR1	= 1<<0,		/* r1 has been shifted */
+	SR0	= 1<<1,		/* r0 has been shifted */
+	UR1	= 1<<2,		/* r1 has not been used */
+	UR0	= 1<<3,		/* r0 has not been used */
+};
+
+Multab*
+mulcon0(long v)
+{
+	int a1, a2, g;
+	Multab *m, *m1;
+	char hint[10];
+
+	if(v < 0)
+		v = -v;
+
+	/*
+	 * look in cache
+	 */
+	m = multab;
+	for(g=0; g<nelem(multab); g++) {
+		if(m->val == v) {
+			if(m->code[0] == 0)
+				return 0;
+			return m;
+		}
+		m++;
+	}
+
+	/*
+	 * select a spot in cache to overwrite
+	 */
+	multabp++;
+	if(multabp < 0 || multabp >= nelem(multab))
+		multabp = 0;
+	m = multab+multabp;
+	m->val = v;
+	mulval = v;
+
+	/*
+	 * look in execption hint table
+	 */
+	a1 = 0;
+	a2 = hintabsize;
+	for(;;) {
+		if(a1 >= a2)
+			goto no;
+		g = (a2 + a1)/2;
+		if(v < hintab[g].val) {
+			a2 = g;
+			continue;
+		}
+		if(v > hintab[g].val) {
+			a1 = g+1;
+			continue;
+		}
+		break;
+	}
+
+	if(docode(hintab[g].hint, m->code, 1, 0))
+		return m;
+	print("multiply table failure %ld\n", v);
+	m->code[0] = 0;
+	return 0;
+
+no:
+	/*
+	 * try to search
+	 */
+	hint[0] = 0;
+	for(g=1; g<=maxmulops; g++) {
+		if(g >= maxmulops && v >= 65535)
+			break;
+		mulcp = hint+g;
+		*mulcp = 0;
+		if(gen1(g)) {
+			if(docode(hint, m->code, 1, 0))
+				return m;
+			print("multiply table failure %ld\n", v);
+			break;
+		}
+	}
+
+	/*
+	 * try a recur followed by a shift
+	 */
+	g = 0;
+	while(!(v & 1)) {
+		g++;
+		v >>= 1;
+	}
+	if(g) {
+		m1 = mulcon0(v);
+		if(m1) {
+			strcpy(m->code, m1->code);
+			sprint(strchr(m->code, 0), "%c0", g+'a');
+			return m;
+		}
+	}
+	m->code[0] = 0;
+	return 0;
+}
+
+static int
+docode(char *hp, char *cp, int r0, int r1)
+{
+	int c, i;
+
+	c = *hp++;
+	*cp = c;
+	cp += 2;
+	switch(c) {
+	default:
+		c -= 'a';
+		if(c < 1 || c >= 30)
+			break;
+		for(i=0; i<4; i++) {
+			switch(i) {
+			case 0:
+				if(docode(hp, cp, r0<<c, r1))
+					goto out;
+				break;
+			case 1:
+				if(docode(hp, cp, r1<<c, r1))
+					goto out;
+				break;
+			case 2:
+				if(docode(hp, cp, r0, r0<<c))
+					goto out;
+				break;
+			case 3:
+				if(docode(hp, cp, r0, r1<<c))
+					goto out;
+				break;
+			}
+		}
+		break;
+
+	case '+':
+		for(i=0; i<8; i++) {
+			cp[-1] = i+'0';
+			switch(i) {
+			case 1:
+				if(docode(hp, cp, r0+r1, r1))
+					goto out;
+				break;
+			case 5:
+				if(docode(hp, cp, r0, r0+r1))
+					goto out;
+				break;
+			}
+		}
+		break;
+
+	case '-':
+		for(i=0; i<8; i++) {
+			cp[-1] = i+'0';
+			switch(i) {
+			case 1:
+				if(docode(hp, cp, r0-r1, r1))
+					goto out;
+				break;
+			case 2:
+				if(docode(hp, cp, r1-r0, r1))
+					goto out;
+				break;
+			case 5:
+				if(docode(hp, cp, r0, r0-r1))
+					goto out;
+				break;
+			case 6:
+				if(docode(hp, cp, r0, r1-r0))
+					goto out;
+				break;
+			}
+		}
+		break;
+
+	case 0:
+		if(r0 == mulval)
+			return 1;
+	}
+	return 0;
+
+out:
+	cp[-1] = i+'0';
+	return 1;
+}
+
+static int
+gen1(int len)
+{
+	int i;
+
+	for(shmax=1; shmax<30; shmax++) {
+		valmax = 1<<shmax;
+		if(valmax >= mulval)
+			break;
+	}
+	if(mulval == 1)
+		return 1;
+
+	len--;
+	for(i=1; i<=shmax; i++)
+		if(gen2(len, 1<<i)) {
+			*--mulcp = 'a'+i;
+			return 1;
+		}
+	return 0;
+}
+
+static int
+gen2(int len, long r1)
+{
+	int i;
+
+	if(len <= 0) {
+		if(r1 == mulval)
+			return 1;
+		return 0;
+	}
+
+	len--;
+	if(len == 0)
+		goto calcr0;
+
+	if(gen3(len, r1, r1+1, UR1)) {
+		i = '+';
+		goto out;
+	}
+	if(gen3(len, r1-1, r1, UR0)) {
+		i = '-';
+		goto out;
+	}
+	if(gen3(len, 1, r1+1, UR1)) {
+		i = '+';
+		goto out;
+	}
+	if(gen3(len, 1, r1-1, UR1)) {
+		i = '-';
+		goto out;
+	}
+
+	return 0;
+
+calcr0:
+	if(mulval == r1+1) {
+		i = '+';
+		goto out;
+	}
+	if(mulval == r1-1) {
+		i = '-';
+		goto out;
+	}
+	return 0;
+
+out:
+	*--mulcp = i;
+	return 1;
+}
+
+static int
+gen3(int len, long r0, long r1, int flag)
+{
+	int i, f1, f2;
+	long x;
+
+	if(r0 <= 0 ||
+	   r0 >= r1 ||
+	   r1 > valmax)
+		return 0;
+
+	len--;
+	if(len == 0)
+		goto calcr0;
+
+	if(!(flag & UR1)) {
+		f1 = UR1|SR1;
+		for(i=1; i<=shmax; i++) {
+			x = r0<<i;
+			if(x > valmax)
+				break;
+			if(gen3(len, r0, x, f1)) {
+				i += 'a';
+				goto out;
+			}
+		}
+	}
+
+	if(!(flag & UR0)) {
+		f1 = UR1|SR1;
+		for(i=1; i<=shmax; i++) {
+			x = r1<<i;
+			if(x > valmax)
+				break;
+			if(gen3(len, r1, x, f1)) {
+				i += 'a';
+				goto out;
+			}
+		}
+	}
+
+	if(!(flag & SR1)) {
+		f1 = UR1|SR1|(flag&UR0);
+		for(i=1; i<=shmax; i++) {
+			x = r1<<i;
+			if(x > valmax)
+				break;
+			if(gen3(len, r0, x, f1)) {
+				i += 'a';
+				goto out;
+			}
+		}
+	}
+
+	if(!(flag & SR0)) {
+		f1 = UR0|SR0|(flag&(SR1|UR1));
+
+		f2 = UR1|SR1;
+		if(flag & UR1)
+			f2 |= UR0;
+		if(flag & SR1)
+			f2 |= SR0;
+
+		for(i=1; i<=shmax; i++) {
+			x = r0<<i;
+			if(x > valmax)
+				break;
+			if(x > r1) {
+				if(gen3(len, r1, x, f2)) {
+					i += 'a';
+					goto out;
+				}
+			} else
+				if(gen3(len, x, r1, f1)) {
+					i += 'a';
+					goto out;
+				}
+		}
+	}
+
+	x = r1+r0;
+	if(gen3(len, r0, x, UR1)) {
+		i = '+';
+		goto out;
+	}
+
+	if(gen3(len, r1, x, UR1)) {
+		i = '+';
+		goto out;
+	}
+
+	x = r1-r0;
+	if(gen3(len, x, r1, UR0)) {
+		i = '-';
+		goto out;
+	}
+
+	if(x > r0) {
+		if(gen3(len, r0, x, UR1)) {
+			i = '-';
+			goto out;
+		}
+	} else
+		if(gen3(len, x, r0, UR0)) {
+			i = '-';
+			goto out;
+		}
+
+	return 0;
+
+calcr0:
+	f1 = flag & (UR0|UR1);
+	if(f1 == UR1) {
+		for(i=1; i<=shmax; i++) {
+			x = r1<<i;
+			if(x >= mulval) {
+				if(x == mulval) {
+					i += 'a';
+					goto out;
+				}
+				break;
+			}
+		}
+	}
+
+	if(mulval == r1+r0) {
+		i = '+';
+		goto out;
+	}
+	if(mulval == r1-r0) {
+		i = '-';
+		goto out;
+	}
+
+	return 0;
+
+out:
+	*--mulcp = i;
+	return 1;
+}
+
+/*
+ * hint table has numbers that
+ * the search algorithm fails on.
+ * <1000:
+ *	all numbers
+ * <5000:
+ * 	÷ by 5
+ * <10000:
+ * 	÷ by 50
+ * <65536:
+ * 	÷ by 250
+ */
+Hintab	hintab[] =
+{
+	683,	"b++d+e+",
+	687,	"b+e++e-",
+	691,	"b++d+e+",
+	731,	"b++d+e+",
+	811,	"b++d+i+",
+	821,	"b++e+e+",
+	843,	"b+d++e+",
+	851,	"b+f-+e-",
+	853,	"b++e+e+",
+	877,	"c++++g-",
+	933,	"b+c++g-",
+	981,	"c-+e-d+",
+	1375,	"b+c+b+h-",
+	1675,	"d+b++h+",
+	2425,	"c++f-e+",
+	2675,	"c+d++f-",
+	2750,	"b+d-b+h-",
+	2775,	"c-+g-e-",
+	3125,	"b++e+g+",
+	3275,	"b+c+g+e+",
+	3350,	"c++++i+",
+	3475,	"c-+e-f-",
+	3525,	"c-+d+g-",
+	3625,	"c-+e-j+",
+	3675,	"b+d+d+e+",
+	3725,	"b+d-+h+",
+	3925,	"b+d+f-d-",
+	4275,	"b+g++e+",
+	4325,	"b+h-+d+",
+	4425,	"b+b+g-j-",
+	4525,	"b+d-d+f+",
+	4675,	"c++d-g+",
+	4775,	"b+d+b+g-",
+	4825,	"c+c-+i-",
+	4850,	"c++++i-",
+	4925,	"b++e-g-",
+	4975,	"c+f++e-",
+	5500,	"b+g-c+d+",
+	6700,	"d+b++i+",
+	9700,	"d++++j-",
+	11000,	"b+f-c-h-",
+	11750,	"b+d+g+j-",
+	12500,	"b+c+e-k+",
+	13250,	"b+d+e-f+",
+	13750,	"b+h-c-d+",
+	14250,	"b+g-c+e-",
+	14500,	"c+f+j-d-",
+	14750,	"d-g--f+",
+	16750,	"b+e-d-n+",
+	17750,	"c+h-b+e+",
+	18250,	"d+b+h-d+",
+	18750,	"b+g-++f+",
+	19250,	"b+e+b+h+",
+	19750,	"b++h--f-",
+	20250,	"b+e-l-c+",
+	20750,	"c++bi+e-",
+	21250,	"b+i+l+c+",
+	22000,	"b+e+d-g-",
+	22250,	"b+d-h+k-",
+	22750,	"b+d-e-g+",
+	23250,	"b+c+h+e-",
+	23500,	"b+g-c-g-",
+	23750,	"b+g-b+h-",
+	24250,	"c++g+m-",
+	24750,	"b+e+e+j-",
+	25000,	"b++dh+g+",
+	25250,	"b+e+d-g-",
+	25750,	"b+e+b+j+",
+	26250,	"b+h+c+e+",
+	26500,	"b+h+c+g+",
+	26750,	"b+d+e+g-",
+	27250,	"b+e+e+f+",
+	27500,	"c-i-c-d+",
+	27750,	"b+bd++j+",
+	28250,	"d-d-++i-",
+	28500,	"c+c-h-e-",
+	29000,	"b+g-d-f+",
+	29500,	"c+h+++e-",
+	29750,	"b+g+f-c+",
+	30250,	"b+f-g-c+",
+	33500,	"c-f-d-n+",
+	33750,	"b+d-b+j-",
+	34250,	"c+e+++i+",
+	35250,	"e+b+d+k+",
+	35500,	"c+e+d-g-",
+	35750,	"c+i-++e+",
+	36250,	"b+bh-d+e+",
+	36500,	"c+c-h-e-",
+	36750,	"d+e--i+",
+	37250,	"b+g+g+b+",
+	37500,	"b+h-b+f+",
+	37750,	"c+be++j-",
+	38500,	"b+e+b+i+",
+	38750,	"d+i-b+d+",
+	39250,	"b+g-l-+d+",
+	39500,	"b+g-c+g-",
+	39750,	"b+bh-c+f-",
+	40250,	"b+bf+d+g-",
+	40500,	"b+g-c+g+",
+	40750,	"c+b+i-e+",
+	41250,	"d++bf+h+",
+	41500,	"b+j+c+d-",
+	41750,	"c+f+b+h-",
+	42500,	"c+h++g+",
+	42750,	"b+g+d-f-",
+	43250,	"b+l-e+d-",
+	43750,	"c+bd+h+f-",
+	44000,	"b+f+g-d-",
+	44250,	"b+d-g--f+",
+	44500,	"c+e+c+h+",
+	44750,	"b+e+d-h-",
+	45250,	"b++g+j-g+",
+	45500,	"c+d+e-g+",
+	45750,	"b+d-h-e-",
+	46250,	"c+bd++j+",
+	46500,	"b+d-c-j-",
+	46750,	"e-e-b+g-",
+	47000,	"b+c+d-j-",
+	47250,	"b+e+e-g-",
+	47500,	"b+g-c-h-",
+	47750,	"b+f-c+h-",
+	48250,	"d--h+n-",
+	48500,	"b+c-g+m-",
+	48750,	"b+e+e-g+",
+	49500,	"c-f+e+j-",
+	49750,	"c+c+g++f-",
+	50000,	"b+e+e+k+",
+	50250,	"b++i++g+",
+	50500,	"c+g+f-i+",
+	50750,	"b+e+d+k-",
+	51500,	"b+i+c-f+",
+	51750,	"b+bd+g-e-",
+	52250,	"b+d+g-j+",
+	52500,	"c+c+f+g+",
+	52750,	"b+c+e+i+",
+	53000,	"b+i+c+g+",
+	53500,	"c+g+g-n+",
+	53750,	"b+j+d-c+",
+	54250,	"b+d-g-j-",
+	54500,	"c-f+e+f+",
+	54750,	"b+f-+c+g+",
+	55000,	"b+g-d-g-",
+	55250,	"b+e+e+g+",
+	55500,	"b+cd++j+",
+	55750,	"b+bh-d-f-",
+	56250,	"c+d-b+j-",
+	56500,	"c+d+c+i+",
+	56750,	"b+e+d++h-",
+	57000,	"b+d+g-f+",
+	57250,	"b+f-m+d-",
+	57750,	"b+i+c+e-",
+	58000,	"b+e+d+h+",
+	58250,	"c+b+g+g+",
+	58750,	"d-e-j--e+",
+	59000,	"d-i-+e+",
+	59250,	"e--h-m+",
+	59500,	"c+c-h+f-",
+	59750,	"b+bh-e+i-",
+	60250,	"b+bh-e-e-",
+	60500,	"c+c-g-g-",
+	60750,	"b+e-l-e-",
+	61250,	"b+g-g-c+",
+	61750,	"b+g-c+g+",
+	62250,	"f--+c-i-",
+	62750,	"e+f--+g+",
+	64750,	"b+f+d+p-",
+};
+int	hintabsize	= nelem(hintab);
--- /dev/null
+++ b/sys/src/cmd/7c/peep.c
@@ -1,0 +1,1546 @@
+#include "gc.h"
+
+int xtramodes(Reg*, Adr*);
+
+void
+peep(void)
+{
+	Reg *r, *r1, *r2;
+	Prog *p, *p1;
+	int t;
+/*
+ * complete R structure
+ */
+	t = 0;
+	for(r=firstr; r!=R; r=r1) {
+		r1 = r->link;
+		if(r1 == R)
+			break;
+		p = r->prog->link;
+		while(p != r1->prog)
+		switch(p->as) {
+		default:
+			r2 = rega();
+			r->link = r2;
+			r2->link = r1;
+
+			r2->prog = p;
+			r2->p1 = r;
+			r->s1 = r2;
+			r2->s1 = r1;
+			r1->p1 = r2;
+
+			r = r2;
+			t++;
+
+		case ADATA:
+		case AGLOBL:
+		case ANAME:
+		case ASIGNAME:
+			p = p->link;
+		}
+	}
+
+loop1:
+	t = 0;
+	for(r=firstr; r!=R; r=r->link) {
+		p = r->prog;
+		if(p->as == ALSL || p->as == ALSR || p->as == AASR ||
+		   p->as == ALSLW || p->as == ALSRW || p->as == AASRW) {
+			/*
+			 * elide shift into D_SHIFT operand of subsequent instruction
+			 */
+			if(0 && shiftprop(r)) {
+				excise(r);
+				t++;
+			}
+		}
+		if(p->as == AMOV || p->as == AMOVW || p->as == AFMOVS || p->as == AFMOVD)
+		if(regtyp(&p->to)) {
+			if(p->from.type == D_CONST)
+				constprop(&p->from, &p->to, r->s1);
+			else if(regtyp(&p->from))
+			if(p->from.type == p->to.type) {
+				if(copyprop(r)) {
+					excise(r);
+					t++;
+				} else
+				if(subprop(r) && copyprop(r)) {
+					excise(r);
+					t++;
+				}
+			}
+			if(regzer(&p->from))
+			if(p->to.type == D_REG) {
+				p->from.type = D_REG;
+				p->from.reg = REGZERO;
+				if(copyprop(r)) {
+					excise(r);
+					t++;
+				} else
+				if(subprop(r) && copyprop(r)) {
+					excise(r);
+					t++;
+				}
+			}
+		}
+	}
+	if(t)
+		goto loop1;
+	/*
+	 * look for MOVB x,R; MOVB R,R
+	 */
+	for(r=firstr; r!=R; r=r->link) {
+		p = r->prog;
+		switch(p->as) {
+		default:
+			continue;
+		case AEOR:
+			/*
+			 * EOR -1,x,y => MVN x,y
+			 */
+			if(p->from.type == D_CONST && p->from.offset == -1) {
+				p->as = AMVN;
+				p->from.type = D_REG;
+				if(p->reg != NREG)
+					p->from.reg = p->reg;
+				else
+					p->from.reg = p->to.reg;
+				p->reg = NREG;
+			}
+			continue;
+		case AEORW:
+			/*
+			 * EOR -1,x,y => MVN x,y
+			 */
+			if(p->from.type == D_CONST && (p->from.offset&0xFFFFFFFF)==0xFFFFFFFF){
+				p->as = AMVNW;
+				p->from.type = D_REG;
+				if(p->reg != NREG)
+					p->from.reg = p->reg;
+				else
+					p->from.reg = p->to.reg;
+				p->reg = NREG;
+			}
+			continue;
+		case AMOVH:
+		case AMOVHU:
+		case AMOVB:
+		case AMOVBU:
+		case AMOVW:
+		case AMOVWU:
+			if(p->to.type != D_REG)
+				continue;
+			break;
+		}
+		r1 = r->link;
+		if(r1 == R)
+			continue;
+		p1 = r1->prog;
+		if(p1->as != p->as)
+			continue;
+		if(p1->from.type != D_REG || p1->from.reg != p->to.reg)
+			continue;
+		if(p1->to.type != D_REG || p1->to.reg != p->to.reg)
+			continue;
+		excise(r1);
+	}
+
+#ifdef NOTYET
+	for(r=firstr; r!=R; r=r->link) {
+		p = r->prog;
+		switch(p->as) {
+		case AMOVW:
+		case AMOVB:
+		case AMOVBU:
+			if(p->from.type == D_OREG && p->from.offset == 0)
+				xtramodes(r, &p->from);
+			else if(p->to.type == D_OREG && p->to.offset == 0)
+				xtramodes(r, &p->to);
+			else
+				continue;
+			break;
+		case ACMP:
+			/*
+			 * elide CMP $0,x if calculation of x can set condition codes
+			 */
+			if(p->from.type != D_CONST || p->from.offset != 0)
+				continue;
+			r2 = r->s1;
+			if(r2 == R)
+				continue;
+			t = r2->prog->as;
+			switch(t) {
+			default:
+				continue;
+			case ABEQ:
+			case ABNE:
+			case ABMI:
+			case ABPL:
+				break;
+			case ABGE:
+				t = ABPL;
+				break;
+			case ABLT:
+				t = ABMI;
+				break;
+			case ABHI:
+				t = ABNE;
+				break;
+			case ABLS:
+				t = ABEQ;
+				break;
+			}
+			r1 = r;
+			do
+				r1 = uniqp(r1);
+			while (r1 != R && r1->prog->as == ANOP);
+			if(r1 == R)
+				continue;
+			p1 = r1->prog;
+			if(p1->to.type != D_REG)
+				continue;
+			if(p1->to.reg != p->reg)
+			if(!(p1->as == AMOVW && p1->from.type == D_REG && p1->from.reg == p->reg))
+				continue;
+			switch(p1->as) {
+			default:
+				continue;
+			case AMOVW:
+				if(p1->from.type != D_REG)
+					continue;
+			case AAND:
+			case AEOR:
+			case AORR:
+			case ABIC:
+			case AMVN:
+			case ASUB:
+			case AADD:
+			case AADC:
+			case ASBC:
+				break;
+			}
+			p1->scond |= C_SBIT;
+			r2->prog->as = t;
+			excise(r);
+			continue;
+		}
+	}
+#endif
+
+#ifdef XXX
+	predicate();
+#endif
+}
+
+void
+excise(Reg *r)
+{
+	Prog *p;
+
+	p = r->prog;
+	p->as = ANOP;
+	p->scond = zprog.scond;
+	p->from = zprog.from;
+	p->to = zprog.to;
+	p->reg = zprog.reg; /**/
+}
+
+Reg*
+uniqp(Reg *r)
+{
+	Reg *r1;
+
+	r1 = r->p1;
+	if(r1 == R) {
+		r1 = r->p2;
+		if(r1 == R || r1->p2link != R)
+			return R;
+	} else
+		if(r->p2 != R)
+			return R;
+	return r1;
+}
+
+Reg*
+uniqs(Reg *r)
+{
+	Reg *r1;
+
+	r1 = r->s1;
+	if(r1 == R) {
+		r1 = r->s2;
+		if(r1 == R)
+			return R;
+	} else
+		if(r->s2 != R)
+			return R;
+	return r1;
+}
+
+/*
+ * convert references to $0 to references to ZR (REGZERO)
+ */
+int
+regzer(Adr *a)
+{
+return 0;
+	switch(a->type){
+	case D_CONST:
+		return a->sym == S && a->offset == 0;
+	case D_REG:
+		return a->reg == REGZERO;
+	}
+	return 0;
+}
+
+int
+regtyp(Adr *a)
+{
+
+	if(a->type == D_REG){
+		if(a->reg == REGZERO)
+			return 0;
+		return 1;
+	}
+	if(a->type == D_FREG)
+		return 1;
+	return 0;
+}
+
+/*
+ * the idea is to substitute
+ * one register for another
+ * from one MOV to another
+ *	MOV	a, R0
+ *	ADD	b, R0	/ no use of R1
+ *	MOV	R0, R1
+ * would be converted to
+ *	MOV	a, R1
+ *	ADD	b, R1
+ *	MOV	R1, R0
+ * hopefully, then the former or latter MOV
+ * will be eliminated by copy propagation.
+ */
+int
+subprop(Reg *r0)
+{
+	Prog *p;
+	Adr *v1, *v2;
+	Reg *r;
+	int t;
+
+	p = r0->prog;
+	v1 = &p->from;
+	if(!regtyp(v1))
+		return 0;
+	v2 = &p->to;
+	if(!regtyp(v2))
+		return 0;
+	for(r=uniqp(r0); r!=R; r=uniqp(r)) {
+		if(uniqs(r) == R)
+			break;
+		p = r->prog;
+		switch(p->as) {
+		case ABL:
+			return 0;
+
+		case ACMP:
+		case ACMN:
+		case AADC:
+		case AADCS:
+		case AADD:
+		case AADDS:
+		case ASUB:
+		case ASUBS:
+		case ALSL:
+		case ALSR:
+		case AASR:
+		case AORR:
+		case AAND:
+		case AANDS:
+		case AEOR:
+		case AMUL:
+		case ASDIV:
+		case AUDIV:
+		case AREM:
+		case AUREM:
+
+		case ACMPW:
+		case ACMNW:
+		case AADCW:
+		case AADCSW:
+		case AADDSW:
+		case AADDW:
+		case ASUBSW:
+		case ASUBW:
+		case ALSLW:
+		case ALSRW:
+		case AASRW:
+		case AORRW:
+		case AANDW:
+		case AANDSW:
+		case AEORW:
+		case AMULW:
+		case ASDIVW:
+		case AUDIVW:
+		case AREMW:
+		case AUREMW:
+
+		case AFCMPS:
+		case AFCMPD:
+		case AFADDD:
+		case AFADDS:
+		case AFSUBD:
+		case AFSUBS:
+		case AFMULD:
+		case AFMULS:
+		case AFDIVD:
+		case AFDIVS:
+			if(p->to.type == v1->type)
+			if(p->to.reg == v1->reg) {
+				if(p->reg == NREG)
+					p->reg = p->to.reg;
+				goto gotit;
+			}
+			break;
+
+		case ANEG:
+		case ANEGS:
+		case ANEGSW:
+		case ANEGW:
+		case ANGC:
+		case ANGCS:
+		case ANGCSW:
+		case ANGCW:
+		case AMVN:
+		case AMVNW:
+		case AFNEGD:
+		case AFNEGS:
+		case AFABSS:
+		case AFABSD:
+		case AFSQRTS:
+		case AFSQRTD:
+		case AFMOVS:
+		case AFMOVD:
+		case AMOVW:
+		case AMOV:
+			if(p->to.type == v1->type)
+			if(p->to.reg == v1->reg)
+				goto gotit;
+			break;
+
+		}
+		if(copyau(&p->from, v2) ||
+		   copyau1(p, v2) ||
+		   copyau(&p->to, v2))
+			break;
+		if(copysub(&p->from, v1, v2, 0) ||
+		   copysub1(p, v1, v2, 0) ||
+		   copysub(&p->to, v1, v2, 0))
+			break;
+	}
+	return 0;
+
+gotit:
+	copysub(&p->to, v1, v2, 1);
+	if(debug['P']) {
+		print("gotit: %D->%D\n%P", v1, v2, r->prog);
+		if(p->from.type == v2->type)
+			print(" excise");
+		print("\n");
+	}
+	for(r=uniqs(r); r!=r0; r=uniqs(r)) {
+		p = r->prog;
+		copysub(&p->from, v1, v2, 1);
+		copysub1(p, v1, v2, 1);
+		copysub(&p->to, v1, v2, 1);
+		if(debug['P'])
+			print("%P\n", r->prog);
+	}
+	t = v1->reg;
+	v1->reg = v2->reg;
+	v2->reg = t;
+	if(debug['P'])
+		print("%P last\n", r->prog);
+	return 1;
+}
+
+/*
+ * The idea is to remove redundant copies.
+ *	v1->v2	F=0
+ *	(use v2	s/v2/v1/)*
+ *	set v1	F=1
+ *	use v2	return fail
+ *	-----------------
+ *	v1->v2	F=0
+ *	(use v2	s/v2/v1/)*
+ *	set v1	F=1
+ *	set v2	return success
+ */
+int
+copyprop(Reg *r0)
+{
+	Prog *p;
+	Adr *v1, *v2;
+	Reg *r;
+
+	p = r0->prog;
+	v1 = &p->from;
+	v2 = &p->to;
+	if(copyas(v1, v2))
+		return 1;
+	for(r=firstr; r!=R; r=r->link)
+		r->active = 0;
+	return copy1(v1, v2, r0->s1, 0);
+}
+
+int
+copy1(Adr *v1, Adr *v2, Reg *r, int f)
+{
+	int t;
+	Prog *p;
+
+	if(r->active) {
+		if(debug['P'])
+			print("act set; return 1\n");
+		return 1;
+	}
+	r->active = 1;
+	if(debug['P'])
+		print("copy %D->%D f=%d\n", v1, v2, f);
+	for(; r != R; r = r->s1) {
+		p = r->prog;
+		if(debug['P'])
+			print("%P", p);
+		if(!f && uniqp(r) == R) {
+			f = 1;
+			if(debug['P'])
+				print("; merge; f=%d", f);
+		}
+		t = copyu(p, v2, A);
+		switch(t) {
+		case 2:	/* rar, cant split */
+			if(debug['P'])
+				print("; %Drar; return 0\n", v2);
+			return 0;
+
+		case 3:	/* set */
+			if(debug['P'])
+				print("; %Dset; return 1\n", v2);
+			return 1;
+
+		case 1:	/* used, substitute */
+		case 4:	/* use and set */
+			if(f) {
+				if(!debug['P'])
+					return 0;
+				if(t == 4)
+					print("; %Dused+set and f=%d; return 0\n", v2, f);
+				else
+					print("; %Dused and f=%d; return 0\n", v2, f);
+				return 0;
+			}
+			if(copyu(p, v2, v1)) {
+				if(debug['P'])
+					print("; sub fail; return 0\n");
+				return 0;
+			}
+			if(debug['P'])
+				print("; sub%D/%D", v2, v1);
+			if(t == 4) {
+				if(debug['P'])
+					print("; %Dused+set; return 1\n", v2);
+				return 1;
+			}
+			break;
+		}
+		if(!f) {
+			t = copyu(p, v1, A);
+			if(!f && (t == 2 || t == 3 || t == 4)) {
+				f = 1;
+				if(debug['P'])
+					print("; %Dset and !f; f=%d", v1, f);
+			}
+		}
+		if(debug['P'])
+			print("\n");
+		if(r->s2)
+			if(!copy1(v1, v2, r->s2, f))
+				return 0;
+	}
+	return 1;
+}
+
+/*
+ * The idea is to remove redundant constants.
+ *	$c1->v1
+ *	($c1->v2 s/$c1/v1)*
+ *	set v1  return
+ * The v1->v2 should be eliminated by copy propagation.
+ */
+void
+constprop(Adr *c1, Adr *v1, Reg *r)
+{
+	Prog *p;
+
+	if(debug['C'])
+		print("constprop %D->%D\n", c1, v1);
+	for(; r != R; r = r->s1) {
+		p = r->prog;
+		if(debug['C'])
+			print("%P", p);
+		if(uniqp(r) == R) {
+			if(debug['C'])
+				print("; merge; return\n");
+			return;
+		}
+		if(p->as == AMOVW && copyas(&p->from, c1)) {
+			if(debug['C'])
+				print("; sub%D/%D", &p->from, v1);
+			p->from = *v1;
+		} else if(copyu(p, v1, A) > 1) {
+			if(debug['C'])
+				print("; %Dset; return\n", v1);
+			return;
+		}
+		if(debug['C'])
+			print("\n");
+		if(r->s2)
+			constprop(c1, v1, r->s2);
+	}
+}
+
+/*
+ * ALSL x,y,w
+ * .. (not use w, not set x y w)
+ * AXXX w,a,b (a != w)
+ * .. (not use w)
+ * (set w)
+ * ----------- changed to
+ * ..
+ * AXXX (x<<y),a,b
+ * ..
+ */
+#define FAIL(msg) { if(debug['H']) print("\t%s; FAILURE\n", msg); return 0; }
+int
+shiftprop(Reg *r)
+{
+	Reg *r1;
+	Prog *p, *p1, *p2;
+	int n, o;
+	Adr a;
+
+	p = r->prog;
+	if(p->to.type != D_REG)
+		FAIL("BOTCH: result not reg");
+	n = p->to.reg;
+	a = zprog.from;
+	if(p->reg != NREG && p->reg != p->to.reg) {
+		a.type = D_REG;
+		a.reg = p->reg;
+	}
+	if(debug['H'])
+		print("shiftprop\n%P", p);
+	r1 = r;
+	for(;;) {
+		/* find first use of shift result; abort if shift operands or result are changed */
+		r1 = uniqs(r1);
+		if(r1 == R)
+			FAIL("branch");
+		if(uniqp(r1) == R)
+			FAIL("merge");
+		p1 = r1->prog;
+		if(debug['H'])
+			print("\n%P", p1);
+		switch(copyu(p1, &p->to, A)) {
+		case 0:	/* not used or set */
+			if((p->from.type == D_REG && copyu(p1, &p->from, A) > 1) ||
+			   (a.type == D_REG && copyu(p1, &a, A) > 1))
+				FAIL("args modified");
+			continue;
+		case 3:	/* set, not used */
+			FAIL("BOTCH: noref");
+		}
+		break;
+	}
+	/* check whether substitution can be done */
+	switch(p1->as) {
+	case ABIC:
+	case ACMP:
+	case ACMN:
+		if(p1->reg == n)
+			FAIL("can't swap");
+		if(p1->reg == NREG && p1->to.reg == n)
+			FAIL("shift result used twice");
+	case AMVN:
+		if(p1->from.type == D_SHIFT)
+			FAIL("shift result used in shift");
+		if(p1->from.type != D_REG || p1->from.reg != n)
+			FAIL("BOTCH: where is it used?");
+		break;
+	}
+	/* check whether shift result is used subsequently */
+	p2 = p1;
+	if(p1->to.reg != n)
+	for (;;) {
+		r1 = uniqs(r1);
+		if(r1 == R)
+			FAIL("inconclusive");
+		p1 = r1->prog;
+		if(debug['H'])
+			print("\n%P", p1);
+		switch(copyu(p1, &p->to, A)) {
+		case 0:	/* not used or set */
+			continue;
+		case 3: /* set, not used */
+			break;
+		default:/* used */
+			FAIL("reused");
+		}
+		break;
+	}
+	/* make the substitution */
+	p2->from.type = D_SHIFT;
+	p2->from.reg = NREG;
+	o = p->reg;
+	if(o == NREG)
+		o = p->to.reg;
+	switch(p->from.type){
+	case D_CONST:
+		o |= (p->from.offset&0x3f)<<7;
+		break;
+	case D_REG:
+		o |= (1<<4) | (p->from.reg<<8);
+		break;
+	}
+	switch(p->as){
+	case ALSL:
+		o |= 0<<5;
+		break;
+	case ALSR:
+		o |= 1<<5;
+		break;
+	case AASR:
+		o |= 2<<5;
+		break;
+	}
+	p2->from.offset = o;
+	if(debug['H'])
+		print("\t=>%P\tSUCCEED\n", p2);
+	return 1;
+}
+
+Reg*
+findpre(Reg *r, Adr *v)
+{
+	Reg *r1;
+
+	for(r1=uniqp(r); r1!=R; r=r1,r1=uniqp(r)) {
+		if(uniqs(r1) != r)
+			return R;
+		switch(copyu(r1->prog, v, A)) {
+		case 1: /* used */
+		case 2: /* read-alter-rewrite */
+			return R;
+		case 3: /* set */
+		case 4: /* set and used */
+			return r1;
+		}
+	}
+	return R;
+}
+
+Reg*
+findinc(Reg *r, Reg *r2, Adr *v)
+{
+	Reg *r1;
+	Prog *p;
+
+
+	for(r1=uniqs(r); r1!=R && r1!=r2; r=r1,r1=uniqs(r)) {
+		if(uniqp(r1) != r)
+			return R;
+		switch(copyu(r1->prog, v, A)) {
+		case 0: /* not touched */
+			continue;
+		case 4: /* set and used */
+			p = r1->prog;
+			if(p->as == AADD)
+			if(p->from.type == D_CONST)
+			if(p->from.offset > -256 && p->from.offset < 256)
+				return r1;
+		default:
+			return R;
+		}
+	}
+	return R;
+}
+
+int
+nochange(Reg *r, Reg *r2, Prog *p)
+{
+	Adr a[3];
+	int i, n;
+
+	if(r == r2)
+		return 1;
+	n = 0;
+	if(p->reg != NREG && p->reg != p->to.reg) {
+		a[n].type = D_REG;
+		a[n++].reg = p->reg;
+	}
+	switch(p->from.type) {
+	case D_SHIFT:
+		a[n].type = D_REG;
+		a[n++].reg = p->from.offset&0xf;
+	case D_REG:
+		a[n].type = D_REG;
+		a[n++].reg = p->from.reg;
+	}
+	if(n == 0)
+		return 1;
+	for(; r!=R && r!=r2; r=uniqs(r)) {
+		p = r->prog;
+		for(i=0; i<n; i++)
+			if(copyu(p, &a[i], A) > 1)
+				return 0;
+	}
+	return 1;
+}
+
+int
+findu1(Reg *r, Adr *v)
+{
+	for(; r != R; r = r->s1) {
+		if(r->active)
+			return 0;
+		r->active = 1;
+		switch(copyu(r->prog, v, A)) {
+		case 1: /* used */
+		case 2: /* read-alter-rewrite */
+		case 4: /* set and used */
+			return 1;
+		case 3: /* set */
+			return 0;
+		}
+		if(r->s2)
+			if (findu1(r->s2, v))
+				return 1;
+	}
+	return 0;
+}
+
+int
+finduse(Reg *r, Adr *v)
+{
+	Reg *r1;
+
+	for(r1=firstr; r1!=R; r1=r1->link)
+		r1->active = 0;
+	return findu1(r, v);
+}
+
+#ifdef NOTYET
+int
+xtramodes(Reg *r, Adr *a)
+{
+	Reg *r1, *r2, *r3;
+	Prog *p, *p1;
+	Adr v;
+
+	p = r->prog;
+	if(debug['h'] && p->as == AMOVB && p->from.type == D_OREG)	/* byte load */
+		return 0;
+	v = *a;
+	v.type = D_REG;
+	r1 = findpre(r, &v);
+	if(r1 != R) {
+		p1 = r1->prog;
+		if(p1->to.type == D_REG && p1->to.reg == v.reg)
+		switch(p1->as) {
+		case AADD:
+			if(p1->from.type == D_REG ||
+			   (p1->from.type == D_SHIFT && (p1->from.offset&(1<<4)) == 0 &&
+			    (p->as != AMOVB || (a == &p->from && (p1->from.offset&~0xf) == 0))) ||
+			   (p1->from.type == D_CONST && 
+			    p1->from.offset > -4096 && p1->from.offset < 4096))
+			if(nochange(uniqs(r1), r, p1)) {
+				if(a != &p->from || v.reg != p->to.reg)
+				if (finduse(r->s1, &v)) {
+					if(p1->reg == NREG || p1->reg == v.reg)
+						/* pre-indexing */
+						p->scond |= C_WBIT;
+					else return 0;	
+				}
+				switch (p1->from.type) {
+				case D_REG:
+					/* register offset */
+					a->type = D_SHIFT;
+					a->offset = p1->from.reg;
+					break;
+				case D_SHIFT:
+					/* scaled register offset */
+					a->type = D_SHIFT;
+				case D_CONST:
+					/* immediate offset */
+					a->offset = p1->from.offset;
+					break;
+				}
+				if(p1->reg != NREG)
+					a->reg = p1->reg;
+				excise(r1);
+				return 1;
+			}
+			break;
+		case AMOVW:
+			if(p1->from.type == D_REG)
+			if((r2 = findinc(r1, r, &p1->from)) != R) {
+			for(r3=uniqs(r2); r3->prog->as==ANOP; r3=uniqs(r3))
+				;
+			if(r3 == r) {
+				/* post-indexing */
+				p1 = r2->prog;
+				a->reg = p1->to.reg;
+				a->offset = p1->from.offset;
+				p->scond |= C_PBIT;
+				if(!finduse(r, &r1->prog->to))
+					excise(r1);
+				excise(r2);
+				return 1;
+			}
+			}
+			break;
+		}
+	}
+	if(a != &p->from || a->reg != p->to.reg)
+	if((r1 = findinc(r, R, &v)) != R) {
+		/* post-indexing */
+		p1 = r1->prog;
+		a->offset = p1->from.offset;
+		p->scond |= C_PBIT;
+		excise(r1);
+		return 1;
+	}
+	return 0;
+}
+#endif
+
+/*
+ * return
+ * 1 if v only used (and substitute),
+ * 2 if read-alter-rewrite
+ * 3 if set
+ * 4 if set and used
+ * 0 otherwise (not touched)
+ */
+int
+copyu(Prog *p, Adr *v, Adr *s)
+{
+
+	switch(p->as) {
+
+	default:
+		if(debug['P'])
+			print(" (unk)");
+		return 2;
+
+	case ANOP:	/* read, write */
+	case AFMOVS:
+	case AFMOVD:
+	case AMOVH:
+	case AMOVHU:
+	case AMOVB:
+	case AMOVBU:
+	case AMOVW:
+	case AMOVWU:
+	case AMOV:
+
+	case AMVN:
+	case AMVNW:
+	case ANEG:
+	case ANEGS:
+	case ANEGW:
+	case ANEGSW:
+	case ANGC:
+	case ANGCS:
+	case ANGCW:
+	case ANGCSW:
+	case AFCVTSD:
+	case AFCVTDS:
+	case AFCVTZSD:
+	case AFCVTZSDW:
+	case AFCVTZSS:
+	case AFCVTZSSW:
+	case AFCVTZUD:
+	case AFCVTZUDW:
+	case AFCVTZUS:
+	case AFCVTZUSW:
+	case ASCVTFD:
+	case ASCVTFS:
+	case ASCVTFWD:
+	case ASCVTFWS:
+	case AUCVTFD:
+	case AUCVTFS:
+	case AUCVTFWD:
+	case AUCVTFWS:
+	case AFNEGD:
+	case AFNEGS:
+	case AFABSD:
+	case AFABSS:
+	case AFSQRTD:
+	case AFSQRTS:
+	case ACASE:
+#ifdef YYY
+		if(p->scond&(C_WBIT|C_PBIT))
+		if(v->type == D_REG) {
+			if(p->from.type == D_OREG || p->from.type == D_SHIFT) {
+				if(p->from.reg == v->reg)
+					return 2;
+			} else {
+		  		if(p->to.reg == v->reg)
+					return 2;
+			}
+		}
+#endif
+		if(s != A) {
+			if(copysub(&p->from, v, s, 1))
+				return 1;
+			if(!copyas(&p->to, v))
+				if(copysub(&p->to, v, s, 1))
+					return 1;
+			return 0;
+		}
+		if(copyas(&p->to, v)) {
+			if(copyau(&p->from, v))
+				return 4;
+			return 3;
+		}
+		if(copyau(&p->from, v))
+			return 1;
+		if(copyau(&p->to, v))
+			return 1;
+		return 0;
+
+
+	case AADD:	/* read, read, write */
+	case AADDW:
+	case AADDS:
+	case AADDSW:
+	case ASUB:
+	case ASUBW:
+	case ASUBS:
+	case ASUBSW:
+		if(0 && p->from.type == D_CONST){
+			if(s != A && regzer(s))
+				return 4;	/* add/sub $c,r,r -> r31 is sp not zr, don't touch */
+		}
+
+	case ALSL:
+	case ALSLW:
+	case ALSR:
+	case ALSRW:
+	case AASR:
+	case AASRW:
+	case AORR:
+	case AORRW:
+	case AAND:
+	case AANDW:
+	case AEOR:
+	case AEORW:
+	case AMUL:
+	case AMULW:
+	case AUMULL:
+	case AREM:
+	case AREMW:
+	case ASDIV:
+	case ASDIVW:
+	case AUDIV:
+	case AUDIVW:
+	case AUREM:
+	case AUREMW:
+	case AFADDS:
+	case AFADDD:
+	case AFSUBS:
+	case AFSUBD:
+	case AFMULS:
+	case AFMULD:
+	case AFDIVS:
+	case AFDIVD:
+		if(s != A) {
+			if(copysub(&p->from, v, s, 1))
+				return 1;
+			if(copysub1(p, v, s, 1))
+				return 1;
+			if(!copyas(&p->to, v))
+				if(copysub(&p->to, v, s, 1))
+					return 1;
+			return 0;
+		}
+		if(copyas(&p->to, v)) {
+			if(p->reg == NREG)
+				p->reg = p->to.reg;
+			if(copyau(&p->from, v))
+				return 4;
+			if(copyau1(p, v))
+				return 4;
+			return 3;
+		}
+		if(copyau(&p->from, v))
+			return 1;
+		if(copyau1(p, v))
+			return 1;
+		if(copyau(&p->to, v))
+			return 1;
+		return 0;
+
+	case ABEQ:	/* read, read */
+	case ABNE:
+	case ABCS:
+	case ABHS:
+	case ABCC:
+	case ABLO:
+	case ABMI:
+	case ABPL:
+	case ABVS:
+	case ABVC:
+	case ABHI:
+	case ABLS:
+	case ABGE:
+	case ABLT:
+	case ABGT:
+	case ABLE:
+
+	case AFCMPS:
+	case AFCMPD:
+	case ACMP:
+	case ACMPW:
+	case ACMN:
+	case ACMNW:
+		if(s != A) {
+			if(copysub(&p->from, v, s, 1))
+				return 1;
+			return copysub1(p, v, s, 1);
+		}
+		if(copyau(&p->from, v))
+			return 1;
+		if(copyau1(p, v))
+			return 1;
+		return 0;
+
+	case AB:	/* funny */
+		if(s != A) {
+			if(copysub(&p->to, v, s, 1))
+				return 1;
+			return 0;
+		}
+		if(copyau(&p->to, v))
+			return 1;
+		return 0;
+
+	case ARET:
+	case ARETURN:	/* funny */
+		if(v->type == D_REG)
+		if(v->reg == REGRET)
+			return 2;
+		if(v->type == D_FREG)
+		if(v->reg == FREGRET)
+			return 2;
+
+	case ABL:	/* funny */
+		if(v->type == D_REG) {
+			if(v->reg <= REGEXT && v->reg > exregoffset)
+				return 2;
+			if(v->reg == REGARG)
+				return 2;
+		}
+		if(v->type == D_FREG)
+			if(v->reg <= FREGEXT && v->reg > exfregoffset)
+				return 2;
+
+		if(s != A) {
+			if(copysub(&p->to, v, s, 1))
+				return 1;
+			return 0;
+		}
+		if(copyau(&p->to, v))
+			return 4;
+		return 3;
+
+	case ATEXT:	/* funny */
+		if(v->type == D_REG)
+			if(v->reg == REGARG)
+				return 3;
+		return 0;
+	}
+}
+
+int
+a2type(Prog *p)
+{
+
+	switch(p->as) {
+
+	case ACMP:
+	case ACMPW:
+	case ACMN:
+	case ACMNW:
+
+	case AADD:
+	case AADDW:
+	case AADDS:
+	case AADDSW:
+	case ASUB:
+	case ASUBS:
+	case ASUBSW:
+	case ASUBW:
+	case ALSL:
+	case ALSLW:
+	case ALSR:
+	case ALSRW:
+	case AASR:
+	case AASRW:
+	case AORR:
+	case AORRW:
+	case AAND:
+	case AANDW:
+	case AANDS:
+	case AANDSW:
+	case AEOR:
+	case AEORW:
+	case AMUL:
+	case AMULW:
+	case AUMULL:
+	case AREM:
+	case AREMW:
+	case ASDIV:
+	case ASDIVW:
+	case AUDIV:
+	case AUDIVW:
+	case AUREM:
+	case AUREMW:
+		return D_REG;
+
+	case AFCMPS:
+	case AFCMPD:
+
+	case AFADDS:
+	case AFADDD:
+	case AFSUBS:
+	case AFSUBD:
+	case AFMULS:
+	case AFMULD:
+	case AFDIVS:
+	case AFDIVD:
+		return D_FREG;
+	}
+	return D_NONE;
+}
+
+/*
+ * direct reference,
+ * could be set/use depending on
+ * semantics
+ */
+int
+copyas(Adr *a, Adr *v)
+{
+
+	if(regtyp(v)) {
+		if(a->type == v->type)
+		if(a->reg == v->reg)
+			return 1;
+	} else if(v->type == D_CONST) {		/* for constprop */
+		if(a->type == v->type)
+		if(a->name == v->name)
+		if(a->sym == v->sym)
+		if(a->reg == v->reg)
+		if(a->offset == v->offset)
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * either direct or indirect
+ */
+int
+copyau(Adr *a, Adr *v)
+{
+
+	if(copyas(a, v))
+		return 1;
+	if(v->type == D_REG) {
+		if(a->type == D_OREG) {
+			if(v->reg == a->reg)
+				return 1;
+		} else if(a->type == D_SHIFT) {
+			if(((a->offset>>16)&0x1F) == v->reg)
+				return 1;
+		} else if(a->type == D_EXTREG) {
+			if(a->reg == v->reg || ((a->offset>>16)&0x1F) == v->reg)
+				return 1;
+		}
+	}
+	return 0;
+}
+
+int
+copyau1(Prog *p, Adr *v)
+{
+
+	if(regtyp(v)) {
+		if(a2type(p) == v->type)
+		if(p->reg == v->reg) {
+			if(a2type(p) != v->type)
+				print("botch a2type %P\n", p);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * substitute s for v in a
+ * return failure to substitute
+ */
+int
+copysub(Adr *a, Adr *v, Adr *s, int f)
+{
+
+	if(f)
+	if(copyau(a, v)) {
+		if(a->type == D_SHIFT) {
+			if(((a->offset>>16)&0x1F) == v->reg)
+				a->offset = (a->offset&~(0x1F<<16))|(s->reg<<16);
+		} else
+			a->reg = s->reg;
+	}
+	return 0;
+}
+
+int
+copysub1(Prog *p1, Adr *v, Adr *s, int f)
+{
+
+	if(f)
+	if(copyau1(p1, v))
+		p1->reg = s->reg;
+	return 0;
+}
+
+struct {
+	int opcode;
+	int notopcode;
+	int scond; 
+	int notscond; 
+} predinfo[]  = { 
+	{ ABEQ,	ABNE,	0x0,	0x1, }, 
+	{ ABNE,	ABEQ,	0x1,	0x0, }, 
+	{ ABCS,	ABCC,	0x2,	0x3, }, 
+	{ ABHS,	ABLO,	0x2,	0x3, }, 
+	{ ABCC,	ABCS,	0x3,	0x2, }, 
+	{ ABLO,	ABHS,	0x3,	0x2, }, 
+	{ ABMI,	ABPL,	0x4,	0x5, }, 
+	{ ABPL,	ABMI,	0x5,	0x4, }, 
+	{ ABVS,	ABVC,	0x6,	0x7, }, 
+	{ ABVC,	ABVS,	0x7,	0x6, }, 
+	{ ABHI,	ABLS,	0x8,	0x9, }, 
+	{ ABLS,	ABHI,	0x9,	0x8, }, 
+	{ ABGE,	ABLT,	0xA,	0xB, }, 
+	{ ABLT,	ABGE,	0xB,	0xA, }, 
+	{ ABGT,	ABLE,	0xC,	0xD, }, 
+	{ ABLE,	ABGT,	0xD,	0xC, }, 
+}; 
+
+typedef struct {
+	Reg *start;
+	Reg *last;
+	Reg *end;
+	int len;
+} Joininfo;
+
+enum {
+	Join,
+	Split,
+	End,
+	Branch,
+	Setcond,
+	Toolong
+};
+	
+enum {
+	Falsecond,
+	Truecond,
+	Delbranch,
+	Keepbranch
+};
+
+int 
+isbranch(Prog *p)
+{
+	return (ABEQ <= p->as) && (p->as <= ABLE); 
+}
+
+int
+predicable(Prog *p)
+{
+	if (isbranch(p)
+		|| p->as == ANOP
+		|| p->as == AXXX
+		|| p->as == ADATA
+		|| p->as == AGLOBL
+		|| p->as == AGOK
+		|| p->as == AHISTORY
+		|| p->as == ANAME
+		|| p->as == ASIGNAME
+		|| p->as == ATEXT
+		|| p->as == AWORD
+		|| p->as == ADYNT
+		|| p->as == AINIT
+		|| p->as == ABCASE
+		|| p->as == ACASE)
+		return 0; 
+	return 1; 
+}
+
+/* 
+ * Depends on an analysis of the encodings performed by 5l. 
+ * These seem to be all of the opcodes that lead to the "S" bit
+ * being set in the instruction encodings. 
+ * 
+ * C_SBIT may also have been set explicitly in p->scond.
+ */ 
+int
+modifiescpsr(Prog *p)
+{
+//	return (p->scond&C_SBIT)
+	return 1
+		|| p->as == ATST 
+		|| p->as == ACMN
+		|| p->as == ACMP
+		|| p->as == AUMULL
+		|| p->as == AUDIV
+		|| p->as == AMUL
+		|| p->as == ASDIV
+		|| p->as == AREM
+		|| p->as == AUREM
+		|| p->as == ABL;
+} 
+
+/*
+ * Find the maximal chain of instructions starting with r which could
+ * be executed conditionally
+ */
+int
+joinsplit(Reg *r, Joininfo *j)
+{
+	j->start = r;
+	j->last = r;
+	j->len = 0;
+	do {
+		if (r->p2 && (r->p1 || r->p2->p2link)) {
+			j->end = r;
+			return Join;
+		}
+		if (r->s1 && r->s2) {
+			j->end = r;
+			return Split;
+		}
+		j->last = r;
+		if (r->prog->as != ANOP)
+			j->len++;
+		if (!r->s1 && !r->s2) {
+			j->end = r->link;
+			return End;
+		}
+		if (r->s2) {
+			j->end = r->s2;
+			return Branch;
+		}
+		if (modifiescpsr(r->prog)) {
+			j->end = r->s1;
+			return Setcond;
+		}
+		r = r->s1;
+	} while (j->len < 4);
+	j->end = r;
+	return Toolong;
+}
+
+Reg *
+successor(Reg *r)
+{
+	if (r->s1)
+		return r->s1; 
+	else
+		return r->s2; 
+}
+
+#ifdef XXX
+void
+applypred(Reg *rstart, Joininfo *j, int cond, int branch)
+{
+	int pred; 
+	Reg *r; 
+
+	if(j->len == 0)
+		return;
+	if (cond == Truecond)
+		pred = predinfo[rstart->prog->as - ABEQ].scond;
+	else
+		pred = predinfo[rstart->prog->as - ABEQ].notscond; 
+	
+	for (r = j->start; ; r = successor(r)) {
+		if (r->prog->as == AB) {
+			if (r != j->last || branch == Delbranch)
+				excise(r);
+			else {
+			  if (cond == Truecond)
+				r->prog->as = predinfo[rstart->prog->as - ABEQ].opcode;
+			  else
+				r->prog->as = predinfo[rstart->prog->as - ABEQ].notopcode;
+			}
+		}
+		else if (predicable(r->prog)) 
+			r->prog->scond = (r->prog->scond&~C_SCOND)|pred;
+		if (r->s1 != r->link) {
+			r->s1 = r->link;
+			r->link->p1 = r;
+		}
+		if (r == j->last)
+			break;
+	}
+}
+
+
+void
+predicate(void)
+{	
+	Reg *r;
+	int t1, t2;
+	Joininfo j1, j2;
+
+	for(r=firstr; r!=R; r=r->link) {
+		if (isbranch(r->prog)) {
+			t1 = joinsplit(r->s1, &j1);
+			t2 = joinsplit(r->s2, &j2);
+			if(j1.last->link != j2.start)
+				continue;
+			if(j1.end == j2.end)
+			if((t1 == Branch && (t2 == Join || t2 == Setcond)) ||
+			   (t2 == Join && (t1 == Join || t1 == Setcond))) {
+				applypred(r, &j1, Falsecond, Delbranch);
+				applypred(r, &j2, Truecond, Delbranch);
+				excise(r);
+				continue;
+			}
+			if(t1 == End || t1 == Branch) {
+				applypred(r, &j1, Falsecond, Keepbranch);
+				excise(r);
+				continue;
+			}
+		} 
+	} 
+}
+#endif
--- /dev/null
+++ b/sys/src/cmd/7c/reg.c
@@ -1,0 +1,1160 @@
+#include "gc.h"
+
+void	addsplits(void);
+
+Reg*
+rega(void)
+{
+	Reg *r;
+
+	r = freer;
+	if(r == R) {
+		r = alloc(sizeof(*r));
+	} else
+		freer = r->link;
+
+	*r = zreg;
+	return r;
+}
+
+int
+rcmp(void *a1, void *a2)
+{
+	Rgn *p1, *p2;
+	int c1, c2;
+
+	p1 = (Rgn*)a1;
+	p2 = (Rgn*)a2;
+	c1 = p2->cost;
+	c2 = p1->cost;
+	if(c1 -= c2)
+		return c1;
+	return p2->varno - p1->varno;
+}
+
+void
+regopt(Prog *p)
+{
+	Reg *r, *r1, *r2;
+	Prog *p1;
+	int i, z;
+	long initpc, val, npc;
+	ulong vreg;
+	Bits bit;
+	struct
+	{
+		long	m;
+		long	c;
+		Reg*	p;
+	} log5[6], *lp;
+
+	firstr = R;
+	lastr = R;
+	nvar = 0;
+	regbits = 0;
+	for(z=0; z<BITS; z++) {
+		externs.b[z] = 0;
+		params.b[z] = 0;
+		consts.b[z] = 0;
+		addrs.b[z] = 0;
+	}
+
+	/*
+	 * pass 1
+	 * build aux data structure
+	 * allocate pcs
+	 * find use and set of variables
+	 */
+	val = 5L * 5L * 5L * 5L * 5L;
+	lp = log5;
+	for(i=0; i<5; i++) {
+		lp->m = val;
+		lp->c = 0;
+		lp->p = R;
+		val /= 5L;
+		lp++;
+	}
+	val = 0;
+	for(; p != P; p = p->link) {
+		switch(p->as) {
+		case ADATA:
+		case AGLOBL:
+		case ANAME:
+		case ASIGNAME:
+			continue;
+		}
+		r = rega();
+		if(firstr == R) {
+			firstr = r;
+			lastr = r;
+		} else {
+			lastr->link = r;
+			r->p1 = lastr;
+			lastr->s1 = r;
+			lastr = r;
+		}
+		r->prog = p;
+		r->pc = val;
+		val++;
+
+		lp = log5;
+		for(i=0; i<5; i++) {
+			lp->c--;
+			if(lp->c <= 0) {
+				lp->c = lp->m;
+				if(lp->p != R)
+					lp->p->log5 = r;
+				lp->p = r;
+				(lp+1)->c = 0;
+				break;
+			}
+			lp++;
+		}
+
+		r1 = r->p1;
+		if(r1 != R)
+		switch(r1->prog->as) {
+		case ARETURN:
+		case ARET:
+		case AB:
+		case AERET:
+			r->p1 = R;
+			r1->s1 = R;
+		}
+
+		/*
+		 * left side always read
+		 */
+		bit = mkvar(&p->from, p->as==AMOVW || p->as == AMOVWU || p->as == AMOV);
+		for(z=0; z<BITS; z++)
+			r->use1.b[z] |= bit.b[z];
+
+		/*
+		 * right side depends on opcode
+		 */
+		bit = mkvar(&p->to, 0);
+		if(bany(&bit))
+		switch(p->as) {
+		default:
+			diag(Z, "reg: unknown asop: %A", p->as);
+			break;
+
+		/*
+		 * right side write
+		 */
+		case ANOP:
+		case AMOV:
+		case AMOVB:
+		case AMOVBU:
+		case AMOVH:
+		case AMOVHU:
+		case AMOVW:
+		case AMOVWU:
+		case AFMOVS:
+		case AFCVTSD:
+		case AFMOVD:
+		case AFCVTDS:
+			for(z=0; z<BITS; z++)
+				r->set.b[z] |= bit.b[z];
+			break;
+
+		/*
+		 * funny
+		 */
+		case ABL:
+			for(z=0; z<BITS; z++)
+				addrs.b[z] |= bit.b[z];
+			break;
+		}
+
+	}
+	if(firstr == R)
+		return;
+	initpc = pc - val;
+	npc = val;
+
+	/*
+	 * pass 2
+	 * turn branch references to pointers
+	 * build back pointers
+	 */
+	for(r = firstr; r != R; r = r->link) {
+		p = r->prog;
+		if(p->to.type == D_BRANCH) {
+			val = p->to.offset - initpc;
+			r1 = firstr;
+			while(r1 != R) {
+				r2 = r1->log5;
+				if(r2 != R && val >= r2->pc) {
+					r1 = r2;
+					continue;
+				}
+				if(r1->pc == val)
+					break;
+				r1 = r1->link;
+			}
+			if(r1 == R) {
+				nearln = p->lineno;
+				diag(Z, "ref not found\n%P", p);
+				continue;
+			}
+			if(r1 == r) {
+				nearln = p->lineno;
+				diag(Z, "ref to self\n%P", p);
+				continue;
+			}
+			r->s2 = r1;
+			r->p2link = r1->p2;
+			r1->p2 = r;
+		}
+	}
+	if(debug['R']) {
+		p = firstr->prog;
+		print("\n%L %D\n", p->lineno, &p->from);
+	}
+
+	/*
+	 * pass 2.5
+	 * find looping structure
+	 */
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	change = 0;
+	loopit(firstr, npc);
+
+	/*
+	 * pass 3
+	 * iterate propagating usage
+	 * 	back until flow graph is complete
+	 */
+loop1:
+	change = 0;
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	for(r = firstr; r != R; r = r->link)
+		if(r->prog->as == ARET || r->prog->as == ARETURN)
+			prop(r, zbits, zbits);
+loop11:
+	/* pick up unreachable code */
+	i = 0;
+	for(r = firstr; r != R; r = r1) {
+		r1 = r->link;
+		if(r1 && r1->active && !r->active) {
+			prop(r, zbits, zbits);
+			i = 1;
+		}
+	}
+	if(i)
+		goto loop11;
+	if(change)
+		goto loop1;
+
+
+	/*
+	 * pass 4
+	 * iterate propagating register/variable synchrony
+	 * 	forward until graph is complete
+	 */
+loop2:
+	change = 0;
+	for(r = firstr; r != R; r = r->link)
+		r->active = 0;
+	synch(firstr, zbits);
+	if(change)
+		goto loop2;
+
+	addsplits();
+
+	if(debug['R'] && debug['v']) {
+		print("\nprop structure:\n");
+		for(r = firstr; r != R; r = r->link) {
+			print("%ld:%P", r->loop, r->prog);
+			for(z=0; z<BITS; z++)
+				bit.b[z] = r->set.b[z] |
+					r->refahead.b[z] | r->calahead.b[z] |
+					r->refbehind.b[z] | r->calbehind.b[z] |
+					r->use1.b[z] | r->use2.b[z];
+			if(bany(&bit)) {
+				print("\t");
+				if(bany(&r->use1))
+					print(" u1=%B", r->use1);
+				if(bany(&r->use2))
+					print(" u2=%B", r->use2);
+				if(bany(&r->set))
+					print(" st=%B", r->set);
+				if(bany(&r->refahead))
+					print(" ra=%B", r->refahead);
+				if(bany(&r->calahead))
+					print(" ca=%B", r->calahead);
+				if(bany(&r->refbehind))
+					print(" rb=%B", r->refbehind);
+				if(bany(&r->calbehind))
+					print(" cb=%B", r->calbehind);
+			}
+			print("\n");
+		}
+	}
+
+	/*
+	 * pass 5
+	 * isolate regions
+	 * calculate costs (paint1)
+	 */
+	r = firstr;
+	if(r) {
+		for(z=0; z<BITS; z++)
+			bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) &
+			  ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]);
+		if(bany(&bit)) {
+			nearln = r->prog->lineno;
+			warn(Z, "used and not set: %B", bit);
+			if(debug['R'] && !debug['w'])
+				print("used and not set: %B\n", bit);
+		}
+	}
+
+	for(r = firstr; r != R; r = r->link)
+		r->act = zbits;
+	rgp = region;
+	nregion = 0;
+	for(r = firstr; r != R; r = r->link) {
+		for(z=0; z<BITS; z++)
+			bit.b[z] = r->set.b[z] &
+			  ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]);
+		if(bany(&bit)) {
+			nearln = r->prog->lineno;
+			warn(Z, "set and not used: %B", bit);
+			if(debug['R'])
+				print("set and not used: %B\n", bit);
+			excise(r);
+		}
+		for(z=0; z<BITS; z++)
+			bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]);
+		while(bany(&bit)) {
+			i = bnum(bit);
+			rgp->enter = r;
+			rgp->varno = i;
+			change = 0;
+			if(debug['R'] && debug['v'])
+				print("\n");
+			paint1(r, i);
+			bit.b[i/32] &= ~(1L<<(i%32));
+			if(change <= 0) {
+				if(debug['R'])
+					print("%L $%d: %B\n",
+						r->prog->lineno, change, blsh(i));
+				continue;
+			}
+			rgp->cost = change;
+			nregion++;
+			if(nregion >= NRGN) {
+				warn(Z, "too many regions");
+				goto brk;
+			}
+			rgp++;
+		}
+	}
+brk:
+	qsort(region, nregion, sizeof(region[0]), rcmp);
+
+	/*
+	 * pass 6
+	 * determine used registers (paint2)
+	 * replace code (paint3)
+	 */
+	rgp = region;
+	for(i=0; i<nregion; i++) {
+		bit = blsh(rgp->varno);
+		vreg = paint2(rgp->enter, rgp->varno);
+		vreg = allreg(vreg, rgp);
+		if(debug['R']) {
+			if(rgp->regno >= NREG)
+				print("%L $%d F%d: %B\n",
+					rgp->enter->prog->lineno,
+					rgp->cost,
+					rgp->regno-NREG,
+					bit);
+			else
+				print("%L $%d R%d: %B\n",
+					rgp->enter->prog->lineno,
+					rgp->cost,
+					rgp->regno,
+					bit);
+		}
+		if(rgp->regno != 0)
+			paint3(rgp->enter, rgp->varno, vreg, rgp->regno);
+		rgp++;
+	}
+	/*
+	 * pass 7
+	 * peep-hole on basic block
+	 */
+	if(!debug['R'] || debug['P'])
+		peep();
+
+	/*
+	 * pass 8
+	 * recalculate pc
+	 */
+	val = initpc;
+	for(r = firstr; r != R; r = r1) {
+		r->pc = val;
+		p = r->prog;
+		p1 = P;
+		r1 = r->link;
+		if(r1 != R)
+			p1 = r1->prog;
+		for(; p != p1; p = p->link) {
+			switch(p->as) {
+			default:
+				val++;
+				break;
+
+			case ANOP:
+			case ADATA:
+			case AGLOBL:
+			case ANAME:
+			case ASIGNAME:
+				break;
+			}
+		}
+	}
+	pc = val;
+
+	/*
+	 * fix up branches
+	 */
+	if(debug['R'])
+		if(bany(&addrs))
+			print("addrs: %B\n", addrs);
+
+	r1 = 0; /* set */
+	for(r = firstr; r != R; r = r->link) {
+		p = r->prog;
+		if(p->to.type == D_BRANCH)
+			p->to.offset = r->s2->pc;
+		r1 = r;
+	}
+
+	/*
+	 * last pass
+	 * eliminate nops
+	 * free aux structures
+	 */
+	for(p = firstr->prog; p != P; p = p->link){
+		while(p->link && p->link->as == ANOP)
+			p->link = p->link->link;
+	}
+	if(r1 != R) {
+		r1->link = freer;
+		freer = firstr;
+	}
+}
+
+void
+addsplits(void)
+{
+	Reg *r, *r1;
+	int z, i;
+	Bits bit;
+
+	for(r = firstr; r != R; r = r->link) {
+		if(r->loop > 1)
+			continue;
+		if(r->prog->as == ABL)
+			continue;
+		for(r1 = r->p2; r1 != R; r1 = r1->p2link) {
+			if(r1->loop <= 1)
+				continue;
+			for(z=0; z<BITS; z++)
+				bit.b[z] = r1->calbehind.b[z] &
+					(r->refahead.b[z] | r->use1.b[z] | r->use2.b[z]) &
+					~(r->calahead.b[z] & addrs.b[z]);
+			while(bany(&bit)) {
+				i = bnum(bit);
+				bit.b[i/32] &= ~(1L << (i%32));
+			}
+		}
+	}
+}
+
+/*
+ * add mov b,rn
+ * just after r
+ */
+void
+addmove(Reg *r, int bn, int rn, int f)
+{
+	Prog *p, *p1;
+	Adr *a;
+	Var *v;
+
+	p1 = alloc(sizeof(*p1));
+	*p1 = zprog;
+	p = r->prog;
+
+	p1->link = p->link;
+	p->link = p1;
+	p1->lineno = p->lineno;
+
+	v = var + bn;
+
+	a = &p1->to;
+	a->sym = v->sym;
+	a->name = v->name;
+	a->offset = v->offset;
+	a->etype = v->etype;
+	a->type = D_OREG;
+	if(a->etype == TARRAY || a->sym == S)
+		a->type = D_CONST;
+
+	p1->as = AMOVW;
+	if(v->etype == TCHAR || v->etype == TUCHAR)
+		p1->as = AMOVB;
+	if(v->etype == TSHORT || v->etype == TUSHORT)
+		p1->as = AMOVH;
+	if(v->etype == TVLONG || v->etype == TUVLONG || v->etype == TIND)
+		p1->as = AMOV;
+	if(v->etype == TFLOAT)
+		p1->as = AFMOVS;
+	if(v->etype == TDOUBLE)
+		p1->as = AFMOVD;
+
+	p1->from.type = D_REG;
+	p1->from.reg = rn;
+	if(rn >= NREG) {
+		p1->from.type = D_FREG;
+		p1->from.reg = rn-NREG;
+	}
+	if(!f) {
+		p1->from = *a;
+		*a = zprog.from;
+		a->type = D_REG;
+		a->reg = rn;
+		if(rn >= NREG) {
+			a->type = D_FREG;
+			a->reg = rn-NREG;
+		}
+		if(v->etype == TUCHAR)
+			p1->as = AMOVBU;
+		if(v->etype == TUSHORT)
+			p1->as = AMOVHU;
+		if(v->etype == TUINT || v->etype == TULONG)
+			p1->as = AMOVWU;
+	}
+	if(debug['R'])
+		print("%P\t.a%P\n", p, p1);
+}
+
+Bits
+mkvar(Adr *a, int docon)
+{
+	Var *v;
+	int i, t, n, et, z;
+	long o;
+	Bits bit;
+	Sym *s;
+
+	t = a->type;
+	if(t == D_REG && a->reg != NREG)
+		regbits |= RtoB(a->reg);
+	if(t == D_FREG && a->reg != NREG)
+		regbits |= FtoB(a->reg);
+	s = a->sym;
+	o = a->offset;
+	et = a->etype;
+	if(s == S) {
+		if(t != D_CONST || !docon || a->reg != NREG)
+			goto none;
+		et = TLONG;
+	}
+	if(t == D_CONST) {
+		if(s == S && sval(o))
+			goto none;
+	}
+
+	n = a->name;
+	v = var;
+	for(i=0; i<nvar; i++) {
+		if(s == v->sym)
+		if(n == v->name)
+		if(o == v->offset)
+			goto out;
+		v++;
+	}
+	if(s)
+		if(s->name[0] == '.')
+			goto none;
+	if(nvar >= NVAR) {
+		if(debug['w'] > 1 && s)
+			warn(Z, "variable not optimized: %s", s->name);
+		goto none;
+	}
+	i = nvar;
+	nvar++;
+	v = &var[i];
+	v->sym = s;
+	v->offset = o;
+	v->etype = et;
+	v->name = n;
+	if(debug['R'])
+		print("bit=%2d et=%2d %D\n", i, et, a);
+out:
+	bit = blsh(i);
+	if(n == D_EXTERN || n == D_STATIC)
+		for(z=0; z<BITS; z++)
+			externs.b[z] |= bit.b[z];
+	if(n == D_PARAM)
+		for(z=0; z<BITS; z++)
+			params.b[z] |= bit.b[z];
+	if(v->etype != et || !(typechlpfd[et] || typev[et]))	/* funny punning */
+		for(z=0; z<BITS; z++)
+			addrs.b[z] |= bit.b[z];
+	if(t == D_CONST) {
+		if(s == S) {
+			for(z=0; z<BITS; z++)
+				consts.b[z] |= bit.b[z];
+			return bit;
+		}
+		if(et != TARRAY)
+			for(z=0; z<BITS; z++)
+				addrs.b[z] |= bit.b[z];
+		for(z=0; z<BITS; z++)
+			params.b[z] |= bit.b[z];
+		return bit;
+	}
+	if(t == D_OREG)
+		return bit;
+
+none:
+	return zbits;
+}
+
+void
+prop(Reg *r, Bits ref, Bits cal)
+{
+	Reg *r1, *r2;
+	int z;
+
+	for(r1 = r; r1 != R; r1 = r1->p1) {
+		for(z=0; z<BITS; z++) {
+			ref.b[z] |= r1->refahead.b[z];
+			if(ref.b[z] != r1->refahead.b[z]) {
+				r1->refahead.b[z] = ref.b[z];
+				change++;
+			}
+			cal.b[z] |= r1->calahead.b[z];
+			if(cal.b[z] != r1->calahead.b[z]) {
+				r1->calahead.b[z] = cal.b[z];
+				change++;
+			}
+		}
+		switch(r1->prog->as) {
+		case ABL:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] |= ref.b[z] | externs.b[z];
+				ref.b[z] = 0;
+			}
+			break;
+
+		case ATEXT:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] = 0;
+				ref.b[z] = 0;
+			}
+			break;
+
+		case ARET:
+		case ARETURN:
+			for(z=0; z<BITS; z++) {
+				cal.b[z] = externs.b[z];
+				ref.b[z] = 0;
+			}
+		}
+		for(z=0; z<BITS; z++) {
+			ref.b[z] = (ref.b[z] & ~r1->set.b[z]) |
+				r1->use1.b[z] | r1->use2.b[z];
+			cal.b[z] &= ~(r1->set.b[z] | r1->use1.b[z] | r1->use2.b[z]);
+			r1->refbehind.b[z] = ref.b[z];
+			r1->calbehind.b[z] = cal.b[z];
+		}
+		if(r1->active)
+			break;
+		r1->active = 1;
+	}
+	for(; r != r1; r = r->p1)
+		for(r2 = r->p2; r2 != R; r2 = r2->p2link)
+			prop(r2, r->refbehind, r->calbehind);
+}
+
+/*
+ * find looping structure
+ *
+ * 1) find reverse postordering
+ * 2) find approximate dominators,
+ *	the actual dominators if the flow graph is reducible
+ *	otherwise, dominators plus some other non-dominators.
+ *	See Matthew S. Hecht and Jeffrey D. Ullman,
+ *	"Analysis of a Simple Algorithm for Global Data Flow Problems",
+ *	Conf.  Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts,
+ *	Oct. 1-3, 1973, pp.  207-217.
+ * 3) find all nodes with a predecessor dominated by the current node.
+ *	such a node is a loop head.
+ *	recursively, all preds with a greater rpo number are in the loop
+ */
+long
+postorder(Reg *r, Reg **rpo2r, long n)
+{
+	Reg *r1;
+
+	r->rpo = 1;
+	r1 = r->s1;
+	if(r1 && !r1->rpo)
+		n = postorder(r1, rpo2r, n);
+	r1 = r->s2;
+	if(r1 && !r1->rpo)
+		n = postorder(r1, rpo2r, n);
+	rpo2r[n] = r;
+	n++;
+	return n;
+}
+
+long
+rpolca(long *idom, long rpo1, long rpo2)
+{
+	long t;
+
+	if(rpo1 == -1)
+		return rpo2;
+	while(rpo1 != rpo2){
+		if(rpo1 > rpo2){
+			t = rpo2;
+			rpo2 = rpo1;
+			rpo1 = t;
+		}
+		while(rpo1 < rpo2){
+			t = idom[rpo2];
+			if(t >= rpo2)
+				fatal(Z, "bad idom");
+			rpo2 = t;
+		}
+	}
+	return rpo1;
+}
+
+int
+doms(long *idom, long r, long s)
+{
+	while(s > r)
+		s = idom[s];
+	return s == r;
+}
+
+int
+loophead(long *idom, Reg *r)
+{
+	long src;
+
+	src = r->rpo;
+	if(r->p1 != R && doms(idom, src, r->p1->rpo))
+		return 1;
+	for(r = r->p2; r != R; r = r->p2link)
+		if(doms(idom, src, r->rpo))
+			return 1;
+	return 0;
+}
+
+void
+loopmark(Reg **rpo2r, long head, Reg *r)
+{
+	if(r->rpo < head || r->active == head)
+		return;
+	r->active = head;
+	r->loop += LOOP;
+	if(r->p1 != R)
+		loopmark(rpo2r, head, r->p1);
+	for(r = r->p2; r != R; r = r->p2link)
+		loopmark(rpo2r, head, r);
+}
+
+void
+loopit(Reg *r, long nr)
+{
+	Reg *r1;
+	long i, d, me;
+
+	if(nr > maxnr) {
+		rpo2r = alloc(nr * sizeof(Reg*));
+		idom = alloc(nr * sizeof(long));
+		maxnr = nr;
+	}
+
+	d = postorder(r, rpo2r, 0);
+	if(d > nr)
+		fatal(Z, "too many reg nodes");
+	nr = d;
+	for(i = 0; i < nr / 2; i++){
+		r1 = rpo2r[i];
+		rpo2r[i] = rpo2r[nr - 1 - i];
+		rpo2r[nr - 1 - i] = r1;
+	}
+	for(i = 0; i < nr; i++)
+		rpo2r[i]->rpo = i;
+
+	idom[0] = 0;
+	for(i = 0; i < nr; i++){
+		r1 = rpo2r[i];
+		me = r1->rpo;
+		d = -1;
+		if(r1->p1 != R && r1->p1->rpo < me)
+			d = r1->p1->rpo;
+		for(r1 = r1->p2; r1 != nil; r1 = r1->p2link)
+			if(r1->rpo < me)
+				d = rpolca(idom, d, r1->rpo);
+		idom[i] = d;
+	}
+
+	for(i = 0; i < nr; i++){
+		r1 = rpo2r[i];
+		r1->loop++;
+		if(r1->p2 != R && loophead(idom, r1))
+			loopmark(rpo2r, i, r1);
+	}
+}
+
+void
+synch(Reg *r, Bits dif)
+{
+	Reg *r1;
+	int z;
+
+	for(r1 = r; r1 != R; r1 = r1->s1) {
+		for(z=0; z<BITS; z++) {
+			dif.b[z] = (dif.b[z] &
+				~(~r1->refbehind.b[z] & r1->refahead.b[z])) |
+					r1->set.b[z] | r1->regdiff.b[z];
+			if(dif.b[z] != r1->regdiff.b[z]) {
+				r1->regdiff.b[z] = dif.b[z];
+				change++;
+			}
+		}
+		if(r1->active)
+			break;
+		r1->active = 1;
+		for(z=0; z<BITS; z++)
+			dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]);
+		if(r1->s2 != R)
+			synch(r1->s2, dif);
+	}
+}
+
+ulong
+allreg(ulong b, Rgn *r)
+{
+	Var *v;
+	int i;
+
+	v = var + r->varno;
+	r->regno = 0;
+	switch(v->etype) {
+
+	default:
+		diag(Z, "unknown etype %d/%d", bitno(b), v->etype);
+		break;
+
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TIND:
+	case TVLONG:
+	case TUVLONG:
+	case TARRAY:
+		i = BtoR(~b);
+		if(i && r->cost > 0) {
+			r->regno = i;
+			return RtoB(i);
+		}
+		break;
+
+	case TDOUBLE:
+	case TFLOAT:
+		i = BtoF(~b);
+		if(i && r->cost > 0) {
+			r->regno = i+NREG;
+			return FtoB(i);
+		}
+		break;
+	}
+	return 0;
+}
+
+void
+paint1(Reg *r, int bn)
+{
+	Reg *r1;
+	Prog *p;
+	int z;
+	ulong bb;
+
+	z = bn/32;
+	bb = 1L<<(bn%32);
+	if(r->act.b[z] & bb)
+		return;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(r1->act.b[z] & bb)
+			break;
+		r = r1;
+	}
+
+	if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb) {
+		change -= CLOAD * r->loop;
+		if(debug['R'] && debug['v'])
+			print("%ld%P\tld %B $%d\n", r->loop,
+				r->prog, blsh(bn), change);
+	}
+	for(;;) {
+		r->act.b[z] |= bb;
+		p = r->prog;
+
+		if(r->use1.b[z] & bb) {
+			change += CREF * r->loop;
+			if(p->to.type == D_FREG && (p->as == AMOVW || p->as == AMOV))
+				change = -CINF;		/* cant go Rreg to Freg */
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tu1 %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if((r->use2.b[z]|r->set.b[z]) & bb) {
+			change += CREF * r->loop;
+			if(p->from.type == D_FREG && (p->as == AMOVW || p->as == AMOV))
+				change = -CINF;		/* cant go Rreg to Freg */
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tu2 %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if(STORE(r) & r->regdiff.b[z] & bb) {
+			change -= CLOAD * r->loop;
+			if(debug['R'] && debug['v'])
+				print("%ld%P\tst %B $%d\n", r->loop,
+					p, blsh(bn), change);
+		}
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					paint1(r1, bn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				paint1(r1, bn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(r->act.b[z] & bb)
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+}
+
+ulong
+paint2(Reg *r, int bn)
+{
+	Reg *r1;
+	int z;
+	ulong bb, vreg;
+
+	z = bn/32;
+	bb = 1L << (bn%32);
+	vreg = regbits;
+	if(!(r->act.b[z] & bb))
+		return vreg;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(!(r1->act.b[z] & bb))
+			break;
+		r = r1;
+	}
+	for(;;) {
+		r->act.b[z] &= ~bb;
+
+		vreg |= r->regu;
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					vreg |= paint2(r1, bn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				vreg |= paint2(r1, bn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(!(r->act.b[z] & bb))
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+	return vreg;
+}
+
+void
+paint3(Reg *r, int bn, long rb, int rn)
+{
+	Reg *r1;
+	Prog *p;
+	int z;
+	ulong bb;
+
+	z = bn/32;
+	bb = 1L << (bn%32);
+	if(r->act.b[z] & bb)
+		return;
+	for(;;) {
+		if(!(r->refbehind.b[z] & bb))
+			break;
+		r1 = r->p1;
+		if(r1 == R)
+			break;
+		if(!(r1->refahead.b[z] & bb))
+			break;
+		if(r1->act.b[z] & bb)
+			break;
+		r = r1;
+	}
+
+	if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb)
+		addmove(r, bn, rn, 0);
+	for(;;) {
+		r->act.b[z] |= bb;
+		p = r->prog;
+
+		if(r->use1.b[z] & bb) {
+			if(debug['R'])
+				print("%P", p);
+			addreg(&p->from, rn);
+			if(debug['R'])
+				print("\t.c%P\n", p);
+		}
+		if((r->use2.b[z]|r->set.b[z]) & bb) {
+			if(debug['R'])
+				print("%P", p);
+			addreg(&p->to, rn);
+			if(debug['R'])
+				print("\t.c%P\n", p);
+		}
+
+		if(STORE(r) & r->regdiff.b[z] & bb)
+			addmove(r, bn, rn, 1);
+		r->regu |= rb;
+
+		if(r->refbehind.b[z] & bb)
+			for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+				if(r1->refahead.b[z] & bb)
+					paint3(r1, bn, rb, rn);
+
+		if(!(r->refahead.b[z] & bb))
+			break;
+		r1 = r->s2;
+		if(r1 != R)
+			if(r1->refbehind.b[z] & bb)
+				paint3(r1, bn, rb, rn);
+		r = r->s1;
+		if(r == R)
+			break;
+		if(r->act.b[z] & bb)
+			break;
+		if(!(r->refbehind.b[z] & bb))
+			break;
+	}
+}
+
+void
+addreg(Adr *a, int rn)
+{
+
+	a->sym = 0;
+	a->name = D_NONE;
+	a->type = D_REG;
+	a->reg = rn;
+	if(rn >= NREG) {
+		a->type = D_FREG;
+		a->reg = rn-NREG;
+	}
+}
+
+/*
+ *	bit	reg
+ *	0	R9
+ *	1	R10
+ *	...	...
+ *	6	R15
+ */
+long
+RtoB(int r)
+{
+	if(r >= REGMIN && r <= REGMAX)
+		return 1L << (r-REGMIN);
+	return 0;
+}
+
+int
+BtoR(long b)
+{
+	b &= 0x07fL;
+	if(b == 0)
+		return 0;
+	return bitno(b) + REGMIN;
+}
+
+/*
+ *	bit	reg
+ *	22	F7
+ *	23	F8
+ *	...	...
+ *	29	F14
+ */
+long
+FtoB(int f)
+{
+	if(f < FREGMIN || f > FREGEXT)
+		return 0;
+	return 1L << (f - FREGMIN + 22);
+}
+
+int
+BtoF(long b)
+{
+
+	b &= 0x3fc00000L;
+	if(b == 0)
+		return 0;
+	return bitno(b) - 22 + FREGMIN;
+}
--- /dev/null
+++ b/sys/src/cmd/7c/sgen.c
@@ -1,0 +1,247 @@
+#include "gc.h"
+
+void
+noretval(int n)
+{
+
+	if(n & 1) {
+		gins(ANOP, Z, Z);
+		p->to.type = D_REG;
+		p->to.reg = REGRET;
+	}
+	if(n & 2) {
+		gins(ANOP, Z, Z);
+		p->to.type = D_FREG;
+		p->to.reg = FREGRET;
+	}
+}
+
+/*
+ *	calculate addressability as follows
+ *		CONST ==> 20		$value
+ *		NAME ==> 10		name
+ *		REGISTER ==> 11		register
+ *		INDREG ==> 12		*[(reg)+offset]
+ *		&10 ==> 2		$name
+ *		ADD(2, 20) ==> 2	$name+offset
+ *		ADD(3, 20) ==> 3	$(reg)+offset
+ *		&12 ==> 3		$(reg)+offset
+ *		*11 ==> 11		??
+ *		*2 ==> 10		name
+ *		*3 ==> 12		*(reg)+offset
+ *	calculate complexity (number of registers)
+ */
+void
+xcom(Node *n)
+{
+	Node *l, *r;
+	int t;
+
+	if(n == Z)
+		return;
+	l = n->left;
+	r = n->right;
+	n->addable = 0;
+	n->complex = 0;
+	switch(n->op) {
+	case OCONST:
+		n->addable = 20;
+		return;
+
+	case OREGISTER:
+		n->addable = 11;
+		return;
+
+	case OINDREG:
+		n->addable = 12;
+		return;
+
+	case ONAME:
+		n->addable = 10;
+		return;
+
+	case OADDR:
+		xcom(l);
+		if(l->addable == 10)
+			n->addable = 2;
+		if(l->addable == 12)
+			n->addable = 3;
+		break;
+
+	case OIND:
+		xcom(l);
+		if(l->addable == 11)
+			n->addable = 12;
+		if(l->addable == 3)
+			n->addable = 12;
+		if(l->addable == 2)
+			n->addable = 10;
+		break;
+
+	case OADD:
+		xcom(l);
+		xcom(r);
+		if(l->addable == 20) {
+			if(r->addable == 2)
+				n->addable = 2;
+			if(r->addable == 3)
+				n->addable = 3;
+		}
+		if(r->addable == 20) {
+			if(l->addable == 2)
+				n->addable = 2;
+			if(l->addable == 3)
+				n->addable = 3;
+		}
+		break;
+
+	case OASMUL:
+	case OASLMUL:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OASASHL;
+			r->vconst = t;
+			r->type = types[TINT];
+		}
+		break;
+
+	case OMUL:
+	case OLMUL:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OASHL;
+			r->vconst = t;
+			r->type = types[TINT];
+		}
+		t = vlog(l);
+		if(t >= 0) {
+			n->op = OASHL;
+			n->left = r;
+			n->right = l;
+			r = l;
+			l = n->left;
+			r->vconst = t;
+			r->type = types[TINT];
+			simplifyshift(n);
+		}
+		break;
+
+	case OASLDIV:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OASLSHR;
+			r->vconst = t;
+			r->type = types[TINT];
+		}
+		break;
+
+	case OLDIV:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OLSHR;
+			r->vconst = t;
+			r->type = types[TINT];
+			simplifyshift(n);
+		}
+		break;
+
+	case OASLMOD:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OASAND;
+			r->vconst--;
+		}
+		break;
+
+	case OLMOD:
+		xcom(l);
+		xcom(r);
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OAND;
+			r->vconst--;
+		}
+		break;
+
+	case OLSHR:
+	case OASHL:
+	case OASHR:
+		xcom(l);
+		xcom(r);
+		simplifyshift(n);
+		break;
+
+	default:
+		if(l != Z)
+			xcom(l);
+		if(r != Z)
+			xcom(r);
+		break;
+	}
+	if(n->addable >= 10)
+		return;
+
+	if(l != Z)
+		n->complex = l->complex;
+	if(r != Z) {
+		if(r->complex == n->complex)
+			n->complex = r->complex+1;
+		else
+		if(r->complex > n->complex)
+			n->complex = r->complex;
+	}
+	if(n->complex == 0)
+		n->complex++;
+
+//	if(com64(n))
+//		return;
+
+	switch(n->op) {
+	case OFUNC:
+		n->complex = FNX;
+		break;
+
+	case OEQ:
+	case ONE:
+	case OLE:
+	case OLT:
+	case OGE:
+	case OGT:
+	case OHI:
+	case OHS:
+	case OLO:
+	case OLS:
+		/*
+		 * immediate operators, make const on right
+		 */
+		if(l->op == OCONST) {
+			n->left = r;
+			n->right = l;
+			n->op = invrel[relindex(n->op)];
+		}
+		break;
+
+	case OADD:
+	case OXOR:
+	case OAND:
+	case OOR:
+		/*
+		 * immediate operators, make const on right
+		 */
+		if(l->op == OCONST) {
+			n->left = r;
+			n->right = l;
+		}
+		break;
+	}
+}
--- /dev/null
+++ b/sys/src/cmd/7c/swt.c
@@ -1,0 +1,654 @@
+#include "gc.h"
+
+void
+swit1(C1 *q, int nc, long def, Node *n)
+{
+	Node tn;
+	
+	regalloc(&tn, &regnode, Z);
+	swit2(q, nc, def, n, &tn);
+	regfree(&tn);
+}
+
+void
+swit2(C1 *q, int nc, long def, Node *n, Node *tn)
+{
+	C1 *r;
+	int i;
+	long v;
+	Prog *sp;
+
+	if(nc >= 3) {
+		i = (q+nc-1)->val - (q+0)->val;
+		if(i > 0 && i < nc*2)
+			goto direct;
+	}
+	if(nc < 5) {
+		for(i=0; i<nc; i++) {
+			if(debug['K'])
+				print("case = %.8llux\n", q->val);
+			gopcode(OEQ, nodconst(q->val), n, Z);
+			patch(p, q->label);
+			q++;
+		}
+		gbranch(OGOTO);
+		patch(p, def);
+		return;
+	}
+
+	i = nc / 2;
+	r = q+i;
+	if(debug['K'])
+		print("case > %.8llux\n", r->val);
+	gopcode(OGT, nodconst(r->val), n, Z);
+	sp = p;
+	gopcode(OEQ, nodconst(r->val), n, Z);	/* just gen the B.EQ */
+	patch(p, r->label);
+	swit2(q, i, def, n, tn);
+
+	if(debug['K'])
+		print("case < %.8llux\n", r->val);
+	patch(sp, pc);
+	swit2(r+1, nc-i-1, def, n, tn);
+	return;
+
+direct:
+	v = q->val;
+	if(v != 0)
+		gopcode(OSUB, nodconst(v), Z, n);
+	gopcode(OHI, nodconst((q+nc-1)->val - v), n, Z);
+	patch(p, def);
+	gopcode(OCASE, n, Z, tn);
+	for(i=0; i<nc; i++) {
+		if(debug['K'])
+			print("case = %.8llux\n", q->val);
+		while(q->val != v) {
+			nextpc();
+			p->as = ABCASE;
+			patch(p, def);
+			v++;
+		}
+		nextpc();
+		p->as = ABCASE;
+		patch(p, q->label);
+		q++;
+		v++;
+	}
+	gbranch(OGOTO);		/* so that regopt() won't be confused */
+	patch(p, def);
+}
+
+void
+bitload(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+	int sh;
+	long v;
+	Node *l;
+
+	/*
+	 * n1 gets adjusted/masked value
+	 * n2 gets address of cell
+	 * n3 gets contents of cell
+	 */
+	l = b->left;
+	if(n2 != Z) {
+		regalloc(n1, l, nn);
+		reglcgen(n2, l, Z);
+		regalloc(n3, l, Z);
+		gopcode(OAS, n2, Z, n3);
+		gopcode(OAS, n3, Z, n1);
+	} else {
+		regalloc(n1, l, nn);
+		cgen(l, n1);
+	}
+	if(b->type->shift == 0 && typeu[b->type->etype]) {
+		v = ~0 + (1L << b->type->nbits);
+		gopcode(OAND, nodconst(v), Z, n1);
+	} else {
+		sh = 32 - b->type->shift - b->type->nbits;
+		if(sh > 0)
+			gopcode(OASHL, nodconst(sh), Z, n1);
+		sh += b->type->shift;
+		if(sh > 0)
+			if(typeu[b->type->etype])
+				gopcode(OLSHR, nodconst(sh), Z, n1);
+			else
+				gopcode(OASHR, nodconst(sh), Z, n1);
+	}
+}
+
+void
+bitstore(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+	long v;
+	Node nod, *l;
+	int sh;
+
+	/*
+	 * n1 has adjusted/masked value
+	 * n2 has address of cell
+	 * n3 has contents of cell
+	 */
+	l = b->left;
+	regalloc(&nod, l, Z);
+	v = ~0 + (1L << b->type->nbits);
+	gopcode(OAND, nodconst(v), Z, n1);
+	gopcode(OAS, n1, Z, &nod);
+	if(nn != Z)
+		gopcode(OAS, n1, Z, nn);
+	sh = b->type->shift;
+	if(sh > 0)
+		gopcode(OASHL, nodconst(sh), Z, &nod);
+	v <<= sh;
+	gopcode(OAND, nodconst(~v), Z, n3);
+	gopcode(OOR, n3, Z, &nod);
+	gopcode(OAS, &nod, Z, n2);
+
+	regfree(&nod);
+	regfree(n1);
+	regfree(n2);
+	regfree(n3);
+}
+
+long
+outstring(char *s, long n)
+{
+	long r;
+
+	if(suppress)
+		return nstring;
+	r = nstring;
+	while(n) {
+		string[mnstring] = *s++;
+		mnstring++;
+		nstring++;
+		if(mnstring >= NSNAME) {
+			gpseudo(ADATA, symstring, nodconst(0L));
+			p->from.offset += nstring - NSNAME;
+			p->reg = NSNAME;
+			p->to.type = D_SCONST;
+			memmove(p->to.sval, string, NSNAME);
+			mnstring = 0;
+		}
+		n--;
+	}
+	return r;
+}
+
+int
+mulcon(Node *n, Node *nn)
+{
+	Node *l, *r, nod1, nod2;
+	Multab *m;
+	long v, vs;
+	int o;
+	char code[sizeof(m->code)+2], *p;
+
+	if(typefd[n->type->etype])
+		return 0;
+	l = n->left;
+	r = n->right;
+	if(l->op == OCONST) {
+		l = r;
+		r = n->left;
+	}
+	if(r->op != OCONST)
+		return 0;
+	v = convvtox(r->vconst, n->type->etype);
+	if(v != r->vconst) {
+		if(debug['M'])
+			print("%L multiply conv: %lld\n", n->lineno, r->vconst);
+		return 0;
+	}
+	m = mulcon0(v);
+	if(!m) {
+		if(debug['M'])
+			print("%L multiply table: %lld\n", n->lineno, r->vconst);
+		return 0;
+	}
+	if(debug['M'] && debug['v'])
+		print("%L multiply: %ld\n", n->lineno, v);
+
+	memmove(code, m->code, sizeof(m->code));
+	code[sizeof(m->code)] = 0;
+
+	p = code;
+	if(p[1] == 'i')
+		p += 2;
+	regalloc(&nod1, n, nn);
+	cgen(l, &nod1);
+	vs = v;
+	regalloc(&nod2, n, Z);
+
+loop:
+	switch(*p) {
+	case 0:
+		regfree(&nod2);
+		if(vs < 0) {
+			gopcode(OAS, &nod1, Z, &nod1);
+			gopcode(ONEG, &nod1, Z, nn);
+		} else 
+			gopcode(OAS, &nod1, Z, nn);
+		regfree(&nod1);
+		return 1;
+	case '+':
+		o = OADD;
+		goto addsub;
+	case '-':
+		o = OSUB;
+	addsub:	/* number is r,n,l */
+		v = p[1] - '0';
+		r = &nod1;
+		if(v&4)
+			r = &nod2;
+		n = &nod1;
+		if(v&2)
+			n = &nod2;
+		l = &nod1;
+		if(v&1)
+			l = &nod2;
+		gopcode(o, l, n, r);
+		break;
+	default: /* op is shiftcount, number is r,l */
+		v = p[1] - '0';
+		r = &nod1;
+		if(v&2)
+			r = &nod2;
+		l = &nod1;
+		if(v&1)
+			l = &nod2;
+		v = *p - 'a';
+		if(v < 0 || v >= 32) {
+			diag(n, "mulcon unknown op: %c%c", p[0], p[1]);
+			break;
+		}
+		gopcode(OASHL, nodconst(v), l, r);
+		break;
+	}
+	p += 2;
+	goto loop;
+}
+
+void
+gextern(Sym *s, Node *a, long o, long w)
+{
+
+	if(a->op == OCONST && typev[a->type->etype]) {
+		if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+			gpseudo(ADATA, s, nod32const(a->vconst>>32));
+		else
+			gpseudo(ADATA, s, nod32const(a->vconst));
+		p->from.offset += o;
+		p->reg = 4;
+		if(align(0, types[TCHAR], Aarg1))	/* isbigendian */
+			gpseudo(ADATA, s, nod32const(a->vconst));
+		else
+			gpseudo(ADATA, s, nod32const(a->vconst>>32));
+		p->from.offset += o + 4;
+		p->reg = 4;
+		return;
+	}
+	gpseudo(ADATA, s, a);
+	p->from.offset += o;
+	p->reg = w;
+	if(p->to.type == D_OREG)
+		p->to.type = D_CONST;
+}
+
+void	zname(Biobuf*, Sym*, int);
+char*	zaddr(char*, Adr*, int);
+void	zwrite(Biobuf*, Prog*, int, int);
+void	outhist(Biobuf*);
+
+void
+zwrite(Biobuf *b, Prog *p, int sf, int st)
+{
+	char bf[100], *bp;
+	long l;
+
+	bf[0] = p->as;
+	bf[1] = p->as>>8;
+	bf[2] = p->reg;
+	l = p->lineno;
+	bf[3] = l;
+	bf[4] = l>>8;
+	bf[5] = l>>16;
+	bf[6] = l>>24;
+	bp = zaddr(bf+7, &p->from, sf);
+	bp = zaddr(bp, &p->to, st);
+	Bwrite(b, bf, bp-bf);
+}
+
+void
+outcode(void)
+{
+	struct { Sym *sym; short type; } h[NSYM];
+	Prog *p;
+	Sym *s;
+	int sf, st, t, sym;
+
+	if(debug['S']) {
+		for(p = firstp; p != P; p = p->link)
+			if(p->as != ADATA && p->as != AGLOBL)
+				pc--;
+		for(p = firstp; p != P; p = p->link) {
+			print("%P\n", p);
+			if(p->as != ADATA && p->as != AGLOBL)
+				pc++;
+		}
+	}
+	outhist(&outbuf);
+	for(sym=0; sym<NSYM; sym++) {
+		h[sym].sym = S;
+		h[sym].type = 0;
+	}
+	sym = 1;
+	for(p = firstp; p != P; p = p->link) {
+	jackpot:
+		sf = 0;
+		s = p->from.sym;
+		while(s != S) {
+			sf = s->sym;
+			if(sf < 0 || sf >= NSYM)
+				sf = 0;
+			t = p->from.name;
+			if(h[sf].type == t)
+			if(h[sf].sym == s)
+				break;
+			s->sym = sym;
+			zname(&outbuf, s, t);
+			h[sym].sym = s;
+			h[sym].type = t;
+			sf = sym;
+			sym++;
+			if(sym >= NSYM)
+				sym = 1;
+			break;
+		}
+		st = 0;
+		s = p->to.sym;
+		while(s != S) {
+			st = s->sym;
+			if(st < 0 || st >= NSYM)
+				st = 0;
+			t = p->to.name;
+			if(h[st].type == t)
+			if(h[st].sym == s)
+				break;
+			s->sym = sym;
+			zname(&outbuf, s, t);
+			h[sym].sym = s;
+			h[sym].type = t;
+			st = sym;
+			sym++;
+			if(sym >= NSYM)
+				sym = 1;
+			if(st == sf)
+				goto jackpot;
+			break;
+		}
+		zwrite(&outbuf, p, sf, st);
+	}
+	firstp = P;
+	lastp = P;
+}
+
+void
+outhist(Biobuf *b)
+{
+	Hist *h;
+	char *p, *q, *op, c;
+	Prog pg;
+	int n;
+
+	pg = zprog;
+	pg.as = AHISTORY;
+	c = pathchar();
+	for(h = hist; h != H; h = h->link) {
+		p = h->name;
+		op = 0;
+		/* on windows skip drive specifier in pathname */
+		if(systemtype(Windows) && p && p[1] == ':'){
+			p += 2;
+			c = *p;
+		}
+		if(p && p[0] != c && h->offset == 0 && pathname){
+			/* on windows skip drive specifier in pathname */
+			if(systemtype(Windows) && pathname[1] == ':') {
+				op = p;
+				p = pathname+2;
+				c = *p;
+			} else if(pathname[0] == c){
+				op = p;
+				p = pathname;
+			}
+		}
+		while(p) {
+			q = utfrune(p, c);
+			if(q) {
+				n = q-p;
+				if(n == 0){
+					n = 1;	/* leading "/" */
+					*p = '/';	/* don't emit "\" on windows */
+				}
+				q++;
+			} else {
+				n = strlen(p);
+				q = 0;
+			}
+			if(n) {
+				Bputc(b, ANAME);
+				Bputc(b, ANAME>>8);
+				Bputc(b, D_FILE);
+				Bputc(b, 1);
+				Bputc(b, '<');
+				Bwrite(b, p, n);
+				Bputc(b, 0);
+			}
+			p = q;
+			if(p == 0 && op) {
+				p = op;
+				op = 0;
+			}
+		}
+		pg.lineno = h->line;
+		pg.to.type = zprog.to.type;
+		pg.to.offset = h->offset;
+		if(h->offset)
+			pg.to.type = D_CONST;
+
+		zwrite(b, &pg, 0, 0);
+	}
+}
+
+void
+zname(Biobuf *b, Sym *s, int t)
+{
+	char *n, bf[8];
+	ulong sig;
+
+	n = s->name;
+	if(debug['T'] && t == D_EXTERN && s->sig != SIGDONE && s->type != types[TENUM] && s != symrathole){
+		sig = sign(s);
+		bf[0] = ASIGNAME;
+		bf[1] = ASIGNAME>>8;
+		bf[2] = sig;
+		bf[3] = sig>>8;
+		bf[4] = sig>>16;
+		bf[5] = sig>>24;
+		bf[6] = t;
+		bf[7] = s->sym;
+		Bwrite(b, bf, 8);
+		s->sig = SIGDONE;
+	}
+	else{
+		bf[0] = ANAME;
+		bf[1] = ANAME>>8;
+		bf[2] = t;	/* type */
+		bf[3] = s->sym;	/* sym */
+		Bwrite(b, bf, 4);
+	}
+	Bwrite(b, n, strlen(n)+1);
+}
+
+char*
+zaddr(char *bp, Adr *a, int s)
+{
+	long l;
+	Ieee e;
+
+	if(a->type == D_CONST){
+		l = a->offset;
+		if((vlong)l != a->offset)
+			a->type = D_DCONST;
+	}
+	bp[0] = a->type;
+	bp[1] = a->reg;
+	bp[2] = s;
+	bp[3] = a->name;
+	bp += 4;
+	switch(a->type) {
+	default:
+		diag(Z, "unknown type %d in zaddr", a->type);
+
+	case D_NONE:
+	case D_REG:
+	case D_SP:
+	case D_FREG:
+	case D_VREG:
+		break;
+
+	case D_PAIR:
+		*bp++ = a->offset;
+		break;
+
+	case D_OREG:
+	case D_XPRE:
+	case D_XPOST:
+	case D_CONST:
+	case D_BRANCH:
+	case D_SHIFT:
+	case D_EXTREG:
+	case D_SPR:
+		l = a->offset;
+		bp[0] = l;
+		bp[1] = l>>8;
+		bp[2] = l>>16;
+		bp[3] = l>>24;
+		bp += 4;
+		break;
+
+	case D_DCONST:
+		l = a->offset;
+		bp[0] = l;
+		bp[1] = l>>8;
+		bp[2] = l>>16;
+		bp[3] = l>>24;
+		bp += 4;
+		l = a->offset>>32;
+		bp[0] = l;
+		bp[1] = l>>8;
+		bp[2] = l>>16;
+		bp[3] = l>>24;
+		bp += 4;
+		break;
+
+	case D_SCONST:
+		memmove(bp, a->sval, NSNAME);
+		bp += NSNAME;
+		break;
+
+	case D_FCONST:
+		ieeedtod(&e, a->dval);
+		l = e.l;
+		bp[0] = l;
+		bp[1] = l>>8;
+		bp[2] = l>>16;
+		bp[3] = l>>24;
+		bp += 4;
+		l = e.h;
+		bp[0] = l;
+		bp[1] = l>>8;
+		bp[2] = l>>16;
+		bp[3] = l>>24;
+		bp += 4;
+		break;
+	}
+	return bp;
+}
+
+long
+align(long i, Type *t, int op)
+{
+	long o;
+	Type *v;
+	int w;
+
+	o = i;
+	w = 1;
+	switch(op) {
+	default:
+		diag(Z, "unknown align opcode %d", op);
+		break;
+
+	case Asu2:	/* padding at end of a struct */
+		w = SZ_VLONG;
+		if(packflg)
+			w = packflg;
+		break;
+
+	case Ael1:	/* initial align of struct element */
+		for(v=t; v->etype==TARRAY; v=v->link)
+			;
+		w = ewidth[v->etype];
+		if(w <= 0 || w >= SZ_VLONG)
+			w = SZ_VLONG;
+		if(packflg)
+			w = packflg;
+		break;
+
+	case Ael2:	/* width of a struct element */
+		o += t->width;
+		break;
+
+	case Aarg0:	/* initial passbyptr argument in arg list */
+		if(typesu[t->etype]) {
+			o = align(o, types[TIND], Aarg1);
+			o = align(o, types[TIND], Aarg2);
+		}
+		break;
+
+	case Aarg1:	/* initial align of parameter */
+		w = ewidth[t->etype];
+		if(w <= 0 || w >= SZ_VLONG) {
+			w = SZ_VLONG;
+			break;
+		}
+		w = 1;		/* little endian no adjustment */
+		break;
+
+	case Aarg2:	/* width of a parameter */
+		o += t->width;
+		w = SZ_VLONG;
+		break;
+
+	case Aaut3:	/* total align of automatic */
+		o = align(o, t, Ael2);
+		o = align(o, t, Ael1);
+		w = SZ_LONG;	/* because of a pun in cc/dcl.c:contig() */
+		break;
+	}
+	o = round(o, w);
+	if(debug['A'])
+		print("align %s %ld %T = %ld\n", bnames[op], i, t, o);
+	return o;
+}
+
+long
+maxround(long max, long v)
+{
+	v = round(v, SZ_VLONG);
+	if(v > max)
+		return v;
+	return max;
+}
--- /dev/null
+++ b/sys/src/cmd/7c/txt.c
@@ -1,0 +1,1412 @@
+#include "gc.h"
+
+static	char	resvreg[nelem(reg)];
+
+#define	isv(et)	((et) == TVLONG || (et) == TUVLONG || (et) == TIND)
+
+void
+ginit(void)
+{
+	Type *t;
+
+	thechar = '7';
+	thestring = "arm64";
+	exregoffset = REGEXT;
+	exfregoffset = FREGEXT;
+	newvlongcode = 1;
+	listinit();
+	nstring = 0;
+	mnstring = 0;
+	nrathole = 0;
+	pc = 0;
+	breakpc = -1;
+	continpc = -1;
+	cases = C;
+	firstp = P;
+	lastp = P;
+	tfield = types[TLONG];
+
+	typeswitch = typechlv;
+	typeword = typechlvp;
+	typecmplx = typesu;
+	/* TO DO */
+	memmove(typechlpv, typechlp, sizeof(typechlpv));
+	typechlpv[TVLONG] = 1;
+	typechlpv[TUVLONG] = 1;
+
+	zprog.link = P;
+	zprog.as = AGOK;
+	zprog.reg = NREG;
+	zprog.from.type = D_NONE;
+	zprog.from.name = D_NONE;
+	zprog.from.reg = NREG;
+	zprog.to = zprog.from;
+
+	regnode.op = OREGISTER;
+	regnode.class = CEXREG;
+	regnode.reg = REGTMP;
+	regnode.complex = 0;
+	regnode.addable = 11;
+	regnode.type = types[TLONG];
+
+	qregnode = regnode;
+	qregnode.type = types[TVLONG];
+
+	constnode.op = OCONST;
+	constnode.class = CXXX;
+	constnode.complex = 0;
+	constnode.addable = 20;
+	constnode.type = types[TLONG];
+
+	vconstnode = constnode;
+	vconstnode.type = types[TVLONG];
+
+	fconstnode.op = OCONST;
+	fconstnode.class = CXXX;
+	fconstnode.complex = 0;
+	fconstnode.addable = 20;
+	fconstnode.type = types[TDOUBLE];
+
+	nodsafe = new(ONAME, Z, Z);
+	nodsafe->sym = slookup(".safe");
+	nodsafe->type = types[TINT];
+	nodsafe->etype = types[TINT]->etype;
+	nodsafe->class = CAUTO;
+	complex(nodsafe);
+
+	t = typ(TARRAY, types[TCHAR]);
+	symrathole = slookup(".rathole");
+	symrathole->class = CGLOBL;
+	symrathole->type = t;
+
+	nodrat = new(ONAME, Z, Z);
+	nodrat->sym = symrathole;
+	nodrat->type = types[TIND];
+	nodrat->etype = TVOID;
+	nodrat->class = CGLOBL;
+	complex(nodrat);
+	nodrat->type = t;
+
+	nodret = new(ONAME, Z, Z);
+	nodret->sym = slookup(".ret");
+	nodret->type = types[TIND];
+	nodret->etype = TIND;
+	nodret->class = CPARAM;
+	nodret = new(OIND, nodret, Z);
+	complex(nodret);
+
+	com64init();
+
+	memset(reg, 0, sizeof(reg));
+	/* don't allocate */
+	reg[REGTMP] = 1;
+	reg[REGSB] = 1;
+	reg[REGSP] = 1;
+	reg[REGZERO] = 1;
+	/* keep two external registers */
+	reg[REGEXT] = 1;
+	reg[REGEXT-1] = 1;
+	memmove(resvreg, reg, sizeof(reg));
+}
+
+void
+gclean(void)
+{
+	int i;
+	Sym *s;
+
+	for(i=0; i<NREG; i++)
+		if(reg[i] && !resvreg[i])
+			diag(Z, "reg %d left allocated", i);
+	for(i=NREG; i<NREG+NFREG; i++)
+		if(reg[i] && !resvreg[i])
+			diag(Z, "freg %d left allocated", i-NREG);
+	while(mnstring)
+		outstring("", 1L);
+	symstring->type->width = nstring;
+	symrathole->type->width = nrathole;
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		if(s->type == T)
+			continue;
+		if(s->type->width == 0)
+			continue;
+		if(s->class != CGLOBL && s->class != CSTATIC)
+			continue;
+		if(s->type == types[TENUM])
+			continue;
+		gpseudo(AGLOBL, s, nodconst(s->type->width));
+	}
+	nextpc();
+	p->as = AEND;
+	outcode();
+}
+
+void
+nextpc(void)
+{
+
+	p = alloc(sizeof(*p));
+	*p = zprog;
+	p->lineno = nearln;
+	pc++;
+	if(firstp == P) {
+		firstp = p;
+		lastp = p;
+		return;
+	}
+	lastp->link = p;
+	lastp = p;
+}
+
+void
+gargs(Node *n, Node *tn1, Node *tn2)
+{
+	long regs;
+	Node fnxargs[20], *fnxp;
+
+	regs = cursafe;
+
+	fnxp = fnxargs;
+	garg1(n, tn1, tn2, 0, &fnxp);	/* compile fns to temps */
+
+	curarg = 0;
+	fnxp = fnxargs;
+	garg1(n, tn1, tn2, 1, &fnxp);	/* compile normal args and temps */
+
+	cursafe = regs;
+}
+
+void
+garg1(Node *n, Node *tn1, Node *tn2, int f, Node **fnxp)
+{
+	Node nod;
+
+	if(n == Z)
+		return;
+	if(n->op == OLIST) {
+		garg1(n->left, tn1, tn2, f, fnxp);
+		garg1(n->right, tn1, tn2, f, fnxp);
+		return;
+	}
+	if(f == 0) {
+		if(n->complex >= FNX) {
+			regsalloc(*fnxp, n);
+			nod = znode;
+			nod.op = OAS;
+			nod.left = *fnxp;
+			nod.right = n;
+			nod.type = n->type;
+			cgen(&nod, Z);
+			(*fnxp)++;
+		}
+		return;
+	}
+	if(typesu[n->type->etype]) {
+		regaalloc(tn2, n);
+		if(n->complex >= FNX) {
+			sugen(*fnxp, tn2, n->type->width);
+			(*fnxp)++;
+		} else
+			sugen(n, tn2, n->type->width);
+		return;
+	}
+	if(REGARG >= 0 && curarg == 0 && typechlpv[n->type->etype]) {
+		regaalloc1(tn1, n);
+		if(n->complex >= FNX) {
+			cgen(*fnxp, tn1);
+			(*fnxp)++;
+		} else
+			cgen(n, tn1);
+		return;
+	}
+	if(vconst(n) == 0) {
+		regaalloc(tn2, n);
+		gopcode(OAS, n, Z, tn2);
+		return;
+	}
+	regalloc(tn1, n, Z);
+	if(n->complex >= FNX) {
+		cgen(*fnxp, tn1);
+		(*fnxp)++;
+	} else
+		cgen(n, tn1);
+	regaalloc(tn2, n);
+	gopcode(OAS, tn1, Z, tn2);
+	regfree(tn1);
+}
+
+Node*
+nodgconst(vlong v, Type *t)
+{
+	if(!typev[t->etype])
+		return nodconst((long)v);
+	vconstnode.vconst = v;
+	return &vconstnode;
+}
+
+Node*
+nodconst(long v)
+{
+	constnode.vconst = v;
+	return &constnode;
+}
+
+Node*
+nod32const(vlong v)
+{
+	constnode.vconst = v & MASK(32);
+	return &constnode;
+}
+
+Node*
+nodfconst(double d)
+{
+	fconstnode.fconst = d;
+	return &fconstnode;
+}
+
+void
+nodreg(Node *n, Node *nn, int reg)
+{
+	*n = qregnode;
+	n->reg = reg;
+	n->type = nn->type;
+	n->lineno = nn->lineno;
+}
+
+void
+regret(Node *n, Node *nn)
+{
+	int r;
+
+	r = REGRET;
+	if(typefd[nn->type->etype])
+		r = FREGRET+NREG;
+	nodreg(n, nn, r);
+	reg[r]++;
+}
+
+void
+regalloc(Node *n, Node *tn, Node *o)
+{
+	int i, j;
+	static int lasti;
+
+	switch(tn->type->etype) {
+	case TCHAR:
+	case TUCHAR:
+	case TSHORT:
+	case TUSHORT:
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+	case TVLONG:
+	case TUVLONG:
+	case TIND:
+		if(o != Z && o->op == OREGISTER) {
+			i = o->reg;
+			if(i >= 0 && i < NREG)
+				goto out;
+		}
+		j = lasti + REGRET+1;
+		for(i=REGRET+1; i<NREG; i++) {
+			if(j >= NREG)
+				j = REGRET+1;
+			if(reg[j] == 0 && resvreg[j] == 0) {
+				i = j;
+				goto out;
+			}
+			j++;
+		}
+		diag(tn, "out of fixed registers");
+		goto err;
+
+	case TFLOAT:
+	case TDOUBLE:
+		if(o != Z && o->op == OREGISTER) {
+			i = o->reg;
+			if(i >= NREG && i < NREG+NFREG)
+				goto out;
+		}
+		j = lasti + NREG;
+		for(i=NREG; i<NREG+NFREG; i++) {
+			if(j >= NREG+NFREG)
+				j = NREG;
+			if(reg[j] == 0) {
+				i = j;
+				goto out;
+			}
+			j++;
+		}
+		diag(tn, "out of float registers");
+		goto err;
+	}
+	diag(tn, "unknown type in regalloc: %T", tn->type);
+err:
+	nodreg(n, tn, 0);
+	return;
+out:
+	reg[i]++;
+	lasti++;
+	if(lasti >= 5)
+		lasti = 0;
+	nodreg(n, tn, i);
+}
+
+void
+regialloc(Node *n, Node *tn, Node *o)
+{
+	Node nod;
+
+	nod = *tn;
+	nod.type = types[TIND];
+	regalloc(n, &nod, o);
+}
+
+void
+regfree(Node *n)
+{
+	int i;
+
+	i = 0;
+	if(n->op != OREGISTER && n->op != OINDREG)
+		goto err;
+	i = n->reg;
+	if(i < 0 || i >= sizeof(reg))
+		goto err;
+	if(reg[i] <= 0)
+		goto err;
+	reg[i]--;
+	return;
+err:
+	diag(n, "error in regfree: %d", i);
+}
+
+void
+regsalloc(Node *n, Node *nn)
+{
+	cursafe = align(cursafe, nn->type, Aaut3);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+	*n = *nodsafe;
+	n->xoffset = -(stkoff + cursafe);
+	n->type = nn->type;
+	n->etype = nn->type->etype;
+	n->lineno = nn->lineno;
+}
+
+void
+regaalloc1(Node *n, Node *nn)
+{
+	nodreg(n, nn, REGARG);
+	reg[REGARG]++;
+	curarg = align(curarg, nn->type, Aarg1);
+	curarg = align(curarg, nn->type, Aarg2);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+}
+
+void
+regaalloc(Node *n, Node *nn)
+{
+	curarg = align(curarg, nn->type, Aarg1);
+	*n = *nn;
+	n->op = OINDREG;
+	n->reg = REGSP;
+	n->xoffset = curarg + SZ_VLONG;
+	n->complex = 0;
+	n->addable = 20;
+	curarg = align(curarg, nn->type, Aarg2);
+	maxargsafe = maxround(maxargsafe, cursafe+curarg);
+}
+
+void
+regind(Node *n, Node *nn)
+{
+
+	if(n->op != OREGISTER) {
+		diag(n, "regind not OREGISTER");
+		return;
+	}
+	n->op = OINDREG;
+	n->type = nn->type;
+}
+
+void
+raddr(Node *n, Prog *p)
+{
+	Adr a;
+
+	naddr(n, &a);
+	if(a.type == D_CONST && a.offset == 0) {
+		a.type = D_REG;
+		a.reg = REGZERO;
+	}
+	if(a.type != D_REG && a.type != D_FREG) {
+		if(n)
+			diag(n, "bad in raddr: %O", n->op);
+		else
+			diag(n, "bad in raddr: <null>");
+		p->reg = NREG;
+	} else
+		p->reg = a.reg;
+}
+
+void
+naddr(Node *n, Adr *a)
+{
+	long v;
+
+	a->type = D_NONE;
+	if(n == Z)
+		return;
+	switch(n->op) {
+	default:
+	bad:
+		diag(n, "bad in naddr: %O", n->op);
+		break;
+
+	case OREGISTER:
+		a->type = D_REG;
+		a->sym = S;
+		a->reg = n->reg;
+		if(a->reg >= NREG) {
+			a->type = D_FREG;
+			a->reg -= NREG;
+		}
+		break;
+
+	case OIND:
+		naddr(n->left, a);
+		if(a->type == D_REG) {
+			a->type = D_OREG;
+			break;
+		}
+		if(a->type == D_CONST) {
+			a->type = D_OREG;
+			break;
+		}
+		goto bad;
+
+	case OINDREG:
+		a->type = D_OREG;
+		a->sym = S;
+		a->offset = n->xoffset;
+		a->reg = n->reg;
+		break;
+
+	case ONAME:
+		a->etype = n->etype;
+		a->type = D_OREG;
+		a->name = D_STATIC;
+		a->sym = n->sym;
+		a->offset = n->xoffset;
+		if(n->class == CSTATIC)
+			break;
+		if(n->class == CEXTERN || n->class == CGLOBL) {
+			a->name = D_EXTERN;
+			break;
+		}
+		if(n->class == CAUTO) {
+			a->name = D_AUTO;
+			break;
+		}
+		if(n->class == CPARAM) {
+			a->name = D_PARAM;
+			break;
+		}
+		goto bad;
+
+	case OCONST:
+		a->sym = S;
+		a->reg = NREG;
+		if(typefd[n->type->etype]) {
+			a->type = D_FCONST;
+			a->dval = n->fconst;
+		} else {
+			a->type = D_CONST;
+			a->offset = n->vconst;
+		}
+		break;
+
+	case OADDR:
+		naddr(n->left, a);
+		if(a->type == D_OREG) {
+			a->type = D_CONST;
+			break;
+		}
+print("bad addr %D\n", a);
+		goto bad;
+
+	case OADD:
+		if(n->left->op == OCONST) {
+			naddr(n->left, a);
+			v = a->offset;
+			naddr(n->right, a);
+		} else {
+			naddr(n->right, a);
+			v = a->offset;
+			naddr(n->left, a);
+		}
+		a->offset += v;
+		break;
+
+	}
+}
+
+void
+fop(int as, int f1, int f2, Node *t)
+{
+	Node nod1, nod2, nod3;
+
+	nodreg(&nod1, t, NREG+f1);
+	nodreg(&nod2, t, NREG+f2);
+	regalloc(&nod3, t, t);
+	gopcode(as, &nod1, &nod2, &nod3);
+	gmove(&nod3, t);
+	regfree(&nod3);
+}
+
+void
+gmove(Node *f, Node *t)
+{
+	int ft, tt, a;
+	Node nod;
+
+	ft = f->type->etype;
+	tt = t->type->etype;
+
+	if(ft == TDOUBLE && f->op == OCONST) {
+	}
+	if(ft == TFLOAT && f->op == OCONST) {
+	}
+
+	/*
+	 * a load --
+	 * put it into a register then
+	 * worry what to do with it.
+	 */
+	if(f->op == ONAME || f->op == OINDREG || f->op == OIND) {
+		switch(ft) {
+		default:
+			if(ewidth[ft] == 4){
+				if(typeu[ft])
+					a = AMOVWU;
+				else
+					a = AMOVW;
+			}else
+				a = AMOV;
+			break;
+		case TINT:
+			a = AMOVW;
+			break;
+		case TUINT:
+			a = AMOVWU;
+			break;
+		case TFLOAT:
+			a = AFMOVS;
+			break;
+		case TDOUBLE:
+			a = AFMOVD;
+			break;
+		case TCHAR:
+			a = AMOVB;
+			break;
+		case TUCHAR:
+			a = AMOVBU;
+			break;
+		case TSHORT:
+			a = AMOVH;
+			break;
+		case TUSHORT:
+			a = AMOVHU;
+			break;
+		}
+		regalloc(&nod, f, t);
+		gins(a, f, &nod);
+		gmove(&nod, t);
+		regfree(&nod);
+		return;
+	}
+
+	/*
+	 * a store --
+	 * put it into a register then
+	 * store it.
+	 */
+	if(t->op == ONAME || t->op == OINDREG || t->op == OIND) {
+		switch(tt) {
+		default:
+			if(ewidth[tt] == 4)
+				a = AMOVW;
+			else
+				a = AMOV;
+			break;
+		case TINT:
+			a = AMOVW;
+			break;
+		case TUINT:
+			a = AMOVWU;
+			break;
+		case TUCHAR:
+			a = AMOVBU;
+			break;
+		case TCHAR:
+			a = AMOVB;
+			break;
+		case TUSHORT:
+			a = AMOVHU;
+			break;
+		case TSHORT:
+			a = AMOVH;
+			break;
+		case TFLOAT:
+			a = AFMOVS;
+			break;
+		case TDOUBLE:
+			a = AFMOVD;
+			break;
+		}
+		if(!typefd[ft] && vconst(f) == 0) {
+			gins(a, f, t);
+			return;
+		}
+		if(ft == tt)
+			regalloc(&nod, t, f);
+		else
+			regalloc(&nod, t, Z);
+		gmove(f, &nod);
+		gins(a, &nod, t);
+		regfree(&nod);
+		return;
+	}
+
+	/*
+	 * type x type cross table
+	 */
+	a = AGOK;
+	switch(ft) {
+	case TDOUBLE:
+	case TFLOAT:
+		switch(tt) {
+		case TDOUBLE:
+			a = AFMOVD;
+			if(ft == TFLOAT)
+				a = AFCVTSD;
+			break;
+		case TFLOAT:
+			a = AFMOVS;
+			if(ft == TDOUBLE)
+				a = AFCVTDS;
+			break;
+		case TCHAR:
+		case TSHORT:
+		case TINT:
+		case TLONG:
+			a = AFCVTZSDW;
+			if(ft == TFLOAT)
+				a = AFCVTZSSW;
+			break;
+		case TUCHAR:
+		case TUSHORT:
+		case TUINT:
+		case TULONG:
+			a = AFCVTZUDW;
+			if(ft == TFLOAT)
+				a = AFCVTZUSW;
+			break;
+		case TVLONG:
+			a = AFCVTZSD;
+			if(ft == TFLOAT)
+				a = AFCVTZSS;
+			break;
+		case TUVLONG:
+		case TIND:
+			a = AFCVTZUD;
+			if(ft == TFLOAT)
+				a = AFCVTZUS;
+			break;
+		}
+		break;
+	case TUINT:
+	case TULONG:
+	case TINT:
+	case TLONG:
+		switch(tt) {
+		case TDOUBLE:
+			if(ft == TUINT || ft == TULONG)
+				gins(AUCVTFWD, f, t);
+			else
+				gins(ASCVTFWD, f, t);
+			return;
+		case TFLOAT:
+			if(ft == TUINT || ft == TULONG)
+				gins(AUCVTFWS, f, t);
+			else
+				gins(ASCVTFWS, f, t);
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			if(typeu[tt])
+				a = AMOVWU;
+			else
+				a = AMOVW;
+			break;
+		case TVLONG:
+		case TUVLONG:
+		case TIND:
+			a = AMOV;
+			break;
+		}
+		break;
+	case TVLONG:
+	case TUVLONG:
+	case TIND:
+		switch(tt) {
+		case TDOUBLE:
+			if(ft == TVLONG)
+				gins(ASCVTFD, f, t);
+			else
+				gins(AUCVTFD, f, t);
+			return;
+		case TFLOAT:
+			if(ft == TVLONG)
+				gins(ASCVTFS, f, t);
+			else
+				gins(AUCVTFS, f, t);
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TVLONG:
+		case TUVLONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOV;	/* TO DO: conversion done? */
+			break;
+		}
+		break;
+	case TSHORT:
+		switch(tt) {
+		case TDOUBLE:
+			regalloc(&nod, f, Z);
+			gins(AMOVH, f, &nod);
+			gins(ASCVTFWD, &nod, t);
+			regfree(&nod);
+			return;
+		case TFLOAT:
+			regalloc(&nod, f, Z);
+			gins(AMOVH, f, &nod);
+			gins(ASCVTFWS, &nod, t);
+			regfree(&nod);
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TVLONG:
+		case TUVLONG:
+		case TIND:
+			a = AMOVH;
+			break;
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOV;
+			break;
+		}
+		break;
+	case TUSHORT:
+		switch(tt) {
+		case TDOUBLE:
+			regalloc(&nod, f, Z);
+			gins(AMOVHU, f, &nod);
+			gins(AUCVTFWD, &nod, t);
+			regfree(&nod);
+			return;
+		case TFLOAT:
+			regalloc(&nod, f, Z);
+			gins(AMOVHU, f, &nod);
+			gins(AUCVTFWS, &nod, t);
+			regfree(&nod);
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TVLONG:
+		case TUVLONG:
+		case TIND:
+			a = AMOVHU;
+			break;
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOV;
+			break;
+		}
+		break;
+	case TCHAR:
+		switch(tt) {
+		case TDOUBLE:
+			regalloc(&nod, f, Z);
+			gins(AMOVB, f, &nod);
+			gins(ASCVTFWD, &nod, t);
+			regfree(&nod);
+			return;
+		case TFLOAT:
+			regalloc(&nod, f, Z);
+			gins(AMOVB, f, &nod);
+			gins(ASCVTFWS, &nod, t);
+			regfree(&nod);
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+		case TVLONG:
+		case TUVLONG:
+			a = AMOVB;
+			break;
+		case TCHAR:
+		case TUCHAR:
+			a = AMOV;
+			break;
+		}
+		break;
+	case TUCHAR:
+		switch(tt) {
+		case TDOUBLE:
+			regalloc(&nod, f, Z);
+			gins(AMOVBU, f, &nod);
+			gins(AUCVTFWD, &nod, t);
+			regfree(&nod);
+			return;
+		case TFLOAT:
+			regalloc(&nod, f, Z);
+			gins(AMOVBU, f, &nod);
+			gins(AUCVTFWS, &nod, t);
+			regfree(&nod);
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+		case TVLONG:
+		case TUVLONG:
+			a = AMOVBU;
+			break;
+		case TCHAR:
+		case TUCHAR:
+			a = AMOV;
+			break;
+		}
+		break;
+	}
+	if(a == AGOK)
+		diag(Z, "bad opcode in gmove %T -> %T", f->type, t->type);
+	if(a == AMOV || (a == AMOVW || a == AMOVWU) && ewidth[ft] == ewidth[tt] || a == AFMOVS || a == AFMOVD)
+	if(samaddr(f, t))
+		return;
+	gins(a, f, t);
+}
+
+void
+gmover(Node *f, Node *t)
+{
+	int ft, tt, a;
+
+	ft = f->type->etype;
+	tt = t->type->etype;
+	a = AGOK;
+	if(typechlp[ft] && typechlp[tt] && ewidth[ft] >= ewidth[tt]){
+		switch(tt){
+		case TSHORT:
+			a = AMOVH;
+			break;
+		case TUSHORT:
+			a = AMOVHU;
+			break;
+		case TCHAR:
+			a = AMOVB;
+			break;
+		case TUCHAR:
+			a = AMOVBU;
+			break;
+		case TINT:
+			a = AMOVW;
+			break;
+		case TUINT:
+			a = AMOVWU;
+			break;
+		}
+	}
+	if(a == AGOK)
+		gmove(f, t);
+	else
+		gins(a, f, t);
+}
+
+void
+gins(int a, Node *f, Node *t)
+{
+
+	nextpc();
+	p->as = a;
+	if(f != Z)
+		naddr(f, &p->from);
+	if(t != Z)
+		naddr(t, &p->to);
+	if(debug['g'])
+		print("%P\n", p);
+}
+
+void
+gopcode(int o, Node *f1, Node *f2, Node *t)
+{
+	int a, et, true;
+	Adr ta;
+
+	et = TLONG;
+	if(f1 != Z && f1->type != T) {
+		et = f1->type->etype;
+		if(f1->op == OCONST){
+			if(t != Z && t->type != T)
+				et = t->type->etype;
+			else if(f2 != Z && f2->type != T && ewidth[f2->type->etype] > ewidth[et])
+				et = f2->type->etype;
+		}
+	}
+	true = o & BTRUE;
+	o &= ~BTRUE;
+	a = AGOK;
+	switch(o) {
+	case OAS:
+		gmove(f1, t);
+		return;
+
+	case OASADD:
+	case OADD:
+		a = AADDW;
+		if(isv(et))
+			a = AADD;
+		else if(et == TFLOAT)
+			a = AFADDS;
+		else if(et == TDOUBLE)
+			a = AFADDD;
+		break;
+
+	case OASSUB:
+	case OSUB:
+		a = ASUBW;
+		if(isv(et))
+			a = ASUB;
+		else if(et == TFLOAT)
+			a = AFSUBS;
+		else if(et == TDOUBLE)
+			a = AFSUBD;
+		break;
+
+	case OASOR:
+	case OOR:
+		a = AORRW;
+		if(isv(et))
+			a = AORR;
+		break;
+
+	case OASAND:
+	case OAND:
+		a = AANDW;
+		if(isv(et))
+			a = AAND;
+		break;
+
+	case OASXOR:
+	case OXOR:
+		a = AEORW;
+		if(isv(et))
+			a = AEOR;
+		break;
+
+	case OASLSHR:
+	case OLSHR:
+		a = ALSRW;
+		if(isv(et))
+			a = ALSR;
+		break;
+
+	case OASASHR:
+	case OASHR:
+		a = AASRW;
+		if(isv(et))
+			a = AASR;
+		break;
+
+	case OASASHL:
+	case OASHL:
+		a = ALSLW;
+		if(isv(et))
+			a = ALSL;
+		break;
+
+	case OFUNC:
+		a = ABL;
+		break;
+
+	case OASMUL:
+	case OMUL:
+		a = AMULW;
+		if(isv(et))
+			a = AMUL;
+		else if(et == TFLOAT)
+			a = AFMULS;
+		else if(et == TDOUBLE)
+			a = AFMULD;
+		break;
+
+	case OASDIV:
+	case ODIV:
+		a = ASDIVW;
+		if(isv(et))
+			a = ASDIV;
+		else if(et == TFLOAT)
+			a = AFDIVS;
+		else if(et == TDOUBLE)
+			a = AFDIVD;
+		break;
+
+	case OASMOD:
+	case OMOD:
+		a = AREMW;
+		if(isv(et))
+			a = AREM;
+		break;
+
+	case OASLMUL:
+	case OLMUL:
+		a = AUMULL;
+		if(isv(et))
+			a = AMUL;
+		break;
+
+	case OASLMOD:
+	case OLMOD:
+		a = AUREMW;
+		if(isv(et))
+			a = AUREM;
+		break;
+
+	case OASLDIV:
+	case OLDIV:
+		a = AUDIVW;
+		if(isv(et))
+			a = AUDIV;
+		break;
+
+	case OCOM:
+		a = AMVNW;
+		if(isv(et))
+			a = AMVN;
+		break;
+
+	case ONEG:
+		a = ANEGW;
+		if(isv(et))
+			a = ANEG;
+		break;
+
+	case OCASE:
+		a = ACASE;	/* ACASEW? */
+		break;
+
+	case OEQ:
+	case ONE:
+	case OLT:
+	case OLE:
+	case OGE:
+	case OGT:
+	case OLO:
+	case OLS:
+	case OHS:
+	case OHI:
+		a = ACMPW;
+		if(isv(et))
+			a = ACMP;
+		if(et == TFLOAT)
+			a = AFCMPS;
+		else if(et == TDOUBLE)
+			a = AFCMPD;
+		nextpc();
+		p->as = a;
+		naddr(f1, &p->from);
+		if(f1->op == OCONST && p->from.offset < 0){
+			if(a == ACMPW && (ulong)p->from.offset != 0x80000000UL) {
+				p->as = ACMNW;
+				p->from.offset = -p->from.offset;
+			}else if(a == ACMP && p->from.offset != 0x8000000000000000LL){
+				p->as = ACMN;
+				p->from.offset = -p->from.offset;
+			}
+		}
+		raddr(f2, p);
+		switch(o) {
+		case OEQ:
+			a = ABEQ;
+			break;
+		case ONE:
+			a = ABNE;
+			break;
+		case OLT:
+			a = ABLT;
+			/* ensure NaN comparison is always false */
+			if(typefd[et] && !true)
+				a = ABMI;
+			break;
+		case OLE:
+			a = ABLE;
+			if(typefd[et] && !true)
+				a = ABLS;
+			break;
+		case OGE:
+			a = ABGE;
+			if(typefd[et] && true)
+				a = ABPL;
+			break;
+		case OGT:
+			a = ABGT;
+			if(typefd[et] && true)
+				a = ABHI;
+			break;
+		case OLO:
+			a = ABLO;
+			break;
+		case OLS:
+			a = ABLS;
+			break;
+		case OHS:
+			a = ABHS;
+			break;
+		case OHI:
+			a = ABHI;
+			break;
+		}
+		f1 = Z;
+		f2 = Z;
+		break;
+	}
+	if(a == AGOK)
+		diag(Z, "bad in gopcode %O", o);
+	nextpc();
+	p->as = a;
+	if(f1 != Z)
+		naddr(f1, &p->from);
+	if(f2 != Z) {
+		naddr(f2, &ta);
+		p->reg = ta.reg;
+	}
+	if(t != Z)
+		naddr(t, &p->to);
+	if(debug['g'])
+		print("%P\n", p);
+}
+
+int
+samaddr(Node *f, Node *t)
+{
+	return f->op == OREGISTER && t->op == OREGISTER && f->reg == t->reg;
+}
+
+void
+gbranch(int o)
+{
+	int a;
+
+	a = AGOK;
+	switch(o) {
+	case ORETURN:
+		a = ARETURN;
+		break;
+	case OGOTO:
+		a = AB;
+		break;
+	}
+	nextpc();
+	if(a == AGOK) {
+		diag(Z, "bad in gbranch %O",  o);
+		nextpc();
+	}
+	p->as = a;
+}
+
+void
+patch(Prog *op, long pc)
+{
+
+	op->to.offset = pc;
+	op->to.type = D_BRANCH;
+}
+
+void
+gpseudo(int a, Sym *s, Node *n)
+{
+
+	nextpc();
+	p->as = a;
+	p->from.type = D_OREG;
+	p->from.sym = s;
+	p->from.name = D_EXTERN;
+	if(a == ATEXT)
+		p->reg = (profileflg ? 0 : NOPROF);
+	if(s->class == CSTATIC)
+		p->from.name = D_STATIC;
+	naddr(n, &p->to);
+	if(a == ADATA || a == AGLOBL)
+		pc--;
+}
+
+int
+sconst(Node *n)
+{
+	vlong vv;
+
+	if(n->op == OCONST) {
+		if(!typefd[n->type->etype]) {
+			vv = n->vconst;
+			if(vv >= (vlong)(-32766) && vv < (vlong)32766)
+				return 1;
+			/*
+			 * should be specialised for constant values which will
+			 * fit in different instructionsl; for now, let 5l
+			 * sort it out
+			 */
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int
+isaddcon(vlong v)
+{
+	/* uimm12 or uimm24? */
+	if(v < 0)
+		return 0;
+	if((v & 0xFFF) == 0)
+		v >>= 12;
+	return v <= 0xFFF;
+}
+
+int
+sval(long v)
+{
+	return isaddcon(v) || isaddcon(-v);
+}
+
+int
+usableoffset(Node *n, vlong o, Node *v)
+{
+	int s, et;
+
+	et = v->type->etype;
+	if(v->op != OCONST || typefd[et])
+		return 0;
+	s = n->type->width;
+	if(s > 16)
+		s = 16;
+	o += v->vconst;
+	return o >= -256 || o < 4095*s;
+}
+
+long
+exreg(Type *t)
+{
+	long o;
+
+	if(typechlpv[t->etype]) {
+		if(exregoffset <= REGEXT-2)
+			return 0;
+		o = exregoffset;
+		if(reg[o] && !resvreg[o])
+			return 0;
+		resvreg[o] = reg[o] = 1;
+		exregoffset--;
+		return o;
+	}
+	if(typefd[t->etype]) {
+		if(exfregoffset <= NFREG-1)
+			return 0;
+		o = exfregoffset + NREG;
+		if(reg[o] && !resvreg[o])
+			return 0;
+		resvreg[o] = reg[o] = 1;
+		exfregoffset--;
+		return o;
+	}
+	return 0;
+}
+
+schar	ewidth[NTYPE] =
+{
+	-1,		/* [TXXX] */
+	SZ_CHAR,	/* [TCHAR] */
+	SZ_CHAR,	/* [TUCHAR] */
+	SZ_SHORT,	/* [TSHORT] */
+	SZ_SHORT,	/* [TUSHORT] */
+	SZ_INT,		/* [TINT] */
+	SZ_INT,		/* [TUINT] */
+	SZ_LONG,	/* [TLONG] */
+	SZ_LONG,	/* [TULONG] */
+	SZ_VLONG,	/* [TVLONG] */
+	SZ_VLONG,	/* [TUVLONG] */
+	SZ_FLOAT,	/* [TFLOAT] */
+	SZ_DOUBLE,	/* [TDOUBLE] */
+	SZ_IND,		/* [TIND] */
+	0,		/* [TFUNC] */
+	-1,		/* [TARRAY] */
+	0,		/* [TVOID] */
+	-1,		/* [TSTRUCT] */
+	-1,		/* [TUNION] */
+	SZ_INT,		/* [TENUM] */
+};
+
+long	ncast[NTYPE] =
+{
+	0,				/* [TXXX] */
+	BCHAR|BUCHAR,			/* [TCHAR] */
+	BCHAR|BUCHAR,			/* [TUCHAR] */
+	BSHORT|BUSHORT,			/* [TSHORT] */
+	BSHORT|BUSHORT,			/* [TUSHORT] */
+	BINT|BUINT|BLONG|BULONG,	/* [TINT] */
+	BINT|BUINT|BLONG|BULONG,	/* [TUINT] */
+	BINT|BUINT|BLONG|BULONG,	/* [TLONG] */
+	BINT|BUINT|BLONG|BULONG,	/* [TULONG] */
+	BVLONG|BUVLONG|BIND,			/* [TVLONG] */
+	BVLONG|BUVLONG|BIND,			/* [TUVLONG] */
+	BFLOAT,				/* [TFLOAT] */
+	BDOUBLE,			/* [TDOUBLE] */
+	BVLONG|BUVLONG|BIND,		/* [TIND] */
+	0,				/* [TFUNC] */
+	0,				/* [TARRAY] */
+	0,				/* [TVOID] */
+	BSTRUCT,			/* [TSTRUCT] */
+	BUNION,				/* [TUNION] */
+	0,				/* [TENUM] */
+};