shithub: loong

Download patch

ref: 1900acf7ad612d4f40701ad9b32434579c269304
author: kws <kws@cirno>
date: Wed Nov 29 22:10:52 EST 2023

basic efi

diff: cannot open b/loong/include//null: file does not exist: 'b/loong/include//null' diff: cannot open b/loong//null: file does not exist: 'b/loong//null' diff: cannot open b/sys/include//null: file does not exist: 'b/sys/include//null' diff: cannot open b/sys/lib//null: file does not exist: 'b/sys/lib//null' diff: cannot open b/sys/src/boot/efi//null: file does not exist: 'b/sys/src/boot/efi//null' diff: cannot open b/sys/src/boot//null: file does not exist: 'b/sys/src/boot//null' diff: cannot open b/sys/src/cmd/aux//null: file does not exist: 'b/sys/src/cmd/aux//null' diff: cannot open b/sys/src/cmd/za//null: file does not exist: 'b/sys/src/cmd/za//null' diff: cannot open b/sys/src/cmd/zc//null: file does not exist: 'b/sys/src/cmd/zc//null' diff: cannot open b/sys/src/cmd/zl//null: file does not exist: 'b/sys/src/cmd/zl//null' diff: cannot open b/sys/src/cmd//null: file does not exist: 'b/sys/src/cmd//null' diff: cannot open b/sys/src/libmach//null: file does not exist: 'b/sys/src/libmach//null' diff: cannot open b/sys/src//null: file does not exist: 'b/sys/src//null' diff: cannot open b/sys//null: file does not exist: 'b/sys//null'
--- /dev/null
+++ b/NOTICE
@@ -1,0 +1,35 @@
+Thanks Tim Weiss for the original 4[acl] compilers based on earlier work from the Labs.
+
+Thanks Cherry (Zhang) Mui for her Loongson work based off of Tim Weiss' compilers.
+
+Thanks Richard Miller for his RISC-V work which served as a useful reference.
+
+The LoongArch linker (zl) contains some definitions derived from the Go LoongArch sources:
+
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--- /dev/null
+++ b/bind.rc
@@ -1,0 +1,10 @@
+#!/bin/rc
+
+bind -a . /
+bind -b sys/include /sys/include
+bind -bc sys/src/libmach /sys/src/libmach
+bind -bc sys/src/boot/efi /sys/src/boot/efi
+bind -ac sys/src/cmd /sys/src/cmd
+bind -c sys/src/cmd/aux/aout2efi.c /sys/src/cmd/aux/aout2efi.c
+bind -c sys/lib/rootstub /sys/lib/rootstub
+bind -c sys/src/mkfile.proto /sys/src/mkfile.proto
--- /dev/null
+++ b/loong/include/u.h
@@ -1,0 +1,79 @@
+// TODO: LoongArch
+
+#define nil		((void*)0)
+
+typedef	unsigned short	ushort;
+typedef	unsigned char	uchar;
+typedef	unsigned long	ulong;
+typedef	unsigned int	uint;
+typedef	signed char	schar;
+typedef	long long	vlong;
+typedef	unsigned long long uvlong;
+typedef vlong	intptr;
+typedef uvlong	uintptr;
+typedef unsigned long long usize;
+typedef	uint		Rune;
+typedef union FPdbleword FPdbleword;
+typedef uintptr	jmp_buf[2];
+#define	JMPBUFSP	0
+#define	JMPBUFPC	1
+#define	JMPBUFDPC	0
+typedef unsigned int	mpdigit;	/* for /sys/include/mp.h */
+typedef unsigned char u8int;
+typedef unsigned short u16int;
+typedef unsigned int	u32int;
+typedef unsigned long long u64int;
+typedef signed char s8int;
+typedef signed short s16int;
+typedef signed int s32int;
+typedef signed long long s64int;
+
+/* FPCR (control) */
+#define	FPINEX		(1<<12)
+#define	FPUNFL		(1<<11)
+#define	FPOVFL		(1<<10)
+#define	FPZDIV		(1<<9)
+#define	FPINVAL		(1<<8)
+
+#define	FPRNR		(0<<22)
+#define	FPRPINF		(1<<22)
+#define	FPRNINF		(2<<22)
+#define	FPRZ		(3<<22)
+
+#define	FPRMASK		(3<<22)
+
+/* FPSR (status) */
+#define	FPPEXT	0
+#define	FPPSGL	0
+#define	FPPDBL	0
+#define	FPPMASK	0
+#define	FPAINEX		(1<<4)
+#define	FPAUNFL		(1<<3)
+#define	FPAOVFL		(1<<2)
+#define	FPAZDIV		(1<<1)
+#define	FPAINVAL	(1<<0)
+
+union FPdbleword
+{
+	double	x;
+	struct {	/* little endian */
+		ulong lo;
+		ulong hi;
+	};
+};
+
+typedef	char*	va_list;
+#define va_start(list, start) list =\
+	(sizeof(start) < 8?\
+		(char*)((vlong*)&(start)+1):\
+		(char*)(&(start)+1))
+#define va_end(list)\
+	USED(list)
+#define va_arg(list, mode)\
+	((sizeof(mode) == 1)?\
+		((list += 8), (mode*)list)[-8]:\
+	(sizeof(mode) == 2)?\
+		((list += 8), (mode*)list)[-4]:\
+	(sizeof(mode) == 4)?\
+		((list += 8), (mode*)list)[-2]:\
+		((list += sizeof(mode)), (mode*)list)[-1])
--- /dev/null
+++ b/loong/include/ureg.h
@@ -1,0 +1,42 @@
+// TODO: LoongArch
+
+typedef struct Ureg {
+	u64int	r0;
+	u64int	r1;
+	u64int	r2;
+	u64int	r3;
+	u64int	r4;
+	u64int	r5;
+	u64int	r6;
+	u64int	r7;
+	u64int	r8;
+	u64int	r9;
+	u64int	r10;
+	u64int	r11;
+	u64int	r12;
+	u64int	r13;
+	u64int	r14;
+	u64int	r15;
+	u64int	r16;
+	u64int	r17;
+	u64int	r18;
+	u64int	r19;
+	u64int	r20;
+	u64int	r21;
+	u64int	r22;
+	u64int	r23;
+	u64int	r24;
+	u64int	r25;
+	u64int	r26;
+	u64int	r27;
+	u64int	r28;	/* sb */
+	u64int	r29;
+	union {
+		u64int	r30;
+		u64int	link;
+	};
+	u64int	sp;
+	u64int	pc;	/* interrupted addr */
+	u64int	psr;
+	u64int	type;	/* of exception */
+} Ureg;
--- /dev/null
+++ b/loong/mkfile
@@ -1,0 +1,6 @@
+</sys/src/mkfile.proto
+
+CC=zc
+LD=zl
+AS=za
+O=z
--- /dev/null
+++ b/sys/include/a.out.h
@@ -1,0 +1,48 @@
+typedef	struct	Exec	Exec;
+struct	Exec
+{
+	long	magic;		/* magic number */
+	long	text;	 	/* size of text segment */
+	long	data;	 	/* size of initialized data */
+	long	bss;	  	/* size of uninitialized data */
+	long	syms;	 	/* size of symbol table */
+	long	entry;	 	/* entry point */
+	long	spsz;		/* size of pc/sp offset table */
+	long	pcsz;		/* size of pc/line number table */
+};
+
+#define HDR_MAGIC	0x00008000		/* header expansion */
+
+#define	_MAGIC(f, b)	((f)|((((4*(b))+0)*(b))+7))
+#define	A_MAGIC		_MAGIC(0, 8)		/* 68020 */
+#define	I_MAGIC		_MAGIC(0, 11)		/* intel 386 */
+#define	J_MAGIC		_MAGIC(0, 12)		/* intel 960 (retired) */
+#define	K_MAGIC		_MAGIC(0, 13)		/* sparc */
+#define	V_MAGIC		_MAGIC(0, 16)		/* mips 3000 BE */
+#define	X_MAGIC		_MAGIC(0, 17)		/* att dsp 3210 (retired) */
+#define	M_MAGIC		_MAGIC(0, 18)		/* mips 4000 BE */
+#define	D_MAGIC		_MAGIC(0, 19)		/* amd 29000 (retired) */
+#define	E_MAGIC		_MAGIC(0, 20)		/* arm */
+#define	Q_MAGIC		_MAGIC(0, 21)		/* powerpc */
+#define	N_MAGIC		_MAGIC(0, 22)		/* mips 4000 LE */
+#define	L_MAGIC		_MAGIC(0, 23)		/* dec alpha (retired) */
+#define	P_MAGIC		_MAGIC(0, 24)		/* mips 3000 LE */
+#define	U_MAGIC		_MAGIC(0, 25)		/* sparc64 */
+#define	S_MAGIC		_MAGIC(HDR_MAGIC, 26)	/* amd64 */
+#define	T_MAGIC		_MAGIC(HDR_MAGIC, 27)	/* powerpc64 */
+#define	R_MAGIC		_MAGIC(HDR_MAGIC, 28)	/* arm64 */
+#define Z_MAGIC		_MAGIC(HDR_MAGIC, 29)	/* loong */
+
+#define	MIN_MAGIC	8
+#define	MAX_MAGIC	28			/* <= 90 */
+
+#define	DYN_MAGIC	0x80000000		/* dlm */
+
+typedef	struct	Sym	Sym;
+struct	Sym
+{
+	vlong	value;
+	uint	sig;
+	char	type;
+	char	*name;
+};
--- /dev/null
+++ b/sys/include/mach.h
@@ -1,0 +1,331 @@
+/*
+ *	Architecture-dependent application data
+ */
+#include "a.out.h"
+#pragma	src	"/sys/src/libmach"
+#pragma	lib	"libmach.a"
+/*
+ *	Supported architectures:
+ *		mips,
+ *		68020,
+ *		i386,
+ *		amd64,
+ *		sparc,
+ *		sparc64,
+ *		mips2 (R4000)
+ *		arm
+ *		powerpc,
+ *		powerpc64
+ *		alpha
+ *		arm64
+ *		loong
+ */
+enum
+{
+	MMIPS,			/* machine types */
+	MSPARC,
+	M68020,
+	MI386,
+	MI960,			/* retired */
+	M3210,			/* retired */
+	MMIPS2,
+	NMIPS2,
+	M29000,			/* retired */
+	MARM,
+	MPOWER,
+	MALPHA,
+	NMIPS,
+	MSPARC64,
+	MAMD64,
+	MPOWER64,
+	MARM64,
+	MLOONG,
+				/* types of executables */
+	FNONE = 0,		/* unidentified */
+	FMIPS,			/* v.out */
+	FMIPSB,			/* mips bootable */
+	FSPARC,			/* k.out */
+	FSPARCB,		/* Sparc bootable */
+	F68020,			/* 2.out */
+	F68020B,		/* 68020 bootable */
+	FNEXTB,			/* Next bootable */
+	FI386,			/* 8.out */
+	FI386B,			/* I386 bootable */
+	FI960,			/* retired */
+	FI960B,			/* retired */
+	F3210,			/* retired */
+	FMIPS2BE,		/* 4.out */
+	F29000,			/* retired */
+	FARM,			/* 5.out */
+	FARMB,			/* ARM bootable */
+	FPOWER,			/* q.out */
+	FPOWERB,		/* power pc bootable */
+	FMIPS2LE,		/* 0.out */
+	FALPHA,			/* 7.out */
+	FALPHAB,		/* DEC Alpha bootable */
+	FMIPSLE,		/* 3k little endian */
+	FSPARC64,		/* u.out */
+	FAMD64,			/* 6.out */
+	FAMD64B,		/* 6.out bootable */
+	FPOWER64,		/* 9.out */
+	FPOWER64B,		/* 9.out bootable */
+	FARM64,			/* arm64 */
+	FARM64B,		/* arm64 bootable */
+	FLOONG,			/* loong */
+	FLOONGB,		/* loong bootable */
+
+	ANONE = 0,		/* dissembler types */
+	AMIPS,
+	AMIPSCO,		/* native mips */
+	ASPARC,
+	ASUNSPARC,		/* native sun */
+	A68020,
+	AI386,
+	AI8086,			/* oh god */
+	AI960,			/* retired */
+	A29000,			/* retired */
+	AARM,
+	APOWER,
+	AALPHA,
+	ASPARC64,
+	AAMD64,
+	APOWER64,
+	AARM64,
+	ALOONG,
+				/* object file types */
+	Obj68020 = 0,		/* .2 */
+	ObjSparc,		/* .k */
+	ObjMips,		/* .v */
+	Obj386,			/* .8 */
+	Obj960,			/* retired */
+	Obj3210,		/* retired */
+	ObjMips2,		/* .4 */
+	Obj29000,		/* retired */
+	ObjArm,			/* .5 */
+	ObjPower,		/* .q */
+	ObjMips2le,		/* .0 */
+	ObjAlpha,		/* .7 */
+	ObjSparc64,		/* .u */
+	ObjAmd64,		/* .6 */
+	ObjSpim,		/* .0 */
+	ObjPower64,		/* .9 */
+	ObjArm64,		/* .4? */
+	ObjLoong,		/* .z */
+	Maxobjtype,
+
+	CNONE  = 0,		/* symbol table classes */
+	CAUTO,
+	CPARAM,
+	CSTAB,
+	CTEXT,
+	CDATA,
+	CANY,			/* to look for any class */
+};
+
+typedef	struct	Map	Map;
+typedef struct	Symbol	Symbol;
+typedef	struct	Reglist	Reglist;
+typedef	struct	Mach	Mach;
+typedef	struct	Machdata Machdata;
+
+/*
+ * 	Structure to map a segment to a position in a file
+ */
+struct Map {
+	int	nsegs;			/* number of segments */
+	struct segment {		/* per-segment map */
+		char	*name;		/* the segment name */
+		int	fd;		/* file descriptor */
+		long	(*read)(int, void *, long, vlong);
+		int	inuse;		/* in use - not in use */
+		int	cache;		/* should cache reads? */
+		uvlong	b;		/* base */
+		uvlong	e;		/* end */
+		vlong	f;		/* offset within file */
+	} seg[1];			/* actually n of these */
+};
+
+/*
+ *	Internal structure describing a symbol table entry
+ */
+struct Symbol {
+	void 	*handle;		/* used internally - owning func */
+	struct {
+		char	*name;
+		vlong	value;		/* address or stack offset */
+		char	type;		/* as in a.out.h */
+		char	class;		/* as above */
+		int	index;		/* in findlocal, globalsym, textsym */
+	};
+};
+
+/*
+ *	machine register description
+ */
+struct Reglist {
+	char	*rname;			/* register name */
+	short	roffs;			/* offset in u-block */
+	char	rflags;			/* INTEGER/FLOAT, WRITABLE */
+	char	rformat;		/* print format: 'x', 'X', 'f', '8', '3', 'Y', 'W' */
+};
+
+enum {					/* bits in rflags field */
+	RINT	= (0<<0),
+	RFLT	= (1<<0),
+	RRDONLY	= (1<<1),
+};
+
+/*
+ *	Machine-dependent data is stored in two structures:
+ *		Mach  - miscellaneous general parameters
+ *		Machdata - jump vector of service functions used by debuggers
+ *
+ *	Mach is defined in ?.c and set in executable.c
+ *
+ *	Machdata is defined in ?db.c
+ *		and set in the debugger startup.
+ */
+struct Mach{
+	char	*name;
+	int	mtype;			/* machine type code */
+	Reglist *reglist;		/* register set */
+	long	regsize;		/* sizeof registers in bytes */
+	long	fpregsize;		/* sizeof fp registers in bytes */
+	char	*pc;			/* pc name */
+	char	*sp;			/* sp name */
+	char	*link;			/* link register name */
+	char	*sbreg;			/* static base register name */
+	uvlong	sb;			/* static base register value */
+	int	pgsize;			/* page size */
+	uvlong	kbase;			/* kernel base address */
+	uvlong	ktmask;			/* ktzero = kbase & ~ktmask */
+	uvlong	utop;			/* user stack top */
+	int	pcquant;		/* quantization of pc */
+	int	szaddr;			/* sizeof(void*) */
+	int	szreg;			/* sizeof(register) */
+	int	szfloat;		/* sizeof(float) */
+	int	szdouble;		/* sizeof(double) */
+};
+
+extern	Mach	*mach;			/* Current machine */
+
+typedef uvlong	(*Rgetter)(Map*, char*);
+typedef	void	(*Tracer)(Map*, uvlong, uvlong, Symbol*);
+
+struct	Machdata {		/* Machine-dependent debugger support */
+	uchar	bpinst[4];			/* break point instr. */
+	short	bpsize;				/* size of break point instr. */
+
+	ushort	(*swab)(ushort);		/* ushort to local byte order */
+	ulong	(*swal)(ulong);			/* ulong to local byte order */
+	uvlong	(*swav)(uvlong);		/* uvlong to local byte order */
+	int	(*ctrace)(Map*, uvlong, uvlong, uvlong, Tracer); /* C traceback */
+	uvlong	(*findframe)(Map*, uvlong, uvlong, uvlong, uvlong);/* frame finder */
+	char*	(*excep)(Map*, Rgetter);	/* last exception */
+	ulong	(*bpfix)(uvlong);		/* breakpoint fixup */
+	int	(*sftos)(char*, int, void*);	/* single precision float */
+	int	(*dftos)(char*, int, void*);	/* double precision float */
+	int	(*foll)(Map*, uvlong, Rgetter, uvlong*);/* follow set */
+	int	(*das)(Map*, uvlong, char, char*, int);	/* symbolic disassembly */
+	int	(*hexinst)(Map*, uvlong, char*, int); 	/* hex disassembly */
+	int	(*instsize)(Map*, uvlong);	/* instruction size */
+};
+
+/*
+ *	Common a.out header describing all architectures
+ */
+typedef struct Fhdr
+{
+	char	*name;		/* identifier of executable */
+	uchar	type;		/* file type - see codes above */
+	uchar	hdrsz;		/* header size */
+	uchar	_magic;		/* _MAGIC() magic */
+	uchar	spare;
+	long	magic;		/* magic number */
+	uvlong	txtaddr;	/* text address */
+	vlong	txtoff;		/* start of text in file */
+	uvlong	dataddr;	/* start of data segment */
+	vlong	datoff;		/* offset to data seg in file */
+	vlong	symoff;		/* offset of symbol table in file */
+	uvlong	entry;		/* entry point */
+	vlong	sppcoff;	/* offset of sp-pc table in file */
+	vlong	lnpcoff;	/* offset of line number-pc table in file */
+	long	txtsz;		/* text size */
+	long	datsz;		/* size of data seg */
+	long	bsssz;		/* size of bss */
+	long	symsz;		/* size of symbol table */
+	long	sppcsz;		/* size of sp-pc table */
+	long	lnpcsz;		/* size of line number-pc table */
+} Fhdr;
+
+extern	int	asstype;	/* dissembler type - machdata.c */
+extern	Machdata *machdata;	/* jump vector - machdata.c */
+
+Map*		attachproc(int, int, int, Fhdr*);
+int		beieee80ftos(char*, int, void*);
+int		beieeesftos(char*, int, void*);
+int		beieeedftos(char*, int, void*);
+ushort		beswab(ushort);
+ulong		beswal(ulong);
+uvlong		beswav(uvlong);
+uvlong		ciscframe(Map*, uvlong, uvlong, uvlong, uvlong);
+int		cisctrace(Map*, uvlong, uvlong, uvlong, Tracer);
+int		crackhdr(int fd, Fhdr*);
+uvlong		file2pc(char*, long);
+int		fileelem(Sym**, uchar *, char*, int);
+long		fileline(char*, int, uvlong);
+int		filesym(int, char*, int);
+int		findlocal(Symbol*, char*, Symbol*);
+int		findseg(Map*, char*);
+int		findsym(uvlong, int, Symbol *);
+int		fnbound(uvlong, uvlong*);
+int		fpformat(Map*, Reglist*, char*, int, int);
+int		get1(Map*, uvlong, uchar*, int);
+int		get2(Map*, uvlong, ushort*);
+int		get4(Map*, uvlong, ulong*);
+int		get8(Map*, uvlong, uvlong*);
+int		geta(Map*, uvlong, uvlong*);
+int		getauto(Symbol*, int, int, Symbol*);
+Sym*		getsym(int);
+int		globalsym(Symbol *, int);
+char*		_hexify(char*, ulong, int);
+int		ieeesftos(char*, int, ulong);
+int		ieeedftos(char*, int, ulong, ulong);
+int		isar(Biobuf*);
+int		leieee80ftos(char*, int, void*);
+int		leieeesftos(char*, int, void*);
+int		leieeedftos(char*, int, void*);
+ushort		leswab(ushort);
+ulong		leswal(ulong);
+uvlong		leswav(uvlong);
+uvlong		line2addr(long, uvlong, uvlong);
+Map*		loadmap(Map*, int, Fhdr*);
+int		localaddr(Map*, char*, char*, uvlong*, Rgetter);
+int		localsym(Symbol*, int);
+int		lookup(char*, char*, Symbol*);
+void		machbytype(int);
+int		machbyname(char*);
+int		nextar(Biobuf*, int, char*);
+Map*		newmap(Map*, int);
+void		objtraverse(void(*)(Sym*, void*), void*);
+int		objtype(Biobuf*, char**);
+uvlong		pc2sp(uvlong);
+long		pc2line(uvlong);
+int		put1(Map*, uvlong, uchar*, int);
+int		put2(Map*, uvlong, ushort);
+int		put4(Map*, uvlong, ulong);
+int		put8(Map*, uvlong, uvlong);
+int		puta(Map*, uvlong, uvlong);
+int		readar(Biobuf*, int, vlong, int);
+int		readobj(Biobuf*, int);
+uvlong		riscframe(Map*, uvlong, uvlong, uvlong, uvlong);
+int		risctrace(Map*, uvlong, uvlong, uvlong, Tracer);
+int		setmap(Map*, int, uvlong, uvlong, vlong, char*);
+Sym*		symbase(long*);
+int		syminit(int, Fhdr*);
+int		symoff(char*, int, uvlong, int);
+void		textseg(uvlong, Fhdr*);
+int		textsym(Symbol*, int);
+void		thumbpctab(int, Fhdr*);
+int		thumbpclookup(uvlong);
+void		unusemap(Map*, int);
--- /dev/null
+++ b/sys/lib/rootstub
@@ -1,0 +1,254 @@
+mkdir -p 386/bin/ape
+mkdir -p 386/bin/audio
+mkdir -p 386/bin/auth
+mkdir -p 386/bin/aux
+mkdir -p 386/bin/bitsy
+mkdir -p 386/bin/dial
+mkdir -p 386/bin/disk
+mkdir -p 386/bin/fs
+mkdir -p 386/bin/games
+mkdir -p 386/bin/ip/httpd
+mkdir -p 386/bin/ndb
+mkdir -p 386/bin/nusb
+mkdir -p 386/bin/replica
+mkdir -p 386/bin/upas
+mkdir -p 386/bin/venti
+mkdir -p 386/lib/ape
+mkdir -p 68000/bin/ape
+mkdir -p 68000/bin/audio
+mkdir -p 68000/bin/auth
+mkdir -p 68000/bin/aux
+mkdir -p 68000/bin/bitsy
+mkdir -p 68000/bin/dial
+mkdir -p 68000/bin/disk
+mkdir -p 68000/bin/fs
+mkdir -p 68000/bin/games
+mkdir -p 68000/bin/ip/httpd
+mkdir -p 68000/bin/ndb
+mkdir -p 68000/bin/nusb
+mkdir -p 68000/bin/replica
+mkdir -p 68000/bin/upas
+mkdir -p 68000/bin/venti
+mkdir -p 68000/lib/ape
+mkdir -p 68020/bin/ape
+mkdir -p 68020/bin/audio
+mkdir -p 68020/bin/auth
+mkdir -p 68020/bin/aux
+mkdir -p 68020/bin/bitsy
+mkdir -p 68020/bin/dial
+mkdir -p 68020/bin/disk
+mkdir -p 68020/bin/fs
+mkdir -p 68020/bin/games
+mkdir -p 68020/bin/ip/httpd
+mkdir -p 68020/bin/ndb
+mkdir -p 68020/bin/nusb
+mkdir -p 68020/bin/replica
+mkdir -p 68020/bin/upas
+mkdir -p 68020/bin/venti
+mkdir -p 68020/lib/ape
+mkdir -p acme/bin/amd64
+mkdir -p acme/bin/386
+mkdir -p acme/bin/arm
+mkdir -p acme/bin/arm64
+mkdir -p acme/bin/loong
+mkdir -p acme/bin/dial
+mkdir -p acme/bin/mips
+mkdir -p acme/bin/spim
+mkdir -p acme/bin/power
+mkdir -p acme/bin/power64
+mkdir -p acme/bin/sparc
+mkdir -p acme/bin/sparc64
+mkdir -p amd64/bin/ape
+mkdir -p amd64/bin/audio
+mkdir -p amd64/bin/auth
+mkdir -p amd64/bin/aux
+mkdir -p amd64/bin/bitsy
+mkdir -p amd64/bin/dial
+mkdir -p amd64/bin/disk
+mkdir -p amd64/bin/fs
+mkdir -p amd64/bin/games
+mkdir -p amd64/bin/ip/httpd
+mkdir -p amd64/bin/ndb
+mkdir -p amd64/bin/nusb
+mkdir -p amd64/bin/replica
+mkdir -p amd64/bin/upas
+mkdir -p amd64/bin/venti
+mkdir -p amd64/lib/ape
+mkdir -p arm/bin/ape
+mkdir -p arm/bin/audio
+mkdir -p arm/bin/auth
+mkdir -p arm/bin/aux
+mkdir -p arm/bin/bitsy
+mkdir -p arm/bin/dial
+mkdir -p arm/bin/disk
+mkdir -p arm/bin/fs
+mkdir -p arm/bin/games
+mkdir -p arm/bin/ip/httpd
+mkdir -p arm/bin/ndb
+mkdir -p arm/bin/nusb
+mkdir -p arm/bin/replica
+mkdir -p arm/bin/upas
+mkdir -p arm/bin/venti
+mkdir -p arm/lib/ape
+mkdir -p arm64/bin/ape
+mkdir -p arm64/bin/audio
+mkdir -p arm64/bin/auth
+mkdir -p arm64/bin/aux
+mkdir -p arm64/bin/bitsy
+mkdir -p arm64/bin/dial
+mkdir -p arm64/bin/disk
+mkdir -p arm64/bin/fs
+mkdir -p arm64/bin/games
+mkdir -p arm64/bin/ip/httpd
+mkdir -p arm64/bin/ndb
+mkdir -p arm64/bin/nusb
+mkdir -p arm64/bin/replica
+mkdir -p arm64/bin/upas
+mkdir -p arm64/bin/venti
+mkdir -p arm64/lib/ape
+mkdir -p loong/bin/ape
+mkdir -p loong/bin/audio
+mkdir -p loong/bin/auth
+mkdir -p loong/bin/aux
+mkdir -p loong/bin/bitsy
+mkdir -p loong/bin/dial
+mkdir -p loong/bin/disk
+mkdir -p loong/bin/fs
+mkdir -p loong/bin/games
+mkdir -p loong/bin/ip/httpd
+mkdir -p loong/bin/ndb
+mkdir -p loong/bin/nusb
+mkdir -p loong/bin/replica
+mkdir -p loong/bin/upas
+mkdir -p loong/bin/venti
+mkdir -p loong/lib/ape
+mkdir -p cfg/pxe
+mkdir -p cron
+mkdir -p dist/plan9front
+mkdir -p lib/audio/icon
+mkdir -p lib/firmware
+mkdir -p lib/ndb/dhcp
+mkdir -p lib/news
+mkdir -p mail/box/glenda/mbox
+mkdir -p mail/faxoutqueue
+mkdir -p mail/faxqueue
+mkdir -p mail/fs
+mkdir -p mail/grey
+mkdir -p mail/lib
+mkdir -p mail/queue
+mkdir -p mail/tmp
+mkdir -p mips/bin/ape
+mkdir -p mips/bin/audio
+mkdir -p mips/bin/auth
+mkdir -p mips/bin/aux
+mkdir -p mips/bin/bitsy
+mkdir -p mips/bin/dial
+mkdir -p mips/bin/disk
+mkdir -p mips/bin/fs
+mkdir -p mips/bin/games
+mkdir -p mips/bin/ip/httpd
+mkdir -p mips/bin/ndb
+mkdir -p mips/bin/nusb
+mkdir -p mips/bin/replica
+mkdir -p mips/bin/upas
+mkdir -p mips/bin/venti
+mkdir -p mips/lib/ape
+mkdir -p spim/bin/ape
+mkdir -p spim/bin/audio
+mkdir -p spim/bin/auth
+mkdir -p spim/bin/aux
+mkdir -p spim/bin/bitsy
+mkdir -p spim/bin/dial
+mkdir -p spim/bin/disk
+mkdir -p spim/bin/fs
+mkdir -p spim/bin/games
+mkdir -p spim/bin/ip/httpd
+mkdir -p spim/bin/ndb
+mkdir -p spim/bin/nusb
+mkdir -p spim/bin/replica
+mkdir -p spim/bin/upas
+mkdir -p spim/bin/venti
+mkdir -p spim/lib/ape
+mkdir -p power/bin/ape
+mkdir -p power/bin/audio
+mkdir -p power/bin/auth
+mkdir -p power/bin/aux
+mkdir -p power/bin/bitsy
+mkdir -p power/bin/dial
+mkdir -p power/bin/disk
+mkdir -p power/bin/fs
+mkdir -p power/bin/games
+mkdir -p power/bin/ip/httpd
+mkdir -p power/bin/ndb
+mkdir -p power/bin/nusb
+mkdir -p power/bin/replica
+mkdir -p power/bin/upas
+mkdir -p power/bin/venti
+mkdir -p power/lib/ape
+mkdir -p power64/bin/ape
+mkdir -p power64/bin/audio
+mkdir -p power64/bin/auth
+mkdir -p power64/bin/aux
+mkdir -p power64/bin/bitsy
+mkdir -p power64/bin/dial
+mkdir -p power64/bin/disk
+mkdir -p power64/bin/fs
+mkdir -p power64/bin/games
+mkdir -p power64/bin/ip/httpd
+mkdir -p power64/bin/ndb
+mkdir -p power64/bin/nusb
+mkdir -p power64/bin/replica
+mkdir -p power64/bin/upas
+mkdir -p power64/bin/venti
+mkdir -p power64/lib/ape
+mkdir -p rc/bin/aux
+mkdir -p rc/bin/postscript
+mkdir -p rc/lib/ape
+mkdir -p sparc/bin/ape
+mkdir -p sparc/bin/audio
+mkdir -p sparc/bin/auth
+mkdir -p sparc/bin/aux
+mkdir -p sparc/bin/bitsy
+mkdir -p sparc/bin/dial
+mkdir -p sparc/bin/disk
+mkdir -p sparc/bin/fs
+mkdir -p sparc/bin/games
+mkdir -p sparc/bin/ip/httpd
+mkdir -p sparc/bin/ndb
+mkdir -p sparc/bin/nusb
+mkdir -p sparc/bin/replica
+mkdir -p sparc/bin/upas
+mkdir -p sparc/bin/venti
+mkdir -p sparc/lib/ape
+mkdir -p sparc64/bin/ape
+mkdir -p sparc64/bin/audio
+mkdir -p sparc64/bin/auth
+mkdir -p sparc64/bin/aux
+mkdir -p sparc64/bin/bitsy
+mkdir -p sparc64/bin/dial
+mkdir -p sparc64/bin/disk
+mkdir -p sparc64/bin/fs
+mkdir -p sparc64/bin/games
+mkdir -p sparc64/bin/ip/httpd
+mkdir -p sparc64/bin/ndb
+mkdir -p sparc64/bin/nusb
+mkdir -p sparc64/bin/replica
+mkdir -p sparc64/bin/upas
+mkdir -p sparc64/bin/venti
+mkdir -p sparc64/lib/ape
+mkdir -p sys/lib/dist/usr/glenda/bin/386
+mkdir -p sys/lib/dist/usr/glenda/bin/amd64
+mkdir -p sys/lib/dist/usr/glenda/bin/arm
+mkdir -p sys/lib/dist/usr/glenda/bin/arm64
+mkdir -p sys/lib/dist/usr/glenda/bin/loong
+mkdir -p sys/lib/dist/usr/glenda/bin/mips
+mkdir -p sys/lib/dist/usr/glenda/tmp
+mkdir -p sys/lib/dist/ndb/dhcp
+mkdir -p sys/lib/dist/cfg/pxe
+mkdir -p sys/lib/tls/acmed
+mkdir -p sys/lib/lp/log
+mkdir -p sys/lib/lp/tmp
+mkdir -p sys/lib/ssl
+mkdir -p sys/log/httpd
+mkdir -p tmp
+mkdir -p usr
--- /dev/null
+++ b/sys/src/boot/efi/loong.s
@@ -1,0 +1,25 @@
+TEXT start(SB), $-8
+	// TODO: Rebase
+	JMP	efimain(SB)
+
+TEXT rebase(SB), $-8
+
+TEXT eficall(SB), $-8
+	MOVV	R4,	R12
+	MOVV	0x08(FP), R4
+	MOVV	0x10(FP), R5
+	MOVV	0x18(FP), R6
+	MOVV	0x20(FP), R7
+	MOVV	0x28(FP), R8
+	MOVV	0x30(FP), R9
+	MOVV	0x38(FP), R10
+	MOVV	0x40(FP), R11
+	JMP	(R12)
+
+TEXT jump(SB), $-8
+	MOVV	R4, R7
+	MOVV	0x08(FP), R4
+	JMP	(R7)
+
+GLOBL	confaddr(SB), $8
+GLOBL	argsbuf<>(SB), $0x1000
--- /dev/null
+++ b/sys/src/boot/efi/mkfile
@@ -1,0 +1,101 @@
+TARG=bootia32.efi bootx64.efi bootaa64.efi bootloongarch64.efi efiboot.fat
+HFILES=fns.h mem.h
+IMAGEBASE=0x8000
+CFLAGS=-FTVw
+PEFLAGS=$CFLAGS '-DIMAGEBASE='$IMAGEBASE
+
+all:V: $TARG
+
+install:V: $TARG
+	cp $prereq /386
+
+%.8:	%.s
+	8a $PEFLAGS $stem.s
+%.8:	%.c
+	8c $CFLAGS $stem.c
+bootia32.efi:	ia32.8 efi.8 fs.8 pxe.8 iso.8 sub.8
+	8l -l -s -R1 -T0x8200 -o bootia32.out $prereq
+	aux/aout2efi -Z$IMAGEBASE -o $target bootia32.out
+%.8:	$HFILES
+
+
+%.6:	%.s
+	6a $PEFLAGS $stem.s
+%.6:	%.c
+	6c $CFLAGS $stem.c
+bootx64.efi:	x64.6 efi.6 fs.6 pxe.6 iso.6 sub.6
+	6l -l -s -R1 -T0x8200 -o bootx64.out $prereq
+	aux/aout2efi -Z$IMAGEBASE -o $target bootx64.out
+%.6:	$HFILES
+
+
+%.7:	%.s
+	7a $PEFLAGS $stem.s
+%.7:	%.c
+	7c $CFLAGS $stem.c
+bootaa64.efi:	aa64.7 efi.7 fs.7 pxe.7 iso.7 sub.7
+	7l -l -s -R1 -T0x8200 -o bootaa64.out $prereq
+	aux/aout2efi -Z$IMAGEBASE -o $target bootaa64.out
+%.7:	$HFILES
+
+
+%.z:	%.s
+	za $PEFLAGS $stem.s
+%.z:	%.c
+	zc $CFLAGS $stem.c
+bootloongarch64.efi:	loong.z efi.z fs.z pxe.z iso.z sub.z
+	zl -l -s -R1 -T0x8200 -o bootloongarch64.out $prereq
+	aux/aout2efi -Z$IMAGEBASE -o $target bootloongarch64.out
+%.z:	$HFILES
+
+
+efiboot.fat:D:	bootia32.efi bootx64.efi bootaa64.efi bootloongarch64.efi
+	s = $target.$pid
+	rm -f $target
+	dd -if /dev/zero -of $target -bs 1024 -count 1024
+	disk/format -xd -t hard $target
+	dossrv -f $target $s
+	mount -c /srv/$s /n/esp
+	mkdir /n/esp/efi
+	mkdir /n/esp/efi/boot
+	cp bootia32.efi /n/esp/efi/boot
+	cp bootx64.efi /n/esp/efi/boot
+	cp bootaa64.efi /n/esp/efi/boot
+	cp bootloongarch64.efi /n/esp/efi/boot
+	unmount /n/esp
+	rm /srv/$s
+
+
+test.iso:D:	efiboot.fat
+	rm -fr tmp
+	mkdir tmp
+	mkdir tmp/cfg
+	mkdir tmp/386
+	cp efiboot.fat tmp/386
+	cp /386/9bootiso tmp/386
+	cp /386/9pc tmp/386
+	echo 'bootfile=/386/9pc' >tmp/cfg/plan9.ini
+	disk/mk9660 -B 386/9bootiso -E 386/efiboot.fat -p <{echo +} -s tmp $target
+	rm -r tmp
+
+test.fat:D:	bootia32.efi bootx64.efi bootaa64.efi bootloongarch64.efi
+	s = $target.$pid
+	rm -f $target
+	dd -if /dev/zero -of $target -bs 65536 -count 128
+	disk/format -xd -t hard $target
+	dossrv -f $target $s
+	mount -c /srv/$s /n/esp
+	mkdir /n/esp/efi
+	mkdir /n/esp/efi/boot
+	cp bootia32.efi /n/esp/efi/boot
+	cp bootx64.efi /n/esp/efi/boot
+	cp bootaa64.efi /n/esp/efi/boot
+	cp bootloongarch64.efi /n/esp/efi/boot
+	cp /386/9pc /n/esp
+	echo 'bootfile=9pc' >/n/esp/plan9.ini
+	unmount /n/esp
+	rm /srv/$s
+
+
+clean:V:
+	rm -f *.[678] *.out $TARG test.* 
--- /dev/null
+++ b/sys/src/cmd/aux/aout2efi.c
@@ -1,0 +1,244 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+#define	HDRSZ	0x200	/* EFI header size */
+
+enum {
+	EFI_IMAGE_MACHINE_IA32		= 0x014c,
+	EFI_IMAGE_MACHINE_x64		= 0x8664,
+	EFI_IMAGE_MACHINE_AARCH64	= 0xAA64,
+	EFI_IMAGE_MACHINE_LOONGARCH64	= 0x6264,
+};
+
+static int	is32;
+static int	infd, outfd;
+static uchar	buf[IOUNIT];
+
+static void
+pack(uchar **pp, char *fmt, ...)
+{
+	va_list args;
+	int c;
+	uchar *p;
+	u32int u;
+	u64int l;
+
+	va_start(args, fmt);
+	for(; *fmt; fmt++){
+		c = *fmt;
+		if(c == 'q' && is32)
+			c = 'l';
+		p = *pp;
+		switch(c){
+		case 'b':
+			p[0] = va_arg(args, int);
+			*pp += 1;
+			break;
+		case 'w':
+			u = va_arg(args, u32int);
+			p[0] = u;
+			p[1] = u>>8;
+			*pp += 2;
+			break;
+		case 'l':
+			u = va_arg(args, u32int);
+			p[0] = u;
+			p[1] = u>>8;
+			p[2] = u>>16;
+			p[3] = u>>24;
+			*pp += 4;
+			break;
+		case 'q':
+			l = va_arg(args, u64int);
+			p[0] = l;
+			p[1] = l>>8;
+			p[2] = l>>16;
+			p[3] = l>>24;
+			p[4] = l>>32;
+			p[5] = l>>40;
+			p[6] = l>>48;
+			p[7] = l>>56;
+			*pp += 8;
+			break;
+		case '0':
+			*pp += va_arg(args, int);
+			break;
+		default:
+			sysfatal("pack: %c", c);
+		}
+	}
+	va_end(args);
+}
+
+static void
+usage(void)
+{
+	fprint(2, "usage: %s a.out\n", argv0);
+	exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+	Fhdr fhdr;
+	u64int kzero;
+	uchar *header;
+	char *ofile, *iname;
+	int arch, chars, relocs;
+	long n, szofdat, szofimage;
+
+	kzero = 0x8000;
+	ofile = nil;
+	relocs = 0;
+	ARGBEGIN{
+	case 'Z':
+		kzero = strtoull(EARGF(usage()), 0, 0);
+		break;
+	case 'o':
+		ofile = strdup(EARGF(usage()));
+		break;
+	default:
+		usage();
+	}ARGEND;
+
+	if(argc != 1)
+		usage();
+
+	infd = open(argv[0], OREAD);
+	if(infd < 0)
+		sysfatal("infd: %r");
+
+	if(crackhdr(infd, &fhdr) == 0)
+		sysfatal("crackhdr: %r");
+	switch(mach->mtype){
+	case MI386:
+		arch = EFI_IMAGE_MACHINE_IA32;
+		is32 = 1;
+		chars = 2103;
+		break;
+	case MAMD64:
+		arch = EFI_IMAGE_MACHINE_x64;
+		chars = 2223;
+		break;
+	case MARM64:
+		arch = EFI_IMAGE_MACHINE_AARCH64;
+		chars = 518;
+		relocs = 1;
+		break;
+	case MLOONG:
+		arch = EFI_IMAGE_MACHINE_LOONGARCH64;
+		chars = 518;
+		relocs = 1;
+		break;
+	default:
+		SET(arch, chars);
+		sysfatal("archloch");
+	}
+	szofdat = fhdr.txtsz + fhdr.datsz;
+	szofimage = szofdat + fhdr.bsssz + HDRSZ;
+
+	iname = strrchr(argv[0], '/');
+	if(iname != nil)
+		iname++;
+	else
+		iname = argv[0];
+	if(ofile == nil)
+		ofile = smprint("%s.efi", iname);
+	outfd = create(ofile, OWRITE|OTRUNC, 0666);
+	if(outfd < 0)
+		sysfatal("create: %r");
+
+	header = buf;
+
+	/* mzhdr */
+	pack(&header, "bb0l",
+		'M', 'Z',	/* e_magic */
+		0x3a,		/* UNUSED */
+		0x40);		/* e_lfanew */
+
+	/* pehdr */
+	pack(&header, "bbbbwwlllww",
+		'P', 'E', 0, 0,
+		arch,			/* Machine */
+		1+relocs,		/* NumberOfSections */
+		0,			/* TimeDateStamp UNUSED */
+		0,			/* PointerToSymbolTable UNUSED */
+		0,			/* NumberOfSymbols UNUSED */
+		is32 ? 0xE0 : 0xF0,	/* SizeOfOptionalHeader */
+		chars);			/* Characteristics */
+	pack(&header, "wbblllll",
+		is32 ? 0x10B : 0x20B,	/* Magic */
+    		9,			/* MajorLinkerVersion UNUSED */
+		0,			/* MinorLinkerVersion UNUSED */
+		0,			/* SizeOfCode UNUSED */
+		0,			/* SizeOfInitializedData UNUSED */
+		0,			/* SizeOfUninitializedData UNUSED */
+		fhdr.entry-kzero,	/* AddressOfEntryPoint */
+		0);			/* BaseOfCode UNUSED */
+	if(is32)
+	pack(&header, "l", 0);	/* BaseOfData UNUSED */
+	pack(&header, "qllwwwwwwllllwwqqqqll0",
+		kzero,		/* ImageBase */
+		HDRSZ,		/* SectionAlignment */
+		HDRSZ,		/* FileAlignment */
+		4,		/* MajorOperatingSystemVersion UNUSED */
+		0,		/* MinorOperatingSystemVersion UNUSED */
+		0,		/* MajorImageVersion UNUSED */
+		0,		/* MinorImageVersion UNUSED */
+		4,		/* MajorSubsystemVersion */
+		0,		/* MinorSubsystemVersion UNUSED */
+		0,		/* Win32VersionValue UNUSED */
+		szofimage,	/* SizeOfImage */
+ 		HDRSZ,		/* SizeOfHeaders */
+ 		0,		/* CheckSum UNUSED */
+		10,		/* Subsystem (10 = efi application) */
+		0,		/* DllCharacteristics UNUSED */
+		0,		/* SizeOfStackReserve UNUSED */
+		0,		/* SizeOfStackCommit UNUSED */
+		0,		/* SizeOfHeapReserve UNUSED */
+		0,		/* SizeOfHeapCommit UNUSED */
+		0,		/* LoaderFlags UNUSED */
+		16,		/* NumberOfRvaAndSizes UNUSED */
+		32*4);		/* RVA UNUSED */
+	if(relocs)
+	pack(&header, "bbbbbbbbllllllwwl",
+		'.', 'r', 'e', 'l', 'o', 'c', 0, 0,
+		0,		/* VirtualSize */
+		0,		/* VirtualAddress */
+		0,		/* SizeOfData */
+		0,		/* PointerToRawData */
+		0,		/* PointerToRelocations UNUSED */
+		0,		/* PointerToLinenumbers UNUSED */
+		0,		/* NumberOfRelocations UNUSED */
+		0,		/* NumberOfLinenumbers UNUSED */
+		0x42100040);	/* Characteristics (read, discardable) */
+	pack(&header, "bbbbbbbbllllllwwl",
+		'.', 't', 'e', 'x', 't', 0, 0, 0,
+		szofdat,	/* VirtualSize */
+		HDRSZ,		/* VirtualAddress */
+		szofdat,	/* SizeOfData */
+		HDRSZ,		/* PointerToRawData */
+		0,		/* PointerToRelocations UNUSED */
+		0,		/* PointerToLinenumbers UNUSED */
+		0,		/* NumberOfRelocations UNUSED */
+		0,		/* NumberOfLinenumbers UNUSED */
+		0x86000020);	/* Characteristics (code, RWX) */
+
+	if(write(outfd, buf, HDRSZ) != HDRSZ)
+		sysfatal("write: %r");
+	if(seek(infd, fhdr.hdrsz, 0) != fhdr.hdrsz)
+		sysfatal("seek: %r");
+	for(;;){
+		n = read(infd, buf, sizeof(buf));
+		if(n < 0)
+			sysfatal("read: %r");
+		if(n == 0)
+			break;
+		if(write(outfd, buf, n) != n)
+			sysfatal("write: %r");
+	}
+
+	exits(nil);
+}
--- /dev/null
+++ b/sys/src/cmd/za/a.h
@@ -1,0 +1,177 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "../zc/z.out.h"
+
+#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;
+	vlong	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;
+	vlong	offset;
+	short	type;
+	short	reg;
+	short	name;
+	double	dval;
+	char	sval[8];
+};
+
+struct	Hist
+{
+	Hist*	link;
+	char*	name;
+	long	line;
+	vlong	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*	include[NINCLUDE];
+EXTERN	Io*	iofree;
+EXTERN	Io*	ionext;
+EXTERN	Io*	iostack;
+EXTERN	long	lineno;
+EXTERN	int	nerrors;
+EXTERN	int	ninclude;
+EXTERN	Gen	nullgen;
+EXTERN	char*	outfile;
+EXTERN	int	pass;
+EXTERN	char*	pathname;
+EXTERN	vlong	pc;
+EXTERN	int	peekc;
+EXTERN	int	sym;
+EXTERN	char	symb[NSYMB];
+EXTERN	int	thechar;
+EXTERN	char*	thestring;
+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);
+int	isreg(Gen*);
+void	outcode(int, Gen*, int, 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);
+int	macexpand(Sym*, char*, int);
+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	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);
+char*	mygetwd(char*, int);
+int	myexec(char*, char*[]);
+int	mydup(int, int);
+int	myfork(void);
+int	mypipe(int*);
+void*	mysbrk(ulong);
--- /dev/null
+++ b/sys/src/cmd/za/a.y
@@ -1,0 +1,580 @@
+%{
+#include "a.h"
+%}
+%union
+{
+	Sym	*sym;
+	vlong	lval;
+	double	dval;
+	char	sval[8];
+	Gen	gen;
+}
+%left	'|'
+%left	'^'
+%left	'&'
+%left	'<' '>'
+%left	'+' '-'
+%left	'*' '/' '%'
+%token	<lval>	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>	LCONST LSP LSB LFP LPC LTYPEX
+%token	<lval>	LREG LFREG LFCCREG LFCSREG LR LF LFCC LFCSR
+%token	<dval>	LFCONST
+%token	<sval>	LSCONST
+%token	<lval>	LVCONST
+%token	<sym>	LNAME LLAB LVAR
+%type	<lval>	con expr pointer offset sreg
+%type	<gen>	gen vgen lgen vlgen rel reg freg fccreg fcsreg
+%type	<gen>	imm ximm ireg name oreg imr nireg fgen
+%%
+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:
+/*
+ * Immed-type
+ */
+	LTYPE1 imr ',' sreg ',' reg
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LTYPE1 imr ',' reg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * NOR
+ */
+|	LTYPE2 imr ',' sreg ',' imr
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LTYPE2 imr ',' imr
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * LOAD/STORE, but not MOVW
+ */
+|	LTYPE3 lgen ',' gen
+	{
+		if(!isreg(&$2) && !isreg(&$4))
+			print("one side must be register\n");
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * SPECIAL
+ */
+|	LTYPE4 comma
+	{
+		outcode($1, &nullgen, NREG, &nullgen);
+	}
+/*
+ * MOVW
+ */
+|	LTYPE5 vlgen ',' vgen
+	{
+		if(!isreg(&$2) && !isreg(&$4))
+			print("one side must be register\n");
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * MUL/DIV
+ */
+|	LTYPE6 reg ',' sreg comma
+	{
+		outcode($1, &$2, $4, &nullgen);
+	}
+|	LTYPE6 reg ',' sreg ',' reg
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+/*
+ * JMP/JAL
+ */
+|	LTYPE7 comma rel
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LTYPE7 comma nireg
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LTYPE8 comma rel
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LTYPE8 comma nireg
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LTYPE8 sreg ',' nireg
+	{
+		outcode($1, &nullgen, $2, &$4);
+	}
+/*
+ * BEQ/BNE
+ */
+|	LTYPE9 gen ',' rel
+	{
+		if(!isreg(&$2))
+			print("left side must be register\n");
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LTYPE9 gen ',' sreg ',' rel
+	{
+		if(!isreg(&$2))
+			print("left side must be register\n");
+		outcode($1, &$2, $4, &$6);
+	}
+/*
+ * B-other
+ */
+|	LTYPEA gen ',' rel
+	{
+		if(!isreg(&$2))
+			print("left side must be register\n");
+		outcode($1, &$2, NREG, &$4);
+	}
+/*
+ * 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);
+	}
+/*
+ * floating-type
+ */
+|	LTYPED freg ',' freg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LTYPEE freg ',' freg
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+|	LTYPEE freg ',' LFREG ',' freg
+	{
+		outcode($1, &$2, $4, &$6);
+	}
+|	LTYPEF freg ',' LFREG comma
+	{
+		outcode($1, &$2, $4, &nullgen);
+	}
+/*
+ * coprocessor branch
+ */
+|	LTYPEG comma rel
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+/*
+ * word
+ */
+|	LTYPEH comma ximm
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+/*
+ * NOP
+ */
+|	LTYPEI comma
+	{
+		outcode($1, &nullgen, NREG, &nullgen);
+	}
+|	LTYPEI ',' vgen
+	{
+		outcode($1, &nullgen, NREG, &$3);
+	}
+|	LTYPEI vgen comma
+	{
+		outcode($1, &$2, NREG, &nullgen);
+	}
+/*
+ * BREAK -- overloaded with CACHE opcode
+ */
+|	LTYPEJ comma
+	{
+		outcode($1, &nullgen, NREG, &nullgen);
+	}
+|	LTYPEJ vgen ',' vgen
+	{
+		outcode($1, &$2, NREG, &$4);
+	}
+
+comma:
+|	','
+
+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;
+	}
+
+vlgen:
+	lgen
+|	fgen
+|	fccreg
+|	fcsreg
+
+vgen:
+	gen
+|	fgen
+|	fccreg
+|	fcsreg
+
+lgen:
+	gen
+|	ximm
+
+fgen:
+	freg
+
+fccreg:
+	LFCCREG
+	{
+		$$ = nullgen;
+		$$.type = D_FCCREG;
+		$$.reg = $1;
+	}
+|	LFCC '(' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_FCCREG;
+		$$.reg = $3;
+	}
+
+fcsreg:
+	LFCSREG
+	{
+		$$ = nullgen;
+		$$.type = D_FCSREG;
+		$$.reg = $1;
+	}
+|	LFCSR '(' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_FCSREG;
+		$$.reg = $3;
+	}
+
+freg:
+	LFREG
+	{
+		$$ = nullgen;
+		$$.type = D_FREG;
+		$$.reg = $1;
+	}
+|	LF '(' con ')'
+	{
+		$$ = nullgen;
+		$$.type = D_FREG;
+		$$.reg = $3;
+	}
+
+ximm:	'$' con
+	{
+		$$ = nullgen;
+		if(isvconst($2))
+			$$.type = D_VCONST;
+		else
+			$$.type = D_CONST;
+		$$.offset = $2;
+	}
+|	'$' oreg
+	{
+		$$ = $2;
+		$$.type = D_CONST;
+	}
+|	'$' '*' '$' oreg
+	{
+		$$ = $4;
+		$$.type = D_OCONST;
+	}
+|	'$' LSCONST
+	{
+		$$ = nullgen;
+		$$.type = D_SCONST;
+		memcpy($$.sval, $2, sizeof($$.sval));
+	}
+|	'$' LFCONST
+	{
+		$$ = nullgen;
+		$$.type = D_FCONST;
+		$$.dval = $2;
+	}
+|	'$' '-' LFCONST
+	{
+		$$ = nullgen;
+		$$.type = D_FCONST;
+		$$.dval = -$3;
+	}
+
+nireg:
+	ireg
+|	con ireg
+	{
+		if($1 != 0)
+			yyerror("offset must be zero");
+		$$ = $2;
+	}
+|	name
+	{
+		$$ = $1;
+		if($1.name != D_EXTERN && $1.name != D_STATIC) {
+		}
+	}
+
+ireg:
+	'(' sreg ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.reg = $2;
+		$$.offset = 0;
+	}
+
+gen:
+	reg
+|	con
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.offset = $1;
+	}
+|	oreg
+
+oreg:
+	name
+|	name '(' sreg ')'
+	{
+		$$ = $1;
+		$$.type = D_OREG;
+		$$.reg = $3;
+	}
+|	'(' sreg ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.reg = $2;
+		$$.offset = 0;
+	}
+|	con '(' sreg ')'
+	{
+		$$ = nullgen;
+		$$.type = D_OREG;
+		$$.reg = $3;
+		$$.offset = $1;
+	}
+
+imr:
+	reg
+|	imm
+
+imm:	'$' con
+	{
+		$$ = nullgen;
+		if(isvconst($2))
+			$$.type = D_VCONST;
+		else
+			$$.type = D_CONST;
+		$$.offset = $2;
+	}
+
+reg:
+	sreg
+	{
+		$$ = nullgen;
+		$$.type = D_REG;
+		$$.reg = $1;
+	}
+
+sreg:
+	LREG
+|	LR '(' con ')'
+	{
+		if($$ < 0 || $$ >= NREG)
+			print("register value out of range\n");
+		$$ = $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;
+	}
+%%
+
+int
+isvconst(vlong con)
+{
+	long l;
+
+	l = con;
+	return (vlong)l != con;
+}
--- /dev/null
+++ b/sys/src/cmd/za/lex.c
@@ -1,0 +1,643 @@
+#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 = 'z';
+	thestring = "loong";
+	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;
+	ushort	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,
+	"R31",		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,
+
+	"FCC",		LFCC,		0,
+	"FCC0",		LFCCREG,	0,
+	"FCC1",		LFCCREG,	1,
+	"FCC2",		LFCCREG,	2,
+	"FCC3",		LFCCREG,	3,
+	"FCC4",		LFCCREG,	4,
+	"FCC5",		LFCCREG,	5,
+	"FCC6",		LFCCREG,	6,
+	"FCC7",		LFCCREG,	7,
+
+	"FCSR",		LFCSR,		0,
+	"FCSR0",	LFCSREG,	0,
+	"FCSR1",	LFCSREG,	1,
+	"FCSR2",	LFCSREG,	2,
+	"FCSR3",	LFCSREG,	3,
+
+	"ADD",		LTYPE1, AADD,
+	"ADDU",		LTYPE1, AADDU,
+	"SUB",		LTYPE1, ASUB,	/* converted to ADD(-) in loader */
+	"SUBU",		LTYPE1, ASUBU,
+	"SGT",		LTYPE1, ASGT,
+	"SGTU",		LTYPE1, ASGTU,
+	"AND",		LTYPE1, AAND,
+	"OR",		LTYPE1, AOR,
+	"XOR",		LTYPE1, AXOR,
+	"SLL",		LTYPE1, ASLL,
+	"SRL",		LTYPE1, ASRL,
+	"SRA",		LTYPE1, ASRA,
+
+	"ADDV",		LTYPE1, AADDV,
+	"ADDVU",		LTYPE1, AADDVU,
+	"SUBV",		LTYPE1, ASUBV,	/* converted to ADD(-) in loader */
+	"SUBVU",		LTYPE1, ASUBVU,
+	"SLLV",		LTYPE1, ASLLV,
+	"SRLV",		LTYPE1, ASRLV,
+	"SRAV",		LTYPE1, ASRAV,
+
+	"NOR",		LTYPE2, ANOR,
+
+	"MOVB",		LTYPE3, AMOVB,
+	"MOVBU",	LTYPE3, AMOVBU,
+	"MOVH",		LTYPE3, AMOVH,
+	"MOVHU",	LTYPE3, AMOVHU,
+	"MOVWL",	LTYPE3, AMOVWL,
+	"MOVWR",	LTYPE3, AMOVWR,
+	"MOVVL",	LTYPE3, AMOVVL,
+	"MOVVR",	LTYPE3, AMOVVR,
+
+	"BREAK",	LTYPEJ, ABREAK,		/* overloaded CACHE opcode */
+	"END",		LTYPE4, AEND,
+	"REM",		LTYPE6, AREM,
+	"REMU",		LTYPE6, AREMU,
+	"RET",		LTYPE4, ARET,
+	"SYSCALL",	LTYPE4, ASYSCALL,
+
+	"MOVW",		LTYPE5, AMOVW,
+	"MOVV",		LTYPE5, AMOVV,
+	"MOVD",		LTYPE5, AMOVD,
+	"MOVF",		LTYPE5, AMOVF,
+
+	"DIV",		LTYPE6, ADIV,
+	"DIVU",		LTYPE6, ADIVU,
+	"MUL",		LTYPE6, AMUL,
+	"MULU",		LTYPE6, AMULU,
+	"DIVV",		LTYPE6, ADIVV,
+	"DIVVU",		LTYPE6, ADIVVU,
+	"MULV",		LTYPE6, AMULV,
+	"MULVU",		LTYPE6, AMULVU,
+
+	"JMP",		LTYPE7, AJMP,
+
+	"JAL",		LTYPE8, AJAL,
+
+	"BEQ",		LTYPE9, ABEQ,
+	"BNE",		LTYPE9, ABNE,
+
+	"BGEZ",		LTYPEA, ABGEZ,
+	"BGTZ",		LTYPEA, ABGTZ,
+	"BLEZ",		LTYPEA, ABLEZ,
+	"BLTZ",		LTYPEA, ABLTZ,
+
+	"TEXT",		LTYPEB, ATEXT,
+	"GLOBL",	LTYPEB, AGLOBL,
+
+	"DATA",		LTYPEC, ADATA,
+
+	"MOVDF",	LTYPE5, AMOVDF,
+	"MOVDW",	LTYPE5, AMOVDW,
+	"MOVFD",	LTYPE5, AMOVFD,
+	"MOVFW",	LTYPE5, AMOVFW,
+	"MOVWD",	LTYPE5, AMOVWD,
+	"MOVWF",	LTYPE5, AMOVWF,
+
+	"ABSD",		LTYPED, AABSD,
+	"ABSF",		LTYPED, AABSF,
+	"NEGD",		LTYPED, ANEGD,
+	"NEGF",		LTYPED, ANEGF,
+	"NEGW",		LTYPED, ANEGW,
+
+	"CMPEQD",	LTYPEF, ACMPEQD,
+	"CMPEQF",	LTYPEF, ACMPEQF,
+	"CMPGED",	LTYPEF, ACMPGED,
+	"CMPGEF",	LTYPEF, ACMPGEF,
+	"CMPGTD",	LTYPEF, ACMPGTD,
+	"CMPGTF",	LTYPEF, ACMPGTF,
+
+	"ADDD",		LTYPEE, AADDD,
+	"ADDF",		LTYPEE, AADDF,
+	"ADDW",		LTYPEE, AADDW,
+	"DIVD",		LTYPEE, ADIVD,
+	"DIVF",		LTYPEE, ADIVF,
+	"DIVW",		LTYPEE, ADIVW,
+	"MULD",		LTYPEE, AMULD,
+	"MULF",		LTYPEE, AMULF,
+	"MULW",		LTYPEE, AMULW,
+	"SUBD",		LTYPEE, ASUBD,
+	"SUBF",		LTYPEE, ASUBF,
+	"SUBW",		LTYPEE, ASUBW,
+
+	"BFPT",		LTYPEG, ABFPT,
+	"BFPF",		LTYPEG, ABFPF,
+
+	"WORD",		LTYPEH, AWORD,
+	"NOP",		LTYPEI, ANOP,
+	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;
+	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;
+	for(i=0; i<NHASH; i++)
+		hash[i] = S;
+	for(i=0; itab[i].name; i++) {
+		s = slookup(itab[i].name);
+		s->type = itab[i].type;
+		s->value = itab[i].value;
+	}
+
+	pathname = allocn(pathname, 0, 100);
+	if(mygetwd(pathname, 99) == 0) {
+		pathname = allocn(pathname, 100, 900);
+		if(mygetwd(pathname, 999) == 0)
+			strcpy(pathname, "/???");
+	}
+}
+
+void
+syminit(Sym *s)
+{
+
+	s->type = LNAME;
+	s->value = 0;
+}
+
+int
+isreg(Gen *g)
+{
+
+	USED(g);
+	return 1;
+}
+
+void
+cclean(void)
+{
+
+	outcode(AEND, &nullgen, NREG, &nullgen);
+	Bflush(&obuf);
+}
+
+void
+zname(char *n, int t, int s)
+{
+
+	Bputc(&obuf, ANAME);
+	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;
+	vlong v;
+
+	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_FREG:
+	case D_FCCREG:
+	case D_FCSREG:
+		break;
+
+	case D_OREG:
+	case D_CONST:
+	case D_OCONST:
+	case D_BRANCH:
+		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;
+
+	case D_VCONST:
+		v = a->offset;
+		Bputc(&obuf, v);
+		Bputc(&obuf, v>>8);
+		Bputc(&obuf, v>>16);
+		Bputc(&obuf, v>>24);
+		Bputc(&obuf, v>>32);
+		Bputc(&obuf, v>>40);
+		Bputc(&obuf, v>>48);
+		Bputc(&obuf, v>>56);
+		break;
+	}
+}
+
+void
+outcode(int a, Gen *g1, int reg, Gen *g2)
+{
+	int sf, st, t;
+	Sym *s;
+
+	if(pass == 1)
+		goto out;
+jackpot:
+	sf = 0;
+	s = g1->sym;
+	while(s != S) {
+		sf = s->sym;
+		if(sf < 0 || sf >= NSYM)
+			sf = 0;
+		t = g1->name;
+		if(h[sf].type == t)
+		if(h[sf].sym == s)
+			break;
+		zname(s->name, t, sym);
+		s->sym = sym;
+		h[sym].sym = s;
+		h[sym].type = t;
+		sf = sym;
+		sym++;
+		if(sym >= NSYM)
+			sym = 1;
+		break;
+	}
+	st = 0;
+	s = g2->sym;
+	while(s != S) {
+		st = s->sym;
+		if(st < 0 || st >= NSYM)
+			st = 0;
+		t = g2->name;
+		if(h[st].type == t)
+		if(h[st].sym == s)
+			break;
+		zname(s->name, t, sym);
+		s->sym = sym;
+		h[sym].sym = s;
+		h[sym].type = t;
+		st = sym;
+		sym++;
+		if(sym >= NSYM)
+			sym = 1;
+		if(st == sf)
+			goto jackpot;
+		break;
+	}
+	Bputc(&obuf, a);
+	Bputc(&obuf, reg);
+	Bputc(&obuf, lineno);
+	Bputc(&obuf, lineno>>8);
+	Bputc(&obuf, lineno>>16);
+	Bputc(&obuf, lineno>>24);
+	zaddr(g1, sf);
+	zaddr(g2, st);
+
+out:
+	if(a != AGLOBL && a != ADATA)
+		pc++;
+}
+
+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, 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, 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/za/mkfile
@@ -1,0 +1,19 @@
+</$objtype/mkfile
+
+TARG=za
+OFILES=\
+	y.tab.$O\
+	lex.$O\
+
+HFILES=\
+	../zc/z.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 ../cc/compat
--- /dev/null
+++ b/sys/src/cmd/zc/cgen.c
@@ -1,0 +1,1177 @@
+#include "gc.h"
+
+static void genasop(int, Node*, Node*, Node*);
+
+void
+cgen(Node *n, Node *nn)
+{
+	Node *l, *r;
+	Prog *p1;
+	Node nod, nod1, nod2, nod3, nod4;
+	int o;
+	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:
+		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 OAS:
+		if(l->op == OBIT)
+			goto bitas;
+		if(l->addable >= INDEXED && l->complex < FNX) {
+			if(nn != Z || r->addable < INDEXED) {
+				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) {
+			reglcgen(&nod1, l, Z);
+			if(r->addable >= INDEXED) {
+				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 OADD:
+	case OSUB:
+	case OAND:
+	case OOR:
+	case OXOR:
+	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:
+	case ODIV:
+	case OMOD:
+		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, r, Z);
+			cgen(r, &nod1);
+			gopcode(o, &nod1, Z, &nod);
+		} else {
+			regalloc(&nod, r, nn);
+			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, r, nn);
+			gopcode(OAS, &nod2, Z, &nod);
+			gopcode(o, r, Z, &nod);
+			gopcode(OAS, &nod, Z, &nod2);
+
+			regfree(&nod);
+			if(l->addable < INDEXED)
+				regfree(&nod2);
+			break;
+		}
+		genasop(o, l, r, nn);
+		break;
+
+	case OASLMUL:
+	case OASLDIV:
+	case OASLMOD:
+	case OASMUL:
+	case OASDIV:
+	case OASMOD:
+		if(l->op == OBIT)
+			goto asbitop;
+		genasop(o, l, r, nn);
+		break;
+
+	asbitop:
+		regalloc(&nod4, n, nn);
+		if(l->complex >= r->complex) {
+			bitload(l, &nod, &nod1, &nod2, &nod4);
+			regalloc(&nod3, r, Z);
+			cgen(r, &nod3);
+		} else {
+			regalloc(&nod3, r, Z);
+			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:
+		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;
+		}
+		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)
+			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;
+			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)) {
+			if(nocast(n->type, nn->type)) {
+				cgen(l, nn);
+				break;
+			}
+		}
+		regalloc(&nod, l, nn);
+		cgen(l, &nod);
+		regalloc(&nod1, n, &nod);
+		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;
+}
+
+static void
+genasop(int o, Node *l, Node *r, Node *nn)
+{
+	Node nod, nod1, nod2;
+	int hardleft;
+
+	hardleft = l->addable < INDEXED || l->complex >= FNX;
+	if(l->complex >= r->complex) {
+		if(hardleft)
+			reglcgen(&nod2, l, Z);
+		else
+			nod2 = *l;
+		regalloc(&nod1, r, Z);
+		cgen(r, &nod1);
+	} else {
+		regalloc(&nod1, r, Z);
+		cgen(r, &nod1);
+		if(hardleft)
+			reglcgen(&nod2, l, Z);
+		else
+			nod2 = *l;
+	}
+	if(nod1.type == nod2.type || !typefd[nod1.type->etype])
+		regalloc(&nod, &nod2, nn);
+	else
+		regalloc(&nod, &nod1, Z);
+	gmove(&nod2, &nod);
+	gopcode(o, &nod1, Z, &nod);
+	gmove(&nod, &nod2);
+	if(nn != Z)
+		gmove(&nod2, nn);
+	regfree(&nod);
+	regfree(&nod1);
+	if(hardleft)
+		regfree(&nod2);
+}
+
+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;
+			r->vconst = 0;
+			lcgen(n, t);
+			t->xoffset += v;
+			r->vconst = v;
+			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);
+		if(nn == Z || typefd[n->type->etype]) {
+			o = ONE;
+			if(true)
+				o = comrel[relindex(o)];
+			if(typefd[n->type->etype]) {
+				regalloc(&nod1, n, Z);
+				gmove(nodfconst(0.0), &nod1);
+				gopcode(o, &nod, &nod1, Z);
+				regfree(&nod1);
+			} else
+				gopcode(o, &nod, Z, Z);
+			regfree(&nod);
+			goto com;
+		}
+		if(true)
+			gopcode(OCOND, &nod, nodconst(0), &nod);
+		else
+			gopcode(OCOND, nodconst(1), &nod, &nod);
+		gopcode(OAS, &nod, Z, nn);
+		regfree(&nod);
+		break;
+
+	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);
+			cgen(r, &nod);
+			regsalloc(&nod1, r);
+			gopcode(OAS, &nod, Z, &nod1);
+			regfree(&nod);
+			nod = *n;
+			nod.right = &nod1;
+			boolgen(&nod, true, nn);
+			break;
+		}
+		if(nn != Z && !typefd[l->type->etype]) {
+			if(l->complex >= r->complex) {
+				regalloc(&nod1, l, nn);
+				cgen(l, &nod1);
+				regalloc(&nod, r, Z);
+				cgen(r, &nod);
+			} else {
+				regalloc(&nod, r, nn);
+				cgen(r, &nod);
+				regalloc(&nod1, l, Z);
+				cgen(l, &nod1);
+			}
+			switch(o) {
+			case OEQ:
+				gopcode(OSUB, &nod1, &nod, &nod);
+				gopcode(OCOND, &nod, nodconst(0), &nod);
+				break;
+			case ONE:
+				gopcode(OSUB, &nod1, &nod, &nod);
+				gopcode(OCOND, nodconst(1), &nod, &nod);
+				break;
+			case OLE:
+				gopcode(OCOMMA, &nod1, &nod, &nod);
+				break;
+			case OGT:
+				gopcode(OCOMMA, &nod1, &nod, &nod);
+				gopcode(OXOR, nodconst(1), &nod, &nod);
+				break;
+			case OLT:
+				gopcode(OCOMMA, &nod, &nod1, &nod);
+				gopcode(OXOR, nodconst(1), &nod, &nod);
+				break;
+			case OGE:
+				gopcode(OCOMMA, &nod, &nod1, &nod);
+				break;
+			case OLS:
+				gopcode(OCOND, &nod1, &nod, &nod);
+				break;
+			case OHI:
+				gopcode(OCOND, &nod1, &nod, &nod);
+				gopcode(OXOR, nodconst(1), &nod, &nod);
+				break;
+			case OLO:
+				gopcode(OCOND, &nod, &nod1, &nod);
+				gopcode(OXOR, nodconst(1), &nod, &nod);
+				break;
+			case OHS:
+				gopcode(OCOND, &nod, &nod1, &nod);
+				break;
+			}
+			gopcode(OAS, &nod, Z, nn);
+			regfree(&nod);
+			regfree(&nod1);
+			break;
+		}
+		if(sconst(l)) {
+			switch(o) {
+			default:
+				if(l->vconst != 0)
+					break;
+
+			case OGT:
+			case OHI:
+			case OLE:
+			case OLS:
+				regalloc(&nod, r, nn);
+				cgen(r, &nod);
+				gopcode(o, l, &nod, Z);
+				regfree(&nod);
+				goto com;
+			}
+		}
+		if(sconst(r)) {
+			switch(o) {
+			default:
+				if(r->vconst != 0)
+					break;
+
+			case OGE:
+			case OHS:
+			case OLT:
+			case OLO:
+				regalloc(&nod, l, nn);
+				cgen(l, &nod);
+				gopcode(o, &nod, r, Z);
+				regfree(&nod);
+				goto com;
+			}
+		}
+		if(l->complex >= r->complex) {
+			regalloc(&nod1, l, nn);
+			cgen(l, &nod1);
+			regalloc(&nod, r, Z);
+			cgen(r, &nod);
+		} else {
+			regalloc(&nod, r, nn);
+			cgen(r, &nod);
+			regalloc(&nod1, l, Z);
+			cgen(l, &nod1);
+		}
+		gopcode(o, &nod1, &nod, 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 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);
+			regalloc(&nod2, &nod1, Z);
+			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;
+	}
+
+	if(n->complex > nn->complex) {
+		t = n->type;
+		n->type = types[TVLONG];
+		reglcgen(&nod1, n, Z);
+		n->type = t;
+
+		t = nn->type;
+		nn->type = types[TVLONG];
+		reglcgen(&nod2, nn, Z);
+		nn->type = t;
+	} else {
+		t = nn->type;
+		nn->type = types[TVLONG];
+		reglcgen(&nod2, nn, Z);
+		nn->type = t;
+
+		t = n->type;
+		n->type = types[TVLONG];
+		reglcgen(&nod1, n, Z);
+		n->type = t;
+	}
+
+	w /= SZ_VLONG;
+	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(1), Z, &nod3);
+	nod1.op = OREGISTER;
+	gopcode(OADD, nodconst(c*SZ_VLONG), Z, &nod1);
+	nod2.op = OREGISTER;
+	gopcode(OADD, nodconst(c*SZ_VLONG), Z, &nod2);
+
+	gopcode(OEQ, &nod3, Z, Z);
+	p->as = ABGTZ;
+	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);
+	t1.type = types[TVLONG];
+	t2.type = types[TVLONG];
+	if(c > 0) {
+		gopcode(OAS, f, Z, &t1);
+		f->xoffset += SZ_VLONG;
+	}
+	if(cn != Z)
+		gopcode(OAS, nodconst(cv), Z, cn);
+	if(c > 1) {
+		gopcode(OAS, f, Z, &t2);
+		f->xoffset += SZ_VLONG;
+	}
+	if(c > 0) {
+		gopcode(OAS, &t1, Z, t);
+		t->xoffset += SZ_VLONG;
+	}
+	if(c > 2) {
+		gopcode(OAS, f, Z, &t1);
+		f->xoffset += SZ_VLONG;
+	}
+	if(c > 1) {
+		gopcode(OAS, &t2, Z, t);
+		t->xoffset += SZ_VLONG;
+	}
+	if(c > 2) {
+		gopcode(OAS, &t1, Z, t);
+		t->xoffset += SZ_VLONG;
+	}
+	regfree(&t1);
+	regfree(&t2);
+}
--- /dev/null
+++ b/sys/src/cmd/zc/enam.c
@@ -1,0 +1,116 @@
+char*	anames[] =
+{
+	"XXX",
+	"ABSD",
+	"ABSF",
+	"ADD",
+	"ADDD",
+	"ADDF",
+	"ADDU",
+	"ADDW",
+	"AND",
+	"BEQ",
+	"BFPF",
+	"BFPT",
+	"BGEZ",
+	"BGTZ",
+	"BLEZ",
+	"BLTZ",
+	"BNE",
+	"BREAK",
+	"CMPEQD",
+	"CMPEQF",
+	"CMPGED",
+	"CMPGEF",
+	"CMPGTD",
+	"CMPGTF",
+	"DATA",
+	"DIV",
+	"DIVD",
+	"DIVF",
+	"DIVU",
+	"DIVW",
+	"GLOBL",
+	"GOK",
+	"HISTORY",
+	"JAL",
+	"JMP",
+	"MOVB",
+	"MOVBU",
+	"MOVD",
+	"MOVDF",
+	"MOVDW",
+	"MOVF",
+	"MOVFD",
+	"MOVFW",
+	"MOVH",
+	"MOVHU",
+	"MOVW",
+	"MOVWD",
+	"MOVWF",
+	"MOVWL",
+	"MOVWR",
+	"MUL",
+	"MULD",
+	"MULF",
+	"MULU",
+	"MULW",
+	"NAME32",
+	"NAME",
+	"NEGD",
+	"NEGF",
+	"NEGW",
+	"NOP",
+	"NOR",
+	"OR",
+	"REM",
+	"REMU",
+	"RET",
+	"SGT",
+	"SGTU",
+	"SLL",
+	"SRA",
+	"SRL",
+	"SUB",
+	"SUBD",
+	"SUBF",
+	"SUBU",
+	"SUBW",
+	"SYSCALL",
+	"TEXT",
+	"WORD",
+	"XOR",
+	"END",
+	"MOVV",
+	"MOVVL",
+	"MOVVR",
+	"SLLV",
+	"SRAV",
+	"SRLV",
+	"DIVV",
+	"DIVVU",
+	"REMV",
+	"REMVU",
+	"MULV",
+	"MULVU",
+	"ADDV",
+	"ADDVU",
+	"SUBV",
+	"SUBVU",
+	"DYNT",
+	"INIT",
+	"TRUNCFV",
+	"TRUNCDV",
+	"TRUNCFW",
+	"TRUNCDW",
+	"MOVWU",
+	"MOVFV",
+	"MOVDV",
+	"MOVVF",
+	"MOVVD",
+	"LU12IW",
+	"LU32ID",
+	"LU52ID",
+	"SIGNAME",
+	"LAST",
+};
--- /dev/null
+++ b/sys/src/cmd/zc/gc.h
@@ -1,0 +1,334 @@
+#include	"../cc/cc.h"
+#include	"../zc/z.out.h"
+
+/*
+ * zc/loong
+ * LoongArch
+ */
+#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
+
+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;
+	char	as;
+	char	reg;
+};
+#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
+{
+	long	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
+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	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	char	string[NSNAME];
+EXTERN	Sym*	symrathole;
+EXTERN	Node	znode;
+EXTERN	Prog	zprog;
+EXTERN	char	reg[NREG+NREG];
+EXTERN	long	exregoffset;
+EXTERN	long	exfregoffset;
+
+#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	int	suppress;
+
+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	xcom(Node*);
+int	bcomplex(Node*, Node*);
+void	usedset(Node*, int);
+
+/*
+ * cgen.c
+ */
+void	cgen(Node*, Node*);
+void	reglcgen(Node*, Node*, 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	gmove(Node*, Node*);
+void	gins(int a, Node*, Node*);
+void	gopcode(int, Node*, Node*, Node*);
+int	samaddr(Node*, Node*);
+void	gbranch(int);
+void	patch(Prog*, vlong);
+int	sconst(Node*);
+int	llconst(Node*);
+int	sval(long);
+void	gpseudo(int, Sym*, Node*);
+
+/*
+ * swt.c
+ */
+int	swcmp(const void*, const 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*);
+
+/*
+ * reg.c
+ */
+Reg*	rega(void);
+int	rcmp(const void*, const 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	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);
+
+#pragma	varargck	type	"A"	int
+#pragma	varargck	type	"B"	Bits
+#pragma	varargck	type	"D"	Adr*
+#pragma	varargck	type	"N"	Adr*
+#pragma	varargck	type	"P"	Prog*
+#pragma	varargck	type	"S"	char*
--- /dev/null
+++ b/sys/src/cmd/zc/lex.c
@@ -1,0 +1,1552 @@
+#include	"cc.h"
+#include	"y.tab.h"
+
+#ifndef	CPP
+#define	CPP	"/bin/cpp"
+#endif
+
+/*
+ * known debug flags
+ *	-a		acid declaration output
+ *	-A		!B
+ *	-B		non ANSI
+ *	-d		print declarations
+ *	-D name		define
+ *	-F		format specification check
+ *	-i		print initialization
+ *	-I path		include
+ *	-l		generate little-endian code
+ *	-L		print every NAME symbol
+ *	-M		constant multiplication
+ *	-m		print add/sub/mul trees
+ *	-n		print acid to file (%.c=%.acid) (with -a or -aa)
+ *	-o file		output file
+ *	-p		use standard cpp ANSI preprocessor (not on windows)
+ *	-r		print registerization
+ *	-s		print structure offsets (with -a or -aa)
+ *	-S		print assembly
+ *	-t		print type trees
+ *	-V		enable void* conversion warnings
+ *	-v		verbose printing
+ *	-w		print warnings
+ *	-X		abort on error
+ *	-.		Inhibit search for includes in source directory
+ */
+
+void
+main(int argc, char *argv[])
+{
+	char **defs, **np, *p;
+	int nproc, nout, status, i, c, ndef, maxdef;
+
+	memset(debug, 0, sizeof(debug));
+	tinit();
+	cinit();
+	ginit();
+	arginit();
+
+	profileflg = 1;	/* #pragma can turn it off */
+	tufield = simplet((1L<<tfield->etype) | BUNSIGNED);
+	maxdef = 0;
+	ndef = 0;
+	outfile = 0;
+	defs = nil;
+	setinclude(".");
+	ARGBEGIN {
+	default:
+		c = ARGC();
+		if(c >= 0 && c < sizeof(debug))
+			debug[c]++;
+		break;
+
+	case 'o':
+		outfile = ARGF();
+		break;
+
+	case 'D':
+		p = ARGF();
+		if(p) {
+			if(ndef >= maxdef){
+				maxdef += 50;
+				np = alloc(maxdef * sizeof *np);
+				if(defs != nil)
+					memmove(np, defs, (maxdef - 50) * sizeof *np);
+				defs = np;
+			}
+			defs[ndef++] = p;
+			dodefine(p);
+		}
+		break;
+
+	case 'I':
+		p = ARGF();
+		if(p)
+			setinclude(p);
+		break;
+	} ARGEND
+	if(argc < 1 && outfile == 0) {
+		print("usage: %cc [-options] files\n", thechar);
+		errorexit();
+	}
+	if(argc > 1 && systemtype(Windows)){
+		print("can't compile multiple files on windows\n");
+		errorexit();
+	}
+	if(argc > 1 && !systemtype(Windows)) {
+		nproc = 1;
+		/*
+		 * if we're writing acid to standard output, don't compile
+		 * concurrently, to avoid interleaving output.
+		 */
+		if(((!debug['a'] && !debug['Z']) || debug['n']) &&
+		    (p = getenv("NPROC")) != nil)
+			nproc = atol(p);	/* */
+		c = 0;
+		nout = 0;
+		for(;;) {
+			while(nout < nproc && argc > 0) {
+				i = myfork();
+				if(i < 0) {
+					i = mywait(&status);
+					if(i < 0) {
+						print("cannot create a process\n");
+						errorexit();
+					}
+					if(status)
+						c++;
+					nout--;
+					continue;
+				}
+				if(i == 0) {
+					fprint(2, "%s:\n", *argv);
+					if (compile(*argv, defs, ndef))
+						errorexit();
+					exits(0);
+				}
+				nout++;
+				argc--;
+				argv++;
+			}
+			i = mywait(&status);
+			if(i < 0) {
+				if(c)
+					errorexit();
+				exits(0);
+			}
+			if(status)
+				c++;
+			nout--;
+		}
+	}
+
+	if(argc == 0)
+		c = compile("stdin", defs, ndef);
+	else
+		c = compile(argv[0], defs, ndef);
+
+	if(c)
+		errorexit();
+	exits(0);
+}
+
+int
+compile(char *file, char **defs, int ndef)
+{
+	char ofile[400], incfile[20];
+	char *p, **av, opt[256];
+	int i, c, fd[2];
+	static int first = 1;
+
+	strcpy(ofile, file);
+	p = utfrrune(ofile, pathchar());
+	if(p) {
+		*p++ = 0;
+		if(!debug['.'])
+			include[0] = strdup(ofile);
+	} else
+		p = ofile;
+
+	if(outfile == 0) {
+		outfile = p;
+		if(outfile) {
+			if(p = utfrrune(outfile, '.'))
+				if(p[1] == 'c' && p[2] == 0)
+					p[0] = 0;
+			p = utfrune(outfile, 0);
+			if(debug['a'] && debug['n'])
+				strcat(p, ".acid");
+			else if(debug['Z'] && debug['n'])
+				strcat(p, "_pickle.c");
+			else {
+				p[0] = '.';
+				p[1] = thechar;
+				p[2] = 0;
+			}
+		} else
+			outfile = "/dev/null";
+	}
+
+	if(p = getenv("INCLUDE")) {
+		setinclude(p);
+	} else {
+		if(systemtype(Plan9)) {
+			sprint(incfile, "/%s/include", thestring);
+			setinclude(strdup(incfile));
+			setinclude("/sys/include");
+		}
+	}
+	if (first)
+		Binit(&diagbuf, 1, OWRITE);
+	/*
+	 * if we're writing acid to standard output, don't keep scratching
+	 * outbuf.
+	 */
+	if((debug['a'] || debug['Z']) && !debug['n']) {
+		if (first) {
+			outfile = 0;
+			Binit(&outbuf, dup(1, -1), OWRITE);
+			dup(2, 1);
+		}
+	} else {
+		c = mycreat(outfile, 0664);
+		if(c < 0) {
+			diag(Z, "cannot open %s - %r", outfile);
+			outfile = 0;
+			errorexit();
+		}
+		Binit(&outbuf, c, OWRITE);
+	}
+	newio();
+	first = 0;
+
+	/* Use an ANSI preprocessor */
+	if(debug['p']) {
+		if(systemtype(Windows)) {
+			diag(Z, "-p option not supported on windows");
+			errorexit();
+		}
+		if(myaccess(file) < 0) {
+			diag(Z, "%s does not exist", file);
+			errorexit();
+		}
+		if(mypipe(fd) < 0) {
+			diag(Z, "pipe failed");
+			errorexit();
+		}
+		switch(myfork()) {
+		case -1:
+			diag(Z, "fork failed");
+			errorexit();
+		case 0:
+			close(fd[0]);
+			mydup(fd[1], 1);
+			close(fd[1]);
+			av = alloc((3 + ndef + ninclude + 2) * sizeof *av);
+			av[0] = CPP;
+			i = 1;
+			if(debug['.'])
+				av[i++] = strdup("-.");
+			/* 1999 ANSI C requires recognising // comments */
+			av[i++] = strdup("-+");
+			for(c = 0; c < ndef; c++) {
+				sprint(opt, "-D%s", defs[c]);
+				av[i++] = strdup(opt);
+			}
+			for(c = 0; c < ninclude; c++) {
+				sprint(opt, "-I%s", include[c]);
+				av[i++] = strdup(opt);
+			}
+			if(strcmp(file, "stdin") != 0)
+				av[i++] = file;
+			av[i] = 0;
+			if(debug['p'] > 1) {
+				for(c = 0; c < i; c++)
+					fprint(2, "%s ", av[c]);
+				fprint(2, "\n");
+			}
+			myexec(av[0], av);
+			fprint(2, "can't exec C preprocessor %s: %r\n", CPP);
+			errorexit();
+		default:
+			close(fd[1]);
+			newfile(file, fd[0]);
+			break;
+		}
+	} else {
+		if(strcmp(file, "stdin") == 0)
+			newfile(file, 0);
+		else
+			newfile(file, -1);
+	}
+	yyparse();
+	if(!debug['a'] && !debug['Z'])
+		gclean();
+	return nerrors;
+}
+
+void
+errorexit(void)
+{
+	if(outfile)
+		remove(outfile);
+	exits("error");
+}
+
+void
+pushio(void)
+{
+	Io *i;
+
+	i = iostack;
+	if(i == I) {
+		yyerror("botch in pushio");
+		errorexit();
+	}
+	i->p = fi.p;
+	i->c = fi.c;
+}
+
+void
+newio(void)
+{
+	Io *i;
+	static int pushdepth = 0;
+
+	i = iofree;
+	if(i == I) {
+		pushdepth++;
+		if(pushdepth > 1000) {
+			yyerror("macro/io expansion too deep");
+			errorexit();
+		}
+		i = alloc(sizeof(*i));
+	} else
+		iofree = i->link;
+	i->c = 0;
+	i->f = -1;
+	ionext = i;
+}
+
+void
+newfile(char *s, int f)
+{
+	Io *i;
+
+	if(debug['e'])
+		print("%L: %s\n", lineno, s);
+
+	i = ionext;
+	i->link = iostack;
+	iostack = i;
+	i->f = f;
+	if(f < 0)
+		i->f = open(s, 0);
+	if(i->f < 0) {
+		yyerror("%cc: %r: %s", thechar, s);
+		errorexit();
+	}
+	fi.c = 0;
+	linehist(s, 0);
+}
+
+Sym*
+slookup(char *s)
+{
+
+	strcpy(symb, s);
+	return lookup();
+}
+
+Sym*
+lookup(void)
+{
+	Sym *s;
+	ulong h;
+	char *p;
+	int c, n;
+
+	h = 0;
+	for(p=symb; *p;) {
+		h = h * 3;
+		h += *p++;
+	}
+	n = (p - symb) + 1;
+	if((long)h < 0)
+		h = ~h;
+	h %= NHASH;
+	c = symb[0];
+	for(s = hash[h]; s != S; s = s->link) {
+		if(s->name[0] != c)
+			continue;
+		if(strcmp(s->name, symb) == 0)
+			return s;
+	}
+	s = alloc(sizeof(*s));
+	s->name = alloc(n);
+	memmove(s->name, symb, n);
+
+	strcpy(s->name, symb);
+	s->link = hash[h];
+	hash[h] = s;
+	syminit(s);
+
+	return s;
+}
+
+void
+syminit(Sym *s)
+{
+	s->lexical = LNAME;
+	s->block = 0;
+	s->offset = 0;
+	s->type = T;
+	s->suetag = T;
+	s->class = CXXX;
+	s->aused = 0;
+	s->sig = SIGNONE;
+}
+
+#define	EOF	(-1)
+#define	IGN	(-2)
+#define	ESC	(Runemask+1)		/* Rune flag: a literal byte */
+#define	GETC()	((--fi.c < 0)? filbuf(): (*fi.p++ & 0xff))
+
+enum
+{
+	Numdec		= 1<<0,
+	Numlong		= 1<<1,
+	Numuns		= 1<<2,
+	Numvlong	= 1<<3,
+	Numflt		= 1<<4,
+};
+
+long
+yylex(void)
+{
+	vlong vv;
+	long c, c1, t;
+	char *cp;
+	Rune rune;
+	Sym *s;
+
+	if(peekc != IGN) {
+		c = peekc;
+		peekc = IGN;
+		goto l1;
+	}
+l0:
+	c = GETC();
+
+l1:
+	if(c >= Runeself) {
+		/*
+		 * extension --
+		 *	all multibyte runes are alpha
+		 */
+		cp = symb;
+		goto talph;
+	}
+	if(isspace(c)) {
+		if(c == '\n')
+			lineno++;
+		goto l0;
+	}
+	if(isalpha(c)) {
+		cp = symb;
+		if(c != 'L')
+			goto talph;
+		*cp++ = c;
+		c = GETC();
+		if(c == '\'') {
+			/* L'x' */
+			c = escchar('\'', 1, 0);
+			if(c == EOF)
+				c = '\'';
+			c1 = escchar('\'', 1, 0);
+			if(c1 != EOF) {
+				yyerror("missing '");
+				peekc = c1;
+			}
+			yylval.vval = convvtox(c, TRUNE);
+			return LUCONST;
+		}
+		if(c == '"') {
+			goto caselq;
+		}
+		goto talph;
+	}
+	if(isdigit(c))
+		goto tnum;
+	switch(c)
+	{
+
+	case EOF:
+		peekc = EOF;
+		return -1;
+
+	case '_':
+		cp = symb;
+		goto talph;
+
+	case '#':
+		domacro();
+		goto l0;
+
+	case '.':
+		c1 = GETC();
+		if(isdigit(c1)) {
+			cp = symb;
+			*cp++ = c;
+			c = c1;
+			c1 = 0;
+			goto casedot;
+		}
+		break;
+
+	case '"':
+		strcpy(symb, "\"<string>\"");
+		cp = alloc(0);
+		c1 = 0;
+
+		/* "..." */
+		for(;;) {
+			c = escchar('"', 0, 1);
+			if(c == EOF)
+				break;
+			if(c & ESC) {
+				cp = allocn(cp, c1, 1);
+				cp[c1++] = c;
+			} else {
+				rune = c;
+				c = runelen(rune);
+				cp = allocn(cp, c1, c);
+				runetochar(cp+c1, &rune);
+				c1 += c;
+			}
+		}
+		yylval.sval.l = c1;
+		do {
+			cp = allocn(cp, c1, 1);
+			cp[c1++] = 0;
+		} while(c1 & MAXALIGN);
+		yylval.sval.s = cp;
+		return LSTRING;
+
+	caselq:
+		/* L"..." */
+		strcpy(symb, "\"L<string>\"");
+		cp = alloc(0);
+		c1 = 0;
+		for(;;) {
+			c = escchar('"', 1, 0);
+			if(c == EOF)
+				break;
+			cp = allocn(cp, c1, sizeof(TRune));
+			*(TRune*)(cp + c1) = c;
+			c1 += sizeof(TRune);
+		}
+		yylval.sval.l = c1;
+		do {
+			cp = allocn(cp, c1, sizeof(TRune));
+			*(TRune*)(cp + c1) = 0;
+			c1 += sizeof(TRune);
+		} while(c1 & MAXALIGN);
+		yylval.sval.s = cp;
+		return LLSTRING;
+
+	case '\'':
+		/* '.' */
+		c = escchar('\'', 0, 0);
+		if(c == EOF)
+			c = '\'';
+		c1 = escchar('\'', 0, 0);
+		if(c1 != EOF) {
+			yyerror("missing '");
+			peekc = c1;
+		}
+		vv = c;
+		yylval.vval = convvtox(vv, TUCHAR);
+		if(yylval.vval != vv)
+			yyerror("overflow in character constant: 0x%lx", c);
+		else
+		if(c & 0x80){
+			nearln = lineno;
+			warn(Z, "sign-extended character constant");
+		}
+		yylval.vval = convvtox(vv, TCHAR);
+		return LCONST;
+
+	case '/':
+		c1 = GETC();
+		if(c1 == '*') {
+			for(;;) {
+				c = getr();
+				while(c == '*') {
+					c = getr();
+					if(c == '/')
+						goto l0;
+				}
+				if(c == EOF) {
+					yyerror("eof in comment");
+					errorexit();
+				}
+			}
+		}
+		if(c1 == '/') {
+			for(;;) {
+				c = getr();
+				if(c == '\n')
+					goto l0;
+				if(c == EOF) {
+					yyerror("eof in comment");
+					errorexit();
+				}
+			}
+		}
+		if(c1 == '=')
+			return LDVE;
+		break;
+
+	case '*':
+		c1 = GETC();
+		if(c1 == '=')
+			return LMLE;
+		break;
+
+	case '%':
+		c1 = GETC();
+		if(c1 == '=')
+			return LMDE;
+		break;
+
+	case '+':
+		c1 = GETC();
+		if(c1 == '+')
+			return LPP;
+		if(c1 == '=')
+			return LPE;
+		break;
+
+	case '-':
+		c1 = GETC();
+		if(c1 == '-')
+			return LMM;
+		if(c1 == '=')
+			return LME;
+		if(c1 == '>')
+			return LMG;
+		break;
+
+	case '>':
+		c1 = GETC();
+		if(c1 == '>') {
+			c = LRSH;
+			c1 = GETC();
+			if(c1 == '=')
+				return LRSHE;
+			break;
+		}
+		if(c1 == '=')
+			return LGE;
+		break;
+
+	case '<':
+		c1 = GETC();
+		if(c1 == '<') {
+			c = LLSH;
+			c1 = GETC();
+			if(c1 == '=')
+				return LLSHE;
+			break;
+		}
+		if(c1 == '=')
+			return LLE;
+		break;
+
+	case '=':
+		c1 = GETC();
+		if(c1 == '=')
+			return LEQ;
+		break;
+
+	case '!':
+		c1 = GETC();
+		if(c1 == '=')
+			return LNE;
+		break;
+
+	case '&':
+		c1 = GETC();
+		if(c1 == '&')
+			return LANDAND;
+		if(c1 == '=')
+			return LANDE;
+		break;
+
+	case '|':
+		c1 = GETC();
+		if(c1 == '|')
+			return LOROR;
+		if(c1 == '=')
+			return LORE;
+		break;
+
+	case '^':
+		c1 = GETC();
+		if(c1 == '=')
+			return LXORE;
+		break;
+
+	default:
+		return c;
+	}
+	peekc = c1;
+	return c;
+
+talph:
+	/*
+	 * cp is set to symb and some
+	 * prefix has been stored
+	 */
+	for(;;) {
+		if(c >= Runeself) {
+			for(c1=0;;) {
+				cp[c1++] = c;
+				if(fullrune(cp, c1))
+					break;
+				c = GETC();
+			}
+			cp += c1;
+			c = GETC();
+			continue;
+		}
+		if(!isalnum(c) && c != '_')
+			break;
+		*cp++ = c;
+		c = GETC();
+	}
+	*cp = 0;
+	if(debug['L'])
+		print("%L: %s\n", lineno, symb);
+	peekc = c;
+	s = lookup();
+	if(s->macro) {
+		newio();
+		cp = ionext->b;
+		macexpand(s, cp);
+		pushio();
+		ionext->link = iostack;
+		iostack = ionext;
+		fi.p = cp;
+		fi.c = strlen(cp);
+		if(peekc != IGN) {
+			cp[fi.c++] = peekc;
+			cp[fi.c] = 0;
+			peekc = IGN;
+		}
+		goto l0;
+	}
+	yylval.sym = s;
+	if(s->class == CTYPEDEF || s->class == CTYPESTR)
+		return LTYPE;
+	return s->lexical;
+
+tnum:
+	c1 = 0;
+	cp = symb;
+	if(c != '0') {
+		c1 |= Numdec;
+		for(;;) {
+			*cp++ = c;
+			c = GETC();
+			if(isdigit(c))
+				continue;
+			goto dc;
+		}
+	}
+	*cp++ = c;
+	c = GETC();
+	if(c == 'x' || c == 'X')
+		for(;;) {
+			*cp++ = c;
+			c = GETC();
+			if(isdigit(c))
+				continue;
+			if(c >= 'a' && c <= 'f')
+				continue;
+			if(c >= 'A' && c <= 'F')
+				continue;
+			if(cp == symb+2)
+				yyerror("malformed hex constant");
+			goto ncu;
+		}
+	if(c < '0' || c > '7')
+		goto dc;
+	for(;;) {
+		if(c >= '0' && c <= '7') {
+			*cp++ = c;
+			c = GETC();
+			continue;
+		}
+		goto ncu;
+	}
+
+dc:
+	if(c == '.')
+		goto casedot;
+	if(c == 'e' || c == 'E')
+		goto casee;
+
+ncu:
+	if((c == 'U' || c == 'u') && !(c1 & Numuns)) {
+		c = GETC();
+		c1 |= Numuns;
+		goto ncu;
+	}
+	if((c == 'L' || c == 'l') && !(c1 & Numvlong)) {
+		c = GETC();
+		if(c1 & Numlong)
+			c1 |= Numvlong;
+		c1 |= Numlong;
+		goto ncu;
+	}
+	*cp = 0;
+	peekc = c;
+	if(mpatov(symb, &yylval.vval))
+		yyerror("overflow in constant");
+
+	vv = yylval.vval;
+	if(c1 & Numvlong) {
+		if((c1 & Numuns) || convvtox(vv, TVLONG) < 0) {
+			c = LUVLCONST;
+			t = TUVLONG;
+			goto nret;
+		}
+		c = LVLCONST;
+		t = TVLONG;
+		goto nret;
+	}
+	if(c1 & Numlong) {
+		if((c1 & Numuns) || convvtox(vv, TLONG) < 0) {
+			c = LULCONST;
+			t = TULONG;
+			goto nret;
+		}
+		c = LLCONST;
+		t = TLONG;
+		goto nret;
+	}
+	if((c1 & Numuns) || convvtox(vv, TINT) < 0) {
+		c = LUCONST;
+		t = TUINT;
+		goto nret;
+	}
+	c = LCONST;
+	t = TINT;
+	goto nret;
+
+nret:
+	yylval.vval = convvtox(vv, t);
+	if(yylval.vval != vv){
+		nearln = lineno;
+		warn(Z, "truncated constant: %T %s", types[t], symb);
+	}
+	return c;
+
+casedot:
+	for(;;) {
+		*cp++ = c;
+		c = GETC();
+		if(!isdigit(c))
+			break;
+	}
+	if(c != 'e' && c != 'E')
+		goto caseout;
+
+casee:
+	*cp++ = 'e';
+	c = GETC();
+	if(c == '+' || c == '-') {
+		*cp++ = c;
+		c = GETC();
+	}
+	if(!isdigit(c))
+		yyerror("malformed fp constant exponent");
+	while(isdigit(c)) {
+		*cp++ = c;
+		c = GETC();
+	}
+
+caseout:
+	if(c == 'L' || c == 'l') {
+		c = GETC();
+		c1 |= Numlong;
+	} else
+	if(c == 'F' || c == 'f') {
+		c = GETC();
+		c1 |= Numflt;
+	}
+	*cp = 0;
+	peekc = c;
+	yylval.dval = strtod(symb, nil);
+	if(isInf(yylval.dval, 1) || isInf(yylval.dval, -1)) {
+		yyerror("overflow in float constant");
+		yylval.dval = 0;
+	}
+	if(c1 & Numflt)
+		return LFCONST;
+	return LDCONST;
+}
+
+/*
+ * convert a string, s, to vlong in *v
+ * return conversion overflow.
+ * required syntax is [0[x]]d*
+ */
+int
+mpatov(char *s, vlong *v)
+{
+	vlong n, nn;
+	int c;
+
+	n = 0;
+	c = *s;
+	if(c == '0')
+		goto oct;
+	while(c = *s++) {
+		if(c >= '0' && c <= '9')
+			nn = n*10 + c-'0';
+		else
+			goto bad;
+		if(n < 0 && nn >= 0)
+			goto bad;
+		n = nn;
+	}
+	goto out;
+
+oct:
+	s++;
+	c = *s;
+	if(c == 'x' || c == 'X')
+		goto hex;
+	while(c = *s++) {
+		if(c >= '0' || c <= '7')
+			nn = n*8 + c-'0';
+		else
+			goto bad;
+		if(n < 0 && nn >= 0)
+			goto bad;
+		n = nn;
+	}
+	goto out;
+
+hex:
+	s++;
+	while(c = *s++) {
+		if(c >= '0' && c <= '9')
+			c += 0-'0';
+		else
+		if(c >= 'a' && c <= 'f')
+			c += 10-'a';
+		else
+		if(c >= 'A' && c <= 'F')
+			c += 10-'A';
+		else
+			goto bad;
+		nn = n*16 + c;
+		if(n < 0 && nn >= 0)
+			goto bad;
+		n = nn;
+	}
+out:
+	*v = n;
+	return 0;
+
+bad:
+	*v = ~0;
+	return 1;
+}
+
+int
+getc(void)
+{
+	int c;
+
+	if(peekc != IGN) {
+		c = peekc;
+		peekc = IGN;
+	} else
+		c = GETC();
+	if(c == '\n')
+		lineno++;
+	if(c == EOF) {
+		yyerror("End of file");
+		errorexit();
+	}
+	return c;
+}
+
+long
+getr(void)
+{
+	int c, i;
+	char str[UTFmax+1];
+	Rune rune;
+
+
+	c = getc();
+	if(c < Runeself)
+		return c;
+	i = 0;
+	str[i++] = c;
+
+loop:
+	c = getc();
+	str[i++] = c;
+	if(!fullrune(str, i))
+		goto loop;
+	c = chartorune(&rune, str);
+	if(rune == Runeerror && c == 1) {
+		nearln = lineno;
+		diag(Z, "illegal rune in string");
+		for(c=0; c<i; c++)
+			print(" %.2x", *(uchar*)(str+c));
+		print("\n");
+	}
+	return rune;
+}
+
+int
+getnsc(void)
+{
+	int c;
+
+	if(peekc != IGN) {
+		c = peekc;
+		peekc = IGN;
+	} else
+		c = GETC();
+	for(;;) {
+		if(c >= Runeself || !isspace(c))
+			return c;
+		if(c == '\n') {
+			lineno++;
+			return c;
+		}
+		c = GETC();
+	}
+}
+
+void
+unget(int c)
+{
+
+	peekc = c;
+	if(c == '\n')
+		lineno--;
+}
+
+long
+escchar(long e, int longflg, int escflg)
+{
+	long c, l;
+	int i;
+
+loop:
+	c = getr();
+	if(c == '\n') {
+		yyerror("newline in string");
+		return EOF;
+	}
+	if(c != '\\') {
+		if(c == e)
+			c = EOF;
+		return c;
+	}
+	c = getr();
+	if(c == 'x') {
+		/*
+		 * note this is not ansi,
+		 * supposed to only accept 2 hex
+		 */
+		i = 2;
+		if(longflg)
+			i = 6;
+		l = 0;
+		for(; i>0; i--) {
+			c = getc();
+			if(c >= '0' && c <= '9') {
+				l = l*16 + c-'0';
+				continue;
+			}
+			if(c >= 'a' && c <= 'f') {
+				l = l*16 + c-'a' + 10;
+				continue;
+			}
+			if(c >= 'A' && c <= 'F') {
+				l = l*16 + c-'A' + 10;
+				continue;
+			}
+			unget(c);
+			break;
+		}
+		if(escflg)
+			l |= ESC;
+		return l;
+	}
+	if(c >= '0' && c <= '7') {
+		/*
+		 * note this is not ansi,
+		 * supposed to only accept 3 oct
+		 */
+		i = 2;
+		if(longflg)
+			i = 8;
+		l = c - '0';
+		for(; i>0; i--) {
+			c = getc();
+			if(c >= '0' && c <= '7') {
+				l = l*8 + c-'0';
+				continue;
+			}
+			unget(c);
+		}
+		if(escflg)
+			l |= ESC;
+		return l;
+	}
+	switch(c)
+	{
+	case '\n':	goto loop;
+	case 'n':	return '\n';
+	case 't':	return '\t';
+	case 'b':	return '\b';
+	case 'r':	return '\r';
+	case 'f':	return '\f';
+	case 'a':	return '\a';
+	case 'v':	return '\v';
+	}
+	return c;
+}
+
+struct
+{
+	char	*name;
+	ushort	lexical;
+	ushort	type;
+} itab[] =
+{
+	"auto",		LAUTO,		0,
+	"break",	LBREAK,		0,
+	"case",		LCASE,		0,
+	"char",		LCHAR,		TCHAR,
+	"const",	LCONSTNT,	0,
+	"continue",	LCONTINUE,	0,
+	"default",	LDEFAULT,	0,
+	"do",		LDO,		0,
+	"double",	LDOUBLE,	TDOUBLE,
+	"else",		LELSE,		0,
+	"enum",		LENUM,		0,
+	"extern",	LEXTERN,	0,
+	"float",	LFLOAT,		TFLOAT,
+	"for",		LFOR,		0,
+	"goto",		LGOTO,		0,
+	"if",		LIF,		0,
+	"inline",	LINLINE,	0,
+	"int",		LINT,		TINT,
+	"long",		LLONG,		TLONG,
+	"register",	LREGISTER,	0,
+	"restrict",	LRESTRICT,	0,
+	"return",	LRETURN,	0,
+	"SET",		LSET,		0,
+	"short",	LSHORT,		TSHORT,
+	"signed",	LSIGNED,	0,
+	"signof",	LSIGNOF,	0,
+	"sizeof",	LSIZEOF,	0,
+	"static",	LSTATIC,	0,
+	"struct",	LSTRUCT,	0,
+	"switch",	LSWITCH,	0,
+	"typedef",	LTYPEDEF,	0,
+	"typestr",	LTYPESTR,	0,
+	"union",	LUNION,		0,
+	"unsigned",	LUNSIGNED,	0,
+	"USED",		LUSED,		0,
+	"void",		LVOID,		TVOID,
+	"volatile",	LVOLATILE,	0,
+	"while",	LWHILE,		0,
+	0
+};
+
+void
+cinit(void)
+{
+	Sym *s;
+	int i;
+	Type *t;
+
+	nerrors = 0;
+	lineno = 1;
+	iostack = I;
+	iofree = I;
+	peekc = IGN;
+	nhunk = 0;
+
+	types[TXXX] = T;
+	types[TCHAR] = typ(TCHAR, T);
+	types[TUCHAR] = typ(TUCHAR, T);
+	types[TSHORT] = typ(TSHORT, T);
+	types[TUSHORT] = typ(TUSHORT, T);
+	types[TINT] = typ(TINT, T);
+	types[TUINT] = typ(TUINT, T);
+	types[TLONG] = typ(TLONG, T);
+	types[TULONG] = typ(TULONG, T);
+	types[TVLONG] = typ(TVLONG, T);
+	types[TUVLONG] = typ(TUVLONG, T);
+	types[TFLOAT] = typ(TFLOAT, T);
+	types[TDOUBLE] = typ(TDOUBLE, T);
+	types[TVOID] = typ(TVOID, T);
+	types[TENUM] = typ(TENUM, T);
+	types[TFUNC] = typ(TFUNC, types[TINT]);
+	types[TIND] = typ(TIND, types[TVOID]);
+
+	for(i=0; i<NHASH; i++)
+		hash[i] = S;
+	for(i=0; itab[i].name; i++) {
+		s = slookup(itab[i].name);
+		s->lexical = itab[i].lexical;
+		if(itab[i].type != 0)
+			s->type = types[itab[i].type];
+	}
+	blockno = 0;
+	autobn = 0;
+	autoffset = 0;
+
+	t = typ(TARRAY, types[TCHAR]);
+	t->width = 0;
+	symstring = slookup(".string");
+	symstring->class = CSTATIC;
+	symstring->type = t;
+
+	t = typ(TARRAY, types[TCHAR]);
+	t->width = 0;
+
+	nodproto = new(OPROTO, Z, Z);
+	dclstack = D;
+
+	pathname = allocn(pathname, 0, 100);
+	if(mygetwd(pathname, 99) == 0) {
+		pathname = allocn(pathname, 100, 900);
+		if(mygetwd(pathname, 999) == 0)
+			strcpy(pathname, "/???");
+	}
+
+	fmtinstall('O', Oconv);
+	fmtinstall('T', Tconv);
+	fmtinstall('F', FNconv);
+	fmtinstall('L', Lconv);
+	fmtinstall('Q', Qconv);
+	fmtinstall('|', VBconv);
+}
+
+int
+filbuf(void)
+{
+	Io *i;
+
+loop:
+	i = iostack;
+	if(i == I)
+		return EOF;
+	if(i->f < 0)
+		goto pop;
+	fi.c = read(i->f, i->b, BUFSIZ) - 1;
+	if(fi.c < 0) {
+		close(i->f);
+		linehist(0, 0);
+		goto pop;
+	}
+	fi.p = i->b + 1;
+	return i->b[0] & 0xff;
+
+pop:
+	iostack = i->link;
+	i->link = iofree;
+	iofree = i;
+	i = iostack;
+	if(i == I)
+		return EOF;
+	fi.p = i->p;
+	fi.c = i->c;
+	if(--fi.c < 0)
+		goto loop;
+	return *fi.p++ & 0xff;
+}
+
+int
+Oconv(Fmt *fp)
+{
+	int a;
+
+	a = va_arg(fp->args, int);
+	if(a < OXXX || a > OEND)
+		return fmtprint(fp, "***badO %d***", a);
+
+	return fmtstrcpy(fp, onames[a]);
+}
+
+int
+Lconv(Fmt *fp)
+{
+	char str[STRINGSZ], s[STRINGSZ];
+	Hist *h;
+	struct
+	{
+		Hist*	incl;	/* start of this include file */
+		long	idel;	/* delta line number to apply to include */
+		Hist*	line;	/* start of this #line directive */
+		long	ldel;	/* delta line number to apply to #line */
+	} a[HISTSZ];
+	long l, d;
+	int i, n;
+
+	l = va_arg(fp->args, long);
+	n = 0;
+	for(h = hist; h != H; h = h->link) {
+		if(l < h->line)
+			break;
+		if(h->name) {
+			if(h->offset != 0) {		/* #line directive, not #pragma */
+				if(n > 0 && n < HISTSZ && h->offset >= 0) {
+					a[n-1].line = h;
+					a[n-1].ldel = h->line - h->offset + 1;
+				}
+			} else {
+				if(n < HISTSZ) {	/* beginning of file */
+					a[n].incl = h;
+					a[n].idel = h->line;
+					a[n].line = 0;
+				}
+				n++;
+			}
+			continue;
+		}
+		n--;
+		if(n > 0 && n < HISTSZ) {
+			d = h->line - a[n].incl->line;
+			a[n-1].ldel += d;
+			a[n-1].idel += d;
+		}
+	}
+	if(n > HISTSZ)
+		n = HISTSZ;
+	str[0] = 0;
+	for(i=n-1; i>=0; i--) {
+		if(i != n-1) {
+			if(fp->flags & ~(FmtWidth|FmtPrec))	/* BUG ROB - was f3 */
+				break;
+			strcat(str, " ");
+		}
+		if(a[i].line)
+			snprint(s, STRINGSZ, "%s:%ld[%s:%ld]",
+				a[i].line->name, l-a[i].ldel+1,
+				a[i].incl->name, l-a[i].idel+1);
+		else
+			snprint(s, STRINGSZ, "%s:%ld",
+				a[i].incl->name, l-a[i].idel+1);
+		if(strlen(s)+strlen(str) >= STRINGSZ-10)
+			break;
+		strcat(str, s);
+		l = a[i].incl->line - 1;	/* now print out start of this file */
+	}
+	if(n == 0)
+		strcat(str, "<eof>");
+	return fmtstrcpy(fp, str);
+}
+
+int
+Tconv(Fmt *fp)
+{
+	char str[STRINGSZ+20], s[STRINGSZ+20];
+	Type *t, *t1;
+	int et;
+	long n;
+
+	str[0] = 0;
+	for(t = va_arg(fp->args, Type*); t != T; t = t->link) {
+		et = t->etype;
+		if(str[0])
+			strcat(str, " ");
+		if(t->garb&~GINCOMPLETE) {
+			sprint(s, "%s ", gnames[t->garb&~GINCOMPLETE]);
+			if(strlen(str) + strlen(s) < STRINGSZ)
+				strcat(str, s);
+		}
+		sprint(s, "%s", tnames[et]);
+		if(strlen(str) + strlen(s) < STRINGSZ)
+			strcat(str, s);
+		if(et == TFUNC && (t1 = t->down)) {
+			sprint(s, "(%T", t1);
+			if(strlen(str) + strlen(s) < STRINGSZ)
+				strcat(str, s);
+			while(t1 = t1->down) {
+				sprint(s, ", %T", t1);
+				if(strlen(str) + strlen(s) < STRINGSZ)
+					strcat(str, s);
+			}
+			if(strlen(str) + strlen(s) < STRINGSZ)
+				strcat(str, ")");
+		}
+		if(et == TARRAY) {
+			n = t->width;
+			if(t->link && t->link->width)
+				n /= t->link->width;
+			sprint(s, "[%ld]", n);
+			if(strlen(str) + strlen(s) < STRINGSZ)
+				strcat(str, s);
+		}
+		if(t->nbits) {
+			sprint(s, " %d:%d", t->shift, t->nbits);
+			if(strlen(str) + strlen(s) < STRINGSZ)
+				strcat(str, s);
+		}
+		if(typesu[et]) {
+			if(t->tag) {
+				strcat(str, " ");
+				if(strlen(str) + strlen(t->tag->name) < STRINGSZ)
+					strcat(str, t->tag->name);
+			} else
+				strcat(str, " {}");
+			break;
+		}
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+FNconv(Fmt *fp)
+{
+	char *str;
+	Node *n;
+
+	n = va_arg(fp->args, Node*);
+	str = "<indirect>";
+	if(n != Z && (n->op == ONAME || n->op == ODOT || n->op == OELEM))
+		str = n->sym->name;
+	return fmtstrcpy(fp, str);
+}
+
+int
+Qconv(Fmt *fp)
+{
+	char str[STRINGSZ+20], *s;
+	long b;
+	int i;
+
+	str[0] = 0;
+	for(b = va_arg(fp->args, long); b;) {
+		i = bitno(b);
+		if(str[0])
+			strcat(str, " ");
+		s = qnames[i];
+		if(strlen(str) + strlen(s) >= STRINGSZ)
+			break;
+		strcat(str, s);
+		b &= ~(1L << i);
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+VBconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	int i, n, t, pc;
+
+	n = va_arg(fp->args, int);
+	pc = 0;	/* BUG: was printcol */
+	i = 0;
+	while(pc < n) {
+		t = (pc+4) & ~3;
+		if(t <= n) {
+			str[i++] = '\t';
+			pc = t;
+			continue;
+		}
+		str[i++] = ' ';
+		pc++;
+	}
+	str[i] = 0;
+
+	return fmtstrcpy(fp, str);
+}
+
+/*
+ * real allocs
+ */
+void*
+alloc(long n)
+{
+	void *p;
+
+	while((uintptr)hunk & MAXALIGN) {
+		hunk++;
+		nhunk--;
+	}
+	while(nhunk < n)
+		gethunk();
+	p = hunk;
+	nhunk -= n;
+	hunk += n;
+	return p;
+}
+
+void*
+allocn(void *p, long on, long n)
+{
+	void *q;
+
+	q = (uchar*)p + on;
+	if(q != hunk || nhunk < n) {
+		while(nhunk < on+n)
+			gethunk();
+		memmove(hunk, p, on);
+		p = hunk;
+		hunk += on;
+		nhunk -= on;
+	}
+	hunk += n;
+	nhunk -= n;
+	return p;
+}
+
+void
+setinclude(char *p)
+{
+	int i;
+	char *e, **np;
+
+	while(*p != 0) {
+		e = strchr(p, ' ');
+		if(e != 0)
+			*e = '\0';
+
+		for(i=0; i < ninclude; i++)
+			if(strcmp(p, include[i]) == 0)
+				break;
+
+		if(i >= ninclude){
+			if(i >= maxinclude){
+				maxinclude += 20;
+				np = alloc(maxinclude * sizeof *np);
+				if(include != nil)
+					memmove(np, include, (maxinclude - 20) * sizeof *np);
+				include = np;
+			}
+			include[ninclude++] = p;
+		}
+
+		if(e == 0)
+			break;
+		p = e+1;
+	}
+}
--- /dev/null
+++ b/sys/src/cmd/zc/list.c
@@ -1,0 +1,236 @@
+#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);
+}
+
+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) {
+			sprint(ss, "$%ld", 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);
+}
+
+int
+Pconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Prog *p;
+	int a;
+
+	p = va_arg(fp->args, Prog*);
+	a = p->as;
+	if(a == ADATA)
+		sprint(str, "	%A	%D/%d,%D", a, &p->from, p->reg, &p->to);
+	else
+	if(p->as == ATEXT)
+		sprint(str, "	%A	%D,%d,%D", a, &p->from, p->reg, &p->to);
+	else
+	if(p->reg == NREG)
+		sprint(str, "	%A	%D,%D", a, &p->from, &p->to);
+	else
+	if(p->from.type != D_FREG)
+		sprint(str, "	%A	%D,R%d,%D", a, &p->from, p->reg, &p->to);
+	else
+		sprint(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;
+
+	a = va_arg(fp->args, Adr*);
+	switch(a->type) {
+
+	default:
+		sprint(str, "GOK-type(%d)", a->type);
+		break;
+
+	case D_NONE:
+		str[0] = 0;
+		if(a->name != D_NONE || a->reg != NREG || a->sym != S)
+			sprint(str, "%N(R%d)(NONE)", a, a->reg);
+		break;
+
+	case D_CONST:
+		if(a->reg != NREG)
+			sprint(str, "$%N(R%d)", a, a->reg);
+		else
+			sprint(str, "$%N", a);
+		break;
+
+	case D_OREG:
+		if(a->reg != NREG)
+			sprint(str, "%N(R%d)", a, a->reg);
+		else
+			sprint(str, "%N", a);
+		break;
+
+	case D_REG:
+		sprint(str, "R%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_FCCREG:
+		sprint(str, "FCC%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_FCSREG:
+		sprint(str, "FCSR%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_BRANCH:
+		sprint(str, "%lld(PC)", a->offset-pc);
+		break;
+
+	case D_FCONST:
+		sprint(str, "$%.17e", a->dval);
+		break;
+
+	case D_SCONST:
+		sprint(str, "$\"%S\"", a->sval);
+		break;
+
+	case D_VCONST:
+		sprint(str, "$0x%llux", a->offset);
+		break;
+	}
+	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) {
+		sprint(str, "%lld", a->offset);
+		goto out;
+	}
+	switch(a->name) {
+	default:
+		sprint(str, "GOK-name(%d)", a->name);
+		break;
+
+	case D_NONE:
+		sprint(str, "%lld", a->offset);
+		break;
+
+	case D_EXTERN:
+		sprint(str, "%s+%lld(SB)", s->name, a->offset);
+		break;
+
+	case D_STATIC:
+		sprint(str, "%s<>+%lld(SB)", s->name, a->offset);
+		break;
+
+	case D_AUTO:
+		sprint(str, "%s-%lld(SP)", s->name, -a->offset);
+		break;
+
+	case D_PARAM:
+		sprint(str, "%s+%lld(FP)", s->name, a->offset);
+		break;
+	}
+out:
+	return fmtstrcpy(fp, str);
+}
--- /dev/null
+++ b/sys/src/cmd/zc/machcap.c
@@ -1,0 +1,77 @@
+#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 OCOM:
+	case ONEG:
+		break;
+	}
+	return 0;
+}
--- /dev/null
+++ b/sys/src/cmd/zc/mkenam
@@ -1,0 +1,15 @@
+ed - ../zc/z.out.h <<'!'
+v/^	A/d
+,s/^	A/	"/
+g/ .*$/s///
+,s/,*$/",/
+1i
+char*	anames[] =
+{
+.
+$a
+};
+.
+w enam.c
+Q
+!
--- /dev/null
+++ b/sys/src/cmd/zc/mkfile
@@ -1,0 +1,42 @@
+</$objtype/mkfile
+
+TARG=zc
+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\
+	z.out.h\
+	../cc/compat.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:	z.out.h
+	rc mkenam
--- /dev/null
+++ b/sys/src/cmd/zc/mul.c
@@ -1,0 +1,608 @@
+#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	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<=6; g++) {
+		if(g >= 6 && 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/zc/peep.c
@@ -1,0 +1,739 @@
+#include "gc.h"
+
+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 == AMOVW ||*/ p->as == AMOVV || p->as == AMOVF || p->as == AMOVD)
+		if(regtyp(&p->to)) {
+			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 = 0;
+				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 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);
+	}
+}
+
+void
+excise(Reg *r)
+{
+	Prog *p;
+
+	p = r->prog;
+	p->as = ANOP;
+	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;
+}
+
+int
+regzer(Adr *a)
+{
+
+	if(a->type == D_CONST)
+		if(a->sym == S)
+			if(a->offset == 0)
+				return 1;
+	if(a->type == D_REG)
+		if(a->reg == 0)
+			return 1;
+	return 0;
+}
+
+int
+regtyp(Adr *a)
+{
+
+	if(a->type == D_REG) {
+		if(a->reg != 0)
+			return 1;
+		return 0;
+	}
+	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 AJAL:
+			return 0;
+
+		case ASGT:
+		case ASGTU:
+
+		case AADD:
+		case AADDU:
+		case ASUB:
+		case ASUBU:
+		case ASLL:
+		case ASRL:
+		case ASRA:
+		case AOR:
+		case AAND:
+		case AXOR:
+		case AMUL:
+		case AMULU:
+		case ADIV:
+		case ADIVU:
+
+		case AADDV:
+		case AADDVU:
+		case ASUBV:
+		case ASUBVU:
+		case ASLLV:
+		case ASRLV:
+		case ASRAV:
+		case AMULV:
+		case AMULVU:
+		case ADIVV:
+		case ADIVVU:
+
+		case AADDD:
+		case AADDF:
+		case ASUBD:
+		case ASUBF:
+		case AMULD:
+		case AMULF:
+		case ADIVD:
+		case ADIVF:
+			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 AMOVF:
+		case AMOVD:
+		case AMOVW:
+		case AMOVV:
+			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;
+}
+
+/*
+ * 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)
+ */
+copyu(Prog *p, Adr *v, Adr *s)
+{
+
+	switch(p->as) {
+
+	default:
+		if(debug['P'])
+			print(" (???)");
+		return 2;
+
+
+	case ANOP:	/* read, write */
+	case AMOVW:
+	case AMOVV:
+	case AMOVF:
+	case AMOVD:
+	case AMOVH:
+	case AMOVHU:
+	case AMOVB:
+	case AMOVBU:
+	case AMOVFD:
+	case AMOVDF:
+	case AMOVDW:
+	case AMOVWD:
+	case AMOVFW:
+	case AMOVWF:
+	case AMOVDV:
+	case AMOVVD:
+	case AMOVFV:
+	case AMOVVF:
+		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 ASGT:	/* read, read, write */
+	case ASGTU:
+
+	case AADD:
+	case AADDU:
+	case ASUB:
+	case ASUBU:
+	case ASLL:
+	case ASRL:
+	case ASRA:
+	case AOR:
+	case ANOR:
+	case AAND:
+	case AXOR:
+	case AMUL:
+	case AMULU:
+	case ADIV:
+	case ADIVU:
+
+	case AADDV:
+	case AADDVU:
+	case ASUBV:
+	case ASUBVU:
+	case ASLLV:
+	case ASRLV:
+	case ASRAV:
+	case AMULV:
+	case AMULVU:
+	case ADIVV:
+	case ADIVVU:
+
+	case AADDF:
+	case AADDD:
+	case ASUBF:
+	case ASUBD:
+	case AMULF:
+	case AMULD:
+	case ADIVF:
+	case ADIVD:
+		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 ABGTZ:
+	case ABGEZ:
+	case ABLTZ:
+	case ABLEZ:
+
+	case ACMPEQD:
+	case ACMPEQF:
+	case ACMPGED:
+	case ACMPGEF:
+	case ACMPGTD:
+	case ACMPGTF:
+	case ABFPF:
+	case ABFPT:
+		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 AJMP:	/* 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:	/* funny */
+		if(v->type == D_REG)
+		if(v->reg == REGRET)
+			return 2;
+		if(v->type == D_FREG)
+		if(v->reg == FREGRET)
+			return 2;
+
+	case AJAL:	/* funny */
+		if(v->type == D_REG) {
+			if(v->reg <= REGEXT && v->reg > exregoffset)
+				return 2;
+			if(REGARG && 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 ABEQ:
+	case ABNE:
+	case ABGTZ:
+	case ABGEZ:
+	case ABLTZ:
+	case ABLEZ:
+
+	case ASGT:
+	case ASGTU:
+
+	case AADD:
+	case AADDU:
+	case ASUB:
+	case ASUBU:
+	case ASLL:
+	case ASRL:
+	case ASRA:
+	case AOR:
+	case AAND:
+	case AXOR:
+	case AMUL:
+	case AMULU:
+	case ADIV:
+	case ADIVU:
+
+	case AADDV:
+	case AADDVU:
+	case ASUBV:
+	case ASUBVU:
+	case ASLLV:
+	case ASRLV:
+	case ASRAV:
+	case AMULV:
+	case AMULVU:
+	case ADIVV:
+	case ADIVVU:
+		return D_REG;
+
+	case ACMPEQD:
+	case ACMPEQF:
+	case ACMPGED:
+	case ACMPGEF:
+	case ACMPGTD:
+	case ACMPGTF:
+
+	case AADDF:
+	case AADDD:
+	case ASUBF:
+	case ASUBD:
+	case AMULF:
+	case AMULD:
+	case ADIVF:
+	case ADIVD:
+		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;
+	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;
+	return 0;
+}
+
+int
+copyau1(Prog *p, Adr *v)
+{
+
+	if(regtyp(v))
+		if(p->from.type == v->type || p->to.type == 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))
+		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;
+}
--- /dev/null
+++ b/sys/src/cmd/zc/reg
@@ -1,0 +1,213 @@
+cgen.c:1126: 	p->as = ABGTZ;
+lex.c:1290: 	a = va_arg(fp->args, int);
+lex.c:1312: 	l = va_arg(fp->args, long);
+lex.c:1345: 			if(fp->flags & ~(FmtWidth|FmtPrec))	/* BUG ROB - was f3 */
+lex.c:1375: 	for(t = va_arg(fp->args, Type*); t != T; t = t->link) {
+lex.c:1431: 	n = va_arg(fp->args, Node*);
+lex.c:1446: 	for(b = va_arg(fp->args, long); b;) {
+lex.c:1465: 	n = va_arg(fp->args, int);
+list.c:23: 	bits = va_arg(fp->args, Bits);
+list.c:48: 	p = va_arg(fp->args, Prog*);
+list.c:49: 	a = p->as;
+list.c:51: 		sprint(str, "	%A	%D/%d,%D", a, &p->from, p->reg, &p->to);
+list.c:53: 	if(p->as == ATEXT)
+list.c:54: 		sprint(str, "	%A	%D,%d,%D", a, &p->from, p->reg, &p->to);
+list.c:56: 	if(p->reg == NREG)
+list.c:57: 		sprint(str, "	%A	%D,%D", a, &p->from, &p->to);
+list.c:59: 	if(p->from.type != D_FREG)
+list.c:60: 		sprint(str, "	%A	%D,R%d,%D", a, &p->from, p->reg, &p->to);
+list.c:62: 		sprint(str, "	%A	%D,F%d,%D", a, &p->from, p->reg, &p->to);
+list.c:72: 	a = va_arg(fp->args, int);
+list.c:85: 	a = va_arg(fp->args, Adr*);
+list.c:155: 	a = va_arg(fp->args, char*);
+list.c:203: 	a = va_arg(fp->args, Adr*);
+mul.c:606: 	64750,	"b+f+d+p-",
+peep.c:19: 		switch(p->as) {
+peep.c:38: 			p = p->link;
+peep.c:46: 		if(/*p->as == AMOVW ||*/ p->as == AMOVV || p->as == AMOVF || p->as == AMOVD)
+peep.c:47: 		if(regtyp(&p->to)) {
+peep.c:48: 			if(regtyp(&p->from))
+peep.c:49: 			if(p->from.type == p->to.type) {
+peep.c:59: 			if(regzer(&p->from))
+peep.c:60: 			if(p->to.type == D_REG) {
+peep.c:61: 				p->from.type = D_REG;
+peep.c:62: 				p->from.reg = 0;
+peep.c:81: 		switch(p->as) {
+peep.c:90: 			if(p->to.type != D_REG)
+peep.c:98: 		if(p1->as != p->as)
+peep.c:100: 		if(p1->from.type != D_REG || p1->from.reg != p->to.reg)
+peep.c:102: 		if(p1->to.type != D_REG || p1->to.reg != p->to.reg)
+peep.c:114: 	p->as = ANOP;
+peep.c:115: 	p->from = zprog.from;
+peep.c:116: 	p->to = zprog.to;
+peep.c:117: 	p->reg = zprog.reg; /**/
+peep.c:203: 	v1 = &p->from;
+peep.c:206: 	v2 = &p->to;
+peep.c:213: 		switch(p->as) {
+peep.c:255: 			if(p->to.type == v1->type)
+peep.c:256: 			if(p->to.reg == v1->reg) {
+peep.c:257: 				if(p->reg == NREG)
+peep.c:258: 					p->reg = p->to.reg;
+peep.c:267: 			if(p->to.type == v1->type)
+peep.c:268: 			if(p->to.reg == v1->reg)
+peep.c:272: 		if(copyau(&p->from, v2) ||
+peep.c:274: 		   copyau(&p->to, v2))
+peep.c:276: 		if(copysub(&p->from, v1, v2, 0) ||
+peep.c:278: 		   copysub(&p->to, v1, v2, 0))
+peep.c:284: 	copysub(&p->to, v1, v2, 1);
+peep.c:287: 		if(p->from.type == v2->type)
+peep.c:293: 		copysub(&p->from, v1, v2, 1);
+peep.c:295: 		copysub(&p->to, v1, v2, 1);
+peep.c:327: 	v1 = &p->from;
+peep.c:328: 	v2 = &p->to;
+peep.c:424: 	switch(p->as) {
+peep.c:452: 			if(copysub(&p->from, v, s, 1))
+peep.c:454: 			if(!copyas(&p->to, v))
+peep.c:455: 				if(copysub(&p->to, v, s, 1))
+peep.c:459: 		if(copyas(&p->to, v)) {
+peep.c:460: 			if(copyau(&p->from, v))
+peep.c:464: 		if(copyau(&p->from, v))
+peep.c:466: 		if(copyau(&p->to, v))
+peep.c:510: 			if(copysub(&p->from, v, s, 1))
+peep.c:514: 			if(!copyas(&p->to, v))
+peep.c:515: 				if(copysub(&p->to, v, s, 1))
+peep.c:519: 		if(copyas(&p->to, v)) {
+peep.c:520: 			if(p->reg == NREG)
+peep.c:521: 				p->reg = p->to.reg;
+peep.c:522: 			if(copyau(&p->from, v))
+peep.c:528: 		if(copyau(&p->from, v))
+peep.c:532: 		if(copyau(&p->to, v))
+peep.c:552: 			if(copysub(&p->from, v, s, 1))
+peep.c:556: 		if(copyau(&p->from, v))
+peep.c:564: 			if(copysub(&p->to, v, s, 1))
+peep.c:568: 		if(copyau(&p->to, v))
+peep.c:592: 			if(copysub(&p->to, v, s, 1))
+peep.c:596: 		if(copyau(&p->to, v))
+peep.c:612: 	switch(p->as) {
+peep.c:708: 		if(p->from.type == v->type || p->to.type == v->type)
+peep.c:709: 		if(p->reg == v->reg) {
+reg.c:71: 		lp->m = val;
+reg.c:72: 		lp->c = 0;
+reg.c:73: 		lp->p = R;
+reg.c:78: 	for(; p != P; p = p->link) {
+reg.c:79: 		switch(p->as) {
+reg.c:102: 			lp->c--;
+reg.c:103: 			if(lp->c <= 0) {
+reg.c:104: 				lp->c = lp->m;
+reg.c:105: 				if(lp->p != R)
+reg.c:106: 					lp->p->log5 = r;
+reg.c:107: 				lp->p = r;
+reg.c:127: 		bit = mkvar(&p->from, p->as==AMOVW);
+reg.c:134: 		bit = mkvar(&p->to, 0);
+reg.c:136: 		switch(p->as) {
+reg.c:138: 			diag(Z, "reg: unknown asop: %A", p->as);
+reg.c:178: 		if(p->to.type == D_BRANCH) {
+reg.c:179: 			val = p->to.offset - initpc;
+reg.c:192: 				nearln = p->lineno;
+reg.c:197: 				nearln = p->lineno;
+reg.c:208: 		print("\n%L %D\n", p->lineno, &p->from);
+reg.c:332: 			rgp->enter = r;
+reg.c:333: 			rgp->varno = i;
+reg.c:345: 			rgp->cost = change;
+reg.c:364: 		bit = blsh(rgp->varno);
+reg.c:365: 		vreg = paint2(rgp->enter, rgp->varno);
+reg.c:368: 			if(rgp->regno >= NREG)
+reg.c:370: 					rgp->enter->prog->lineno,
+reg.c:371: 					rgp->cost,
+reg.c:372: 					rgp->regno-NREG,
+reg.c:376: 					rgp->enter->prog->lineno,
+reg.c:377: 					rgp->cost,
+reg.c:378: 					rgp->regno,
+reg.c:381: 		if(rgp->regno != 0)
+reg.c:382: 			paint3(rgp->enter, rgp->varno, vreg, rgp->regno);
+reg.c:387: 	 * peep-hole on basic block
+reg.c:404: 		for(; p != p1; p = p->link) {
+reg.c:405: 			switch(p->as) {
+reg.c:431: 		if(p->to.type == D_BRANCH)
+reg.c:432: 			p->to.offset = r->s2->pc;
+reg.c:441: 	for(p = firstr->prog; p != P; p = p->link){
+reg.c:442: 		while(p->link && p->link->as == ANOP)
+reg.c:443: 			p->link = p->link->link;
+reg.c:493: 	p1->link = p->link;
+reg.c:494: 	p->link = p1;
+reg.c:495: 	p1->lineno = p->lineno;
+reg.c:1052: 			addreg(&p->from, rn);
+reg.c:1059: 			addreg(&p->to, rn);
+sgen.c:9: 		p->to.type = D_REG;
+sgen.c:10: 		p->to.reg = REGRET;
+sgen.c:14: 		p->to.type = D_FREG;
+sgen.c:15: 		p->to.reg = FREGRET;
+swt.c:136: 			p->from.offset += nstring - NSNAME;
+swt.c:137: 			p->reg = NSNAME;
+swt.c:138: 			p->to.type = D_SCONST;
+swt.c:139: 			memmove(p->to.sval, string, NSNAME);
+swt.c:247: 		p->from.offset += o;
+swt.c:248: 		p->reg = 4;
+swt.c:253: 		p->from.offset += o + 4;
+swt.c:254: 		p->reg = 4;
+swt.c:258: 	p->from.offset += o;
+swt.c:259: 	p->reg = w;
+swt.c:260: 	if(p->to.type == D_OREG)
+swt.c:261: 		p->to.type = D_CONST;
+swt.c:274: 	bf[0] = p->as;
+swt.c:275: 	bf[1] = p->reg;
+swt.c:276: 	bf[2] = p->lineno;
+swt.c:277: 	bf[3] = p->lineno>>8;
+swt.c:278: 	bf[4] = p->lineno>>16;
+swt.c:279: 	bf[5] = p->lineno>>24;
+swt.c:280: 	bp = zaddr(bf+6, &p->from, sf);
+swt.c:281: 	bp = zaddr(bp, &p->to, st);
+swt.c:282: 	Bwrite(b, bf, bp-bf);
+swt.c:294: 		for(p = firstp; p != P; p = p->link)
+swt.c:295: 			if(p->as != ADATA && p->as != AGLOBL)
+swt.c:297: 		for(p = firstp; p != P; p = p->link) {
+swt.c:299: 			if(p->as != ADATA && p->as != AGLOBL)
+swt.c:309: 	for(p = firstp; p != P; p = p->link) {
+swt.c:312: 		s = p->from.sym;
+swt.c:317: 			t = p->from.name;
+swt.c:332: 		s = p->to.sym;
+swt.c:337: 			t = p->to.name;
+txt.c:123: 	p->as = AEND;
+txt.c:133: 	p->lineno = nearln;
+txt.c:140: 	lastp->link = p;
+txt.c:435: 		p->reg = NREG;
+txt.c:437: 		p->reg = a.reg;
+txt.c:726: 				p->as = ATRUNCFW;
+txt.c:736: 				p->as = ATRUNCFV;
+txt.c:753: 				p->reg = FREGZERO;
+txt.c:768: 				p->reg = FREGZERO;
+txt.c:942: 	p->as = a;
+txt.c:944: 		naddr(f, &p->from);
+txt.c:946: 		naddr(t, &p->to);
+txt.c:1112: 		naddr(f1, &p->from);
+txt.c:1117: 		p->as = a;
+txt.c:1121: 		p->as = AMOVW;
+txt.c:1123: 			p->as = AMOVV;
+txt.c:1127: 		p->from.type = a;
+txt.c:1128: 		naddr(t, &p->to);
+txt.c:1166: 			p->as = a;
+txt.c:1167: 			naddr(f1, &p->from);
+txt.c:1175: 			p->as = a;
+txt.c:1209: 			naddr(f1, &p->from);
+txt.c:1212: 			naddr(f2, &p->from);
+txt.c:1215: 		naddr(&regnode, &p->to);
+txt.c:1216: 		p->to.reg = tmpreg();
+txt.c:1220: 		p->as = a;
+txt.c:1225: 		naddr(&regnode, &p->from);
+txt.c:1226: 		p->from.reg = tmpreg();
+txt.c:1230: 		p->as = a;
+txt.c:1238: 	p->as = a;
+txt.c:1240: 		naddr(f1, &p->from);
+txt.c:1243: 		p->reg = ta.reg;
+txt.c:1245: 			p->reg = REGZERO;
+txt.c:1248: 		naddr(t, &p->to);
+txt.c:1288: 	p->as = a;
+txt.c:1295: 	op->to.offset = pc;
+txt.c:1296: 	op->to.type = D_BRANCH;
+txt.c:1304: 	p->as = a;
+txt.c:1305: 	p->from.type = D_OREG;
+txt.c:1306: 	p->from.sym = s;
+txt.c:1308: 		p->reg = (profileflg ? 0 : NOPROF);
+txt.c:1309: 	p->from.name = D_EXTERN;
+txt.c:1311: 		p->from.name = D_STATIC;
+txt.c:1312: 	naddr(n, &p->to);
--- /dev/null
+++ b/sys/src/cmd/zc/reg.c
@@ -1,0 +1,1150 @@
+#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(const void *a1, const 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 ARET:
+		case AJMP:
+			r->p1 = R;
+			r1->s1 = R;
+		}
+
+		/*
+		 * left side always read
+		 */
+		bit = mkvar(&p->from, p->as==AMOVW);
+		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 AMOVB:
+		case AMOVBU:
+		case AMOVH:
+		case AMOVHU:
+		case AMOVW:
+		case AMOVV:
+		case AMOVF:
+		case AMOVD:
+			for(z=0; z<BITS; z++)
+				r->set.b[z] |= bit.b[z];
+			break;
+
+		/*
+		 * funny
+		 */
+		case AJAL:
+			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)
+			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);
+				if(bany(&r->regdiff))
+					print(" rd=%B", r->regdiff);
+			}
+			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 == AJAL)
+			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 = AMOVV;
+	if(v->etype == TFLOAT)
+		p1->as = AMOVF;
+	if(v->etype == TDOUBLE)
+		p1->as = AMOVD;
+
+	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(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 AJAL:
+			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:
+			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 TVLONG:
+	case TUVLONG:
+	case TIND:
+	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(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(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	R3
+ *	1	R4
+ *	...	...
+ *	19	R22
+ *	20	R23
+ */
+long
+RtoB(int r)
+{
+
+	if(r >= REGMIN || r <= REGMAX)
+		return 1L << (r-REGMIN);
+	return 0;
+}
+
+BtoR(long b)
+{
+	b &= (1 << (1+REGMAX-REGMIN))-1;
+	b &= ~(1 << (REGTMP-REGMIN));
+	if(b == 0)
+		return 0;
+	return bitno(b) + REGMIN;
+}
+
+/*
+ *	bit	reg
+ *	22	F4
+ *	23	F6
+ *	...	...
+ *	31	F22
+ */
+long
+FtoB(int f)
+{
+	if(f < FREGMIN || f >= FREGEXT)
+		return 0;
+	return 1L << (f - FREGMIN + 22);
+}
+
+int
+BtoF(long b)
+{
+	b &= ((1 << (FREGEXT - FREGMIN))-1) << 22;
+	if(b == 0)
+		return 0;
+	return bitno(b) - 22 + FREGMIN;
+}
--- /dev/null
+++ b/sys/src/cmd/zc/sgen.c
@@ -1,0 +1,223 @@
+#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 OASLMUL:
+	case OASMUL:
+		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(l);
+		if(t >= 0) {
+			n->left = r;
+			n->right = l;
+			l = r;
+			r = n->right;
+		}
+		t = vlog(r);
+		if(t >= 0) {
+			n->op = OASHL;
+			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++;
+
+	switch(n->op) {
+	case OFUNC:
+		n->complex = FNX;
+		break;
+
+	case OADD:
+	case OXOR:
+	case OAND:
+	case OOR:
+	case OEQ:
+	case ONE:
+		/*
+		 * immediate operators, make const on right
+		 */
+		if(l->op == OCONST) {
+			n->left = r;
+			n->right = l;
+		}
+		break;
+	}
+}
--- /dev/null
+++ b/sys/src/cmd/zc/swt.c
@@ -1,0 +1,606 @@
+#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;
+	Prog *sp;
+
+	if(nc < 5) {
+		for(i=0; i<nc; i++) {
+			if(debug['K'])
+				print("case = %.8llux\n", q->val);
+			gmove(nodconst(q->val), tn);
+			gopcode(OEQ, n, tn, 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);
+	gmove(nodconst(r->val), tn);
+	gopcode(OLT, tn, n, Z);
+	sp = p;
+	gopcode(OEQ, n, tn, Z);
+	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);
+}
+
+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;
+	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);
+	if(v < 0)
+		gopcode(OSUB, &nod1, nodconst(0), &nod1);
+	regalloc(&nod2, n, Z);
+
+loop:
+	switch(*p) {
+	case 0:
+		regfree(&nod2);
+		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;
+
+	bf[0] = p->as;
+	bf[1] = p->reg;
+	bf[2] = p->lineno;
+	bf[3] = p->lineno>>8;
+	bf[4] = p->lineno>>16;
+	bf[5] = p->lineno>>24;
+	bp = zaddr(bf+6, &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, 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[7];
+	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] = sig;
+		bf[2] = sig>>8;
+		bf[3] = sig>>16;
+		bf[4] = sig>>24;
+		bf[5] = t;
+		bf[6] = s->sym;
+		Bwrite(b, bf, 7);
+		s->sig = SIGDONE;
+	}
+	else{
+		bf[0] = ANAME;
+		bf[1] = t;	/* type */
+		bf[2] = s->sym;	/* sym */
+		Bwrite(b, bf, 3);
+	}
+	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_VCONST;
+	}
+	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_FREG:
+	case D_FCCREG:
+	case D_FCSREG:
+		break;
+
+	case D_OREG:
+	case D_CONST:
+	case D_BRANCH:
+		l = a->offset;
+		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;
+
+	case D_VCONST:
+		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;
+	}
+	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 allign 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 allign of parameter */
+		w = ewidth[t->etype];
+		if(w <= 0 || w >= SZ_VLONG) {
+			w = SZ_VLONG;
+			break;
+		}
+		if(thechar == '4')
+			o += SZ_VLONG - w;	/* big endian adjustment */
+		w = 1;
+		break;
+
+	case Aarg2:	/* width of a parameter */
+		o += t->width;
+		w = SZ_VLONG;
+		break;
+
+	case Aaut3:	/* total allign of automatic */
+		o = align(o, t, Ael1);
+		o = align(o, t, Ael2);
+		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/zc/txt.c
@@ -1,0 +1,1414 @@
+#include "gc.h"
+
+static char	resvreg[nelem(reg)];
+
+void
+ginit(void)
+{
+	Type *t;
+
+	thechar = 'z';
+	thestring = "loong";
+	exregoffset = REGEXT;
+	exfregoffset = FREGEXT;
+	listinit();
+	nstring = 0;
+	mnstring = 0;
+	nrathole = 0;
+	pc = 0;
+	breakpc = -1;
+	continpc = -1;
+	cases = C;
+	firstp = P;
+	lastp = P;
+	tfield = types[TLONG];
+
+	/* 64-bit machine */
+	typeword = typechlvp;
+	typeswitch = typechlv;
+	typecmplx = typesu;
+
+	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];
+
+	constnode.op = OCONST;
+	constnode.class = CXXX;
+	constnode.complex = 0;
+	constnode.addable = 20;
+	constnode.type = types[TLONG];
+
+	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);
+
+	memset(reg, 0, sizeof(reg));
+	reg[REGTMP] = 1;
+	reg[REGSB] = 1;
+	reg[REGLINK] = 1;
+	reg[REGSP] = 1;
+	reg[REGZERO] = 1;
+	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+NREG; 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 && curarg == 0 && typeword[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*
+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 = regnode;
+	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]++;
+}
+
+int
+tmpreg(void)
+{
+	int i;
+
+	for(i=REGRET+1; i<NREG; i++)
+		if(reg[i] == 0)
+			return i;
+	diag(Z, "out of fixed registers");
+	return 0;
+}
+
+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 TIND:
+	case TUVLONG:
+	case TVLONG:
+		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+NREG)
+				goto out;
+		}
+		j = 0*2 + NREG;
+		for(i=NREG; i<NREG+NREG; i+=2) {
+			if(j >= NREG+NREG)
+				j = NREG;
+			if(reg[j] == 0) {
+				i = j;
+				goto out;
+			}
+			j += 2;
+		}
+		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 = 0;
+	}
+	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;
+		}
+		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;
+	Prog *p1;
+
+	ft = f->type->etype;
+	tt = t->type->etype;
+
+	/*
+	 * 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(typefd[tt]) {
+				/* special case can load mem to Freg */
+				regalloc(&nod, t, t);
+				gins(AMOVW, f, &nod);
+				a = AMOVWD;
+				if(tt == TFLOAT)
+					a = AMOVWF;
+				gins(a, &nod, &nod);
+				gmove(&nod, t);
+				regfree(&nod);
+				return;
+			}
+			a = AMOVW;
+			break;
+		case TCHAR:
+			a = AMOVB;
+			break;
+		case TUCHAR:
+			a = AMOVBU;
+			break;
+		case TSHORT:
+			a = AMOVH;
+			break;
+		case TUSHORT:
+			a = AMOVHU;
+			break;
+		case TFLOAT:
+			a = AMOVF;
+			break;
+		case TDOUBLE:
+			a = AMOVD;
+			break;
+		case TUVLONG:
+		case TVLONG:
+		case TIND:
+			a = AMOVV;
+			break;
+		}
+		if(typechlp[ft] && typeilp[tt])
+			regalloc(&nod, t, t);
+		else
+			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:
+			a = AMOVW;
+			break;
+		case TUCHAR:
+			a = AMOVBU;
+			break;
+		case TCHAR:
+			a = AMOVB;
+			break;
+		case TUSHORT:
+			a = AMOVHU;
+			break;
+		case TSHORT:
+			a = AMOVH;
+			break;
+		case TFLOAT:
+			a = AMOVF;
+			break;
+		case TDOUBLE:
+			a = AMOVD;
+			break;
+		case TUVLONG:
+		case TVLONG:
+		case TIND:
+			a = AMOVV;
+			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 TUVLONG:
+	case TVLONG:
+	case TIND:
+		switch(tt) {
+		case TUVLONG:
+		case TVLONG:
+		case TIND:
+			a = AMOVV;
+			break;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		case TDOUBLE:
+			gins(AMOVV, f, t);
+			gins(AMOVVD, t, t);
+			return;
+		case TFLOAT:
+			gins(AMOVV, f, t);
+			gins(AMOVVF, t, t);
+			return;
+		}
+		break;
+	case TDOUBLE:
+	case TFLOAT:
+		switch(tt) {
+		case TDOUBLE:
+			a = AMOVD;
+			if(ft == TFLOAT)
+				a = AMOVFD;
+			break;
+		case TFLOAT:
+			a = AMOVDF;
+			if(ft == TFLOAT)
+				a = AMOVF;
+			break;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			regalloc(&nod, f, Z);
+			gins(ATRUNCDW, f, &nod);
+			if(ft == TFLOAT)
+				p->as = ATRUNCFW;
+			gins(AMOVW, &nod, t);
+			regfree(&nod);
+			return;
+		case TUVLONG:
+		case TVLONG:
+		case TIND:
+			regalloc(&nod, f, Z);
+			gins(ATRUNCDV, f, &nod);
+			if(ft == TFLOAT)
+				p->as = ATRUNCFV;
+			gins(AMOVV, &nod, t);
+			regfree(&nod);
+			return;
+		}
+		break;
+	case TINT:
+	case TUINT:
+	case TLONG:
+	case TULONG:
+		switch(tt) {
+		case TDOUBLE:
+			diag(Z, "TODO long -> double");
+#ifdef TODO
+			gins(AMOVW, f, t);
+			gins(AMOVWD, t, t);
+			if(ft == TULONG || ft == TUINT) {
+				regalloc(&nod, t, Z);
+				gins(ACMPGED, t, Z);
+				p->reg = FREGZERO;
+				gins(ABFPT, Z, Z);
+				p1 = p;
+				gins(AMOVD, nodfconst(4294967296.), &nod);
+				gins(AADDD, &nod, t);
+				patch(p1, pc);
+				regfree(&nod);
+			}
+#endif
+			return;
+		case TFLOAT:
+			diag(Z, "TODO long -> float");
+#ifdef TODO
+			gins(AMOVW, f, t);
+			gins(AMOVWF, t, t);
+			if(ft == TULONG || ft == TUINT) {
+				regalloc(&nod, t, Z);
+				gins(ACMPGEF, t, Z);
+				p->reg = FREGZERO;
+				gins(ABFPT, Z, Z);
+				p1 = p;
+				gins(AMOVF, nodfconst(4294967296.), &nod);
+				gins(AADDF, &nod, t);
+				patch(p1, pc);
+				regfree(&nod);
+			}
+#endif
+			return;
+		case TUVLONG:
+		case TVLONG:
+		case TIND:
+			if(ft == TULONG || ft == TUINT) {
+				a = AMOVWU;
+				break;
+			}
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TSHORT:
+		case TUSHORT:
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVW;
+			break;
+		}
+		break;
+	case TSHORT:
+		switch(tt) {
+		case TDOUBLE:
+			regalloc(&nod, f, Z);
+			gins(AMOVH, f, &nod);
+			gins(AMOVW, &nod, t);
+			gins(AMOVWD, t, t);
+			regfree(&nod);
+			return;
+		case TFLOAT:
+			regalloc(&nod, f, Z);
+			gins(AMOVH, f, &nod);
+			gins(AMOVW, &nod, t);
+			gins(AMOVWF, t, 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 = AMOVV;
+			break;
+		}
+		break;
+	case TUSHORT:
+		switch(tt) {
+		case TDOUBLE:
+			regalloc(&nod, f, Z);
+			gins(AMOVHU, f, &nod);
+			gins(AMOVW, &nod, t);
+			gins(AMOVWD, t, t);
+			regfree(&nod);
+			return;
+		case TFLOAT:
+			regalloc(&nod, f, Z);
+			gins(AMOVHU, f, &nod);
+			gins(AMOVW, &nod, t);
+			gins(AMOVWF, t, 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 = AMOVV;
+			break;
+		}
+		break;
+	case TCHAR:
+		switch(tt) {
+		case TDOUBLE:
+			regalloc(&nod, f, Z);
+			gins(AMOVB, f, &nod);
+			gins(AMOVW, &nod, t);
+			gins(AMOVWD, t, t);
+			regfree(&nod);
+			return;
+		case TFLOAT:
+			regalloc(&nod, f, Z);
+			gins(AMOVB, f, &nod);
+			gins(AMOVW, &nod, t);
+			gins(AMOVWF, t, t);
+			regfree(&nod);
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TVLONG:
+		case TUVLONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+			a = AMOVB;
+			break;
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVV;
+			break;
+		}
+		break;
+	case TUCHAR:
+		switch(tt) {
+		case TDOUBLE:
+			regalloc(&nod, f, Z);
+			gins(AMOVBU, f, &nod);
+			gins(AMOVW, &nod, t);
+			gins(AMOVWD, t, t);
+			regfree(&nod);
+			return;
+		case TFLOAT:
+			regalloc(&nod, f, Z);
+			gins(AMOVBU, f, &nod);
+			gins(AMOVW, &nod, t);
+			gins(AMOVWF, t, t);
+			regfree(&nod);
+			return;
+		case TINT:
+		case TUINT:
+		case TLONG:
+		case TULONG:
+		case TVLONG:
+		case TUVLONG:
+		case TIND:
+		case TSHORT:
+		case TUSHORT:
+			a = AMOVBU;
+			break;
+		case TCHAR:
+		case TUCHAR:
+			a = AMOVV;
+			break;
+		}
+		break;
+	}
+	if(a == AGOK)
+		diag(Z, "bad opcode in gmove %T -> %T", f->type, t->type);
+	if((a == AMOVW && ewidth[ft] == ewidth[tt]) || a == AMOVF || a == AMOVD || a == AMOVV)
+	if(samaddr(f, t))
+		return;
+	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, ett;
+	Adr ta;
+	Node nod;
+
+	et = TLONG;
+	if(f1 != Z && f1->type != T)
+		if(f1-> op == OCONST && t != Z && t->type != T)
+			et = t->type->etype;
+		else
+			et = f1->type->etype;
+	ett = TLONG;
+	if(t != Z && t->type != T)
+		ett = t->type->etype;
+	if(llconst(f1) && o != OAS) {
+		regalloc(&nod, f1, Z);
+		gmove(f1, &nod);
+		gopcode(o, &nod, f2, t);
+		regfree(&nod);
+		return;
+	}
+	a = AGOK;
+	switch(o) {
+	case OAS:
+		gmove(f1, t);
+		return;
+
+	case OASADD:
+	case OADD:
+		a = AADDU;
+		if(et == TVLONG || et == TUVLONG || et == TIND)
+			a = AADDVU;
+		else
+		if(et == TFLOAT)
+			a = AADDF;
+		else
+		if(et == TDOUBLE)
+			a = AADDD;
+		break;
+
+	case OASSUB:
+	case OSUB:
+		a = ASUBU;
+		if(et == TVLONG || et == TUVLONG || et == TIND)
+			a = ASUBVU;
+		else
+		if(et == TFLOAT)
+			a = ASUBF;
+		else
+		if(et == TDOUBLE)
+			a = ASUBD;
+		break;
+
+	case OASOR:
+	case OOR:
+		a = AOR;
+		break;
+
+	case OASAND:
+	case OAND:
+		a = AAND;
+		break;
+
+	case OASXOR:
+	case OXOR:
+		a = AXOR;
+		break;
+
+	case OASLSHR:
+	case OLSHR:
+		a = ASRL;
+		if(ett == TVLONG || ett == TUVLONG || et == TIND)
+			a = ASRLV;
+		break;
+
+	case OASASHR:
+	case OASHR:
+		a = ASRA;
+		if(ett == TVLONG || ett == TUVLONG || et == TIND)
+			a = ASRAV;
+		break;
+
+	case OASASHL:
+	case OASHL:
+		a = ASLL;
+		if(ett == TVLONG || ett == TUVLONG || et == TIND)
+			a = ASLLV;
+		break;
+
+	case OFUNC:
+		a = AJAL;
+		break;
+
+	case OCOND:
+		a = ASGTU;
+		break;
+
+	case OCOMMA:
+		a = ASGT;
+		break;
+
+	case OASMUL:
+	case OMUL:
+		if(et == TFLOAT) {
+			a = AMULF;
+			break;
+		} else
+		if(et == TDOUBLE) {
+			a = AMULD;
+			break;
+		}
+		a = AMUL;
+		if(et == TVLONG || et == TUVLONG || et == TIND)
+			a = AMULV;
+		break;
+
+	case OASDIV:
+	case ODIV:
+		if(et == TFLOAT) {
+			a = ADIVF;
+			break;
+		} else
+		if(et == TDOUBLE) {
+			a = ADIVD;
+			break;
+		}
+		a = ADIV;
+		if(et == TVLONG || et == TUVLONG || et == TIND)
+			a = ADIVV;
+		break;
+
+	case OASMOD:
+	case OMOD:
+		a = ADIV;
+		o = OMOD;
+		if(et == TVLONG || et == TUVLONG || et == TIND)
+			a = ADIVV;
+		break;
+
+	case OASLMUL:
+	case OLMUL:
+		a = AMULU;
+		if(et == TVLONG || et == TUVLONG || et == TIND)
+			a = AMULVU;
+		break;
+
+	case OASLMOD:
+	case OLMOD:
+		o = OMOD;
+
+	case OASLDIV:
+	case OLDIV:
+		a = ADIVU;
+		if(et == TVLONG || et == TUVLONG || et == TIND)
+			a = ADIVVU;
+		break;
+
+	case OEQ:
+		if(!typefd[et]) {
+			a = ABEQ;
+			break;
+		}
+
+	case ONE:
+		if(!typefd[et]) {
+			a = ABNE;
+			break;
+		}
+
+	case OLT:
+	case OLE:
+	case OGE:
+	case OGT:
+		if(typefd[et]) {
+			nextpc();
+			if(et == TFLOAT) {
+				a = ACMPGTF;
+				if(o == OEQ || o == ONE)
+					a = ACMPEQF;
+				else
+				if(o == OLT || o == OGE)
+					a = ACMPGEF;
+			} else {
+				a = ACMPGTD;
+				if(o == OEQ || o == ONE)
+					a = ACMPEQD;
+				else
+				if(o == OLT || o == OGE)
+					a = ACMPGED;
+			}
+			p->as = a;
+			naddr(f1, &p->from);
+			raddr(f2, p);
+			if(debug['g'])
+				print("%P\n", p);
+			nextpc();
+			a = ABFPF;
+			if(o == OEQ || o == OGE || o == OGT)
+				a = ABFPT;
+			p->as = a;
+			if(debug['g'])
+				print("%P\n", p);
+			return;
+		}
+		if(vconst(f1) == 0 || vconst(f2) == 0) {
+			if(vconst(f1) == 0) {
+				o = invrel[relindex(o)];
+				f1 = f2;
+			}
+			switch(o) {
+			case OLT:
+				a = ABLTZ;
+				break;
+			case OLE:
+				a = ABLEZ;
+				break;
+			case OGE:
+				a = ABGEZ;
+				break;
+			case OGT:
+				a = ABGTZ;
+				break;
+			}
+			f2 = Z;
+			break;
+		}
+
+	case OLO:
+	case OLS:
+	case OHS:
+	case OHI:
+		nextpc();
+		if(o == OLE || o == OGT || o == OLS || o == OHI) {
+			naddr(f1, &p->from);
+			raddr(f2, p);
+		} else {
+			naddr(f2, &p->from);
+			raddr(f1, p);
+		}
+		naddr(&regnode, &p->to);
+		p->to.reg = tmpreg();
+		a = ASGT;
+		if(o == OLO || o == OLS || o == OHS || o == OHI)
+			a = ASGTU;
+		p->as = a;
+		if(debug['g'])
+			print("%P\n", p);
+
+		nextpc();
+		naddr(&regnode, &p->from);
+		p->from.reg = tmpreg();
+		a = ABEQ;
+		if(o == OLT || o == OGT || o == OLO || o == OHI)
+			a = ABNE;
+		p->as = a;
+		if(debug['g'])
+			print("%P\n", p);
+		return;
+	}
+	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(ta.type == D_CONST && ta.offset == 0)
+			p->reg = REGZERO;
+	}
+	if(t != Z)
+		naddr(t, &p->to);
+	if(debug['g'])
+		print("%P\n", p);
+}
+
+int
+samaddr(Node *f, Node *t)
+{
+
+	if(f->op != t->op)
+		return 0;
+	switch(f->op) {
+
+	case OREGISTER:
+		if(f->reg != t->reg)
+			break;
+		return 1;
+	}
+	return 0;
+}
+
+void
+gbranch(int o)
+{
+	int a;
+
+	a = AGOK;
+	switch(o) {
+	case ORETURN:
+		a = ARET;
+		break;
+	case OGOTO:
+		a = AJMP;
+		break;
+	}
+	nextpc();
+	if(a == AGOK) {
+		diag(Z, "bad in gbranch %O",  o);
+		nextpc();
+	}
+	p->as = a;
+}
+
+void
+patch(Prog *op, vlong 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;
+	if(a == ATEXT)
+		p->reg = (profileflg ? 0 : NOPROF);
+	p->from.name = D_EXTERN;
+	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;
+		}
+	}
+	return 0;
+}
+
+int
+llconst(Node *n)
+{
+	long l;
+
+	if(n != Z && n->op == OCONST) {
+		if(typev[n->type->etype] || n->type->etype == TIND) {
+			l = n->vconst;
+			return (vlong)l != n->vconst;
+		}
+	}
+	return 0;
+}
+
+int
+sval(long v)
+{
+	if(v >= -32766L && v < 32766L)
+		return 1;
+	return 0;
+}
+
+long
+exreg(Type *t)
+{
+	long o;
+
+	if(typechlp[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] */
+};
--- /dev/null
+++ b/sys/src/cmd/zc/z.out.h
@@ -1,0 +1,197 @@
+#define	NSNAME	8
+#define	NSYM	50
+#define	NREG	32
+
+#define NOPROF	(1<<0)
+#define DUPOK	(1<<1)
+
+#define	REGRET		4
+#define	REGARG		4
+/* compiler allocates R4 up as temps */
+/* compiler allocates register variables R3-R23 */
+#define	REGMIN		5
+#define REGMAX		28
+#define	REGTMP		17
+/* compiler allocates external registers R30 down */
+#define	REGEXT		30
+#define	REGSP		3
+#define	REGSB		31
+#define	REGLINK		1
+#define	REGZERO		0
+
+#define	NFREG		32
+#define	FREGRET		0
+#define	FREGMIN		7
+/* compiler allocates register variables F0 up */
+/* compiler allocates external registers F31 down */
+#define	FREGEXT		31
+
+enum	as
+{
+	AXXX,
+
+	AABSD,
+	AABSF,
+	AADD,
+	AADDD,
+	AADDF,
+	AADDU,
+	AADDW,
+	AAND,
+	ABEQ,
+	ABFPF,
+	ABFPT,
+	ABGEZ,
+	ABGTZ,
+	ABLEZ,
+	ABLTZ,
+	ABNE,
+	ABREAK,
+	ACMPEQD,
+	ACMPEQF,
+	ACMPGED,
+	ACMPGEF,
+	ACMPGTD,
+	ACMPGTF,
+	ADATA,
+	ADIV,
+	ADIVD,
+	ADIVF,
+	ADIVU,
+	ADIVW,
+	AGLOBL,
+	AGOK,
+	AHISTORY,
+	AJAL,
+	AJMP,
+	AMOVB,
+	AMOVBU,
+	AMOVD,
+	AMOVDF,
+	AMOVDW,
+	AMOVF,
+	AMOVFD,
+	AMOVFW,
+	AMOVH,
+	AMOVHU,
+	AMOVW,
+	AMOVWD,
+	AMOVWF,
+	AMOVWL,
+	AMOVWR,
+	AMUL,
+	AMULD,
+	AMULF,
+	AMULU,
+	AMULW,
+	ANAME32,
+	ANAME,
+	ANEGD,
+	ANEGF,
+	ANEGW,
+	ANOP,
+	ANOR,
+	AOR,
+	AREM,
+	AREMU,
+	ARET,
+	ASGT,
+	ASGTU,
+	ASLL,
+	ASRA,
+	ASRL,
+	ASUB,
+	ASUBD,
+	ASUBF,
+	ASUBU,
+	ASUBW,
+	ASYSCALL,
+	ATEXT,
+	AWORD,
+	AXOR,
+
+	AEND,
+
+	AMOVV,
+	AMOVVL,
+	AMOVVR,
+	ASLLV,
+	ASRAV,
+	ASRLV,
+	ADIVV,
+	ADIVVU,
+	AREMV,
+	AREMVU,
+	AMULV,
+	AMULVU,
+	AADDV,
+	AADDVU,
+	ASUBV,
+	ASUBVU,
+
+	ADYNT,
+	AINIT,
+
+	ATRUNCFV,
+	ATRUNCDV,
+	ATRUNCFW,
+	ATRUNCDW,
+	AMOVWU,
+	AMOVFV,
+	AMOVDV,
+	AMOVVF,
+	AMOVVD,
+
+	ALU12IW,
+	ALU32ID,
+	ALU52ID,
+
+	ASIGNAME,
+
+	ALAST,
+};
+
+/* type/name */
+enum
+{
+	D_GOK	= 0,
+	D_NONE,
+
+/* name */
+	D_EXTERN,
+	D_STATIC,
+	D_AUTO,
+	D_PARAM,
+
+/* type */
+	D_BRANCH,
+	D_OREG,
+	D_CONST,
+	D_FCONST,
+	D_SCONST,
+	D_REG,
+	D_FREG,
+	D_FCCREG,
+	D_FCSREG,
+	D_FILE,
+	D_OCONST,
+	D_FILE1,
+	D_VCONST,
+};
+
+/*
+ * 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/zl/asm.c
@@ -1,0 +1,1290 @@
+#include	"l.h"
+
+long	OFFSET;
+
+#define PADDR(a)	((a) & ~0xfffffffff0000000ull)
+
+#define VPUT(v)	VLEPUT(v)
+
+#define VLEPUT(c)\
+	{\
+		cbp[0] = (c);\
+		cbp[1] = (c)>>8;\
+		cbp[2] = (c)>>16;\
+		cbp[3] = (c)>>24;\
+		cbp[4] = (c)>>32;\
+		cbp[5] = (c)>>40;\
+		cbp[6] = (c)>>48;\
+		cbp[7] = (c)>>56;\
+		cbp += 8;\
+		cbc -= 8;\
+		if(cbc <= 0)\
+			cflush();\
+	}
+
+#define VBEPUT(c)\
+	{\
+		cbp[0] = (c)>>56;\
+		cbp[1] = (c)>>48;\
+		cbp[2] = (c)>>40;\
+		cbp[3] = (c)>>32;\
+		cbp[4] = (c)>>24;\
+		cbp[5] = (c)>>16;\
+		cbp[6] = (c)>>8;\
+		cbp[7] = (c);\
+		cbp += 8;\
+		cbc -= 8;\
+		if(cbc <= 0)\
+			cflush();\
+	}
+
+#define LPUT(l)	LLEPUT(l)
+
+#define	LLEPUT(c)\
+	{\
+		cbp[0] = (c);\
+		cbp[1] = (c)>>8;\
+		cbp[2] = (c)>>16;\
+		cbp[3] = (c)>>24;\
+		cbp += 4;\
+		cbc -= 4;\
+		if(cbc <= 0)\
+			cflush();\
+	}
+
+#define	LBEPUT(c)\
+	{\
+		cbp[0] = (c)>>24;\
+		cbp[1] = (c)>>16;\
+		cbp[2] = (c)>>8;\
+		cbp[3] = (c);\
+		cbp += 4;\
+		cbc -= 4;\
+		if(cbc <= 0)\
+			cflush();\
+	}
+
+#define HPUT(h)	HLEPUT(h)
+
+#define	HLEPUT(c)\
+	{\
+		cbp[0] = (c);\
+		cbp[1] = (c)>>8;\
+		cbp += 2;\
+		cbc -= 2;\
+		if(cbc <= 0)\
+			cflush();\
+	}
+
+#define	HBEPUT(c)\
+	{\
+		cbp[0] = (c)>>8;\
+		cbp[1] = (c);\
+		cbp += 2;\
+		cbc -= 2;\
+		if(cbc <= 0)\
+			cflush();\
+	}
+
+
+#define	CPUT(c)\
+	{\
+		cbp[0] = (c);\
+		cbp++;\
+		cbc--;\
+		if(cbc <= 0)\
+			cflush();\
+	}
+
+void
+cput(long l)
+{
+	CPUT(l);
+}
+
+void
+objput(long l)	/* emit long in byte order appropriate to object machine */
+{
+	LPUT(l);
+}
+
+void
+objhput(short s)
+{
+	HPUT(s);
+}
+
+void
+wput(long l)
+{
+
+	cbp[0] = l>>8;
+	cbp[1] = l;
+	cbp += 2;
+	cbc -= 2;
+	if(cbc <= 0)
+		cflush();
+}
+
+void
+wputl(long l)
+{
+
+	cbp[0] = l;
+	cbp[1] = l>>8;
+	cbp += 2;
+	cbc -= 2;
+	if(cbc <= 0)
+		cflush();
+}
+
+void
+lput(long l)		/* emit long in big-endian byte order */
+{
+	LBEPUT(l);
+}
+
+void
+lputl(long l)		/* emit long in big-endian byte order */
+{
+	LLEPUT(l);
+}
+
+void
+llput(vlong v)
+{
+	lput(v>>32);
+	lput(v);
+}
+
+void
+llputl(vlong v)
+{
+	lputl(v);
+	lputl(v>>32);
+}
+
+vlong
+entryvalue(void)
+{
+	char *a;
+	Sym *s;
+
+	a = INITENTRY;
+	if(*a >= '0' && *a <= '9')
+		return atolwhex(a);
+	s = lookup(a, 0);
+	if(s->type == 0)
+		return INITTEXT;
+	if(s->type != STEXT && s->type != SLEAF)
+		diag("entry not text: %s", s->name);
+	return s->value;
+}
+
+void
+asmb(void)
+{
+	Prog *p;
+	long magic;
+	vlong vl, t, etext;
+	Optab *o;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f asm\n", cputime());
+	Bflush(&bso);
+	OFFSET = HEADR;
+	seek(cout, OFFSET, 0);
+	pc = INITTEXT;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			curtext = p;
+			autosize = p->to.offset + 8;
+		}
+		if(p->pc != pc) {
+			diag("phase error %llux sb %llux", p->pc, pc);
+			if(!debug['a'])
+				prasm(curp);
+			pc = p->pc;
+		}
+		curp = p;
+		o = oplook(p);	/* could probably avoid this call */
+		if(asmout(p, o, 0)) {
+			p = p->link;
+			pc += 4;
+		}
+		pc += o->size;
+	}
+	if(debug['a'])
+		Bprint(&bso, "\n");
+	Bflush(&bso);
+	cflush();
+
+	etext = INITTEXT + textsize;
+	for(t = pc; t < etext; t += sizeof(buf)-100) {
+		if(etext-t > sizeof(buf)-100)
+			datblk(t, sizeof(buf)-100, 1);
+		else
+			datblk(t, etext-t, 1);
+	}
+
+	Bflush(&bso);
+	cflush();
+
+	curtext = P;
+	switch(HEADTYPE) {
+	case 0:
+	case 2:
+		OFFSET = HEADR+textsize;
+		seek(cout, OFFSET, 0);
+		break;
+	case 6:	/* no header, padded */
+		OFFSET = rnd(HEADR+textsize, INITRND);
+		seek(cout, OFFSET, 0);
+		break;
+	}
+	for(t = 0; t < datsize; t += sizeof(buf)-100) {
+		if(datsize-t > sizeof(buf)-100)
+			datblk(t, sizeof(buf)-100, 0);
+		else
+			datblk(t, datsize-t, 0);
+	}
+
+	symsize = 0;
+	lcsize = 0;
+	if(!debug['s']) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f sym\n", cputime());
+		Bflush(&bso);
+		switch(HEADTYPE) {
+		case 0:
+			debug['s'] = 1;
+			break;
+		case 2:
+			OFFSET = HEADR+textsize+datsize;
+			seek(cout, OFFSET, 0);
+			break;
+		case 6:	/* no header, padded */
+			OFFSET += rnd(datsize, INITRND);
+			seek(cout, OFFSET, 0);
+			break;
+		}
+		if(!debug['s'])
+			asmsym();
+		if(debug['v'])
+			Bprint(&bso, "%5.2f pc\n", cputime());
+		Bflush(&bso);
+		if(!debug['s'])
+			asmlc();
+		cflush();
+	}
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f header\n", cputime());
+	Bflush(&bso);
+	OFFSET = 0;
+	seek(cout, OFFSET, 0);
+	switch(HEADTYPE) {
+	case 0:	/* no header */
+	case 6:	/* no header, padded */
+		break;
+	case 2:
+		magic = 4*29*29+7;
+		magic |= 0x00008000;
+		lput(magic);		/* magic */
+		lput(textsize);		/* sizes */
+		lput(datsize);
+		lput(bsssize);
+		lput(symsize);		/* nsyms */
+		vl = entryvalue();
+		lput(PADDR(vl));	/* va of entry */
+		lput(0L);
+		lput(lcsize);
+		llput(vl);		/* va of entry */
+		break;
+	}
+	cflush();
+}
+
+void
+strnput(char *s, int n)
+{
+	for(; *s; s++){
+		CPUT(*s);
+		n--;
+	}
+	for(; n > 0; n--)
+		CPUT(0);
+}
+
+void
+cflush(void)
+{
+	int n;
+
+	n = sizeof(buf.cbuf) - cbc;
+	if(n)
+		write(cout, buf.cbuf, n);
+	cbp = buf.cbuf;
+	cbc = sizeof(buf.cbuf);
+}
+
+void
+nopstat(char *f, Count *c)
+{
+	if(c->outof)
+	Bprint(&bso, "%s delay %ld/%ld (%.2f)\n", f,
+		c->outof - c->count, c->outof,
+		(double)(c->outof - c->count)/c->outof);
+}
+
+void
+asmsym(void)
+{
+	Prog *p;
+	Auto *a;
+	Sym *s;
+	int h;
+
+	s = lookup("etext", 0);
+	if(s->type == STEXT)
+		putsymb(s->name, 'T', s->value, s->version);
+
+	for(h=0; h<NHASH; h++)
+		for(s=hash[h]; s!=S; s=s->link)
+			switch(s->type) {
+			case SCONST:
+				putsymb(s->name, 'D', s->value, s->version);
+				continue;
+
+			case SSTRING:
+				putsymb(s->name, 'T', s->value, s->version);
+				continue;
+
+			case SDATA:
+				putsymb(s->name, 'D', s->value+INITDAT, s->version);
+				continue;
+
+			case SBSS:
+				putsymb(s->name, 'B', s->value+INITDAT, s->version);
+				continue;
+
+			case SFILE:
+				putsymb(s->name, 'f', s->value, s->version);
+				continue;
+			}
+
+	for(p=textp; p!=P; p=p->cond) {
+		s = p->from.sym;
+		if(s->type != STEXT && s->type != SLEAF)
+			continue;
+
+		/* filenames first */
+		for(a=p->to.autom; a; a=a->link)
+			if(a->type == D_FILE)
+				putsymb(a->asym->name, 'z', a->aoffset, 0);
+			else
+			if(a->type == D_FILE1)
+				putsymb(a->asym->name, 'Z', a->aoffset, 0);
+
+		if(s->type == STEXT)
+			putsymb(s->name, 'T', s->value, s->version);
+		else
+			putsymb(s->name, 'L', s->value, s->version);
+
+		/* frame, auto and param after */
+		putsymb(".frame", 'm', p->to.offset+8, 0);
+		for(a=p->to.autom; a; a=a->link)
+			if(a->type == D_AUTO)
+				putsymb(a->asym->name, 'a', -a->aoffset, 0);
+			else
+			if(a->type == D_PARAM)
+				putsymb(a->asym->name, 'p', a->aoffset, 0);
+	}
+	if(debug['v'] || debug['n'])
+		Bprint(&bso, "symsize = %lud\n", symsize);
+	Bflush(&bso);
+}
+
+void
+putsymb(char *s, int t, vlong v, int ver)
+{
+	int i, f;
+	long l;
+
+	if(t == 'f')
+		s++;
+	if(HEADTYPE == 2) {
+		l = v >> 32;
+		LBEPUT(l);
+	}
+	l = v;
+	LBEPUT(l);
+	if(ver)
+		t += 'a' - 'A';
+	CPUT(t+0x80);			/* 0x80 is variable length */
+
+	if(t == 'Z' || t == 'z') {
+		CPUT(s[0]);
+		for(i=1; s[i] != 0 || s[i+1] != 0; i += 2) {
+			CPUT(s[i]);
+			CPUT(s[i+1]);
+		}
+		CPUT(0);
+		CPUT(0);
+		i++;
+	}
+	else {
+		for(i=0; s[i]; i++)
+			CPUT(s[i]);
+		CPUT(0);
+	}
+	symsize += 4 + 1 + i + 1;
+	if(HEADTYPE == 2)
+		symsize += 4;
+
+	if(debug['n']) {
+		if(t == 'z' || t == 'Z') {
+			Bprint(&bso, "%c %.8llux ", t, v);
+			for(i=1; s[i] != 0 || s[i+1] != 0; i+=2) {
+				f = ((s[i]&0xff) << 8) | (s[i+1]&0xff);
+				Bprint(&bso, "/%x", f);
+			}
+			Bprint(&bso, "\n");
+			return;
+		}
+		if(ver)
+			Bprint(&bso, "%c %.8llux %s<%d>\n", t, v, s, ver);
+		else
+			Bprint(&bso, "%c %.8llux %s\n", t, v, s);
+	}
+}
+
+#define	MINLC	4
+void
+asmlc(void)
+{
+	long oldlc, v, s;
+	vlong oldpc;
+	Prog *p;
+
+	oldpc = INITTEXT;
+	oldlc = 0;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) {
+			if(p->as == ATEXT)
+				curtext = p;
+			if(debug['V'])
+				Bprint(&bso, "%6llux %P\n", p->pc, p);
+			continue;
+		}
+		if(debug['V'])
+			Bprint(&bso, "\t\t%6ld", lcsize);
+		v = (p->pc - oldpc) / MINLC;
+		while(v) {
+			s = 127;
+			if(v < 127)
+				s = v;
+			CPUT(s+128);	/* 129-255 +pc */
+			if(debug['V'])
+				Bprint(&bso, " pc+%ld*%d(%ld)", s, MINLC, s+128);
+			v -= s;
+			lcsize++;
+		}
+		s = p->line - oldlc;
+		oldlc = p->line;
+		oldpc = p->pc + MINLC;
+		if(s > 64 || s < -64) {
+			CPUT(0);	/* 0 vv +lc */
+			CPUT(s>>24);
+			CPUT(s>>16);
+			CPUT(s>>8);
+			CPUT(s);
+			if(debug['V']) {
+				if(s > 0)
+					Bprint(&bso, " lc+%ld(%d,%ld)\n",
+						s, 0, s);
+				else
+					Bprint(&bso, " lc%ld(%d,%ld)\n",
+						s, 0, s);
+				Bprint(&bso, "%6llux %P\n", p->pc, p);
+			}
+			lcsize += 5;
+			continue;
+		}
+		if(s > 0) {
+			CPUT(0+s);	/* 1-64 +lc */
+			if(debug['V']) {
+				Bprint(&bso, " lc+%ld(%ld)\n", s, 0+s);
+				Bprint(&bso, "%6llux %P\n", p->pc, p);
+			}
+		} else {
+			CPUT(64-s);	/* 65-128 -lc */
+			if(debug['V']) {
+				Bprint(&bso, " lc%ld(%ld)\n", s, 64-s);
+				Bprint(&bso, "%6llux %P\n", p->pc, p);
+			}
+		}
+		lcsize++;
+	}
+	while(lcsize & 1) {
+		s = 129;
+		CPUT(s);
+		lcsize++;
+	}
+	if(debug['v'] || debug['V'])
+		Bprint(&bso, "lcsize = %ld\n", lcsize);
+	Bflush(&bso);
+}
+
+void
+datblk(long s, long n, int str)
+{
+	Prog *p;
+	char *cast;
+	long l, fl, j;
+	vlong d;
+	int i, c;
+
+	memset(buf.dbuf, 0, n+100);
+	for(p = datap; p != P; p = p->link) {
+		curp = p;
+		if(str != (p->from.sym->type == SSTRING))
+			continue;
+		l = p->from.sym->value + p->from.offset - s;
+		c = p->reg;
+		i = 0;
+		if(l < 0) {
+			if(l+c <= 0)
+				continue;
+			while(l < 0) {
+				l++;
+				i++;
+			}
+		}
+		if(l >= n)
+			continue;
+		if(p->as != AINIT && p->as != ADYNT) {
+			for(j=l+(c-i)-1; j>=l; j--)
+				if(buf.dbuf[j]) {
+					print("%P\n", p);
+					diag("multiple initialization");
+					break;
+				}
+		}
+		switch(p->to.type) {
+		default:
+			diag("unknown mode in initialization\n%P", p);
+			break;
+
+		case D_FCONST:
+			switch(c) {
+			default:
+			case 4:
+				fl = ieeedtof(p->to.ieee);
+				cast = (char*)&fl;
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[fnuxi4[i]];
+					l++;
+				}
+				break;
+			case 8:
+				cast = (char*)p->to.ieee;
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[fnuxi8[i]];
+					l++;
+				}
+				break;
+			}
+			break;
+
+		case D_SCONST:
+			for(; i<c; i++) {
+				buf.dbuf[l] = p->to.sval[i];
+				l++;
+			}
+			break;
+
+		case D_CONST:
+			d = p->to.offset;
+			if(p->to.sym) {
+				switch(p->to.sym->type) {
+				case STEXT:
+				case SLEAF:
+				case SSTRING:
+					d += p->to.sym->value;
+					break;
+				case SDATA:
+				case SBSS:
+					d += p->to.sym->value + INITDAT;
+					break;
+				}
+			}
+			cast = (char*)&d;
+			switch(c) {
+			default:
+				diag("bad nuxi %d %d\n%P", c, i, curp);
+				break;
+			case 1:
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi1[i]];
+					l++;
+				}
+				break;
+			case 2:
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi2[i]];
+					l++;
+				}
+				break;
+			case 4:
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi4[i]];
+					l++;
+				}
+				break;
+			case 8:
+				for(; i<c; i++) {
+					buf.dbuf[l] = cast[inuxi8[i]];
+					l++;
+				}
+				break;
+			}
+			break;
+		}
+	}
+	write(cout, buf.dbuf, n);
+}
+
+#define	OP_RRR(op,r1,r2,r3)\
+	(op|(((r1)&0x1FL)<<10)|(((r2)&0x1FL)<<5)|(((r3)&0x1FL)<<0))
+#define	OP_RR(op,r2,r3)\
+	(op|(((r2)&0x1FL)<<5)|(((r3)&0x1FL)<<0))
+#define	OP_16IR_5I(op,i,r2)\
+	(op|(((i)&0xFFFFL)<<10)|(((r2)&0x1FL)<<5)|(((i)>>16)&0x1FL))
+#define	OP_16IRR(op,i,r2,r3)\
+	(op|(((i)&0xFFFFL)<<10)|(((r2)&0x1FL)<<5)|(((r3)&0x1FL)<<0))
+#define	OP_12IRR(op,i,r2,r3)\
+	(op|(((i)&0xFFFL)<<10)|(((r2)&0x1FL)<<5)|(((r3)&0x1FL)<<0))
+#define	OP_IR(op,i,r2)\
+	(op|(((i)&0xFFFFFL)<<5)|(((r2)&0x1FL)<<0))
+#define	OP_B_BL(op,i)\
+	(op|(((i)&0xFFFFL)<<10)|(((i)>>16)&0x3FFL))
+
+#define	OP(x,y)\
+	(((x)<<3)|((y)<<0))
+#define	SP(x,y)\
+	(((x)<<29)|((y)<<26))
+#define	OP_TEN(x,y)\
+	(((x)<<21)|((y)<<10))
+
+int vshift(int);
+
+int
+asmout(Prog *p, Optab *o, int aflag)
+{
+	long o1, o2, o3, o4, o5;
+	vlong v;
+	Prog *ct;
+	int r, a;
+
+	o1 = 0;
+	o2 = 0;
+	o3 = 0;
+	o4 = 0;
+	o5 = 0;
+	switch(o->type) {
+	default:
+		diag("unknown type %d", o->type);
+		if(!debug['a'])
+			prasm(p);
+		break;
+
+	case 0:		/* pseudo ops */
+		if(aflag) {
+			if(p->link) {
+				if(p->as == ATEXT) {
+					ct = curtext;
+					o2 = autosize;
+					curtext = p;
+					autosize = p->to.offset + 8;
+					o1 = asmout(p->link, oplook(p->link), aflag);
+					curtext = ct;
+					autosize = o2;
+				} else
+					o1 = asmout(p->link, oplook(p->link), aflag);
+			}
+			return o1;
+		}
+		break;
+
+	case 1:		/* mov[v] r1,r2 ==> OR r1,r0,r2 */
+		o1 = OP_RRR(oprrr(AOR), REGZERO, p->from.reg, p->to.reg);
+		break;
+
+	case 2:		/* add/sub r1,[r2],r3 */
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = OP_RRR(oprrr(p->as), p->from.reg, r, p->to.reg);
+		break;
+
+	case 3:		/* mov $soreg, r ==> or/add $i,o,r */
+		v = regoff(&p->from);
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		a = AADDVU;
+		if(o->a1 == C_ANDCON)
+			a = AOR;
+		o1 = OP_12IRR(opirr(a), v, r, p->to.reg);
+		break;
+
+	case 4:		/* add $scon,[r1],r2 */
+		v = regoff(&p->from);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = OP_12IRR(opirr(p->as), v, r, p->to.reg);
+		break;
+
+	case 5:		/* syscall */
+		if(aflag)
+			return 0;
+		o1 = oprrr(p->as);
+		break;
+
+	case 6:		/* beq r1,[r2],sbra */
+		if(aflag)
+			return 0;
+		v = 0;
+		if(p->cond != P)
+			v = (p->cond->pc - pc) >> 2;
+		if(((v << 16) >> 16) != v)
+			diag("short branch too far: %lld\n%P", v, p);
+		o1 = OP_16IRR(opirr(p->as), v, p->from.reg, p->reg);
+		break;
+
+	case 7:		/* mov r, soreg ==> sw o(r) */
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->to);
+		o1 = OP_12IRR(opirr(p->as), v, r, p->from.reg);
+		break;
+
+	case 8:		/* mov soreg, r ==> lw o(r) */
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->from);
+		o1 = OP_12IRR(opirr(p->as+ALAST), v, r, p->to.reg);
+		break;
+
+	case 9:		/* asl r1,[r2],r3 */
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = OP_RRR(oprrr(p->as), r, p->from.reg, p->to.reg);
+		break;
+
+	case 10:	/* add $con,[r1],r2 ==> mov $con,t; add t,[r1],r2 */
+		v = regoff(&p->from);
+		r = AOR;
+		if(v < 0)
+			r = AADDU;
+		o1 = OP_12IRR(opirr(r), v, 0, REGTMP);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o2 = OP_RRR(oprrr(p->as), REGTMP, r, p->to.reg);
+		break;
+
+	case 11:	/* jmp lbra */
+		if(aflag)
+			return 0;
+		v = 0;
+		if(p->cond != P)
+			v = (p->cond->pc - pc) >> 2;
+		o1 = OP_B_BL(opirr(p->as), v);
+		break;
+
+	case 12:	/* movbs r,r */
+		v = 16;
+		if(p->as == AMOVB)
+			v = 24;
+		o1 = OP_16IRR(opirr(ASLL), v, p->from.reg, p->to.reg);
+		o2 = OP_16IRR(opirr(ASRA), v, p->to.reg, p->to.reg);
+		break;
+
+	case 13:	/* movbu r,r */
+		if(p->as == AMOVBU)
+			o1 = OP_12IRR(opirr(AAND), 0xffL, p->from.reg, p->to.reg);
+		else
+			o1 = (0x33c0<<10) | ((p->from.reg&0x1FL)<<5) | (p->to.reg&0x1FL);
+		break;
+
+	case 14:	/* movwu r,r */
+		o1 = OP_16IRR(opirr(ASLLV+ALAST), 32, p->from.reg, p->to.reg);
+		o2 = OP_16IRR(opirr(ASRLV+ALAST), 32, p->to.reg, p->to.reg);
+		break;
+
+	case 16:	/* sll $c,[r1],r2 */
+		v = regoff(&p->from);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+
+		/* OP_SRR will use only the low 5 bits of the shift value */
+		if(v >= 32 && vshift(p->as))
+			o1 = OP_16IRR(opirr(p->as+ALAST), v&0x3FL, r, p->to.reg);
+		else
+			o1 = OP_16IRR(opirr(p->as), v&0x1FL, r, p->to.reg);
+		break;
+
+	case 18:	/* jmp [r1],0(r2) */
+		if(aflag)
+			return 0;
+		r = p->reg;
+		if(r == NREG)
+			r = o->param;
+		o1 = OP_RRR(oprrr(p->as), 0, p->to.reg, r);
+		break;
+
+	case 19:	/* mov $lcon,r ==> lu+or */
+		v = regoff(&p->from);
+		o1 = OP_IR(opir(ALU12IW), v>>12, p->to.reg);
+		o2 = OP_12IRR(opirr(AOR), v, p->to.reg, p->to.reg);
+		break;
+
+	case 23:	/* add $lcon,r1,r2 ==> lu+or+add */
+		v = regoff(&p->from);
+		if(p->to.reg == REGTMP || p->reg == REGTMP)
+			diag("cant synthesize large constant\n%P", p);
+		o1 = OP_IR(opir(ALU12IW), v>>12, REGTMP);
+		o2 = OP_12IRR(opirr(AOR), v, REGTMP, REGTMP);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o3 = OP_RRR(oprrr(p->as), REGTMP, r, p->to.reg);
+		break;
+
+	case 24:	/* mov $ucon,,r ==> lu r */
+		v = regoff(&p->from);
+		o1 = OP_IR(opir(ALU12IW), v>>12, p->to.reg);
+		break;
+
+	case 25:	/* add/and $ucon,[r1],r2 ==> lu $con,t; add t,[r1],r2 */
+		v = regoff(&p->from);
+		o1 = OP_IR(opir(ALU12IW), v>>12, REGTMP);
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o2 = OP_RRR(oprrr(p->as), REGTMP, r, p->to.reg);
+		break;
+
+	case 26:	/* mov $lsext/auto/oreg,,r2 ==> lu+or+add */
+		v = regoff(&p->from);
+		if(p->to.reg == REGTMP)
+			diag("cant synthesize large constant\n%P", p);
+		o1 = OP_IR(opir(ALU12IW), v>>12, REGTMP);
+		o2 = OP_12IRR(opirr(AOR), v, REGTMP, REGTMP);
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		o3 = OP_RRR(oprrr(AADDVU), REGTMP, r, p->to.reg);
+		break;
+
+	case 27:		/* mov [sl]ext/auto/oreg,fr ==> lwc1 o(r) */
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->from);
+		a = AMOVF+ALAST;
+		if(p->as == AMOVD)
+			a = AMOVD+ALAST;
+		switch(o->size) {
+		case 12:
+			o1 = OP_IR(opir(ALU12IW), (v+1<<11)>>12, REGTMP);
+			o2 = OP_RRR(oprrr(AADDVU), r, REGTMP, REGTMP);
+			o3 = OP_12IRR(opirr(a), v, REGTMP, p->to.reg);
+			break;
+		case 4:
+			o1 = OP_12IRR(opirr(a), v, r, p->to.reg);
+			break;
+		default:
+			diag("bad 27 op size");
+		}
+		break;
+
+	case 28:		/* mov fr,[sl]ext/auto/oreg ==> swc1 o(r) */
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		v = regoff(&p->to);
+		a = AMOVF;
+		if(p->as == AMOVD)
+			a = AMOVD;
+		switch(o->size) {
+		case 12:
+			if(r == REGTMP)
+				diag("cant synthesize large constant\n%P", p);
+			o1 = OP_IR(opir(ALU12IW), (v+1<<11)>>12, REGTMP);
+			o2 = OP_RRR(oprrr(AADDVU), r, REGTMP, REGTMP);
+			o3 = OP_12IRR(opirr(a), v, REGTMP, p->from.reg);
+			break;
+		case 4:
+			o1 = OP_12IRR(opirr(a), v, r, p->from.reg);
+			break;
+		default:
+			diag("bad 28 op size");
+		}
+		break;
+
+	case 30:	/* movw r,fr */
+		a = OP_TEN(8, 1321);	/* movgr2fr.w */
+		o1 = OP_RR(a, p->from.reg, p->to.reg);
+		break;
+
+	case 31:	/* movw fr,r */
+		a = OP_TEN(8, 1325);		/* movfr2gr.s */
+		o1 = OP_RR(a, p->to.reg, p->from.reg);
+		break;
+
+	case 32:	/* fadd fr1,[fr2],fr3 */
+		r = p->reg;
+		if(r == NREG)
+			r = p->to.reg;
+		o1 = OP_RRR(oprrr(p->as), p->from.reg, r, p->to.reg);
+		break;
+
+	case 33:	/* fabs fr1,fr3 */
+		o1 = OP_RRR(oprrr(p->as), 0, p->from.reg, p->to.reg);
+		break;
+
+	case 34:	/* mov $con,fr ==> or/add $i,r,r2 */
+		v = regoff(&p->from);
+		r = AADDU;
+		if(o->a1 == C_ANDCON)
+			r = AOR;
+		o1 = OP_12IRR(opirr(r), v, 0, REGTMP);
+		o2 = OP_RRR(OP_TEN(8, 1321), REGTMP, 0, p->to.reg);	/* movgr2fr.w */
+		break;
+
+	case 35:	/* mov r,lext/luto/oreg ==> sw o(r) */
+		v = regoff(&p->to);
+		r = p->to.reg;
+		if(r == NREG)
+			r = o->param;
+		if(r == REGTMP)
+			diag("cant synthesize large constant\n%P", p);
+		o1 = OP_IR(opir(ALU12IW), (v+1<<11)>>12, REGTMP);
+		o2 = OP_RRR(oprrr(AADDVU), r, REGTMP, REGTMP);
+		o3 = OP_12IRR(opirr(p->as), r, REGTMP, p->from.reg);
+		break;
+
+	case 36:	/* mov lext/lauto/lreg,r ==> lw o(r30) */
+		v = regoff(&p->from);
+		r = p->from.reg;
+		if(r == NREG)
+			r = o->param;
+		if(r == REGTMP)
+			diag("cant synthesize large constant\n%P", p);
+		o1 = OP_IR(opir(ALU12IW), (v+1<<11)>>12, REGTMP);
+		o2 = OP_RRR(oprrr(AADDVU), r, REGTMP, REGTMP);
+		o3 = OP_12IRR(opirr(p->as+ALAST), v, REGTMP, p->to.reg);
+		break;
+
+#ifdef TODO
+	case 37:	/* movw r,mr */
+		a = OP_TEN();	/* movgr2cf */
+		o1 = OP_RR(a, p->from.reg, p->to.reg);
+		break;
+
+	case 38:	/* movw mr,r */
+		a = OP_TEN();	/* movcf2gr */
+		o1 = OP_RR(a, p->to.reg, p->from.reg);
+		break;
+#endif
+
+	case 40:	/* word */
+		if(aflag)
+			return 0;
+		o1 = regoff(&p->to);
+		break;
+
+#ifdef TODO
+	case 41:	/* movw r,fcr */
+		a = OP_TEN(); /* movgr2fcsr */
+		o1 = OP_RR(a, p->from.reg, p->to.reg);
+		break;
+
+	case 42:	/* movw fcr,r */
+		a = OP_TEN();	/* movfcsr2gr */
+		o1 = OP_RR(a, p->to.reg, p->from.reg);
+		break;
+#endif
+
+#ifdef TODO
+	case 47:	/* movv r,fr */
+		r = SP(2,1)|(5<<21);		/* dmtc1 */
+		o1 = OP_RRR(r, p->from.reg, 0, p->to.reg);
+		break;
+
+	case 48:	/* movv fr,r */
+		r = SP(2,1)|(1<<21);		/* dmfc1 */
+		o1 = OP_RRR(r, p->to.reg, 0, p->from.reg);
+		break;
+#endif
+	}
+	if(aflag)
+		return o1;
+	v = p->pc;
+	switch(o->size) {
+	default:
+		if(debug['a'])
+			Bprint(&bso, " %.16llux:\t\t%P\n", v, p);
+		break;
+	case 4:
+		if(debug['a'])
+			Bprint(&bso, " %.16llux: %.8lux\t%P\n", v, o1, p);
+		LPUT(o1);
+		break;
+	case 8:
+		if(debug['a'])
+			Bprint(&bso, " %.16llux: %.8lux %.8lux\t%P\n", v, o1, o2, p);
+		LPUT(o1);
+		LPUT(o2);
+		break;
+	case 12:
+		if(debug['a'])
+			Bprint(&bso, " %.16llux: %.8lux %.8lux %.8lux\t%P\n", v, o1, o2, o3, p);
+		LPUT(o1);
+		LPUT(o2);
+		LPUT(o3);
+		break;
+	case 16:
+		if(debug['a'])
+			Bprint(&bso, " %.16llux: %.8lux %.8lux %.8lux %.8lux%P\n",
+				v, o1, o2, o3, o4, p);
+		LPUT(o1);
+		LPUT(o2);
+		LPUT(o3);
+		LPUT(o4);
+		break;
+	case 20:
+		if(debug['a'])
+			Bprint(&bso, " %.16llux: %.8lux %.8lux %.8lux %.8lux %.8lux%P\n",
+				v, o1, o2, o3, o4, o5, p);
+		LPUT(o1);
+		LPUT(o2);
+		LPUT(o3);
+		LPUT(o4);
+		LPUT(o5);
+		break;
+	}
+	return 0;
+}
+
+int
+isnop(Prog *p)
+{
+	if(p->as != ANOR)
+		return 0;
+	if(p->reg != REGZERO && p->reg != NREG)
+		return 0;
+	if(p->from.type != D_REG || p->from.reg != REGZERO)
+		return 0;
+	if(p->to.type != D_REG || p->to.reg != REGZERO)
+		return 0;
+	return 1;
+}
+
+long
+oprrr(int a)
+{
+	switch(a) {
+	case AADD:
+	case AADDU:	return 0x20<<15;
+	case ASGT:	return 0x24<<15;
+	case ASGTU:	return 0x25<<15;
+	case AAND:	return 0x29<<15;
+	case AOR:	return 0x2a<<15;
+	case AXOR:	return 0x2b<<15;
+	case ASUB:
+	case ASUBU:	return 0x22<<15;
+	case ANOR:	return 0x28<<15;
+	case ASLL:	return 0x2e<<15;
+	case ASRL:	return 0x2f<<15;
+	case ASRA:	return 0x30<<15;
+
+	case ASLLV:	return 0x31<<15;
+	case ASRLV:	return 0x32<<15;
+	case ASRAV:	return 0x33<<15;
+
+	case AADDV:
+	case AADDVU:	return 0x21<<15;
+	case ASUBV:
+	case ASUBVU:	return 0x23<<15;
+	case AREM:	return 0x41<<15;
+	case ADIV:	return 0x40<<15;
+	case AREMU:	return 0x43<<15;
+	case ADIVU:	return 0x42<<15;
+	case AMUL:
+	case AMULU:	return 0x38<<15;
+
+	case AREMV:	return 0x45<<15;
+	case ADIVV:	return 0x44<<15;
+	case AREMVU:	return 0x47<<15;
+	case ADIVVU:	return 0x46<<15;
+	case AMULV:
+	case AMULVU:	return 0x3b<<15;
+
+	case AJMP:	return 0x13<<26;
+	case AJAL:	return (0x13<<26)|1;
+
+	case ABREAK:	return 0x54<<15;
+	case ASYSCALL:	return 0x56<<15;
+
+	case ADIVF:	return 0x20d<<15;
+	case ADIVD:	return 0x20e<<15;
+	case AMULF:	return 0x209<<15;
+	case AMULD:	return 0x20a<<15;
+	case ASUBF:	return 0x205<<15;
+	case ASUBD:	return 0x206<<15;
+	case AADDF:	return 0x201<<15;
+	case AADDD:	return 0x202<<15;
+
+	case ATRUNCFV:	return 0x46a9<<10;
+	case ATRUNCDV:	return 0x46aa<<10;
+	case ATRUNCFW:	return 0x46a1<<10;
+	case ATRUNCDW:	return 0x46a2<<10;
+	case AMOVFV:	return 0x46c9<<10;
+	case AMOVDV:	return 0x46ca<<10;
+	case AMOVVF:	return 0x4746<<10;
+	case AMOVVD:	return 0x474a<<10;
+
+	case AMOVFW:	return 0x46c1<<10;
+	case AMOVDW:	return 0x46c2<<10;
+	case AMOVWF:	return 0x4744<<10;
+	case AMOVDF:	return 0x4646<<10;
+	case AMOVWD:	return 0x4748<<10;
+	case AMOVFD:	return 0x4649<<10;
+	case AABSF:	return 0x4501<<10;
+	case AABSD:	return 0x4502<<10;
+	case AMOVF:	return 0x4525<<10;
+	case AMOVD:	return 0x4526<<10;
+	case ANEGF:	return 0x4505<<10;
+	case ANEGD:	return 0x4506<<10;
+
+	case ACMPEQF:	return (0xc1<<20) | (0x4<<15);
+	case ACMPEQD:	return (0xc2<<20) | (0x4<<15);
+	case ACMPGTF:	return (0xc1<<20) | (0x3<<15);
+	case ACMPGTD:	return (0xc2<<20) | (0x3<<15);
+	case ACMPGEF:	return (0xc1<<20) | (0x7<<15);
+	case ACMPGED:	return (0xc2<<20) | (0x7<<15);
+	}
+	if(a >= ALAST)
+		diag("bad rrr %A+ALAST", a-ALAST);
+	else
+		diag("bad rrr %A", a);
+	return 0;
+}
+
+long
+opir(int a)
+{
+	switch(a){
+	case ALU12IW:	return 0xa<<25;
+	case ALU32ID:	return 0xb<<25;
+	}
+	diag("bad ir %d", a);
+	return 0;
+}
+
+long
+opirr(int a)
+{
+	switch(a) {
+	case AADD:
+	case AADDU:		return 0xa<<22;
+	case ASGT:		return 0x8<<22;
+	case ASGTU:		return 0x9<<22;
+	case AAND:		return 0xd<<22;
+	case AOR:		return 0xe<<22;
+	case AXOR:		return 0xf<<22;
+	case ASLL:		return 0x81<<15;
+	case ASRL:		return 0x89<<15;
+	case ASRA:		return 0x91<<15;
+
+	case AADDV:
+	case AADDVU:		return 0xb<<22;
+
+	case AJMP:		return 0x14<<26;
+	case AJAL:		return 0x15<<26;
+	case ABEQ:		return 0x16<<26;
+	case ABEQ+ALAST:	return 0x10<<26;			/* likely */
+	case ABNE:		return 0x17<<26;
+	case ABNE+ALAST:	return 0x11<<26;			/* likely */
+
+	case ABLEZ:
+	case ABGEZ:		return 0x19<<26;
+	case ABLTZ:
+	case ABGTZ:		return 0x18<<26;
+
+	case ABFPT:		return (0x12<<26) | (0x1<<8);
+	case ABFPF:		return 0x12<<26;
+
+	case AMOVB:
+	case AMOVBU:		return 0xa4<<22;
+	case AMOVH:
+	case AMOVHU:		return 0xa5<<22;
+	case AMOVW:		return 0xa6<<22;
+	case AMOVV:		return 0xa7<<22;
+	case AMOVF:		return 0xad<<22;
+	case AMOVD:		return 0xaf<<22;
+	case AMOVWL:		return 0xbc<<22;
+	case AMOVWR:		return 0xbd<<22;
+	case AMOVVL:		return 0xbe<<22;
+	case AMOVVR:		return 0xbf<<22;
+
+	case ABREAK:		return 0x18<<22;
+
+	case AMOVWL+ALAST:	return 0xb8<<22;
+	case AMOVWR+ALAST:	return 0xb9<<22;
+	case AMOVVL+ALAST:	return 0xba<<22;
+	case AMOVVR+ALAST:	return 0xbb<<22;
+	case AMOVB+ALAST:	return 0xa0<<22;
+	case AMOVBU+ALAST:	return 0xa8<<22;
+	case AMOVH+ALAST:	return 0xa1<<22;
+	case AMOVHU+ALAST:	return 0xa9<<22;
+	case AMOVW+ALAST:	return 0xa2<<22;
+	case AMOVV+ALAST:	return 0xa3<<22;
+	case AMOVF+ALAST:	return 0xac<<22;
+	case AMOVD+ALAST:	return 0xae<<22;
+
+	case ASLLV:
+	case ASLLV+ALAST:	return 0x41<<16;
+	case ASRLV:
+	case ASRLV+ALAST:	return 0x45<<16;
+	case ASRAV:
+	case ASRAV+ALAST:	return 0x49<<16;
+
+	case  ALU52ID:		return 0xc<<22;
+	}
+	if(a >= ALAST)
+		diag("bad irr %A+ALAST", a-ALAST);
+	else
+		diag("bad irr %A", a);
+	return 0;
+}
+
+int
+vshift(int a)
+{
+	switch(a){
+	case ASLLV:		return 1;
+	case ASRLV:		return 1;
+	case ASRAV:		return 1;
+	}
+	return 0;
+}
--- /dev/null
+++ b/sys/src/cmd/zl/compat.c
@@ -1,0 +1,2 @@
+#include	"l.h"
+#include	"../cc/compat"
--- /dev/null
+++ b/sys/src/cmd/zl/l.h
@@ -1,0 +1,341 @@
+#include	<u.h>
+#include	<libc.h>
+#include	<bio.h>
+#include	"../zc/z.out.h"
+#include	"../cc/compat.h"
+
+#ifndef	EXTERN
+#define	EXTERN	extern
+#endif
+
+#define	LIBNAMELEN	300
+
+typedef	struct	Adr	Adr;
+typedef	struct	Sym	Sym;
+typedef	struct	Autom	Auto;
+typedef	struct	Prog	Prog;
+typedef	struct	Optab	Optab;
+typedef	struct	Oprang	Oprang;
+typedef	uchar	Opcross[32][2][32];
+typedef	struct	Count	Count;
+
+#define	P		((Prog*)0)
+#define	S		((Sym*)0)
+#define	TNAME		(curtext&&curtext->from.sym?curtext->from.sym->name:noname)
+
+struct	Adr
+{
+	union
+	{
+		vlong	u0offset;
+		char*	u0sval;
+		Ieee*	u0ieee;
+	} u0;
+	union
+	{
+		Auto*	u1autom;
+		Sym*	u1sym;
+	} u1;
+	char	type;
+	char	reg;
+	char	name;
+	char	class;
+};
+
+#define	offset	u0.u0offset
+#define	sval	u0.u0sval
+#define	ieee	u0.u0ieee
+
+#define	autom	u1.u1autom
+#define	sym	u1.u1sym
+
+struct	Prog
+{
+	Adr	from;
+	Adr	to;
+	union
+	{
+		long	u0regused;
+		Prog*	u0forwd;
+	} u0;
+	Prog*	cond;
+	Prog*	link;
+	vlong	pc;
+	long	line;
+	uchar	mark;
+	uchar	optab;
+	char	as;
+	char	reg;
+};
+#define	regused	u0.u0regused
+#define	forwd	u0.u0forwd
+
+struct	Sym
+{
+	char	*name;
+	short	type;
+	short	version;
+	short	become;
+	short	frame;
+	vlong	value;
+	Sym*	link;
+};
+struct	Autom
+{
+	Sym*	asym;
+	Auto*	link;
+	long	aoffset;
+	short	type;
+};
+struct	Optab
+{
+	char	as;
+	char	a1;
+	char	a2;
+	char	a3;
+	char	type;
+	char	size;
+	char	param;
+};
+struct	Oprang
+{
+	Optab*	start;
+	Optab*	stop;
+};
+struct	Count
+{
+	long	count;
+	long	outof;
+};
+
+enum
+{
+	STEXT		= 1,
+	SDATA,
+	SBSS,
+	SDATA1,
+	SXREF,
+	SLEAF,
+	SFILE,
+	SCONST,
+	SSTRING,
+
+	C_NONE		= 0,
+	C_REG,
+	C_FREG,
+	C_FCCREG,
+	C_FCSREG,
+	C_ZCON,
+	C_SCON,
+	C_ADD0CON,
+	C_AND0CON,
+	C_ADDCON,
+	C_ANDCON,
+	C_UCON,
+	C_LCON,
+	C_SACON,
+	C_SECON,
+	C_LACON,
+	C_LECON,
+	C_SBRA,
+	C_LBRA,
+	C_SAUTO,
+	C_SEXT,
+	C_LAUTO,
+	C_LEXT,
+	C_ZOREG,
+	C_SOREG,
+	C_LOREG,
+	C_GOK,
+
+/* mark flags */
+	FOLL		= 1<<0,
+	LABEL		= 1<<1,
+	LEAF		= 1<<2,
+	SYNC		= 1<<3,
+	BRANCH		= 1<<4,
+	LOAD		= 1<<5,
+	FCMP		= 1<<6,
+
+	BIG		= 2046,
+	STRINGSZ	= 200,
+	NHASH		= 10007,
+	NHUNK		= 100000,
+	MINSIZ		= 64,
+	NENT		= 100,
+	MAXIO		= 8192,
+	MAXHIST		= 20,				/* limit of path elements for history symbols */
+};
+
+EXTERN union
+{
+	struct
+	{
+		uchar	obuf[MAXIO];			/* output buffer */
+		uchar	ibuf[MAXIO];			/* input buffer */
+	} u;
+	char	dbuf[1];
+} buf;
+
+#define	cbuf	u.obuf
+#define	xbuf	u.ibuf
+
+EXTERN	long	HEADR;			/* length of header */
+EXTERN	int	HEADTYPE;		/* type of header */
+EXTERN	vlong	INITDAT;		/* data location */
+EXTERN	vlong	INITRND;		/* data round above text location */
+EXTERN	vlong	INITTEXT;		/* text location */
+EXTERN	vlong	INITTEXTP;		/* text location (physical) */
+EXTERN	char*	INITENTRY;		/* entry point */
+EXTERN	long	autosize;
+EXTERN	Biobuf	bso;
+EXTERN	long	bsssize;
+EXTERN	int	cbc;
+EXTERN	uchar*	cbp;
+EXTERN	int	cout;
+EXTERN	Auto*	curauto;
+EXTERN	Auto*	curhist;
+EXTERN	Prog*	curp;
+EXTERN	Prog*	curtext;
+EXTERN	Prog*	datap;
+EXTERN	long	datsize;
+EXTERN	char	debug[128];
+EXTERN	Prog*	etextp;
+EXTERN	Prog*	firstp;
+EXTERN	char	fnuxi4[4];	/* for 3l [sic] */
+EXTERN	char	fnuxi8[8];
+EXTERN	char*	noname;
+EXTERN	Sym*	hash[NHASH];
+EXTERN	Sym*	histfrog[MAXHIST];
+EXTERN	int	histfrogp;
+EXTERN	int	histgen;
+EXTERN	char*	library[50];
+EXTERN	char*	libraryobj[50];
+EXTERN	int	libraryp;
+EXTERN	int	xrefresolv;
+EXTERN	char	inuxi1[1];
+EXTERN	char	inuxi2[2];
+EXTERN	char	inuxi4[4];
+EXTERN	char	inuxi8[8];
+EXTERN	Prog*	lastp;
+EXTERN	long	lcsize;
+EXTERN	char	literal[32];
+EXTERN	int	nerrors;
+EXTERN	vlong	instoffset;
+EXTERN	Opcross	opcross[10];
+EXTERN	Oprang	oprange[ALAST];
+EXTERN	char*	outfile;
+EXTERN	vlong	pc;
+EXTERN	uchar	repop[ALAST];
+EXTERN	long	symsize;
+EXTERN	Prog*	textp;
+EXTERN	vlong	textsize;
+EXTERN	int	version;
+EXTERN	char	xcmp[32][32];
+EXTERN	Prog	zprg;
+EXTERN	int	dtype;
+
+EXTERN	struct
+{
+	Count	branch;
+	Count	fcmp;
+	Count	load;
+	Count	mfrom;
+	Count	page;
+	Count	jump;
+	Count	store;
+} nop;
+
+extern	char*	anames[];
+extern	Optab	optab[];
+
+#pragma	varargck	type	"A"	int
+#pragma	varargck	type	"D"	Adr*
+#pragma	varargck	type	"N"	Adr*
+#pragma	varargck	type	"P"	Prog*
+#pragma	varargck	type	"S"	char*
+
+#pragma	varargck	argpos	diag 1
+
+int	Aconv(Fmt*);
+int	Dconv(Fmt*);
+int	Nconv(Fmt*);
+int	Pconv(Fmt*);
+int	Sconv(Fmt*);
+int	aclass(Adr*);
+void	addhist(long, int);
+void	addlibpath(char*);
+void	addnop(Prog*);
+void	append(Prog*, Prog*);
+void	asmb(void);
+void	asmlc(void);
+int	asmout(Prog*, Optab*, int);
+void	asmsym(void);
+vlong	atolwhex(char*);
+vlong	atovlwhex(char*);
+Prog*	brloop(Prog*);
+void	buildop(void);
+void	buildrep(int, int);
+void	cflush(void);
+int	cmp(int, int);
+void	cput(long);
+int	compound(Prog*);
+double	cputime(void);
+void	datblk(long, long, int);
+void	diag(char*, ...);
+void	dodata(void);
+void	doprof1(void);
+void	doprof2(void);
+vlong	entryvalue(void);
+void	errorexit(void);
+void	exchange(Prog*);
+int	find1(long, int);
+void	follow(void);
+void	histtoauto(void);
+vlong	ieeedtov(Ieee*);
+double	ieeedtod(Ieee*);
+long	ieeedtof(Ieee*);
+int	isint32(vlong);
+int	isuint32(uvlong);
+int	isnop(Prog*);
+void	ldobj(int, long, char*);
+void	loadlib(void);
+void	listinit(void);
+Sym*	lookup(char*, int);
+void	llput(vlong);
+void	llputl(vlong);
+void	lput(long);
+void	lputl(long);
+void	bput(long);
+void	mkfwd(void);
+void*	mysbrk(ulong);
+void	names(void);
+void	nocache(Prog*);
+void	noops(void);
+void	nopstat(char*, Count*);
+void	nuxiinit(void);
+void	objfile(char*);
+int	ocmp(const void*, const void*);
+long	opir(int);
+long	opirr(int);
+Optab*	oplook(Prog*);
+long	oprrr(int);
+void	patch(void);
+void	prasm(Prog*);
+void	prepend(Prog*, Prog*);
+Prog*	prg(void);
+int	pseudo(Prog*);
+void	putsymb(char*, int, vlong, int);
+long	regoff(Adr*);
+vlong	vregoff(Adr*);
+int	relinv(int);
+vlong	rnd(vlong, long);
+void	sched(Prog*, Prog*);
+void	span(void);
+void	strnput(char*, int);
+void	undef(void);
+void	wput(long);
+void	wputl(long);
+void	xdefine(char*, int, vlong);
+void	xfol(Prog*);
+void	xfol(Prog*);
--- /dev/null
+++ b/sys/src/cmd/zl/list.c
@@ -1,0 +1,263 @@
+#include "l.h"
+
+void
+listinit(void)
+{
+
+	fmtinstall('A', Aconv);
+	fmtinstall('D', Dconv);
+	fmtinstall('P', Pconv);
+	fmtinstall('S', Sconv);
+	fmtinstall('N', Nconv);
+}
+
+void
+prasm(Prog *p)
+{
+	print("%P\n", p);
+}
+
+int
+Pconv(Fmt *fp)
+{
+	char str[STRINGSZ], *s;
+	Prog *p;
+	int a;
+
+	p = va_arg(fp->args, Prog*);
+	curp = p;
+	a = p->as;
+	if(a == ADATA || a == ADYNT || a == AINIT)
+		sprint(str, "(%ld)	%A	%D/%d,%D",
+			p->line, a, &p->from, p->reg, &p->to);
+	else{
+		s = str;
+		s += sprint(s, "(%ld)", p->line);
+		if(p->reg == NREG)
+			sprint(s, "	%A	%D,%D",
+				a, &p->from, &p->to);
+		else
+		if(p->from.type != D_FREG)
+			sprint(s, "	%A	%D,R%d,%D",
+				a, &p->from, p->reg, &p->to);
+		else
+			sprint(s, "	%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;
+	long v;
+
+	a = va_arg(fp->args, Adr*);
+	switch(a->type) {
+
+	default:
+		sprint(str, "GOK-type(%d)", a->type);
+		break;
+
+	case D_NONE:
+		str[0] = 0;
+		if(a->name != D_NONE || a->reg != NREG || a->sym != S)
+			sprint(str, "%N(R%d)(NONE)", a, a->reg);
+		break;
+
+	case D_CONST:
+		sprint(str, "$%N", a);
+		if(a->reg != NREG)
+			sprint(str, "%N(R%d)(CONST)", a, a->reg);
+		break;
+
+	case D_OCONST:
+		sprint(str, "$*$%N", a);
+		if(a->reg != NREG)
+			sprint(str, "%N(R%d)(CONST)", a, a->reg);
+		break;
+
+	case D_OREG:
+		if(a->reg != NREG)
+			sprint(str, "%N(R%d)", a, a->reg);
+		else
+			sprint(str, "%N", a);
+		break;
+
+	case D_REG:
+		sprint(str, "R%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_FREG:
+		sprint(str, "F%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_FCCREG:
+		sprint(str, "FCC%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_FCSREG:
+		sprint(str, "FCSR%d", a->reg);
+		if(a->name != D_NONE || a->sym != S)
+			sprint(str, "%N(R%d)(REG)", a, a->reg);
+		break;
+
+	case D_BRANCH:	/* botch */
+		if(curp->cond != P) {
+			v = curp->cond->pc;
+			if(v >= INITTEXT)
+				v -= INITTEXT-HEADR;
+			if(a->sym != S)
+				sprint(str, "%s+%.5lux(BRANCH)", a->sym->name, v);
+			else
+				sprint(str, "%.5lux(BRANCH)", v);
+		} else
+			if(a->sym != S)
+				sprint(str, "%s+%lld(APC)", a->sym->name, a->offset);
+			else
+				sprint(str, "%lld(APC)", a->offset);
+		break;
+
+	case D_FCONST:
+		sprint(str, "$%e", ieeedtod(a->ieee));
+		break;
+
+	case D_SCONST:
+		sprint(str, "$\"%S\"", a->sval);
+		break;
+	}
+	return fmtstrcpy(fp, str);
+}
+
+int
+Nconv(Fmt *fp)
+{
+	char str[STRINGSZ];
+	Adr *a;
+	Sym *s;
+
+	a = va_arg(fp->args, Adr*);
+	s = a->sym;
+	switch(a->name) {
+	default:
+		sprint(str, "GOK-name(%d)", a->name);
+		break;
+
+	case D_NONE:
+		sprint(str, "%lld", a->offset);
+		break;
+
+	case D_EXTERN:
+		if(s == S)
+			sprint(str, "%lld(SB)", a->offset);
+		else
+			sprint(str, "%s+%lld(SB)", s->name, a->offset);
+		break;
+
+	case D_STATIC:
+		if(s == S)
+			sprint(str, "<>+%lld(SB)", a->offset);
+		else
+			sprint(str, "%s<>+%lld(SB)", s->name, a->offset);
+		break;
+
+	case D_AUTO:
+		if(s == S)
+			sprint(str, "%lld(SP)", a->offset);
+		else
+			sprint(str, "%s-%lld(SP)", s->name, -a->offset);
+		break;
+
+	case D_PARAM:
+		if(s == S)
+			sprint(str, "%lld(FP)", a->offset);
+		else
+			sprint(str, "%s+%lld(FP)", s->name, a->offset);
+		break;
+	}
+
+	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<sizeof(long); 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;
+		}
+		*p++ = (c>>6) + '0';
+		*p++ = ((c>>3) & 7) + '0';
+		*p++ = (c & 7) + '0';
+	}
+	*p = 0;
+	return fmtstrcpy(fp, str);
+}
+
+void
+diag(char *fmt, ...)
+{
+	char buf[STRINGSZ], *tn;
+	va_list arg;
+
+	tn = "??none??";
+	if(curtext != P && curtext->from.sym != S)
+		tn = curtext->from.sym->name;
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	print("%s: %s\n", tn, buf);
+
+	nerrors++;
+	if(nerrors > 10) {
+		print("too many errors\n");
+		errorexit();
+	}
+}
--- /dev/null
+++ b/sys/src/cmd/zl/mkfile
@@ -1,0 +1,33 @@
+</$objtype/mkfile
+
+TARG=zl
+OFILES=\
+	asm.$O\
+	list.$O\
+	noop.$O\
+	obj.$O\
+	optab.$O\
+	pass.$O\
+	span.$O\
+	enam.$O\
+	compat.$O\
+
+HFILES=\
+	l.h\
+	../zc/z.out.h\
+	../cc/compat.h\
+
+BIN=/$objtype/bin
+CFLAGS=$CFLAGS -. -I.
+</sys/src/cmd/mkone
+
+compat.$O:	../cc/compat
+
+enam.$O:	../zc/enam.c
+	$CC $CFLAGS ../zc/enam.c
+
+test:V:	$O.out
+	rm -f xxx
+	mv $O.out xxx
+	./xxx $OFILES
+	cmp $O.out xxx
--- /dev/null
+++ b/sys/src/cmd/zl/noop.c
@@ -1,0 +1,363 @@
+#include	"l.h"
+
+void
+noops(void)
+{
+	Prog *p, *q, *q1;
+	int o, curframe, curbecome, maxbecome;
+
+	/*
+	 * find leaf subroutines
+	 * become sizes
+	 * frame sizes
+	 * strip NOPs
+	 * expand RET
+	 * expand BECOME pseudo
+	 */
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f noops\n", cputime());
+	Bflush(&bso);
+
+	curframe = 0;
+	curbecome = 0;
+	maxbecome = 0;
+	curtext = 0;
+
+	q = P;
+	for(p = firstp; p != P; p = p->link) {
+
+		/* find out how much arg space is used in this TEXT */
+		if(p->to.type == D_OREG && p->to.reg == REGSP)
+			if(p->to.offset > curframe)
+				curframe = p->to.offset;
+
+		switch(p->as) {
+		case ATEXT:
+			if(curtext && curtext->from.sym) {
+				curtext->from.sym->frame = curframe;
+				curtext->from.sym->become = curbecome;
+				if(curbecome > maxbecome)
+					maxbecome = curbecome;
+			}
+			curframe = 0;
+			curbecome = 0;
+
+			p->mark |= LABEL|LEAF|SYNC;
+			if(p->link)
+				p->link->mark |= LABEL;
+			curtext = p;
+			break;
+
+		/* too hard, just leave alone */
+		case AMOVW:
+		case AMOVV:
+			if(p->to.type == D_FCCREG ||
+			   p->to.type == D_FCSREG) {
+				p->mark |= LABEL|SYNC;
+				break;
+			}
+			if(p->from.type == D_FCCREG ||
+			   p->from.type == D_FCSREG) {
+				p->mark |= LABEL|SYNC;
+				break;
+			}
+			break;
+
+		/* too hard, just leave alone */
+		case ASYSCALL:
+		case AWORD:
+			p->mark |= LABEL|SYNC;
+			break;
+
+		case ANOR:
+			if(p->to.type == D_REG && p->to.reg == REGZERO)
+				p->mark |= LABEL|SYNC;
+			break;
+
+		case ARET:
+			/* special form of RET is BECOME */
+			if(p->from.type == D_CONST)
+				if(p->from.offset > curbecome)
+					curbecome = p->from.offset;
+
+			if(p->link != P)
+				p->link->mark |= LABEL;
+			break;
+
+		case ANOP:
+			q1 = p->link;
+			q->link = q1;		/* q is non-nop */
+			q1->mark |= p->mark;
+			continue;
+
+		case AJAL:
+			if(curtext != P)
+				curtext->mark &= ~LEAF;
+
+		case AJMP:
+		case ABEQ:
+		case ABGEZ:
+		case ABGTZ:
+		case ABLEZ:
+		case ABLTZ:
+		case ABNE:
+		case ABFPT:
+		case ABFPF:
+			p->mark |= BRANCH;
+			q1 = p->cond;
+			if(q1 != P) {
+				while(q1->as == ANOP) {
+					q1 = q1->link;
+					p->cond = q1;
+				}
+				if(!(q1->mark & LEAF))
+					q1->mark |= LABEL;
+			} else
+				p->mark |= LABEL;
+			q1 = p->link;
+			if(q1 != P)
+				q1->mark |= LABEL;
+			break;
+		}
+		q = p;
+	}
+
+	if(curtext && curtext->from.sym) {
+		curtext->from.sym->frame = curframe;
+		curtext->from.sym->become = curbecome;
+		if(curbecome > maxbecome)
+			maxbecome = curbecome;
+	}
+
+	if(debug['b'])
+		print("max become = %d\n", maxbecome);
+	xdefine("ALEFbecome", STEXT, maxbecome);
+
+	curtext = 0;
+	for(p = firstp; p != P; p = p->link) {
+		switch(p->as) {
+		case ATEXT:
+			curtext = p;
+			break;
+		case AJAL:
+			if(curtext != P && curtext->from.sym != S && curtext->to.offset >= 0) {
+				o = maxbecome - curtext->from.sym->frame;
+				if(o <= 0)
+					break;
+				/* calling a become or calling a variable */
+				if(p->to.sym == S || p->to.sym->become) {
+					curtext->to.offset += o;
+					if(debug['b']) {
+						curp = p;
+						print("%D calling %D increase %d\n",
+							&curtext->from, &p->to, o);
+					}
+				}
+			}
+			break;
+		}
+	}
+
+	for(p = firstp; p != P; p = p->link) {
+		o = p->as;
+		switch(o) {
+		case ATEXT:
+			curtext = p;
+			autosize = p->to.offset + 8;
+			if(autosize <= 8)
+			if(curtext->mark & LEAF) {
+				p->to.offset = -8;
+				autosize = 0;
+			}
+
+			q = p;
+			if(autosize) {
+				if(autosize & 7)
+					Bprint(&bso, "odd stack in: %s\n",
+						curtext->from.sym->name);
+				q = prg();
+				q->as = AADDV;
+				q->line = p->line;
+				q->from.type = D_CONST;
+				q->from.offset = -autosize;
+				q->to.type = D_REG;
+				q->to.reg = REGSP;
+
+				q->link = p->link;
+				p->link = q;
+			} else
+			if(!(curtext->mark & LEAF)) {
+				if(debug['v'])
+					Bprint(&bso, "save suppressed in: %s\n",
+						curtext->from.sym->name);
+				Bflush(&bso);
+				curtext->mark |= LEAF;
+			}
+
+			if(curtext->mark & LEAF) {
+				if(curtext->from.sym)
+					curtext->from.sym->type = SLEAF;
+				break;
+			}
+
+			q1 = prg();
+			q1->as = AMOVV;
+			q1->line = p->line;
+			q1->from.type = D_REG;
+			q1->from.reg = REGLINK;
+			q1->to.type = D_OREG;
+			q1->from.offset = 0;
+			q1->to.reg = REGSP;
+
+			q1->link = q->link;
+			q->link = q1;
+			break;
+
+		case ARET:
+			nocache(p);
+			if(p->from.type == D_CONST)
+				goto become;
+			if(curtext->mark & LEAF) {
+				if(!autosize) {
+					p->as = AJMP;
+					p->from = zprg.from;
+					p->to.type = D_OREG;
+					p->to.offset = 0;
+					p->to.reg = REGLINK;
+					p->mark |= BRANCH;
+					break;
+				}
+
+				p->as = AADDV;
+				p->from.type = D_CONST;
+				p->from.offset = autosize;
+				p->to.type = D_REG;
+				p->to.reg = REGSP;
+
+				q = prg();
+				q->as = AJMP;
+				q->line = p->line;
+				q->to.type = D_OREG;
+				q->to.offset = 0;
+				q->to.reg = REGLINK;
+				q->mark |= BRANCH;
+
+				q->link = p->link;
+				p->link = q;
+				break;
+			}
+			p->as = AMOVV;
+			p->from.type = D_OREG;
+			p->from.offset = 0;
+			p->from.reg = REGSP;
+			p->to.type = D_REG;
+			p->to.reg = 2;
+
+			q = p;
+			if(autosize) {
+				q = prg();
+				q->as = AADDV;
+				q->line = p->line;
+				q->from.type = D_CONST;
+				q->from.offset = autosize;
+				q->to.type = D_REG;
+				q->to.reg = REGSP;
+
+				q->link = p->link;
+				p->link = q;
+			}
+
+			q1 = prg();
+			q1->as = AJMP;
+			q1->line = p->line;
+			q1->to.type = D_OREG;
+			q1->to.offset = 0;
+			q1->to.reg = 2;
+			q1->mark |= BRANCH;
+
+			q1->link = q->link;
+			q->link = q1;
+			break;
+
+		become:
+			if(curtext->mark & LEAF) {
+
+				q = prg();
+				q->line = p->line;
+				q->as = AJMP;
+				q->from = zprg.from;
+				q->to = p->to;
+				q->cond = p->cond;
+				q->link = p->link;
+				q->mark |= BRANCH;
+				p->link = q;
+
+				p->as = AADDV;
+				p->from = zprg.from;
+				p->from.type = D_CONST;
+				p->from.offset = autosize;
+				p->to = zprg.to;
+				p->to.type = D_REG;
+				p->to.reg = REGSP;
+
+				break;
+			}
+			q = prg();
+			q->line = p->line;
+			q->as = AJMP;
+			q->from = zprg.from;
+			q->to = p->to;
+			q->cond = p->cond;
+			q->link = p->link;
+			q->mark |= BRANCH;
+			p->link = q;
+
+			q = prg();
+			q->line = p->line;
+			q->as = AADDV;
+			q->from.type = D_CONST;
+			q->from.offset = autosize;
+			q->to.type = D_REG;
+			q->to.reg = REGSP;
+			q->link = p->link;
+			p->link = q;
+
+			p->as = AMOVV;
+			p->from = zprg.from;
+			p->from.type = D_OREG;
+			p->from.offset = 0;
+			p->from.reg = REGSP;
+			p->to = zprg.to;
+			p->to.type = D_REG;
+			p->to.reg = REGLINK;
+
+			break;
+		}
+	}
+}
+
+void
+addnop(Prog *p)
+{
+	Prog *q;
+
+	q = prg();
+	q->as = ANOR;
+	q->line = p->line;
+	q->from.type = D_REG;
+	q->from.reg = REGZERO;
+	q->to.type = D_REG;
+	q->to.reg = REGZERO;
+
+	q->link = p->link;
+	p->link = q;
+}
+
+void
+nocache(Prog *p)
+{
+	p->optab = 0;
+	p->from.class = 0;
+	p->to.class = 0;
+}
--- /dev/null
+++ b/sys/src/cmd/zl/obj.c
@@ -1,0 +1,1391 @@
+#define	EXTERN
+#include	"l.h"
+#include	<ar.h>
+
+#ifndef	DEFAULT
+#define	DEFAULT	'9'
+#endif
+
+char	*noname		= "<none>";
+char	symname[]	= SYMDEF;
+char	thechar		= 'z';
+char	*thestring 	= "loong";
+
+char**	libdir;
+int	nlibdir	= 0;
+static	int	maxlibdir = 0;
+
+/*
+ *	-H0				no header
+ *	-H2 -T0x100028 -R0x100000	is plan9 format
+ *	-H6 -R0x10000			no header with segments padded to pages
+ *	-H7				is elf
+ */
+
+void
+usage(void)
+{
+	diag("usage: %s [-options] objects", argv0);
+	errorexit();
+}
+
+void
+main(int argc, char *argv[])
+{
+	int c;
+	char *a;
+
+	Binit(&bso, 1, OWRITE);
+	cout = -1;
+	listinit();
+	outfile = 0;
+	nerrors = 0;
+	curtext = P;
+	INITTEXT = -1;
+	INITDAT = -1;
+	HEADTYPE = -1;
+	INITTEXTP = -1;
+	INITRND = -1;
+	INITENTRY = 0;
+
+	ARGBEGIN {
+	default:
+		c = ARGC();
+		if(c >= 0 && c < sizeof(debug))
+			debug[c]++;
+		break;
+	case 'o':
+		outfile = ARGF();
+		break;
+	case 'E':
+		a = ARGF();
+		if(a)
+			INITENTRY = a;
+		break;
+	case 'T':
+		a = ARGF();
+		if(a)
+			INITTEXT = atolwhex(a);
+		break;
+	case 'P':
+		a = ARGF();
+		if(a)
+			INITTEXTP = atolwhex(a);
+		break;
+	case 'D':
+		a = ARGF();
+		if(a)
+			INITDAT = atolwhex(a);
+		break;
+	case 'R':
+		a = ARGF();
+		if(a)
+			INITRND = atolwhex(a);
+		break;
+	case 'H':
+		a = ARGF();
+		if(a)
+			HEADTYPE = atolwhex(a);
+		break;
+	case 'L':
+		addlibpath(EARGF(usage()));
+		break;
+	} ARGEND
+
+	USED(argc);
+
+	if(*argv == 0)
+		usage();
+	if(!debug['9'] && !debug['U'] && !debug['B'])
+		debug[DEFAULT] = 1;
+	if(HEADTYPE == -1) {
+		if(debug['U'])
+			HEADTYPE = 0;
+		if(debug['B'])
+			HEADTYPE = 1;
+		if(debug['9'])
+			HEADTYPE = 2;
+	}
+	switch(HEADTYPE) {
+	default:
+		diag("unknown -H option");
+		errorexit();
+	case 0:	/* no header */
+	case 6:	/* no header, padded */
+		HEADR = 0L;
+		if(INITTEXT == -1)
+			INITTEXT = 0;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 4;
+		break;
+	case 2:
+		HEADR = 40L;
+		if(INITDAT == -1)
+			INITDAT = 0;
+		if(INITRND == -1)
+			INITRND = 0x10000;
+		if(INITTEXT == -1)
+			INITTEXT = 0x10000+HEADR;
+		break;
+	}
+	if (INITTEXTP == -1)
+		INITTEXTP = INITTEXT;
+	if(INITDAT != 0 && INITRND != 0)
+		print("warning: -D%#llux is ignored because of -R%#llux\n",
+			INITDAT, INITRND);
+	if(debug['v'])
+		Bprint(&bso, "HEADER = -H%d -T%#llux -D%#llux -R%#llux\n",
+			HEADTYPE, INITTEXT, INITDAT, INITRND);
+	Bflush(&bso);
+	zprg.as = AGOK;
+	zprg.reg = NREG;
+	zprg.from.name = D_NONE;
+	zprg.from.type = D_NONE;
+	zprg.from.reg = NREG;
+	zprg.to = zprg.from;
+	buildop();
+	histgen = 0;
+	textp = P;
+	datap = P;
+	pc = 0;
+	dtype = 4;
+	if(outfile == 0) {
+		static char name[20];
+
+		snprint(name, sizeof name, "%c.out", thechar);
+		outfile = name;
+	}
+	cout = create(outfile, 1, 0775);
+	if(cout < 0) {
+		diag("cannot create %s: %r", outfile);
+		errorexit();
+	}
+	nuxiinit();
+
+	version = 0;
+	cbp = buf.cbuf;
+	cbc = sizeof(buf.cbuf);
+	firstp = prg();
+	lastp = firstp;
+
+	if(INITENTRY == 0) {
+		INITENTRY = "_main";
+		if(debug['p'])
+			INITENTRY = "_mainp";
+		if(!debug['l'])
+			lookup(INITENTRY, 0)->type = SXREF;
+	} else if(!(*INITENTRY >= '0' && *INITENTRY <= '9'))
+		lookup(INITENTRY, 0)->type = SXREF;
+
+	while(*argv)
+		objfile(*argv++);
+	if(!debug['l'])
+		loadlib();
+	firstp = firstp->link;
+	if(firstp == P)
+		goto out;
+	patch();
+	if(debug['p'])
+		if(debug['1'])
+			doprof1();
+		else
+			doprof2();
+	dodata();
+	follow();
+	if(firstp == P)
+		goto out;
+	noops();
+	span();
+	asmb();
+	undef();
+
+out:
+	if(debug['v']) {
+		Bprint(&bso, "%5.2f cpu time\n", cputime());
+		Bprint(&bso, "%d sizeof adr\n", sizeof(Adr));
+		Bprint(&bso, "%d sizeof prog\n", sizeof(Prog));
+	}
+	Bflush(&bso);
+	errorexit();
+}
+
+void
+addlibpath(char *arg)
+{
+	char **p;
+
+	if(nlibdir >= maxlibdir) {
+		if(maxlibdir == 0)
+			maxlibdir = 8;
+		else
+			maxlibdir *= 2;
+		p = malloc(maxlibdir*sizeof(*p));
+		if(p == nil) {
+			diag("out of memory");
+			errorexit();
+		}
+		memmove(p, libdir, nlibdir*sizeof(*p));
+		free(libdir);
+		libdir = p;
+	}
+	libdir[nlibdir++] = strdup(arg);
+}
+
+void
+loadlib(void)
+{
+	int i;
+	long h;
+	Sym *s;
+
+loop:
+	xrefresolv = 0;
+	for(i=0; i<libraryp; i++) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f autolib: %s (from %s)\n", cputime(), library[i], libraryobj[i]);
+		objfile(library[i]);
+	}
+	if(xrefresolv)
+	for(h=0; h<nelem(hash); h++)
+	for(s = hash[h]; s != S; s = s->link)
+		if(s->type == SXREF)
+			goto loop;
+}
+
+void
+errorexit(void)
+{
+
+	if(nerrors) {
+		if(cout >= 0)
+			remove(outfile);
+		exits("error");
+	}
+	exits(0);
+}
+
+void
+objfile(char *file)
+{
+	vlong off, esym, cnt, l;
+	int f, work;
+	Sym *s;
+	char magbuf[SARMAG];
+	char name[100], pname[150];
+	struct ar_hdr arhdr;
+	char *e, *start, *stop;
+
+	if(file[0] == '-' && file[1] == 'l') {
+		if(debug['9'])
+			sprint(name, "/%s/lib/lib", thestring);
+		else
+			sprint(name, "/usr/%clib/lib", thechar);
+		strcat(name, file+2);
+		strcat(name, ".a");
+		file = name;
+	}
+	if(debug['v'])
+		Bprint(&bso, "%5.2f ldobj: %s\n", cputime(), file);
+	Bflush(&bso);
+	f = open(file, 0);
+	if(f < 0) {
+		diag("cannot open file: %s", file);
+		errorexit();
+	}
+	l = read(f, magbuf, SARMAG);
+	if(l != SARMAG || strncmp(magbuf, ARMAG, SARMAG)){
+		/* load it as a regular file */
+		l = seek(f, 0L, 2);
+		seek(f, 0L, 0);
+		ldobj(f, l, file);
+		close(f);
+		return;
+	}
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f ldlib: %s\n", cputime(), file);
+	l = read(f, &arhdr, SAR_HDR);
+	if(l != SAR_HDR) {
+		diag("%s: short read on archive file symbol header", file);
+		goto out;
+	}
+	if(strncmp(arhdr.name, symname, strlen(symname))) {
+		diag("%s: first entry not symbol header", file);
+		goto out;
+	}
+
+	esym = SARMAG + SAR_HDR + atolwhex(arhdr.size);
+	off = SARMAG + SAR_HDR;
+
+	/*
+	 * just bang the whole symbol file into memory
+	 */
+	seek(f, off, 0);
+	cnt = esym - off;
+	start = malloc(cnt + 10);
+	cnt = read(f, start, cnt);
+	if(cnt <= 0){
+		close(f);
+		return;
+	}
+	stop = &start[cnt];
+	memset(stop, 0, 10);
+
+	work = 1;
+	while(work){
+		if(debug['v'])
+			Bprint(&bso, "%5.2f library pass: %s\n", cputime(), file);
+		Bflush(&bso);
+		work = 0;
+		for(e = start; e < stop; e = strchr(e+5, 0) + 1) {
+			s = lookup(e+5, 0);
+			if(s->type != SXREF)
+				continue;
+			sprint(pname, "%s(%s)", file, s->name);
+			if(debug['v'])
+				Bprint(&bso, "%5.2f library: %s\n", cputime(), pname);
+			Bflush(&bso);
+			l = e[1] & 0xff;
+			l |= (e[2] & 0xff) << 8;
+			l |= (e[3] & 0xff) << 16;
+			l |= (e[4] & 0xff) << 24;
+			seek(f, l, 0);
+			/* need readn to read the dumps (at least) */
+			l = readn(f, &arhdr, SAR_HDR);
+			if(l != SAR_HDR)
+				goto bad;
+			if(strncmp(arhdr.fmag, ARFMAG, sizeof(arhdr.fmag)))
+				goto bad;
+			l = atolwhex(arhdr.size);
+			ldobj(f, l, pname);
+			if(s->type == SXREF) {
+				diag("%s: failed to load: %s", file, s->name);
+				errorexit();
+			}
+			work = 1;
+			xrefresolv = 1;
+		}
+	}
+	return;
+
+bad:
+	diag("%s: bad or out of date archive", file);
+out:
+	close(f);
+}
+
+int
+zaddr(uchar *p, Adr *a, Sym *h[])
+{
+	int i, c;
+	long l;
+	Sym *s;
+	Auto *u;
+
+	c = p[2];
+	if(c < 0 || c > NSYM){
+		print("sym out of range: %d\n", c);
+		p[0] = ALAST+1;
+		return 0;
+	}
+	a->type = p[0];
+	a->reg = p[1];
+	a->sym = h[c];
+	a->name = p[3];
+	c = 4;
+
+	if(a->reg < 0 || a->reg > NREG) {
+		print("register out of range %d\n", a->reg);
+		p[0] = ALAST+1;
+		return 0;	/*  force real diagnostic */
+	}
+
+	switch(a->type) {
+	default:
+		print("unknown type %d\n", a->type);
+		p[0] = ALAST+1;
+		return 0;	/*  force real diagnostic */
+
+	case D_NONE:
+	case D_REG:
+	case D_FREG:
+	case D_FCCREG:
+	case D_FCSREG:
+		break;
+
+	case D_BRANCH:
+	case D_OREG:
+	case D_CONST:
+	case D_OCONST:
+		l = p[4] | (p[5]<<8) | (p[6]<<16) | (p[7]<<24);
+		a->offset = l;
+		c += 4;
+		break;
+
+	case D_SCONST:
+		a->sval = malloc(NSNAME);
+		memmove(a->sval, p+4, NSNAME);
+		c += NSNAME;
+		break;
+
+	case D_FCONST:
+		a->ieee = malloc(sizeof(Ieee));
+		a->ieee->l = p[4] | (p[5]<<8) |
+			(p[6]<<16) | (p[7]<<24);
+		a->ieee->h = p[8] | (p[9]<<8) |
+			(p[10]<<16) | (p[11]<<24);
+		c += 8;
+		break;
+
+	case D_VCONST:
+		l = p[4] | (p[5]<<8) | (p[6]<<16) | (p[7]<<24);
+		a->offset = (uvlong)l & 0xfffffffful;
+		l = p[8] | (p[9]<<8) | (p[10]<<16) | (p[11]<<24);
+		a->offset |= (vlong)l << 32;
+		c += 8;
+		a->type = D_CONST;
+		break;
+	}
+	s = a->sym;
+	if(s == S)
+		return c;
+	i = a->name;
+	if(i != D_AUTO && i != D_PARAM)
+		return c;
+
+	l = a->offset;
+	for(u=curauto; u; u=u->link)
+		if(u->asym == s)
+		if(u->type == i) {
+			if(u->aoffset > l)
+				u->aoffset = l;
+			return c;
+		}
+
+	u = malloc(sizeof(Auto));
+	memset(u, 0, sizeof(Auto));
+	u->link = curauto;
+	curauto = u;
+	u->asym = s;
+	u->aoffset = l;
+	u->type = i;
+	return c;
+}
+
+void
+addlib(char *obj)
+{
+	char fn1[LIBNAMELEN], comp[LIBNAMELEN], *p, *name;
+	int i, search;
+
+	if(histfrogp <= 0)
+		return;
+
+	name = fn1;
+	search = 0;
+	if(histfrog[0]->name[1] == '/') {
+		sprint(name, "");
+		i = 1;
+	} else if(histfrog[0]->name[1] == '.') {
+		sprint(name, ".");
+		i = 0;
+	} else {
+		sprint(name, "");
+		i = 0;
+		search = 1;
+	}
+
+	for(; i<histfrogp; i++) {
+		snprint(comp, sizeof comp, histfrog[i]->name+1);
+		for(;;) {
+			p = strstr(comp, "$O");
+			if(p == 0)
+				break;
+			memmove(p+1, p+2, strlen(p+2)+1);
+			p[0] = thechar;
+		}
+		for(;;) {
+			p = strstr(comp, "$M");
+			if(p == 0)
+				break;
+			if(strlen(comp)+strlen(thestring)-2+1 >= sizeof comp) {
+				diag("library component too long");
+				return;
+			}
+			memmove(p+strlen(thestring), p+2, strlen(p+2)+1);
+			memmove(p, thestring, strlen(thestring));
+		}
+		if(strlen(fn1) + strlen(comp) + 3 >= sizeof(fn1)) {
+			diag("library component too long");
+			return;
+		}
+		if(i > 0 || !search)
+			strcat(fn1, "/");
+		strcat(fn1, comp);
+	}
+	for(i=0; i<libraryp; i++)
+		if(strcmp(name, library[i]) == 0)
+			return;
+	if(libraryp == nelem(library)){
+		diag("too many autolibs; skipping %s", name);
+		return;
+	}
+
+	p = malloc(strlen(name) + 1);
+	strcpy(p, name);
+	library[libraryp] = p;
+	p = malloc(strlen(obj) + 1);
+	strcpy(p, obj);
+	libraryobj[libraryp] = p;
+	libraryp++;
+}
+
+void
+addhist(long line, int type)
+{
+	Auto *u;
+	Sym *s;
+	int i, j, k;
+
+	u = malloc(sizeof(Auto));
+	s = malloc(sizeof(Sym));
+	s->name = malloc(2*(histfrogp+1) + 1);
+
+	u->asym = s;
+	u->type = type;
+	u->aoffset = line;
+	u->link = curhist;
+	curhist = u;
+
+	j = 1;
+	for(i=0; i<histfrogp; i++) {
+		k = histfrog[i]->value;
+		s->name[j+0] = k>>8;
+		s->name[j+1] = k;
+		j += 2;
+	}
+}
+
+void
+histtoauto(void)
+{
+	Auto *l;
+
+	while(l = curhist) {
+		curhist = l->link;
+		l->link = curauto;
+		curauto = l;
+	}
+}
+
+void
+collapsefrog(Sym *s)
+{
+	int i;
+
+	/*
+	 * bad encoding of path components only allows
+	 * MAXHIST components. if there is an overflow,
+	 * first try to collapse xxx/..
+	 */
+	for(i=1; i<histfrogp; i++)
+		if(strcmp(histfrog[i]->name+1, "..") == 0) {
+			memmove(histfrog+i-1, histfrog+i+1,
+				(histfrogp-i-1)*sizeof(histfrog[0]));
+			histfrogp--;
+			goto out;
+		}
+
+	/*
+	 * next try to collapse .
+	 */
+	for(i=0; i<histfrogp; i++)
+		if(strcmp(histfrog[i]->name+1, ".") == 0) {
+			memmove(histfrog+i, histfrog+i+1,
+				(histfrogp-i-1)*sizeof(histfrog[0]));
+			goto out;
+		}
+
+	/*
+	 * last chance, just truncate from front
+	 */
+	memmove(histfrog+0, histfrog+1,
+		(histfrogp-1)*sizeof(histfrog[0]));
+
+out:
+	histfrog[histfrogp-1] = s;
+}
+
+void
+nopout(Prog *p)
+{
+	p->as = ANOP;
+	p->from.type = D_NONE;
+	p->to.type = D_NONE;
+}
+
+uchar*
+readsome(int f, uchar *buf, uchar *good, uchar *stop, int max)
+{
+	int n;
+
+	n = stop - good;
+	memmove(buf, good, stop - good);
+	stop = buf + n;
+	n = MAXIO - n;
+	if(n > max)
+		n = max;
+	n = read(f, stop, n);
+	if(n <= 0)
+		return 0;
+	return stop + n;
+}
+
+void
+ldobj(int f, long c, char *pn)
+{
+	vlong ipc;
+	Prog *p, *t;
+	uchar *bloc, *bsize, *stop;
+	Sym *h[NSYM], *s, *di;
+	int v, o, r, skip;
+
+	bsize = buf.xbuf;
+	bloc = buf.xbuf;
+	di = S;
+
+newloop:
+	memset(h, 0, sizeof(h));
+	version++;
+	histfrogp = 0;
+	ipc = pc;
+	skip = 0;
+
+loop:
+	if(c <= 0)
+		goto eof;
+	r = bsize - bloc;
+	if(r < 100 && r < c) {		/* enough for largest prog */
+		bsize = readsome(f, buf.xbuf, bloc, bsize, c);
+		if(bsize == 0)
+			goto eof;
+		bloc = buf.xbuf;
+		goto loop;
+	}
+	o = bloc[0];		/* as */
+	if(o <= AXXX || o >= ALAST) {
+		diag("%s: line %lld: opcode out of range %d", pn, pc-ipc, o);
+		print("	probably not a .%c file\n", thechar);
+		errorexit();
+	}
+	if(o == ANAME || o == ASIGNAME) {
+		if(o == ASIGNAME) {
+			bloc += 4;
+			c -= 4;
+		}
+		stop = memchr(&bloc[3], 0, bsize-&bloc[3]);
+		if(stop == 0){
+			bsize = readsome(f, buf.xbuf, bloc, bsize, c);
+			if(bsize == 0)
+				goto eof;
+			bloc = buf.xbuf;
+			stop = memchr(&bloc[3], 0, bsize-&bloc[3]);
+			if(stop == 0){
+				fprint(2, "%s: name too long\n", pn);
+				errorexit();
+			}
+		}
+		v = bloc[1];	/* type */
+		o = bloc[2];	/* sym */
+		bloc += 3;
+		c -= 3;
+
+		r = 0;
+		if(v == D_STATIC)
+			r = version;
+		s = lookup((char*)bloc, r);
+		c -= &stop[1] - bloc;
+		bloc = stop + 1;
+
+		if(debug['W'])
+			print("	ANAME	%s\n", s->name);
+		h[o] = s;
+		if((v == D_EXTERN || v == D_STATIC) && s->type == 0)
+			s->type = SXREF;
+		if(v == D_FILE) {
+			if(s->type != SFILE) {
+				histgen++;
+				s->type = SFILE;
+				s->value = histgen;
+			}
+			if(histfrogp < MAXHIST) {
+				histfrog[histfrogp] = s;
+				histfrogp++;
+			} else
+				collapsefrog(s);
+		}
+		goto loop;
+	}
+
+	p = prg();
+	p->as = o;
+	p->reg = bloc[1] & 0x7f;
+	p->line = bloc[2] | (bloc[3]<<8) | (bloc[4]<<16) | (bloc[5]<<24);
+
+	r = zaddr(bloc+6, &p->from, h) + 6;
+	r += zaddr(bloc+r, &p->to, h);
+	bloc += r;
+	c -= r;
+
+	if(p->reg < 0 || p->reg > NREG)
+		diag("register out of range %d", p->reg);
+
+	p->link = P;
+	p->cond = P;
+
+	if(debug['W'])
+		print("%P\n", p);
+
+	switch(o) {
+	case AHISTORY:
+		if(p->to.offset == -1) {
+			addlib(pn);
+			histfrogp = 0;
+			goto loop;
+		}
+		addhist(p->line, D_FILE);		/* 'z' */
+		if(p->to.offset)
+			addhist(p->to.offset, D_FILE1);	/* 'Z' */
+		histfrogp = 0;
+		goto loop;
+
+	case AEND:
+		histtoauto();
+		if(curtext != P)
+			curtext->to.autom = curauto;
+		curauto = 0;
+		curtext = P;
+		if(c)
+			goto newloop;
+		return;
+
+	case AGLOBL:
+		s = p->from.sym;
+		if(s == S) {
+			diag("GLOBL must have a name\n%P", p);
+			errorexit();
+		}
+		if(s->type == 0 || s->type == SXREF) {
+			s->type = SBSS;
+			s->value = 0;
+		}
+		if(s->type != SBSS) {
+			diag("redefinition: %s\n%P", s->name, p);
+			s->type = SBSS;
+			s->value = 0;
+		}
+		if(p->to.offset > s->value)
+			s->value = p->to.offset;
+		break;
+
+	case ADYNT:
+		if(p->to.sym == S) {
+			diag("DYNT without a sym\n%P", p);
+			break;
+		}
+		di = p->to.sym;
+		p->reg = 4;
+		if(di->type == SXREF) {
+			if(debug['z'])
+				Bprint(&bso, "%P set to %d\n", p, dtype);
+			di->type = SCONST;
+			di->value = dtype;
+			dtype += 4;
+		}
+		if(p->from.sym == S)
+			break;
+
+		p->from.offset = di->value;
+		p->from.sym->type = SDATA;
+		if(curtext == P) {
+			diag("DYNT not in text: %P", p);
+			break;
+		}
+		p->to.sym = curtext->from.sym;
+		p->to.type = D_CONST;
+		p->link = datap;
+		datap = p;
+		break;
+
+	case AINIT:
+		if(p->from.sym == S) {
+			diag("INIT without a sym\n%P", p);
+			break;
+		}
+		if(di == S) {
+			diag("INIT without previous DYNT\n%P", p);
+			break;
+		}
+		p->from.offset = di->value;
+		p->from.sym->type = SDATA;
+		p->link = datap;
+		datap = p;
+		break;
+
+	case ADATA:
+		if(p->from.sym == S) {
+			diag("DATA without a sym\n%P", p);
+			break;
+		}
+		p->link = datap;
+		datap = p;
+		break;
+
+	case AGOK:
+		diag("unknown opcode\n%P", p);
+		p->pc = pc;
+		pc++;
+		break;
+
+	case ATEXT:
+		if(curtext != P) {
+			histtoauto();
+			curtext->to.autom = curauto;
+			curauto = 0;
+		}
+		skip = 0;
+		curtext = p;
+		s = p->from.sym;
+		if(s == S) {
+			diag("TEXT must have a name\n%P", p);
+			errorexit();
+		}
+		autosize = p->to.offset;
+		if(autosize & 7) {
+			diag("stack frame not 8 multiple: %s\n%P", s->name, p);
+			autosize = autosize + 7 & ~7;
+			p->to.offset = autosize;
+		}
+		autosize += 8;
+		if(s->type != 0 && s->type != SXREF) {
+			if(p->reg & DUPOK) {
+				skip = 1;
+				goto casedef;
+			}
+			diag("redefinition: %s\n%P", s->name, p);
+		}
+		s->type = STEXT;
+		s->value = pc;
+		lastp->link = p;
+		lastp = p;
+		p->pc = pc;
+		pc++;
+		if(textp == P) {
+			textp = p;
+			etextp = p;
+			goto loop;
+		}
+		etextp->cond = p;
+		etextp = p;
+		break;
+
+	case ASUB:
+	case ASUBU:
+		if(p->from.type == D_CONST)
+		if(p->from.name == D_NONE) {
+			p->from.offset = -p->from.offset;
+			if(p->as == ASUB)
+				p->as = AADD;
+			else
+				p->as = AADDU;
+		}
+		goto casedef;
+
+	case ASUBV:
+	case ASUBVU:
+		if(p->from.type == D_CONST)
+		if(p->from.name == D_NONE) {
+			p->from.offset = -p->from.offset;
+			if(p->as == ASUBV)
+				p->as = AADDV;
+			else
+				p->as = AADDVU;
+		}
+		goto casedef;
+
+	case AMOVF:
+		if(skip)
+			goto casedef;
+
+		if(p->from.type == D_FCONST) {
+			/* size sb 9 max */
+			sprint(literal, "$%lux", ieeedtof(p->from.ieee));
+			s = lookup(literal, 0);
+			if(s->type == 0) {
+				s->type = SBSS;
+				s->value = 4;
+				t = prg();
+				t->as = ADATA;
+				t->line = p->line;
+				t->from.type = D_OREG;
+				t->from.sym = s;
+				t->from.name = D_EXTERN;
+				t->reg = 4;
+				t->to = p->from;
+				t->link = datap;
+				datap = t;
+			}
+			p->from.type = D_OREG;
+			p->from.sym = s;
+			p->from.name = D_EXTERN;
+			p->from.offset = 0;
+		}
+		goto casedef;
+
+	case AMOVD:
+		if(skip)
+			goto casedef;
+
+		if(p->from.type == D_FCONST) {
+			/* size sb 18 max */
+			sprint(literal, "$%lux.%lux",
+				p->from.ieee->l, p->from.ieee->h);
+			s = lookup(literal, 0);
+			if(s->type == 0) {
+				s->type = SBSS;
+				s->value = 8;
+				t = prg();
+				t->as = ADATA;
+				t->line = p->line;
+				t->from.type = D_OREG;
+				t->from.sym = s;
+				t->from.name = D_EXTERN;
+				t->reg = 8;
+				t->to = p->from;
+				t->link = datap;
+				datap = t;
+			}
+			p->from.type = D_OREG;
+			p->from.sym = s;
+			p->from.name = D_EXTERN;
+			p->from.offset = 0;
+		}
+		goto casedef;
+
+	case AMOVV:
+		if(skip)
+			goto casedef;
+
+		if(p->from.type == D_CONST)
+		if(!isint32(p->from.offset)) {
+			/* size sb 18 max */
+			sprint(literal, "$%llux", p->from.offset);
+			s = lookup(literal, 0);
+			if(s->type == 0) {
+				s->type = SBSS;
+				s->value = 8;
+				t = prg();
+				t->as = ADATA;
+				t->line = p->line;
+				t->from.type = D_OREG;
+				t->from.sym = s;
+				t->from.name = D_EXTERN;
+				t->reg = 8;
+				t->to = p->from;
+				t->link = datap;
+				datap = t;
+			}
+			p->from.type = D_OREG;
+			p->from.sym = s;
+			p->from.name = D_EXTERN;
+			p->from.offset = 0;
+		}
+		goto casedef;
+
+	default:
+	casedef:
+		if(skip)
+			nopout(p);
+
+		if(p->to.type == D_BRANCH)
+			p->to.offset += ipc;
+		lastp->link = p;
+		lastp = p;
+		p->pc = pc;
+		pc++;
+		break;
+	}
+	goto loop;
+
+eof:
+	diag("truncated object file: %s", pn);
+}
+
+Sym*
+lookup(char *symb, int v)
+{
+	Sym *s;
+	char *p;
+	long h;
+	int c, l;
+
+	h = v;
+	for(p=symb; c = *p; p++)
+		h = h+h+h + c;
+	l = (p - symb) + 1;
+	if(h < 0)
+		h = ~h;
+	h &= 0xffffff;		// XXX
+	h %= NHASH;
+	for(s = hash[h]; s != S; s = s->link)
+		if(s->version == v)
+		if(memcmp(s->name, symb, l) == 0)
+			return s;
+
+	s = malloc(sizeof(Sym));
+	memset(s, 0, sizeof(Sym));
+	s->name = malloc(l);
+	memmove(s->name, symb, l);
+
+	s->link = hash[h];
+	s->type = 0;
+	s->version = v;
+	s->value = 0;
+	hash[h] = s;
+	return s;
+}
+
+Prog*
+prg(void)
+{
+	Prog *p = malloc(sizeof(Prog));
+	*p = zprg;
+	return p;
+}
+
+void
+doprof1(void)
+{
+	Sym *s;
+	long n;
+	Prog *p, *q;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f profile 1\n", cputime());
+	Bflush(&bso);
+	s = lookup("__mcount", 0);
+	n = 1;
+	for(p = firstp->link; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			q = prg();
+			q->line = p->line;
+			q->link = datap;
+			datap = q;
+			q->as = ADATA;
+			q->from.type = D_OREG;
+			q->from.name = D_EXTERN;
+			q->from.offset = n*4;
+			q->from.sym = s;
+			q->reg = 4;
+			q->to = p->from;
+			q->to.type = D_CONST;
+
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AMOVW;
+			p->from.type = D_OREG;
+			p->from.name = D_EXTERN;
+			p->from.sym = s;
+			p->from.offset = n*4 + 4;
+			p->to.type = D_REG;
+			p->to.reg = REGTMP;
+
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AADDU;
+			p->from.type = D_CONST;
+			p->from.offset = 1;
+			p->to.type = D_REG;
+			p->to.reg = REGTMP;
+
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			p->link = q;
+			p = q;
+			p->as = AMOVW;
+			p->from.type = D_REG;
+			p->from.reg = REGTMP;
+			p->to.type = D_OREG;
+			p->to.name = D_EXTERN;
+			p->to.sym = s;
+			p->to.offset = n*4 + 4;
+
+			n += 2;
+			continue;
+		}
+	}
+	q = prg();
+	q->line = 0;
+	q->link = datap;
+	datap = q;
+
+	q->as = ADATA;
+	q->from.type = D_OREG;
+	q->from.name = D_EXTERN;
+	q->from.sym = s;
+	q->reg = 4;
+	q->to.type = D_CONST;
+	q->to.offset = n;
+
+	s->type = SBSS;
+	s->value = n*4;
+}
+
+void
+doprof2(void)
+{
+	Sym *s2, *s4;
+	Prog *p, *q, *q2, *ps2, *ps4;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f profile 2\n", cputime());
+	Bflush(&bso);
+
+	if(debug['e']){
+		s2 = lookup("_tracein", 0);
+		s4 = lookup("_traceout", 0);
+	}else{
+		s2 = lookup("_profin", 0);
+		s4 = lookup("_profout", 0);
+	}
+	if(s2->type != STEXT || s4->type != STEXT) {
+		if(debug['e'])
+			diag("_tracein/_traceout not defined %d %d", s2->type, s4->type);
+		else
+			diag("_profin/_profout not defined");
+		return;
+	}
+
+	ps2 = P;
+	ps4 = P;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			if(p->from.sym == s2) {
+				ps2 = p;
+				p->reg = 1;
+			}
+			if(p->from.sym == s4) {
+				ps4 = p;
+				p->reg = 1;
+			}
+		}
+	}
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT) {
+			if(p->reg & NOPROF) {
+				for(;;) {
+					q = p->link;
+					if(q == P)
+						break;
+					if(q->as == ATEXT)
+						break;
+					p = q;
+				}
+				continue;
+			}
+
+			/*
+			 * JAL	profin, R2
+			 */
+			q = prg();
+			q->line = p->line;
+			q->pc = p->pc;
+			q->link = p->link;
+			if(debug['e']){		/* embedded tracing */
+				q2 = prg();
+				p->link = q2;
+				q2->link = q;
+
+				q2->line = p->line;
+				q2->pc = p->pc;
+
+				q2->as = AJMP;
+				q2->to.type = D_BRANCH;
+				q2->to.sym = p->to.sym;
+				q2->cond = q->link;
+			}else
+				p->link = q;
+			p = q;
+			p->as = AJAL;
+			p->to.type = D_BRANCH;
+			p->cond = ps2;
+			p->to.sym = s2;
+
+			continue;
+		}
+		if(p->as == ARET) {
+			/*
+			 * RET (default)
+			 */
+			if(debug['e']){		/* embedded tracing */
+				q = prg();
+				q->line = p->line;
+				q->pc = p->pc;
+				q->link = p->link;
+				p->link = q;
+				p = q;
+			}
+			/*
+			 * RET
+			 */
+			q = prg();
+			q->as = ARET;
+			q->from = p->from;
+			q->to = p->to;
+			q->link = p->link;
+			p->link = q;
+
+			/*
+			 * JAL	profout
+			 */
+			p->as = AJAL;
+			p->from = zprg.from;
+			p->to = zprg.to;
+			p->to.type = D_BRANCH;
+			p->cond = ps4;
+			p->to.sym = s4;
+
+			p = q;
+
+			continue;
+		}
+	}
+}
+
+void
+nuxiinit(void)
+{
+	int i, c;
+
+	for(i=0; i<4; i++){
+		c = find1(0x04030201L, i+1);
+		if(i < 2)
+			inuxi2[i] = c;
+		if(i < 1)
+			inuxi1[i] = c;
+		inuxi4[i] = c;
+		inuxi8[i] = c;
+		inuxi8[i+4] = c+4;
+		fnuxi4[i] = c;
+		fnuxi8[i] = c;
+		fnuxi8[i+4] = c+4;
+	}
+	if(debug['v']) {
+		Bprint(&bso, "inuxi = ");
+		for(i=0; i<1; i++)
+			Bprint(&bso, "%d", inuxi1[i]);
+		Bprint(&bso, " ");
+		for(i=0; i<2; i++)
+			Bprint(&bso, "%d", inuxi2[i]);
+		Bprint(&bso, " ");
+		for(i=0; i<4; i++)
+			Bprint(&bso, "%d", inuxi4[i]);
+		Bprint(&bso, "\nfnuxi = ");
+		for(i=0; i<8; i++)
+			Bprint(&bso, "%d", inuxi8[i]);
+		Bprint(&bso, "\n");
+		for(i=0; i<4; i++)
+			Bprint(&bso, "%d", fnuxi4[i]);
+		Bprint(&bso, "\nfnuxi = ");
+		for(i=0; i<8; i++)
+			Bprint(&bso, "%d", fnuxi8[i]);
+		Bprint(&bso, "\n");
+	}
+	Bflush(&bso);
+}
+
+int
+find1(long l, int c)
+{
+	char *p;
+	int i;
+
+	p = (char*)&l;
+	for(i=0; i<4; i++)
+		if(*p++ == c)
+			return i;
+	return 0;
+}
+
+vlong
+ieeedtov(Ieee *ieeep)
+{
+	vlong v;
+
+	v = (vlong)ieeep->l & 0xffffffffLL;
+	v |= (vlong)ieeep->h << 32;
+	return v;
+}
+
+long
+ieeedtof(Ieee *ieeep)
+{
+	int exp;
+	long v;
+
+	if(ieeep->h == 0)
+		return 0;
+	exp = (ieeep->h>>20) & ((1L<<11)-1L);
+	exp -= (1L<<10) - 2L;
+	v = (ieeep->h & 0xfffffL) << 3;
+	v |= (ieeep->l >> 29) & 0x7L;
+	if((ieeep->l >> 28) & 1) {
+		v++;
+		if(v & 0x800000L) {
+			v = (v & 0x7fffffL) >> 1;
+			exp++;
+		}
+	}
+	if(exp <= -126 || exp >= 130)
+		diag("double fp to single fp overflow");
+	v |= ((exp + 126) & 0xffL) << 23;
+	v |= ieeep->h & 0x80000000L;
+	return v;
+}
+
+double
+ieeedtod(Ieee *ieeep)
+{
+	Ieee e;
+	double fr;
+	int exp;
+
+	if(ieeep->h & (1L<<31)) {
+		e.h = ieeep->h & ~(1L<<31);
+		e.l = ieeep->l;
+		return -ieeedtod(&e);
+	}
+	if(ieeep->l == 0 && ieeep->h == 0)
+		return 0;
+	fr = ieeep->l & ((1L<<16)-1L);
+	fr /= 1L<<16;
+	fr += (ieeep->l>>16) & ((1L<<16)-1L);
+	fr /= 1L<<16;
+	fr += (ieeep->h & (1L<<20)-1L) | (1L<<20);
+	fr /= 1L<<21;
+	exp = (ieeep->h>>20) & ((1L<<11)-1L);
+	exp -= (1L<<10) - 2L;
+	return ldexp(fr, exp);
+}
--- /dev/null
+++ b/sys/src/cmd/zl/optab.c
@@ -1,0 +1,216 @@
+#include	"l.h"
+
+Optab	optab[] =
+{
+	{ ATEXT,	C_LEXT,	C_NONE,	C_LCON, 	 0, 0, 0 },
+	{ ATEXT,	C_LEXT,	C_REG,	C_LCON, 	 0, 0, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_REG,		14, 8, 0 },
+	{ AMOVV,	C_REG,	C_NONE,	C_REG,		 1, 4, 0 },
+	{ AMOVB,	C_REG,	C_NONE,	C_REG,		12, 8, 0 },
+	{ AMOVBU,	C_REG,	C_NONE,	C_REG,		13, 4, 0 },
+	{ AMOVWU,	C_REG,	C_NONE,	C_REG,		14, 8, 0 },
+
+	{ ASUB,		C_REG,	C_REG,	C_REG,		 2, 4, 0 },
+	{ AADD,		C_REG,	C_REG,	C_REG,		 2, 4, 0 },
+	{ AAND,		C_REG,	C_REG,	C_REG,		 2, 4, 0 },
+	{ ASUB,		C_REG,	C_NONE,	C_REG,		 2, 4, 0 },
+	{ AADD,		C_REG,	C_NONE,	C_REG,		 2, 4, 0 },
+	{ AAND,		C_REG,	C_NONE,	C_REG,		 2, 4, 0 },
+
+	{ ASLL,		C_REG,	C_NONE,	C_REG,		 9, 4, 0 },
+	{ ASLL,		C_REG,	C_REG,	C_REG,		 9, 4, 0 },
+
+	{ AADDF,	C_FREG,	C_NONE,	C_FREG,		32, 4, 0 },
+	{ AADDF,	C_FREG,	C_REG,	C_FREG,		32, 4, 0 },
+	{ ACMPEQF,	C_FREG,	C_REG,	C_NONE,		32, 4, 0 },
+	{ AABSF,	C_FREG,	C_NONE,	C_FREG,		33, 4, 0 },
+	{ AMOVF,	C_FREG,	C_NONE,	C_FREG,		33, 4, 0 },
+	{ AMOVD,	C_FREG,	C_NONE,	C_FREG,		33, 4, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_SEXT,		 7, 4, REGSB },
+	{ AMOVV,	C_REG,	C_NONE,	C_SEXT,		 7, 4, REGSB },
+	{ AMOVB,	C_REG,	C_NONE,	C_SEXT,		 7, 4, REGSB },
+	{ AMOVBU,	C_REG,	C_NONE,	C_SEXT,		 7, 4, REGSB },
+	{ AMOVWL,	C_REG,	C_NONE,	C_SEXT,		 7, 4, REGSB },
+	{ AMOVW,	C_REG,	C_NONE,	C_SAUTO,	 7, 4, REGSP },
+	{ AMOVV,	C_REG,	C_NONE,	C_SAUTO,	 7, 4, REGSP },
+	{ AMOVB,	C_REG,	C_NONE,	C_SAUTO,	 7, 4, REGSP },
+	{ AMOVBU,	C_REG,	C_NONE,	C_SAUTO,	 7, 4, REGSP },
+	{ AMOVWL,	C_REG,	C_NONE,	C_SAUTO,	 7, 4, REGSP },
+	{ AMOVW,	C_REG,	C_NONE,	C_SOREG,	 7, 4, REGZERO },
+	{ AMOVV,	C_REG,	C_NONE,	C_SOREG,	 7, 4, REGZERO },
+	{ AMOVB,	C_REG,	C_NONE,	C_SOREG,	 7, 4, REGZERO },
+	{ AMOVBU,	C_REG,	C_NONE,	C_SOREG,	 7, 4, REGZERO },
+	{ AMOVWL,	C_REG,	C_NONE,	C_SOREG,	 7, 4, REGZERO },
+
+	{ AMOVW,	C_SEXT,	C_NONE,	C_REG,		 8, 4, REGSB },
+	{ AMOVV,	C_SEXT,	C_NONE,	C_REG,		 8, 4, REGSB },
+	{ AMOVB,	C_SEXT,	C_NONE,	C_REG,		 8, 4, REGSB },
+	{ AMOVBU,	C_SEXT,	C_NONE,	C_REG,		 8, 4, REGSB },
+	{ AMOVWL,	C_SEXT,	C_NONE,	C_REG,		 8, 4, REGSB },
+	{ AMOVW,	C_SAUTO,C_NONE,	C_REG,		 8, 4, REGSP },
+	{ AMOVV,	C_SAUTO,C_NONE,	C_REG,		 8, 4, REGSP },
+	{ AMOVB,	C_SAUTO,C_NONE,	C_REG,		 8, 4, REGSP },
+	{ AMOVBU,	C_SAUTO,C_NONE,	C_REG,		 8, 4, REGSP },
+	{ AMOVWL,	C_SAUTO,C_NONE,	C_REG,		 8, 4, REGSP },
+	{ AMOVW,	C_SOREG,C_NONE,	C_REG,		 8, 4, REGZERO },
+	{ AMOVV,	C_SOREG,C_NONE,	C_REG,		 8, 4, REGZERO },
+	{ AMOVB,	C_SOREG,C_NONE,	C_REG,		 8, 4, REGZERO },
+	{ AMOVBU,	C_SOREG,C_NONE,	C_REG,		 8, 4, REGZERO },
+	{ AMOVWL,	C_SOREG,C_NONE,	C_REG,		 8, 4, REGZERO },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_LEXT,		35, 16, REGSB },
+	{ AMOVV,	C_REG,	C_NONE,	C_LEXT,		35, 16, REGSB },
+	{ AMOVB,	C_REG,	C_NONE,	C_LEXT,		35, 16, REGSB },
+	{ AMOVBU,	C_REG,	C_NONE,	C_LEXT,		35, 16, REGSB },
+	{ AMOVW,	C_REG,	C_NONE,	C_LAUTO,	35, 16, REGSP },
+	{ AMOVV,	C_REG,	C_NONE,	C_LAUTO,	35, 16, REGSP },
+	{ AMOVB,	C_REG,	C_NONE,	C_LAUTO,	35, 16, REGSP },
+	{ AMOVBU,	C_REG,	C_NONE,	C_LAUTO,	35, 16, REGSP },
+	{ AMOVW,	C_REG,	C_NONE,	C_LOREG,	35, 16, REGZERO },
+	{ AMOVV,	C_REG,	C_NONE,	C_LOREG,	35, 16, REGZERO },
+	{ AMOVB,	C_REG,	C_NONE,	C_LOREG,	35, 16, REGZERO },
+	{ AMOVBU,	C_REG,	C_NONE,	C_LOREG,	35, 16, REGZERO },
+
+	{ AMOVW,	C_LEXT,	C_NONE,	C_REG,		36, 16, REGSB },
+	{ AMOVV,	C_LEXT,	C_NONE,	C_REG,		36, 16, REGSB },
+	{ AMOVB,	C_LEXT,	C_NONE,	C_REG,		36, 16, REGSB },
+	{ AMOVBU,	C_LEXT,	C_NONE,	C_REG,		36, 16, REGSB },
+	{ AMOVW,	C_LAUTO,C_NONE,	C_REG,		36, 16, REGSP },
+	{ AMOVV,	C_LAUTO,C_NONE,	C_REG,		36, 16, REGSP },
+	{ AMOVB,	C_LAUTO,C_NONE,	C_REG,		36, 16, REGSP },
+	{ AMOVBU,	C_LAUTO,C_NONE,	C_REG,		36, 16, REGSP },
+	{ AMOVW,	C_LOREG,C_NONE,	C_REG,		36, 16, REGZERO },
+	{ AMOVV,	C_LOREG,C_NONE,	C_REG,		36, 16, REGZERO },
+	{ AMOVB,	C_LOREG,C_NONE,	C_REG,		36, 16, REGZERO },
+	{ AMOVBU,	C_LOREG,C_NONE,	C_REG,		36, 16, REGZERO },
+
+	{ AMOVW,	C_SECON,C_NONE,	C_REG,		 3, 4, REGSB },
+	{ AMOVV,	C_SECON,C_NONE,	C_REG,		 3, 4, REGSB },
+	{ AMOVW,	C_SACON,C_NONE,	C_REG,		 3, 4, REGSP },
+	{ AMOVV,	C_SACON,C_NONE,	C_REG,		 3, 4, REGSP },
+	{ AMOVW,	C_LECON,C_NONE,	C_REG,		26, 12, REGSB },
+	{ AMOVV,	C_LECON,C_NONE,	C_REG,		26, 12, REGSB },
+	{ AMOVW,	C_LACON,C_NONE,	C_REG,		26, 12, REGSP },
+	{ AMOVV,	C_LACON,C_NONE,	C_REG,		26, 12, REGSP },
+	{ AMOVW,	C_ADDCON,C_NONE,C_REG,		 3, 4, REGZERO },
+	{ AMOVV,	C_ADDCON,C_NONE,C_REG,		 3, 4, REGZERO },
+	{ AMOVW,	C_ANDCON,C_NONE,C_REG,		 3, 4, REGZERO },
+	{ AMOVV,	C_ANDCON,C_NONE,C_REG,		 3, 4, REGZERO },
+
+	{ AMOVW,	C_UCON, C_NONE, C_REG,		24, 4, 0 },
+	{ AMOVV,	C_UCON, C_NONE, C_REG,		24, 4, 0 },
+	{ AMOVW,	C_LCON,	C_NONE,	C_REG,		19, 8, 0 },
+	{ AMOVV,	C_LCON,	C_NONE,	C_REG,		19, 8, 0 },
+
+	{ AMUL,		C_REG,	C_NONE,	C_REG,		 2, 4, 0 },
+	{ AMUL,		C_REG,	C_REG,	C_REG,		 2, 4, 0 },
+	{ AMULV,	C_REG,	C_NONE,	C_REG,		 2, 4, 0 },
+	{ AMULV,	C_REG,	C_REG,	C_REG,		 2, 4, 0 },
+
+	{ AADD,		C_ADD0CON,C_REG,C_REG,		 4, 4, 0 },
+	{ AADD,		C_ADD0CON,C_NONE,C_REG,		 4, 4, 0 },
+	{ AADD,		C_ANDCON,C_REG,	C_REG,		10, 8, 0 },
+	{ AADD,		C_ANDCON,C_NONE,C_REG,		10, 8, 0 },
+
+	{ AAND,		C_AND0CON,C_REG,C_REG,		 4, 4, 0 },
+	{ AAND,		C_AND0CON,C_NONE,C_REG,		 4, 4, 0 },
+	{ AAND,		C_ADDCON,C_REG,	C_REG,		10, 8, 0 },
+	{ AAND,		C_ADDCON,C_NONE,C_REG,		10, 8, 0 },
+
+	{ AADD,		C_UCON,	C_REG,	C_REG,		25, 8, 0 },
+	{ AADD,		C_UCON,	C_NONE,	C_REG,		25, 8, 0 },
+	{ AAND,		C_UCON,	C_REG,	C_REG,		25, 8, 0 },
+	{ AAND,		C_UCON,	C_NONE,	C_REG,		25, 8, 0 },
+
+	{ AADD,		C_LCON,	C_NONE,	C_REG,		23, 12, 0 },
+	{ AAND,		C_LCON,	C_NONE,	C_REG,		23, 12, 0 },
+	{ AADD,		C_LCON,	C_REG,	C_REG,		23, 12, 0 },
+	{ AAND,		C_LCON,	C_REG,	C_REG,		23, 12, 0 },
+
+	{ ASLL,		C_SCON,	C_REG,	C_REG,		16, 4, 0 },
+	{ ASLL,		C_SCON,	C_NONE,	C_REG,		16, 4, 0 },
+
+	{ ASYSCALL,	C_NONE,	C_NONE,	C_NONE,		 5, 4, 0 },
+
+	{ ABEQ,		C_REG,	C_REG,	C_SBRA,		 6, 4, 0 },
+	{ ABEQ,		C_REG,	C_NONE,	C_SBRA,		 6, 4, 0 },
+	{ ABLEZ,	C_REG,	C_NONE,	C_SBRA,		 6, 4, 0 },
+	{ ABFPT,	C_NONE,	C_NONE,	C_SBRA,		 6, 4, 0 },
+
+	{ AJMP,		C_NONE,	C_NONE,	C_LBRA,		11, 4, 0 },
+	{ AJAL,		C_NONE,	C_NONE,	C_LBRA,		11, 4, 0 },
+
+	{ AJMP,		C_NONE,	C_NONE,	C_ZOREG,	18, 4, REGZERO },
+	{ AJAL,		C_NONE,	C_NONE,	C_ZOREG,	18, 4, REGLINK },
+
+	{ AMOVW,	C_SEXT,	C_NONE,	C_FREG,		27, 4, REGSB },
+	{ AMOVF,	C_SEXT,	C_NONE,	C_FREG,		27, 4, REGSB },
+	{ AMOVD,	C_SEXT,	C_NONE,	C_FREG,		27, 4, REGSB },
+	{ AMOVW,	C_SAUTO,C_NONE,	C_FREG,		27, 4, REGSP },
+	{ AMOVF,	C_SAUTO,C_NONE,	C_FREG,		27, 4, REGSP },
+	{ AMOVD,	C_SAUTO,C_NONE,	C_FREG,		27, 4, REGSP },
+	{ AMOVW,	C_SOREG,C_NONE,	C_FREG,		27, 4, REGZERO },
+	{ AMOVF,	C_SOREG,C_NONE,	C_FREG,		27, 4, REGZERO },
+	{ AMOVD,	C_SOREG,C_NONE,	C_FREG,		27, 4, REGZERO },
+
+	{ AMOVW,	C_LEXT,	C_NONE,	C_FREG,		27, 16, REGSB },
+	{ AMOVF,	C_LEXT,	C_NONE,	C_FREG,		27, 16, REGSB },
+	{ AMOVD,	C_LEXT,	C_NONE,	C_FREG,		27, 16, REGSB },
+	{ AMOVW,	C_LAUTO,C_NONE,	C_FREG,		27, 16, REGSP },
+	{ AMOVF,	C_LAUTO,C_NONE,	C_FREG,		27, 16, REGSP },
+	{ AMOVD,	C_LAUTO,C_NONE,	C_FREG,		27, 16, REGSP },
+	{ AMOVW,	C_LOREG,C_NONE,	C_FREG,		27, 16, REGZERO },
+	{ AMOVF,	C_LOREG,C_NONE,	C_FREG,		27, 16, REGZERO },
+	{ AMOVD,	C_LOREG,C_NONE,	C_FREG,		27, 16, REGZERO },
+
+	{ AMOVW,	C_FREG,	C_NONE,	C_SEXT,		28, 4, REGSB },
+	{ AMOVF,	C_FREG,	C_NONE,	C_SEXT,		28, 4, REGSB },
+	{ AMOVD,	C_FREG,	C_NONE,	C_SEXT,		28, 4, REGSB },
+	{ AMOVW,	C_FREG,	C_NONE,	C_SAUTO,	28, 4, REGSP },
+	{ AMOVF,	C_FREG,	C_NONE,	C_SAUTO,	28, 4, REGSP },
+	{ AMOVD,	C_FREG,	C_NONE,	C_SAUTO,	28, 4, REGSP },
+	{ AMOVW,	C_FREG,	C_NONE,	C_SOREG,	28, 4, REGZERO },
+	{ AMOVF,	C_FREG,	C_NONE,	C_SOREG,	28, 4, REGZERO },
+	{ AMOVD,	C_FREG,	C_NONE,	C_SOREG,	28, 4, REGZERO },
+
+	{ AMOVW,	C_FREG,	C_NONE,	C_LEXT,		28, 16, REGSB },
+	{ AMOVF,	C_FREG,	C_NONE,	C_LEXT,		28, 16, REGSB },
+	{ AMOVD,	C_FREG,	C_NONE,	C_LEXT,		28, 16, REGSB },
+	{ AMOVW,	C_FREG,	C_NONE,	C_LAUTO,	28, 16, REGSP },
+	{ AMOVF,	C_FREG,	C_NONE,	C_LAUTO,	28, 16, REGSP },
+	{ AMOVD,	C_FREG,	C_NONE,	C_LAUTO,	28, 16, REGSP },
+	{ AMOVW,	C_FREG,	C_NONE,	C_LOREG,	28, 16, REGZERO },
+	{ AMOVF,	C_FREG,	C_NONE,	C_LOREG,	28, 16, REGZERO },
+	{ AMOVD,	C_FREG,	C_NONE,	C_LOREG,	28, 16, REGZERO },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_FREG,		30, 4, 0 },
+	{ AMOVW,	C_FREG,	C_NONE,	C_REG,		31, 4, 0 },
+	{ AMOVV,	C_REG,	C_NONE,	C_FREG,		47, 4, 0 },
+	{ AMOVV,	C_FREG,	C_NONE,	C_REG,		48, 4, 0 },
+
+	{ AMOVW,	C_ADDCON,C_NONE,C_FREG,		34, 8, 0 },
+	{ AMOVW,	C_ANDCON,C_NONE,C_FREG,		34, 8, 0 },
+	{ AMOVW,	C_UCON, C_NONE, C_FREG,		35, 8, 0 },
+	{ AMOVW,	C_LCON,	C_NONE,	C_FREG,		36, 12, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_FCCREG,	37, 4, 0 },
+	{ AMOVV,	C_REG,	C_NONE,	C_FCCREG,	37, 4, 0 },
+	{ AMOVW,	C_FCCREG,C_NONE,C_REG,		38, 4, 0 },
+	{ AMOVV,	C_FCCREG,C_NONE,C_REG,		38, 4, 0 },
+
+	{ AWORD,	C_NONE,	C_NONE,	C_LCON,		40, 4, 0 },
+
+	{ AMOVW,	C_REG,	C_NONE,	C_FCSREG,	41, 4, 0 },
+	{ AMOVV,	C_REG,	C_NONE,	C_FCSREG,	41, 4, 0 },
+	{ AMOVW,	C_FCSREG,C_NONE,C_REG,		42, 4, 0 },
+	{ AMOVV,	C_FCSREG,C_NONE,C_REG,		42, 4, 0 },
+
+	{ ABREAK,	C_REG,	C_NONE,	C_SEXT,		 7, 4, REGSB },	/* really CACHE instruction */
+	{ ABREAK,	C_REG,	C_NONE,	C_SAUTO,	 7, 4, REGSP },
+	{ ABREAK,	C_REG,	C_NONE,	C_SOREG,	 7, 4, REGZERO },
+	{ ABREAK,	C_NONE,	C_NONE,	C_NONE,		 5, 4, 0 },
+
+	{ AXXX,		C_NONE,	C_NONE,	C_NONE,		 0, 4, 0 },
+};
--- /dev/null
+++ b/sys/src/cmd/zl/pass.c
@@ -1,0 +1,534 @@
+#include	"l.h"
+
+void
+dodata(void)
+{
+	int i, t;
+	Sym *s;
+	Prog *p, *p1;
+	vlong orig, orig1, v;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f dodata\n", cputime());
+	Bflush(&bso);
+	for(p = datap; p != P; p = p->link) {
+		s = p->from.sym;
+		if(p->as == ADYNT || p->as == AINIT)
+			s->value = dtype;
+		if(s->type == SBSS)
+			s->type = SDATA;
+		if(s->type != SDATA)
+			diag("initialize non-data (%d): %s\n%P",
+				s->type, s->name, p);
+		v = p->from.offset + p->reg;
+		if(v > s->value)
+			diag("initialize bounds (%lld): %s\n%P",
+				s->value, s->name, p);
+	}
+
+	if(debug['t']) {
+		/*
+		 * pull out string constants
+		 */
+		for(p = datap; p != P; p = p->link) {
+			s = p->from.sym;
+			if(p->to.type == D_SCONST)
+				s->type = SSTRING;
+		}
+	}
+
+	/*
+	 * pass 1
+	 *	assign 'small' variables to data segment
+	 *	(rational is that data segment is more easily
+	 *	 addressed through offset on R30)
+	 */
+	orig = 0;
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		t = s->type;
+		if(t != SDATA && t != SBSS)
+			continue;
+		v = s->value;
+		if(v == 0) {
+			diag("%s: no size", s->name);
+			v = 1;
+		}
+		while(v & 7)
+			v++;
+		s->value = v;
+		if(v > MINSIZ)
+			continue;
+		s->value = orig;
+		orig += v;
+		s->type = SDATA1;
+	}
+	orig1 = orig;
+
+	/*
+	 * pass 2
+	 *	assign 'data' variables to data segment
+	 */
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		t = s->type;
+		if(t != SDATA) {
+			if(t == SDATA1)
+				s->type = SDATA;
+			continue;
+		}
+		v = s->value;
+		while(v & 7)
+			v++;
+		s->value = orig;
+		orig += v;
+		s->type = SDATA1;
+	}
+
+	while(orig & 7)
+		orig++;
+	datsize = orig;
+
+	/*
+	 * pass 3
+	 *	everything else to bss segment
+	 */
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		if(s->type != SBSS)
+			continue;
+		v = s->value;
+		while(v & 7)
+			v++;
+		s->value = orig;
+		orig += v;
+	}
+	while(orig & 7)
+		orig++;
+	bsssize = orig-datsize;
+
+	/*
+	 * pass 4
+	 *	add literals to all large values.
+	 *	at this time:
+	 *		small data is allocated DATA
+	 *		large data is allocated DATA1
+	 *		large bss is allocated BSS
+	 *	the new literals are loaded between
+	 *	small data and large data.
+	 */
+	orig = 0;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as != AMOVW)
+			continue;
+		if(p->from.type != D_CONST)
+			continue;
+		if(s = p->from.sym) {
+			t = s->type;
+			if(t != SDATA && t != SDATA1 && t != SBSS)
+				continue;
+			t = p->from.name;
+			if(t != D_EXTERN && t != D_STATIC)
+				continue;
+			v = s->value + p->from.offset;
+			if(v >= 0 && v <= 0xfff)
+				continue;
+			if(!strcmp(s->name, "setSB"))
+				continue;
+			/* size should be 19 max */
+			if(strlen(s->name) >= 10)	/* has loader address */
+				sprint(literal, "$%p.%llux", s, p->from.offset);
+			else
+				sprint(literal, "$%s.%d.%llux", s->name,
+					s->version, p->from.offset);
+		} else {
+			if(p->from.name != D_NONE)
+				continue;
+			if(p->from.reg != NREG)
+				continue;
+			v = p->from.offset;
+			if(v >= -0x7ff && v <= 0xfff)
+				continue;
+			if(!(v & 0xfff))
+				continue;
+			/* size should be 9 max */
+			sprint(literal, "$%llux", v);
+		}
+		s = lookup(literal, 0);
+		if(s->type == 0) {
+			s->type = SDATA;
+			s->value = orig1+orig;
+			orig += 8;
+			p1 = prg();
+			p1->line = p->line;
+			p1->as = ADATA;
+			p1->from.type = D_OREG;
+			p1->from.sym = s;
+			p1->from.name = D_EXTERN;
+			p1->reg = 4;
+			p1->to = p->from;
+			p1->link = datap;
+			datap = p1;
+		}
+		if(s->type != SDATA)
+			diag("literal not data: %s", s->name);
+		p->from.type = D_OREG;
+		p->from.sym = s;
+		p->from.name = D_EXTERN;
+		p->from.offset = 0;
+		nocache(p);
+		continue;
+	}
+	while(orig & 7)
+		orig++;
+	/*
+	 * pass 5
+	 *	re-adjust offsets
+	 */
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link) {
+		t = s->type;
+		if(t == SBSS) {
+			s->value += orig;
+			continue;
+		}
+		if(t == SDATA1) {
+			s->type = SDATA;
+			s->value += orig;
+			continue;
+		}
+	}
+	datsize += orig;
+	xdefine("setSB", SDATA, 0L);
+	xdefine("bdata", SDATA, 0L);
+	xdefine("edata", SDATA, datsize);
+	xdefine("end", SBSS, datsize+bsssize);
+	xdefine("etext", STEXT, 0L);
+}
+
+void
+undef(void)
+{
+	int i;
+	Sym *s;
+
+	for(i=0; i<NHASH; i++)
+	for(s = hash[i]; s != S; s = s->link)
+		if(s->type == SXREF)
+			diag("%s: not defined", s->name);
+}
+
+void
+follow(void)
+{
+	if(debug['v'])
+		Bprint(&bso, "%5.2f follow\n", cputime());
+	Bflush(&bso);
+
+	firstp = prg();
+	lastp = firstp;
+	xfol(textp);
+
+	firstp = firstp->link;
+	lastp->link = P;
+}
+
+void
+xfol(Prog *p)
+{
+	Prog *q, *r;
+	int a, i;
+
+loop:
+	if(p == P)
+		return;
+	a = p->as;
+	if(a == ATEXT)
+		curtext = p;
+	if(a == AJMP) {
+		q = p->cond;
+		if(q != P) {
+			p->mark |= FOLL;
+			p = q;
+			if(!(p->mark & FOLL))
+				goto loop;
+		}
+	}
+	if(p->mark & FOLL) {
+		for(i=0,q=p; i<4; i++,q=q->link) {
+			if(q == lastp)
+				break;
+			a = q->as;
+			if(a == ANOP) {
+				i--;
+				continue;
+			}
+			if(a == AJMP || a == ARET)
+				goto copy;
+			if(!q->cond || (q->cond->mark&FOLL))
+				continue;
+			if(a != ABEQ && a != ABNE)
+				continue;
+		copy:
+			for(;;) {
+				r = prg();
+				*r = *p;
+				if(!(r->mark&FOLL))
+					print("cant happen 1\n");
+				r->mark |= FOLL;
+				if(p != q) {
+					p = p->link;
+					lastp->link = r;
+					lastp = r;
+					continue;
+				}
+				lastp->link = r;
+				lastp = r;
+				if(a == AJMP || a == ARET)
+					return;
+				r->as = ABNE;
+				if(a == ABNE)
+					r->as = ABEQ;
+				r->cond = p->link;
+				r->link = p->cond;
+				if(!(r->link->mark&FOLL))
+					xfol(r->link);
+				if(!(r->cond->mark&FOLL))
+					print("cant happen 2\n");
+				return;
+			}
+		}
+		a = AJMP;
+		q = prg();
+		q->as = a;
+		q->line = p->line;
+		q->to.type = D_BRANCH;
+		q->to.offset = p->pc;
+		q->cond = p;
+		p = q;
+	}
+	p->mark |= FOLL;
+	lastp->link = p;
+	lastp = p;
+	if(a == AJMP || a == ARET)
+		return;
+	if(p->cond != P)
+	if(a != AJAL && p->link != P) {
+		xfol(p->link);
+		p = p->cond;
+		if(p == P || (p->mark&FOLL))
+			return;
+		goto loop;
+	}
+	p = p->link;
+	goto loop;
+}
+
+void
+patch(void)
+{
+	vlong c, vexit;
+	Prog *p, *q;
+	Sym *s;
+	int a;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f patch\n", cputime());
+	Bflush(&bso);
+	mkfwd();
+	s = lookup("exit", 0);
+	vexit = s->value;
+	for(p = firstp; p != P; p = p->link) {
+		a = p->as;
+		if(a == ATEXT)
+			curtext = p;
+		if((a == AJAL || a == AJMP || a == ARET) &&
+		   p->to.type != D_BRANCH && p->to.sym != S) {
+			s = p->to.sym;
+			if(s->type != STEXT) {
+				diag("undefined: %s\n%P", s->name, p);
+				s->type = STEXT;
+				s->value = vexit;
+			}
+			p->to.offset = s->value;
+			p->to.type = D_BRANCH;
+		}
+		if(p->to.type != D_BRANCH)
+			continue;
+		c = p->to.offset;
+		for(q = firstp; q != P;) {
+			if(q->forwd != P)
+			if(c >= q->forwd->pc) {
+				q = q->forwd;
+				continue;
+			}
+			if(c == q->pc)
+				break;
+			q = q->link;
+		}
+		if(q == P) {
+			diag("branch out of range %lld\n%P", c, p);
+			p->to.type = D_NONE;
+		}
+		p->cond = q;
+	}
+
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		if(p->cond != P) {
+			p->cond = brloop(p->cond);
+			if(p->cond != P)
+			if(p->to.type == D_BRANCH)
+				p->to.offset = p->cond->pc;
+		}
+	}
+}
+
+#define	LOG	5
+void
+mkfwd(void)
+{
+	Prog *p;
+	long dwn[LOG], cnt[LOG], i;
+	Prog *lst[LOG];
+
+	for(i=0; i<LOG; i++) {
+		if(i == 0)
+			cnt[i] = 1; else
+			cnt[i] = LOG * cnt[i-1];
+		dwn[i] = 1;
+		lst[i] = P;
+	}
+	i = 0;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->as == ATEXT)
+			curtext = p;
+		i--;
+		if(i < 0)
+			i = LOG-1;
+		p->forwd = P;
+		dwn[i]--;
+		if(dwn[i] <= 0) {
+			dwn[i] = cnt[i];
+			if(lst[i] != P)
+				lst[i]->forwd = p;
+			lst[i] = p;
+		}
+	}
+}
+
+Prog*
+brloop(Prog *p)
+{
+	Prog *q;
+	int c;
+
+	for(c=0; p!=P;) {
+		if(p->as != AJMP)
+			return p;
+		q = p->cond;
+		if(q <= p) {
+			c++;
+			if(q == p || c > 5000)
+				break;
+		}
+		p = q;
+	}
+	return P;
+}
+
+vlong
+atolwhex(char *s)
+{
+	vlong n;
+	int f;
+
+	n = 0;
+	f = 0;
+	while(*s == ' ' || *s == '\t')
+		s++;
+	if(*s == '-' || *s == '+') {
+		if(*s++ == '-')
+			f = 1;
+		while(*s == ' ' || *s == '\t')
+			s++;
+	}
+	if(s[0]=='0' && s[1]){
+		if(s[1]=='x' || s[1]=='X'){
+			s += 2;
+			for(;;){
+				if(*s >= '0' && *s <= '9')
+					n = n*16 + *s++ - '0';
+				else if(*s >= 'a' && *s <= 'f')
+					n = n*16 + *s++ - 'a' + 10;
+				else if(*s >= 'A' && *s <= 'F')
+					n = n*16 + *s++ - 'A' + 10;
+				else
+					break;
+			}
+		} else
+			while(*s >= '0' && *s <= '7')
+				n = n*8 + *s++ - '0';
+	} else
+		while(*s >= '0' && *s <= '9')
+			n = n*10 + *s++ - '0';
+	if(f)
+		n = -n;
+	return n;
+}
+
+vlong
+atovlwhex(char *s)
+{
+	vlong n;
+	int f;
+
+	n = 0;
+	f = 0;
+	while(*s == ' ' || *s == '\t')
+		s++;
+	if(*s == '-' || *s == '+') {
+		if(*s++ == '-')
+			f = 1;
+		while(*s == ' ' || *s == '\t')
+			s++;
+	}
+	if(s[0]=='0' && s[1]){
+		if(s[1]=='x' || s[1]=='X'){
+			s += 2;
+			for(;;){
+				if(*s >= '0' && *s <= '9')
+					n = n*16 + *s++ - '0';
+				else if(*s >= 'a' && *s <= 'f')
+					n = n*16 + *s++ - 'a' + 10;
+				else if(*s >= 'A' && *s <= 'F')
+					n = n*16 + *s++ - 'A' + 10;
+				else
+					break;
+			}
+		} else
+			while(*s >= '0' && *s <= '7')
+				n = n*8 + *s++ - '0';
+	} else
+		while(*s >= '0' && *s <= '9')
+			n = n*10 + *s++ - '0';
+	if(f)
+		n = -n;
+	return n;
+}
+
+vlong
+rnd(vlong v, long r)
+{
+	vlong c;
+
+	if(r <= 0)
+		return v;
+	v += r - 1;
+	c = v % r;
+	if(c < 0)
+		c += r;
+	v -= c;
+	return v;
+}
--- /dev/null
+++ b/sys/src/cmd/zl/span.c
@@ -1,0 +1,651 @@
+#include	"l.h"
+
+void
+span(void)
+{
+	Prog *p, *q;
+	Sym *setext, *s;
+	Optab *o;
+	int m, bflag, i;
+	vlong c, otxt, v;
+
+	if(debug['v'])
+		Bprint(&bso, "%5.2f span\n", cputime());
+	Bflush(&bso);
+
+	bflag = 0;
+	c = INITTEXT;
+	otxt = c;
+	for(p = firstp; p != P; p = p->link) {
+		p->pc = c;
+		o = oplook(p);
+		m = o->size;
+		if(m == 0) {
+			if(p->as == ATEXT) {
+				curtext = p;
+				autosize = p->to.offset + 8;
+				if(p->from.sym != S)
+					p->from.sym->value = c;
+				/* need passes to resolve branches */
+				if(c-otxt >= 1L<<17)
+					bflag = 1;
+				otxt = c;
+				continue;
+			}
+			diag("zero-width instruction\n%P", p);
+			continue;
+		}
+		c += m;
+	}
+
+	/*
+	 * if any procedure is large enough to
+	 * generate a large SBRA branch, then
+	 * generate extra passes putting branches
+	 * around jmps to fix. this is rare.
+	 */
+	while(bflag) {
+		if(debug['v'])
+			Bprint(&bso, "%5.2f span1\n", cputime());
+		bflag = 0;
+		c = INITTEXT;
+		for(p = firstp; p != P; p = p->link) {
+			p->pc = c;
+			o = oplook(p);
+			if(o->type == 6 && p->cond) {
+				otxt = p->cond->pc - c;
+				if(otxt < 0)
+					otxt = -otxt;
+				if(otxt >= (1L<<17) - 10) {
+					q = prg();
+					q->link = p->link;
+					p->link = q;
+					q->as = AJMP;
+					q->to.type = D_BRANCH;
+					q->cond = p->cond;
+					p->cond = q;
+					q = prg();
+					q->link = p->link;
+					p->link = q;
+					q->as = AJMP;
+					q->to.type = D_BRANCH;
+					q->cond = q->link->link;
+					addnop(p->link);
+					addnop(p);
+					bflag = 1;
+				}
+			}
+			m = o->size;
+			if(m == 0) {
+				if(p->as == ATEXT) {
+					curtext = p;
+					autosize = p->to.offset + 8;
+					if(p->from.sym != S)
+						p->from.sym->value = c;
+					continue;
+				}
+				diag("zero-width instruction\n%P", p);
+				continue;
+			}
+			c += m;
+		}
+	}
+
+	if(debug['t']) {
+		/*
+		 * add strings to text segment
+		 */
+		c = rnd(c, 8);
+		for(i=0; i<NHASH; i++)
+		for(s = hash[i]; s != S; s = s->link) {
+			if(s->type != SSTRING)
+				continue;
+			v = s->value;
+			while(v & 3)
+				v++;
+			s->value = c;
+			c += v;
+		}
+	}
+
+	c = rnd(c, 8);
+
+	setext = lookup("etext", 0);
+	if(setext != S) {
+		setext->value = c;
+		textsize = c - INITTEXT;
+	}
+	if(INITRND)
+		INITDAT = rnd(c, INITRND);
+	if(debug['v'])
+		Bprint(&bso, "tsize = %llux\n", textsize);
+	Bflush(&bso);
+}
+
+void
+xdefine(char *p, int t, vlong v)
+{
+	Sym *s;
+
+	s = lookup(p, 0);
+	if(s->type == 0 || s->type == SXREF) {
+		s->type = t;
+		s->value = v;
+	}
+}
+
+long
+regoff(Adr *a)
+{
+
+	instoffset = 0;
+	aclass(a);
+	return instoffset;
+}
+
+vlong
+vregoff(Adr *a)
+{
+	instoffset = 0;
+	aclass(a);
+	return instoffset;
+}
+
+int
+isint32(vlong v)
+{
+	long l;
+
+	l = v;
+	return (vlong)l == v;
+}
+
+int
+isuint32(uvlong v)
+{
+	ulong l;
+
+	l = v;
+	return (uvlong)l == v;
+}
+
+int
+aclass(Adr *a)
+{
+	Sym *s;
+	int t;
+
+	switch(a->type) {
+	case D_NONE:
+		return C_NONE;
+
+	case D_REG:
+		return C_REG;
+
+	case D_FREG:
+		return C_FREG;
+
+	case D_FCCREG:
+		return C_FCCREG;
+
+	case D_FCSREG:
+		return C_FCSREG;
+
+	case D_OREG:
+		switch(a->name) {
+		case D_EXTERN:
+		case D_STATIC:
+			if(a->sym == 0 || a->sym->name == 0) {
+				print("null sym external\n");
+				print("%D\n", a);
+				return C_GOK;
+			}
+			t = a->sym->type;
+			if(t == 0 || t == SXREF) {
+				diag("undefined external: %s in %s",
+					a->sym->name, TNAME);
+				a->sym->type = SDATA;
+			}
+			instoffset = a->sym->value + a->offset;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SEXT;
+			return C_LEXT;
+		case D_AUTO:
+			instoffset = autosize + a->offset;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SAUTO;
+			return C_LAUTO;
+
+		case D_PARAM:
+			instoffset = autosize + a->offset + 8L;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SAUTO;
+			return C_LAUTO;
+		case D_NONE:
+			instoffset = a->offset;
+			if(instoffset == 0)
+				return C_ZOREG;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SOREG;
+			return C_LOREG;
+		}
+		return C_GOK;
+
+	case D_OCONST:
+		switch(a->name) {
+		case D_EXTERN:
+		case D_STATIC:
+			s = a->sym;
+			t = s->type;
+			if(t == 0 || t == SXREF) {
+				diag("undefined external: %s in %s",
+					s->name, TNAME);
+				s->type = SDATA;
+			}
+			instoffset = s->value + a->offset + INITDAT;
+			if(s->type == STEXT || s->type == SLEAF)
+				instoffset = s->value + a->offset;
+			return C_LCON;
+		}
+		return C_GOK;
+
+	case D_CONST:
+		switch(a->name) {
+		case D_NONE:
+			instoffset = a->offset;
+		consize:
+			if(instoffset > 0) {
+				if(instoffset <= 0x7ff)
+					return C_SCON;
+				if(instoffset <= 0xfff)
+					return C_ANDCON;
+				if((instoffset & 0xfff) == 0 && isuint32(instoffset))
+					return C_UCON;
+				return C_LCON;
+			}
+			if(instoffset == 0)
+				return C_ZCON;
+			if(instoffset >= -0x800)
+				return C_ADDCON;
+			if((instoffset & 0xfff) == 0 && isint32(instoffset))
+				return C_UCON;
+			return C_LCON;
+
+		case D_EXTERN:
+		case D_STATIC:
+			s = a->sym;
+			if(s == S)
+				break;
+			t = s->type;
+			switch(t) {
+			case 0:
+			case SXREF:
+				diag("undefined external: %s in %s",
+					s->name, TNAME);
+				s->type = SDATA;
+				break;
+			case SCONST:
+				instoffset = s->value + a->offset;
+				goto consize;
+			case STEXT:
+			case SLEAF:
+				instoffset = s->value + a->offset;
+				return C_LCON;
+			}
+			instoffset = s->value + a->offset + INITDAT;
+			return C_LCON;
+
+		case D_AUTO:
+			instoffset = autosize + a->offset;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SACON;
+			return C_LACON;
+
+		case D_PARAM:
+			instoffset = autosize + a->offset + 8L;
+			if(instoffset >= -BIG && instoffset < BIG)
+				return C_SACON;
+			return C_LACON;
+		}
+		return C_GOK;
+
+	case D_BRANCH:
+		return C_SBRA;
+	}
+	return C_GOK;
+}
+
+Optab*
+oplook(Prog *p)
+{
+	int a1, a2, a3, r;
+	char *c1, *c3;
+	Optab *o, *e;
+
+	a1 = p->optab;
+	if(a1)
+		return optab+(a1-1);
+	a1 = p->from.class;
+	if(a1 == 0) {
+		a1 = aclass(&p->from) + 1;
+		p->from.class = a1;
+	}
+	a1--;
+	a3 = p->to.class;
+	if(a3 == 0) {
+		a3 = aclass(&p->to) + 1;
+		p->to.class = a3;
+	}
+	a3--;
+	a2 = C_NONE;
+	if(p->reg != NREG)
+		a2 = C_REG;
+	r = p->as;
+	o = oprange[r].start;
+	if(o == 0) {
+		a1 = opcross[repop[r]][a1][a2][a3];
+		if(a1) {
+			p->optab = a1+1;
+			return optab+a1;
+		}
+		o = oprange[r].stop; /* just generate an error */
+	}
+	e = oprange[r].stop;
+	c1 = xcmp[a1];
+	c3 = xcmp[a3];
+	for(; o<e; o++)
+		if(o->a2 == a2)
+		if(c1[o->a1])
+		if(c3[o->a3]) {
+			p->optab = (o-optab)+1;
+			return o;
+		}
+	diag("illegal combination %A %d %d %d",
+		p->as, p->from.class-1, a2, a3);
+	if(!debug['a'])
+		prasm(p);
+	o = optab;
+	p->optab = (o-optab)+1;
+	return o;
+}
+
+int
+cmp(int a, int b)
+{
+
+	if(a == b)
+		return 1;
+	switch(a) {
+	case C_LCON:
+		if(b == C_ZCON || b == C_SCON || b == C_UCON ||
+		   b == C_ADDCON || b == C_ANDCON)
+			return 1;
+		break;
+	case C_ADD0CON:
+		if(b == C_ADDCON)
+			return 1;
+	case C_ADDCON:
+		if(b == C_ZCON || b == C_SCON)
+			return 1;
+		break;
+	case C_AND0CON:
+		if(b == C_ANDCON)
+			return 1;
+	case C_ANDCON:
+		if(b == C_ZCON || b == C_SCON)
+			return 1;
+		break;
+	case C_UCON:
+		if(b == C_ZCON)
+			return 1;
+		break;
+	case C_SCON:
+		if(b == C_ZCON)
+			return 1;
+		break;
+	case C_LACON:
+		if(b == C_SACON)
+			return 1;
+		break;
+	case C_LBRA:
+		if(b == C_SBRA)
+			return 1;
+		break;
+	case C_LEXT:
+		if(b == C_SEXT)
+			return 1;
+		break;
+	case C_LAUTO:
+		if(b == C_SAUTO)
+			return 1;
+		break;
+	case C_REG:
+		if(b == C_ZCON)
+			return 1;
+		break;
+	case C_LOREG:
+		if(b == C_ZOREG || b == C_SOREG)
+			return 1;
+		break;
+	case C_SOREG:
+		if(b == C_ZOREG)
+			return 1;
+		break;
+	}
+	return 0;
+}
+
+int
+ocmp(const void *a1, const void *a2)
+{
+	Optab *p1, *p2;
+	int n;
+
+	p1 = (Optab*)a1;
+	p2 = (Optab*)a2;
+	n = p1->as - p2->as;
+	if(n)
+		return n;
+	n = p1->a1 - p2->a1;
+	if(n)
+		return n;
+	n = p1->a2 - p2->a2;
+	if(n)
+		return n;
+	n = p1->a3 - p2->a3;
+	if(n)
+		return n;
+	return 0;
+}
+
+void
+buildop(void)
+{
+	int i, n, r;
+
+	for(i=0; i<32; i++)
+		for(n=0; n<32; n++)
+			xcmp[i][n] = cmp(n, i);
+	for(n=0; optab[n].as != AXXX; n++)
+		;
+	qsort(optab, n, sizeof(optab[0]), ocmp);
+	for(i=0; i<n; i++) {
+		r = optab[i].as;
+		oprange[r].start = optab+i;
+		while(optab[i].as == r)
+			i++;
+		oprange[r].stop = optab+i;
+		i--;
+
+		switch(r)
+		{
+		default:
+			diag("unknown op in build: %A", r);
+			errorexit();
+		case AABSF:
+			oprange[AMOVFD] = oprange[r];
+			oprange[AMOVDF] = oprange[r];
+			oprange[AMOVWF] = oprange[r];
+			oprange[AMOVFW] = oprange[r];
+			oprange[AMOVWD] = oprange[r];
+			oprange[AMOVDW] = oprange[r];
+			oprange[ANEGF] = oprange[r];
+			oprange[ANEGD] = oprange[r];
+			oprange[AABSD] = oprange[r];
+			oprange[ATRUNCDW] = oprange[r];
+			oprange[ATRUNCFW] = oprange[r];
+			oprange[ATRUNCDV] = oprange[r];
+			oprange[ATRUNCFV] = oprange[r];
+			oprange[AMOVDV] = oprange[r];
+			oprange[AMOVFV] = oprange[r];
+			oprange[AMOVVD] = oprange[r];
+			oprange[AMOVVF] = oprange[r];
+			break;
+		case AADD:
+			buildrep(1, AADD);
+			oprange[ASGT] = oprange[r];
+			repop[ASGT] = 1;
+			oprange[ASGTU] = oprange[r];
+			repop[ASGTU] = 1;
+			oprange[AADDU] = oprange[r];
+			repop[AADDU] = 1;
+			oprange[AADDVU] = oprange[r];
+			repop[AADDVU] = 1;
+			oprange[AADDV] = oprange[r];
+			repop[AADDV] = 1;
+			break;
+		case AADDF:
+			oprange[ADIVF] = oprange[r];
+			oprange[ADIVD] = oprange[r];
+			oprange[AMULF] = oprange[r];
+			oprange[AMULD] = oprange[r];
+			oprange[ASUBF] = oprange[r];
+			oprange[ASUBD] = oprange[r];
+			oprange[AADDD] = oprange[r];
+			break;
+		case AAND:
+			buildrep(2, AAND);
+			oprange[AXOR] = oprange[r];
+			repop[AXOR] = 2;
+			oprange[AOR] = oprange[r];
+			repop[AOR] = 2;
+			break;
+		case ABEQ:
+			oprange[ABNE] = oprange[r];
+			break;
+		case ABLEZ:
+			oprange[ABGEZ] = oprange[r];
+			oprange[ABLTZ] = oprange[r];
+			oprange[ABGTZ] = oprange[r];
+			break;
+		case AMOVB:
+			buildrep(3, AMOVB);
+			oprange[AMOVH] = oprange[r];
+			repop[AMOVH] = 3;
+			break;
+		case AMOVBU:
+			buildrep(4, AMOVBU);
+			oprange[AMOVHU] = oprange[r];
+			repop[AMOVHU] = 4;
+			break;
+		case AMUL:
+			oprange[AMULU] = oprange[r];
+			oprange[AREM] = oprange[r];
+			oprange[AREMU] = oprange[r];
+			oprange[ADIV] = oprange[r];
+			oprange[ADIVU] = oprange[r];
+			break;
+		case AMULV:
+			oprange[AMULVU] = oprange[r];
+			oprange[AREMV] = oprange[r];
+			oprange[AREMVU] = oprange[r];
+			oprange[ADIVV] = oprange[r];
+			oprange[ADIVVU] = oprange[r];
+			break;
+		case ASLL:
+			oprange[ASRL] = oprange[r];
+			oprange[ASRA] = oprange[r];
+			oprange[ASLLV] = oprange[r];
+			oprange[ASRAV] = oprange[r];
+			oprange[ASRLV] = oprange[r];
+			break;
+		case ASUB:
+			oprange[ASUBU] = oprange[r];
+			oprange[ASUBV] = oprange[r];
+			oprange[ASUBVU] = oprange[r];
+			oprange[ANOR] = oprange[r];
+			break;
+		case ACMPEQF:
+			oprange[ACMPGTF] = oprange[r];
+			oprange[ACMPGTD] = oprange[r];
+			oprange[ACMPGEF] = oprange[r];
+			oprange[ACMPGED] = oprange[r];
+			oprange[ACMPEQD] = oprange[r];
+			break;
+		case ABFPT:
+			oprange[ABFPF] = oprange[r];
+			break;
+		case AMOVWL:
+			oprange[AMOVWR] = oprange[r];
+			oprange[AMOVVR] = oprange[r];
+			oprange[AMOVVL] = oprange[r];
+			break;
+		case AMOVW:
+			buildrep(5, AMOVW);
+			break;
+		case AMOVD:
+			buildrep(6, AMOVD);
+			break;
+		case AMOVF:
+			buildrep(7, AMOVF);
+			break;
+		case AMOVV:
+			buildrep(8, AMOVV);
+			break;
+		case ASYSCALL:
+		case ABREAK:
+		case AWORD:
+		case AJAL:
+		case AJMP:
+		case ATEXT:
+		case AMOVWU:
+			break;
+		}
+	}
+}
+
+void
+buildrep(int x, int as)
+{
+	Opcross *p;
+	Optab *e, *s, *o;
+	int a1, a2, a3, n;
+
+	if(C_NONE != 0 || C_REG != 1 || C_GOK >= 32 || x >= nelem(opcross)) {
+		diag("assumptions fail in buildrep");
+		errorexit();
+	}
+	repop[as] = x;
+	p = (opcross + x);
+	s = oprange[as].start;
+	e = oprange[as].stop;
+	for(o=e-1; o>=s; o--) {
+		n = o-optab;
+		for(a2=0; a2<2; a2++) {
+			if(a2) {
+				if(o->a2 == C_NONE)
+					continue;
+			} else
+				if(o->a2 != C_NONE)
+					continue;
+			for(a1=0; a1<32; a1++) {
+				if(!xcmp[a1][o->a1])
+					continue;
+				for(a3=0; a3<32; a3++)
+					if(xcmp[a3][o->a3])
+						(*p)[a1][a2][a3] = n;
+			}
+		}
+	}
+	oprange[as].start = 0;
+}
--- /dev/null
+++ b/sys/src/libmach/elf.h
@@ -1,0 +1,156 @@
+/*
+ *	Definitions needed for accessing ELF headers
+ */
+typedef struct {
+	uchar	ident[16];	/* ident bytes */
+	ushort	type;		/* file type */
+	ushort	machine;	/* target machine */
+	int	version;	/* file version */
+	ulong	elfentry;	/* start address */
+	ulong	phoff;		/* phdr file offset */
+	ulong	shoff;		/* shdr file offset */
+	int	flags;		/* file flags */
+	ushort	ehsize;		/* sizeof ehdr */
+	ushort	phentsize;	/* sizeof phdr */
+	ushort	phnum;		/* number phdrs */
+	ushort	shentsize;	/* sizeof shdr */
+	ushort	shnum;		/* number shdrs */
+	ushort	shstrndx;	/* shdr string index */
+} Ehdr;
+
+typedef struct {
+	u8int	ident[16];	/* ident bytes */
+	u16int	type;		/* file type */
+	u16int	machine;	/* target machine */
+	u32int	version;	/* file version */
+	u64int	elfentry;	/* start address */
+	u64int	phoff;		/* phdr file offset */
+	u64int	shoff;		/* shdr file offset */
+	u32int	flags;		/* file flags */
+	u16int	ehsize;		/* sizeof ehdr */
+	u16int	phentsize;	/* sizeof phdr */
+	u16int	phnum;		/* number phdrs */
+	u16int	shentsize;	/* sizeof shdr */
+	u16int	shnum;		/* number shdrs */
+	u16int	shstrndx;	/* shdr string index */
+} E64hdr;
+
+typedef struct {
+	int	type;		/* entry type */
+	ulong	offset;		/* file offset */
+	ulong	vaddr;		/* virtual address */
+	ulong	paddr;		/* physical address */
+	int	filesz;		/* file size */
+	ulong	memsz;		/* memory size */
+	int	flags;		/* entry flags */
+	int	align;		/* memory/file alignment */
+} Phdr;
+
+typedef struct {
+	u32int	type;		/* entry type */
+	u32int	flags;		/* entry flags */
+	u64int	offset;		/* file offset */
+	u64int	vaddr;		/* virtual address */
+	u64int	paddr;		/* physical address */
+	u64int	filesz;		/* file size */
+	u64int	memsz;		/* memory size */
+	u64int	align;		/* memory/file alignment */
+} P64hdr;
+
+typedef struct {
+	ulong	name;		/* section name */
+	ulong	type;		/* SHT_... */
+	ulong	flags;		/* SHF_... */
+	ulong	addr;		/* virtual address */
+	ulong	offset;		/* file offset */
+	ulong	size;		/* section size */
+	ulong	link;		/* misc info */
+	ulong	info;		/* misc info */
+	ulong	addralign;	/* memory alignment */
+	ulong	entsize;	/* entry size if table */
+} Shdr;
+
+typedef struct {
+	u32int	name;		/* section name */
+	u32int	type;		/* SHT_... */
+	u64int	flags;		/* SHF_... */
+	u64int	addr;		/* virtual address */
+	u64int	offset;		/* file offset */
+	u64int	size;		/* section size */
+	u32int	link;		/* misc info */
+	u32int	info;		/* misc info */
+	u64int	addralign;	/* memory alignment */
+	u64int	entsize;	/* entry size if table */
+} S64hdr;
+
+enum {
+	/* Ehdr codes */
+	MAG0 = 0,		/* ident[] indexes */
+	MAG1 = 1,
+	MAG2 = 2,
+	MAG3 = 3,
+	CLASS = 4,
+	DATA = 5,
+	VERSION = 6,
+
+	ELFCLASSNONE = 0,	/* ident[CLASS] */
+	ELFCLASS32 = 1,
+	ELFCLASS64 = 2,
+	ELFCLASSNUM = 3,
+
+	ELFDATANONE = 0,	/* ident[DATA] */
+	ELFDATA2LSB = 1,
+	ELFDATA2MSB = 2,
+	ELFDATANUM = 3,
+
+	NOETYPE = 0,		/* type */
+	REL = 1,
+	EXEC = 2,
+	DYN = 3,
+	CORE = 4,
+
+	NONE = 0,		/* machine */
+	M32 = 1,		/* AT&T WE 32100 */
+	SPARC = 2,		/* Sun SPARC */
+	I386 = 3,		/* Intel 80386 */
+	M68K = 4,		/* Motorola 68000 */
+	M88K = 5,		/* Motorola 88000 */
+	I486 = 6,		/* Intel 80486 */
+	I860 = 7,		/* Intel i860 */
+	MIPS = 8,		/* Mips R2000 */
+	S370 = 9,		/* Amdhal	*/
+	SPARC64 = 18,		/* Sun SPARC v9 */
+	POWER = 20,		/* PowerPC */
+	POWER64 = 21,		/* PowerPC64 */
+	ARM = 40,		/* ARM */
+	AMD64 = 62,		/* Amd64 */
+	ARM64 = 183,		/* ARM64 */
+	LOONGARCH = 258,	/* LoongArch */
+
+	NO_VERSION = 0,		/* version, ident[VERSION] */
+	CURRENT = 1,
+
+	/* Phdr Codes */
+	NOPTYPE = 0,		/* type */
+	LOAD = 1,
+	DYNAMIC = 2,
+	INTERP = 3,
+	NOTE = 4,
+	SHLIB = 5,
+	PHDR = 6,
+
+	R = 0x4,		/* flags */
+	W = 0x2,
+	X = 0x1,
+
+	/* Shdr Codes */
+	Progbits = 1,	/* section types */
+	Strtab = 3,
+	Nobits = 8,
+
+	Swrite = 1,	/* section attributes */
+	Salloc = 2,
+	Sexec = 4,
+};
+
+#define	ELF_MAG		((0x7f<<24) | ('E'<<16) | ('L'<<8) | 'F')
--- /dev/null
+++ b/sys/src/libmach/executable.c
@@ -1,0 +1,912 @@
+#include	<u.h>
+#include	<libc.h>
+#include	<bio.h>
+#include	<bootexec.h>
+#include	<mach.h>
+#include	"elf.h"
+
+/*
+ *	All a.out header types.  The dummy entry allows canonical
+ *	processing of the union as a sequence of longs
+ */
+
+typedef struct {
+	union{
+		struct {
+			Exec;		/* a.out.h */
+			uvlong hdr[1];
+		};
+		Ehdr;			/* elf.h */
+		E64hdr;
+		struct mipsexec;	/* bootexec.h */
+		struct mips4kexec;	/* bootexec.h */
+		struct sparcexec;	/* bootexec.h */
+		struct nextexec;	/* bootexec.h */
+	} e;
+	long dummy;			/* padding to ensure extra long */
+} ExecHdr;
+
+static	int	nextboot(int, Fhdr*, ExecHdr*);
+static	int	sparcboot(int, Fhdr*, ExecHdr*);
+static	int	mipsboot(int, Fhdr*, ExecHdr*);
+static	int	mips4kboot(int, Fhdr*, ExecHdr*);
+static	int	common(int, Fhdr*, ExecHdr*);
+static	int	commonllp64(int, Fhdr*, ExecHdr*);
+static	int	adotout(int, Fhdr*, ExecHdr*);
+static	int	elfdotout(int, Fhdr*, ExecHdr*);
+static	int	armdotout(int, Fhdr*, ExecHdr*);
+static	void	setsym(Fhdr*, long, long, long, vlong);
+static	void	setdata(Fhdr*, uvlong, long, vlong, long);
+static	void	settext(Fhdr*, uvlong, uvlong, long, vlong);
+static	void	hswal(void*, int, ulong(*)(ulong));
+static	uvlong	_round(uvlong, ulong);
+
+/*
+ *	definition of per-executable file type structures
+ */
+
+typedef struct Exectable{
+	long	magic;			/* big-endian magic number of file */
+	char	*name;			/* executable identifier */
+	char	*dlmname;		/* dynamically loadable module identifier */
+	uchar	type;			/* Internal code */
+	uchar	_magic;			/* _MAGIC() magic */
+	Mach	*mach;			/* Per-machine data */
+	long	hsize;			/* header size */
+	ulong	(*swal)(ulong);		/* beswal or leswal */
+	int	(*hparse)(int, Fhdr*, ExecHdr*);
+} ExecTable;
+
+extern	Mach	mmips;
+extern	Mach	mmips2le;
+extern	Mach	mmips2be;
+extern	Mach	msparc;
+extern	Mach	msparc64;
+extern	Mach	m68020;
+extern	Mach	mi386;
+extern	Mach	mamd64;
+extern	Mach	marm;
+extern	Mach	mthumb;
+extern	Mach	marm64;
+extern	Mach	mpower;
+extern	Mach	mpower64;
+extern	Mach	mloong;
+
+ExecTable exectab[] =
+{
+	{ V_MAGIC,			/* Mips v.out */
+		"mips plan 9 executable BE",
+		"mips plan 9 dlm BE",
+		FMIPS,
+		1,
+		&mmips,
+		sizeof(Exec),
+		beswal,
+		adotout },
+	{ P_MAGIC,			/* Mips 0.out (r3k le) */
+		"mips plan 9 executable LE",
+		"mips plan 9 dlm LE",
+		FMIPSLE,
+		1,
+		&mmips,
+		sizeof(Exec),
+		beswal,
+		adotout },
+	{ M_MAGIC,			/* Mips 4.out */
+		"mips 4k plan 9 executable BE",
+		"mips 4k plan 9 dlm BE",
+		FMIPS2BE,
+		1,
+		&mmips2be,
+		sizeof(Exec),
+		beswal,
+		adotout },
+	{ N_MAGIC,			/* Mips 0.out */
+		"mips 4k plan 9 executable LE",
+		"mips 4k plan 9 dlm LE",
+		FMIPS2LE,
+		1,
+		&mmips2le,
+		sizeof(Exec),
+		beswal,
+		adotout },
+	{ 0x160<<16,			/* Mips boot image */
+		"mips plan 9 boot image",
+		nil,
+		FMIPSB,
+		0,
+		&mmips,
+		sizeof(struct mipsexec),
+		beswal,
+		mipsboot },
+	{ (0x160<<16)|3,		/* Mips boot image */
+		"mips 4k plan 9 boot image",
+		nil,
+		FMIPSB,
+		0,
+		&mmips2be,
+		sizeof(struct mips4kexec),
+		beswal,
+		mips4kboot },
+	{ K_MAGIC,			/* Sparc k.out */
+		"sparc plan 9 executable",
+		"sparc plan 9 dlm",
+		FSPARC,
+		1,
+		&msparc,
+		sizeof(Exec),
+		beswal,
+		adotout },
+	{ 0x01030107, 			/* Sparc boot image */
+		"sparc plan 9 boot image",
+		nil,
+		FSPARCB,
+		0,
+		&msparc,
+		sizeof(struct sparcexec),
+		beswal,
+		sparcboot },
+	{ U_MAGIC,			/* Sparc64 u.out */
+		"sparc64 plan 9 executable",
+		"sparc64 plan 9 dlm",
+		FSPARC64,
+		1,
+		&msparc64,
+		sizeof(Exec),
+		beswal,
+		adotout },
+	{ A_MAGIC,			/* 68020 2.out & boot image */
+		"68020 plan 9 executable",
+		"68020 plan 9 dlm",
+		F68020,
+		1,
+		&m68020,
+		sizeof(Exec),
+		beswal,
+		common },
+	{ 0xFEEDFACE,			/* Next boot image */
+		"next plan 9 boot image",
+		nil,
+		FNEXTB,
+		0,
+		&m68020,
+		sizeof(struct nextexec),
+		beswal,
+		nextboot },
+	{ I_MAGIC,			/* I386 8.out & boot image */
+		"386 plan 9 executable",
+		"386 plan 9 dlm",
+		FI386,
+		1,
+		&mi386,
+		sizeof(Exec),
+		beswal,
+		common },
+	{ S_MAGIC,			/* amd64 6.out & boot image */
+		"amd64 plan 9 executable",
+		"amd64 plan 9 dlm",
+		FAMD64,
+		1,
+		&mamd64,
+		sizeof(Exec)+8,
+		nil,
+		commonllp64 },
+	{ Q_MAGIC,			/* PowerPC q.out & boot image */
+		"power plan 9 executable",
+		"power plan 9 dlm",
+		FPOWER,
+		1,
+		&mpower,
+		sizeof(Exec),
+		beswal,
+		common },
+	{ T_MAGIC,			/* power64 9.out & boot image */
+		"power64 plan 9 executable",
+		"power64 plan 9 dlm",
+		FPOWER64,
+		1,
+		&mpower64,
+		sizeof(Exec)+8,
+		nil,
+		commonllp64 },
+	{ ELF_MAG,			/* any ELF */
+		"elf executable",
+		nil,
+		FNONE,
+		0,
+		&mi386,
+		sizeof(Ehdr),
+		nil,
+		elfdotout },
+	{ E_MAGIC,			/* Arm 5.out and boot image */
+		"arm plan 9 executable",
+		"arm plan 9 dlm",
+		FARM,
+		1,
+		&marm,
+		sizeof(Exec),
+		beswal,
+		common },
+	{ (143<<16)|0413,		/* (Free|Net)BSD Arm */
+		"arm *bsd executable",
+		nil,
+		FARM,
+		0,
+		&marm,
+		sizeof(Exec),
+		leswal,
+		armdotout },
+	{ R_MAGIC,			/* Arm64 7.out and boot image */
+		"arm64 plan 9 executable",
+		"arm64 plan 9 dlm",
+		FARM64,
+		1,
+		&marm64,
+		sizeof(Exec)+8,
+		nil,
+		commonllp64 },
+	{ Z_MAGIC,			/* LoongArch z.out and boot image */
+		"loong plan 9 executable",
+		"loong plan 9 dlm",
+		FLOONG,
+		1,
+		&mloong,
+		sizeof(Exec)+8,
+		nil,
+		commonllp64 },
+	{ 0 },
+};
+
+Mach	*mach = &mi386;			/* Global current machine table */
+
+int
+crackhdr(int fd, Fhdr *fp)
+{
+	ExecTable *mp;
+	ExecHdr d;
+	int nb, ret;
+	ulong magic;
+
+	fp->type = FNONE;
+	nb = read(fd, (char *)&d.e, sizeof(d.e));
+	if (nb <= 0)
+		return 0;
+
+	ret = 0;
+	magic = beswal(d.e.magic);		/* big-endian */
+	for (mp = exectab; mp->magic; mp++) {
+		if (nb < mp->hsize)
+			continue;
+
+		/*
+		 * The magic number has morphed into something
+		 * with fields (the straw was DYN_MAGIC) so now
+		 * a flag is needed in Fhdr to distinguish _MAGIC()
+		 * magic numbers from foreign magic numbers.
+		 *
+		 * This code is creaking a bit and if it has to
+		 * be modified/extended much more it's probably
+		 * time to step back and redo it all.
+		 */
+		if(mp->_magic){
+			if(mp->magic != (magic & ~DYN_MAGIC))
+				continue;
+
+			if ((magic & DYN_MAGIC) && mp->dlmname != nil)
+				fp->name = mp->dlmname;
+			else
+				fp->name = mp->name;
+		}
+		else{
+			if(mp->magic != magic)
+				continue;
+			fp->name = mp->name;
+		}
+		fp->type = mp->type;
+		fp->hdrsz = mp->hsize;		/* will be zero on bootables */
+		fp->_magic = mp->_magic;
+		fp->magic = magic;
+
+		mach = mp->mach;
+		if(mp->swal != nil)
+			hswal(&d, sizeof(d.e)/sizeof(ulong), mp->swal);
+		ret = mp->hparse(fd, fp, &d);
+		seek(fd, mp->hsize, 0);		/* seek to end of header */
+		break;
+	}
+	switch(mp->magic){
+	case E_MAGIC:
+		thumbpctab(fd, fp);
+		break;
+	case 0:
+		werrstr("unknown header type");
+		break;
+	}
+	return ret;
+}
+
+/*
+ * Convert header to canonical form
+ */
+static void
+hswal(void *v, int n, ulong (*swap)(ulong))
+{
+	ulong *ulp;
+
+	for(ulp = v; n--; ulp++)
+		*ulp = (*swap)(*ulp);
+}
+
+/*
+ *	Crack a normal a.out-type header
+ */
+static int
+adotout(int fd, Fhdr *fp, ExecHdr *hp)
+{
+	long pgsize;
+
+	USED(fd);
+	pgsize = mach->pgsize;
+	settext(fp, hp->e.entry, pgsize+sizeof(Exec),
+			hp->e.text, sizeof(Exec));
+	setdata(fp, _round(pgsize+fp->txtsz+sizeof(Exec), pgsize),
+		hp->e.data, fp->txtsz+sizeof(Exec), hp->e.bss);
+	setsym(fp, hp->e.syms, hp->e.spsz, hp->e.pcsz, fp->datoff+fp->datsz);
+	return 1;
+}
+
+static void
+commonboot(Fhdr *fp)
+{
+	/* arm needs to check for both arm and thumb */
+	if(fp->type == FARM && (fp->entry & mthumb.ktmask))
+		goto FTHUM;
+	if (!(fp->entry & mach->ktmask))
+		return;
+
+	switch(fp->type) {				/* boot image */
+	case F68020:
+		fp->type = F68020B;
+		fp->name = "68020 plan 9 boot image";
+		break;
+	case FI386:
+		fp->type = FI386B;
+		fp->txtaddr = (u32int)fp->entry;
+		fp->name = "386 plan 9 boot image";
+		fp->dataddr = _round(fp->txtaddr+fp->txtsz, mach->pgsize);
+		break;
+	FTHUM:
+	case FARM:
+		fp->type = FARMB;
+		fp->txtaddr = (u32int)fp->entry;
+		fp->name = "ARM plan 9 boot image";
+		fp->dataddr = _round(fp->txtaddr+fp->txtsz, mach->pgsize);
+		return;
+	case FARM64:
+		fp->type = FARM64B;
+		fp->txtaddr = fp->entry;
+		fp->name = "arm64 plan 9 boot image";
+		fp->dataddr = _round(fp->txtaddr+fp->txtsz, mach->pgsize);
+		return;
+	case FLOONG:
+		fp->type = FLOONGB;
+		fp->txtaddr = fp->entry;
+		fp->name = "loong plan 9 boot image";
+		fp->dataddr = _round(fp->txtaddr+fp->txtsz, mach->pgsize);
+		return;
+	case FPOWER:
+		fp->type = FPOWERB;
+		fp->txtaddr = (u32int)fp->entry;
+		fp->name = "power plan 9 boot image";
+		fp->dataddr = fp->txtaddr+fp->txtsz;
+		break;
+	case FAMD64:
+		fp->type = FAMD64B;
+		fp->txtaddr = fp->entry;
+		fp->name = "amd64 plan 9 boot image";
+		fp->dataddr = _round(fp->txtaddr+fp->txtsz, 4096);
+		break;
+	case FPOWER64:
+		fp->type = FPOWER64B;
+		fp->txtaddr = fp->entry;
+		fp->name = "power64 plan 9 boot image";
+		fp->dataddr = fp->txtaddr+fp->txtsz;
+		break;
+	default:
+		return;
+	}
+	fp->hdrsz = 0;			/* header stripped */
+}
+
+/*
+ *	_MAGIC() style headers and
+ *	alpha plan9-style bootable images for axp "headerless" boot
+ *
+ */
+static int
+common(int fd, Fhdr *fp, ExecHdr *hp)
+{
+	adotout(fd, fp, hp);
+	if(hp->e.magic & DYN_MAGIC) {
+		fp->txtaddr = 0;
+		fp->dataddr = fp->txtsz;
+		return 1;
+	}
+	commonboot(fp);
+	return 1;
+}
+
+static int
+commonllp64(int, Fhdr *fp, ExecHdr *hp)
+{
+	long pgsize;
+	uvlong entry;
+
+	hswal(&hp->e, sizeof(Exec)/sizeof(long), beswal);
+	if(!(hp->e.magic & HDR_MAGIC))
+		return 0;
+
+	/*
+	 * There can be more magic here if the
+	 * header ever needs more expansion.
+	 * For now just catch use of any of the
+	 * unused bits.
+	 */
+	if((hp->e.magic & ~DYN_MAGIC)>>16)
+		return 0;
+	entry = beswav(hp->e.hdr[0]);
+
+	pgsize = mach->pgsize;
+	settext(fp, entry, pgsize+fp->hdrsz, hp->e.text, fp->hdrsz);
+	setdata(fp, _round(pgsize+fp->txtsz+fp->hdrsz, pgsize),
+		hp->e.data, fp->txtsz+fp->hdrsz, hp->e.bss);
+	setsym(fp, hp->e.syms, hp->e.spsz, hp->e.pcsz, fp->datoff+fp->datsz);
+
+	if(hp->e.magic & DYN_MAGIC) {
+		fp->txtaddr = 0;
+		fp->dataddr = fp->txtsz;
+		return 1;
+	}
+	commonboot(fp);
+	return 1;
+}
+
+/*
+ *	mips bootable image.
+ */
+static int
+mipsboot(int fd, Fhdr *fp, ExecHdr *hp)
+{
+	USED(fd);
+	fp->type = FMIPSB;
+	switch(hp->e.amagic) {
+	default:
+	case 0407:	/* some kind of mips */
+		settext(fp, (u32int)hp->e.mentry, (u32int)hp->e.text_start,
+			hp->e.tsize, sizeof(struct mipsexec)+4);
+		setdata(fp, (u32int)hp->e.data_start, hp->e.dsize,
+			fp->txtoff+hp->e.tsize, hp->e.bsize);
+		break;
+	case 0413:	/* some kind of mips */
+		settext(fp, (u32int)hp->e.mentry, (u32int)hp->e.text_start,
+			hp->e.tsize, 0);
+		setdata(fp, (u32int)hp->e.data_start, hp->e.dsize,
+			hp->e.tsize, hp->e.bsize);
+		break;
+	}
+	setsym(fp, hp->e.nsyms, 0, hp->e.pcsize, hp->e.symptr);
+	fp->hdrsz = 0;			/* header stripped */
+	return 1;
+}
+
+/*
+ *	mips4k bootable image.
+ */
+static int
+mips4kboot(int fd, Fhdr *fp, ExecHdr *hp)
+{
+	USED(fd);
+	fp->type = FMIPSB;
+	switch(hp->e.h.amagic) {
+	default:
+	case 0407:	/* some kind of mips */
+		settext(fp, (u32int)hp->e.h.mentry, (u32int)hp->e.h.text_start,
+			hp->e.h.tsize, sizeof(struct mips4kexec));
+		setdata(fp, (u32int)hp->e.h.data_start, hp->e.h.dsize,
+			fp->txtoff+hp->e.h.tsize, hp->e.h.bsize);
+		break;
+	case 0413:	/* some kind of mips */
+		settext(fp, (u32int)hp->e.h.mentry, (u32int)hp->e.h.text_start,
+			hp->e.h.tsize, 0);
+		setdata(fp, (u32int)hp->e.h.data_start, hp->e.h.dsize,
+			hp->e.h.tsize, hp->e.h.bsize);
+		break;
+	}
+	setsym(fp, hp->e.h.nsyms, 0, hp->e.h.pcsize, hp->e.h.symptr);
+	fp->hdrsz = 0;			/* header stripped */
+	return 1;
+}
+
+/*
+ *	sparc bootable image
+ */
+static int
+sparcboot(int fd, Fhdr *fp, ExecHdr *hp)
+{
+	USED(fd);
+	fp->type = FSPARCB;
+	settext(fp, hp->e.sentry, hp->e.sentry, hp->e.stext,
+		sizeof(struct sparcexec));
+	setdata(fp, hp->e.sentry+hp->e.stext, hp->e.sdata,
+		fp->txtoff+hp->e.stext, hp->e.sbss);
+	setsym(fp, hp->e.ssyms, 0, hp->e.sdrsize, fp->datoff+hp->e.sdata);
+	fp->hdrsz = 0;			/* header stripped */
+	return 1;
+}
+
+/*
+ *	next bootable image
+ */
+static int
+nextboot(int fd, Fhdr *fp, ExecHdr *hp)
+{
+	USED(fd);
+	fp->type = FNEXTB;
+	settext(fp, hp->e.textc.vmaddr, hp->e.textc.vmaddr,
+		hp->e.texts.size, hp->e.texts.offset);
+	setdata(fp, hp->e.datac.vmaddr, hp->e.datas.size,
+		hp->e.datas.offset, hp->e.bsss.size);
+	setsym(fp, hp->e.symc.nsyms, hp->e.symc.spoff, hp->e.symc.pcoff,
+		hp->e.symc.symoff);
+	fp->hdrsz = 0;			/* header stripped */
+	return 1;
+}
+
+/*
+ * ELF64 binaries.
+ */
+static int
+elf64dotout(int fd, Fhdr *fp, ExecHdr *hp)
+{
+	E64hdr *ep;
+	P64hdr *ph;
+	ushort (*swab)(ushort);
+	ulong (*swal)(ulong);
+	uvlong (*swav)(uvlong);
+	int i, it, id, is, phsz;
+	uvlong uvl;
+
+	ep = &hp->e;
+	if(ep->ident[DATA] == ELFDATA2LSB) {
+		swab = leswab;
+		swal = leswal;
+		swav = leswav;
+	} else if(ep->ident[DATA] == ELFDATA2MSB) {
+		swab = beswab;
+		swal = beswal;
+		swav = beswav;
+	} else {
+		werrstr("bad ELF64 encoding - not big or little endian");
+		return 0;
+	}
+
+	ep->type = swab(ep->type);
+	ep->machine = swab(ep->machine);
+	ep->version = swal(ep->version);
+	if(ep->type != EXEC || ep->version != CURRENT)
+		return 0;
+	ep->elfentry = swav(ep->elfentry);
+	ep->phoff = swav(ep->phoff);
+	ep->shoff = swav(ep->shoff);
+	ep->flags = swal(ep->flags);
+	ep->ehsize = swab(ep->ehsize);
+	ep->phentsize = swab(ep->phentsize);
+	ep->phnum = swab(ep->phnum);
+	ep->shentsize = swab(ep->shentsize);
+	ep->shnum = swab(ep->shnum);
+	ep->shstrndx = swab(ep->shstrndx);
+
+	fp->magic = ELF_MAG;
+	fp->hdrsz = (ep->ehsize+ep->phnum*ep->phentsize+16)&~15;
+	switch(ep->machine) {
+	default:
+		return 0;
+	case AMD64:
+		mach = &mamd64;
+		fp->type = FAMD64;
+		fp->name = "amd64 ELF64 executable";
+		break;
+	case POWER64:
+		mach = &mpower64;
+		fp->type = FPOWER64;
+		fp->name = "power64 ELF64 executable";
+		break;
+	}
+
+	if(ep->phentsize != sizeof(P64hdr)) {
+		werrstr("bad ELF64 header size");
+		return 0;
+	}
+	phsz = sizeof(P64hdr)*ep->phnum;
+	ph = malloc(phsz);
+	if(!ph)
+		return 0;
+	seek(fd, ep->phoff, 0);
+	if(read(fd, ph, phsz) < 0) {
+		free(ph);
+		return 0;
+	}
+	for(i = 0; i < ep->phnum; i++) {
+		ph[i].type = swal(ph[i].type);
+		ph[i].flags = swal(ph[i].flags);
+		ph[i].offset = swav(ph[i].offset);
+		ph[i].vaddr = swav(ph[i].vaddr);
+		ph[i].paddr = swav(ph[i].paddr);
+		ph[i].filesz = swav(ph[i].filesz);
+		ph[i].memsz = swav(ph[i].memsz);
+		ph[i].align = swav(ph[i].align);
+	}
+
+	/* find text, data and symbols and install them */
+	it = id = is = -1;
+	for(i = 0; i < ep->phnum; i++) {
+		if(ph[i].type == LOAD
+		&& (ph[i].flags & (R|X)) == (R|X) && it == -1)
+			it = i;
+		else if(ph[i].type == LOAD
+		&& (ph[i].flags & (R|W)) == (R|W) && id == -1)
+			id = i;
+		else if(ph[i].type == NOPTYPE && is == -1)
+			is = i;
+	}
+	if(it == -1 || id == -1) {
+		werrstr("No ELF64 TEXT or DATA sections");
+		free(ph);
+		return 0;
+	}
+
+	settext(fp, ep->elfentry, ph[it].vaddr, ph[it].memsz, ph[it].offset);
+	/* 8c: out of fixed registers */
+	uvl = ph[id].memsz - ph[id].filesz;
+	setdata(fp, ph[id].vaddr, ph[id].filesz, ph[id].offset, uvl);
+	if(is != -1)
+		setsym(fp, ph[is].filesz, 0, ph[is].memsz, ph[is].offset);
+	free(ph);
+	return 1;
+}
+
+/*
+ * ELF32 binaries.
+ */
+static int
+elf32dotout(int fd, Fhdr *fp, ExecHdr *hp)
+{
+	ulong (*swal)(ulong);
+	ushort (*swab)(ushort);
+	Ehdr *ep;
+	Phdr *ph;
+	int i, it, id, is, phsz;
+
+	/* bitswap the header according to the DATA format */
+	ep = &hp->e;
+	if(ep->ident[DATA] == ELFDATA2LSB) {
+		swab = leswab;
+		swal = leswal;
+	} else if(ep->ident[DATA] == ELFDATA2MSB) {
+		swab = beswab;
+		swal = beswal;
+	} else {
+		werrstr("bad ELF32 encoding - not big or little endian");
+		return 0;
+	}
+
+	ep->type = swab(ep->type);
+	ep->machine = swab(ep->machine);
+	ep->version = swal(ep->version);
+	ep->elfentry = swal(ep->elfentry);
+	ep->phoff = swal(ep->phoff);
+	ep->shoff = swal(ep->shoff);
+	ep->flags = swal(ep->flags);
+	ep->ehsize = swab(ep->ehsize);
+	ep->phentsize = swab(ep->phentsize);
+	ep->phnum = swab(ep->phnum);
+	ep->shentsize = swab(ep->shentsize);
+	ep->shnum = swab(ep->shnum);
+	ep->shstrndx = swab(ep->shstrndx);
+	if(ep->type != EXEC || ep->version != CURRENT)
+		return 0;
+
+	/* we could definitely support a lot more machines here */
+	fp->magic = ELF_MAG;
+	fp->hdrsz = (ep->ehsize+ep->phnum*ep->phentsize+16)&~15;
+	switch(ep->machine) {
+	case I386:
+		mach = &mi386;
+		fp->type = FI386;
+		fp->name = "386 ELF32 executable";
+		break;
+	case MIPS:
+		mach = &mmips;
+		if(ep->ident[DATA] == ELFDATA2LSB){
+			fp->type = FMIPSLE;
+			fp->name = "mips le ELF32 executable";
+		} else {
+			fp->type = FMIPS;
+			fp->name = "mips be ELF32 executable";
+		}
+		break;
+	case SPARC64:
+		mach = &msparc64;
+		fp->type = FSPARC64;
+		fp->name = "sparc64 ELF32 executable";
+		break;
+	case POWER:
+		mach = &mpower;
+		fp->type = FPOWER;
+		fp->name = "power ELF32 executable";
+		break;
+	case POWER64:
+		mach = &mpower64;
+		fp->type = FPOWER64;
+		fp->name = "power64 ELF32 executable";
+		break;
+	case AMD64:
+		mach = &mamd64;
+		fp->type = FAMD64;
+		fp->name = "amd64 ELF32 executable";
+		break;
+	case ARM:
+		mach = &marm;
+		fp->type = FARM;
+		fp->name = "arm ELF32 executable";
+		break;
+	default:
+		return 0;
+	}
+
+	if(ep->phentsize != sizeof(Phdr)) {
+		werrstr("bad ELF32 header size");
+		return 0;
+	}
+	phsz = sizeof(Phdr)*ep->phnum;
+	ph = malloc(phsz);
+	if(!ph)
+		return 0;
+	seek(fd, ep->phoff, 0);
+	if(read(fd, ph, phsz) < 0) {
+		free(ph);
+		return 0;
+	}
+	hswal(ph, phsz/sizeof(ulong), swal);
+
+	/* find text, data and symbols and install them */
+	it = id = is = -1;
+	for(i = 0; i < ep->phnum; i++) {
+		if(ph[i].type == LOAD
+		&& (ph[i].flags & (R|X)) == (R|X) && it == -1)
+			it = i;
+		else if(ph[i].type == LOAD
+		&& (ph[i].flags & (R|W)) == (R|W) && id == -1)
+			id = i;
+		else if(ph[i].type == NOPTYPE && is == -1)
+			is = i;
+	}
+	if(it == -1 || id == -1) {
+		/*
+		 * The SPARC64 boot image is something of an ELF hack.
+		 * Text+Data+BSS are represented by ph[0].  Symbols
+		 * are represented by ph[1]:
+		 *
+		 *		filesz, memsz, vaddr, paddr, off
+		 * ph[0] : txtsz+datsz, txtsz+datsz+bsssz, txtaddr-KZERO, datasize, txtoff
+		 * ph[1] : symsz, lcsz, 0, 0, symoff
+		 */
+		if(ep->machine == SPARC64 && ep->phnum == 2) {
+			ulong txtaddr, txtsz, dataddr, bsssz;
+
+			txtaddr = ph[0].vaddr | 0x80000000;
+			txtsz = ph[0].filesz - ph[0].paddr;
+			dataddr = txtaddr + txtsz;
+			bsssz = ph[0].memsz - ph[0].filesz;
+			settext(fp, ep->elfentry | 0x80000000, txtaddr, txtsz, ph[0].offset);
+			setdata(fp, dataddr, ph[0].paddr, ph[0].offset + txtsz, bsssz);
+			setsym(fp, ph[1].filesz, 0, ph[1].memsz, ph[1].offset);
+			free(ph);
+			return 1;
+		}
+
+		werrstr("No ELF32 TEXT or DATA sections");
+		free(ph);
+		return 0;
+	}
+
+	settext(fp, ep->elfentry, ph[it].vaddr, ph[it].memsz, ph[it].offset);
+	setdata(fp, ph[id].vaddr, ph[id].filesz, ph[id].offset, ph[id].memsz - ph[id].filesz);
+	if(is != -1)
+		setsym(fp, ph[is].filesz, 0, ph[is].memsz, ph[is].offset);
+	free(ph);
+	return 1;
+}
+
+/*
+ * Elf binaries.
+ */
+static int
+elfdotout(int fd, Fhdr *fp, ExecHdr *hp)
+{
+	Ehdr *ep;
+
+	/* bitswap the header according to the DATA format */
+	ep = &hp->e;
+	if(ep->ident[CLASS] == ELFCLASS32)
+		return elf32dotout(fd, fp, hp);
+	else if(ep->ident[CLASS] == ELFCLASS64)
+		return elf64dotout(fd, fp, hp);
+
+	werrstr("bad ELF class - not 32 bit");
+	return 0;
+}
+
+/*
+ * (Free|Net)BSD ARM header.
+ */
+static int
+armdotout(int fd, Fhdr *fp, ExecHdr *hp)
+{
+	uvlong kbase;
+
+	USED(fd);
+	settext(fp, hp->e.entry, sizeof(Exec), hp->e.text, sizeof(Exec));
+	setdata(fp, fp->txtsz, hp->e.data, fp->txtsz, hp->e.bss);
+	setsym(fp, hp->e.syms, hp->e.spsz, hp->e.pcsz, fp->datoff+fp->datsz);
+
+	kbase = 0xF0000000;
+	if ((fp->entry & kbase) == kbase) {		/* Boot image */
+		fp->txtaddr = kbase+sizeof(Exec);
+		fp->name = "ARM *BSD boot image";
+		fp->hdrsz = 0;		/* header stripped */
+		fp->dataddr = kbase+fp->txtsz;
+	}
+	return 1;
+}
+
+static void
+settext(Fhdr *fp, uvlong e, uvlong a, long s, vlong off)
+{
+	fp->txtaddr = a;
+	fp->entry = e;
+	fp->txtsz = s;
+	fp->txtoff = off;
+}
+
+static void
+setdata(Fhdr *fp, uvlong a, long s, vlong off, long bss)
+{
+	fp->dataddr = a;
+	fp->datsz = s;
+	fp->datoff = off;
+	fp->bsssz = bss;
+}
+
+static void
+setsym(Fhdr *fp, long symsz, long sppcsz, long lnpcsz, vlong symoff)
+{
+	fp->symsz = symsz;
+	fp->symoff = symoff;
+	fp->sppcsz = sppcsz;
+	fp->sppcoff = fp->symoff+fp->symsz;
+	fp->lnpcsz = lnpcsz;
+	fp->lnpcoff = fp->sppcoff+fp->sppcsz;
+}
+
+
+static uvlong
+_round(uvlong a, ulong b)
+{
+	uvlong w;
+
+	w = (a/b)*b;
+	if (a!=w)
+		w += b;
+	return(w);
+}
--- /dev/null
+++ b/sys/src/libmach/mkfile
@@ -1,0 +1,75 @@
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libmach.a
+FILES=\
+	executable\
+	map\
+	obj\
+	swap\
+	sym\
+	access\
+	machdata\
+	setmach\
+	t\
+	v\
+	k\
+	u\
+	q\
+	0\
+	2\
+	5\
+	6\
+	7\
+	8\
+	9\
+	z\
+	tdb\
+	vdb\
+	kdb\
+	udb\
+	qdb\
+	2db\
+	5db\
+	7db\
+	8db\
+	zdb\
+	vobj\
+	kobj\
+	uobj\
+	2obj\
+	5obj\
+	6obj\
+	7obj\
+	8obj\
+	9obj\
+	qobj\
+	zobj\
+	vcodas\
+
+HFILES=/sys/include/mach.h elf.h obj.h
+
+CFILES=${FILES:%=%.c}
+
+OFILES=${FILES:%=%.$O}
+
+UPDATE=mkfile\
+	/386/lib/libmach.a\
+	$HFILES\
+	$CFILES\
+
+</sys/src/cmd/mksyslib
+
+CFLAGS=$CFLAGS -I/sys/src/cmd
+
+2obj.$O: /sys/src/cmd/2c/2.out.h
+5obj.$O: /sys/src/cmd/5c/5.out.h
+6obj.$O: /sys/src/cmd/6c/6.out.h
+7obj.$O: /sys/src/cmd/7c/7.out.h
+8obj.$O: /sys/src/cmd/8c/8.out.h
+kobj.$O: /sys/src/cmd/kc/k.out.h
+qobj.$O: /sys/src/cmd/qc/q.out.h
+vobj.$O: /sys/src/cmd/vc/v.out.h
+zobj.$O: /sys/src/cmd/zc/z.out.h
+
+# 9obj.$O: /sys/src/cmd/9c/9.out.h
+# uobj.$O: uc/u.out.h
--- /dev/null
+++ b/sys/src/libmach/obj.c
@@ -1,0 +1,331 @@
+/*
+ * obj.c
+ * routines universal to all object files
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ar.h>
+#include <mach.h>
+#include "obj.h"
+
+#define islocal(t)	((t)=='a' || (t)=='p')
+
+enum
+{
+	NNAMES	= 50,
+	MAXIS	= 8,		/* max length to determine if a file is a .? file */
+	MAXOFF	= 0x7fffffff,	/* larger than any possible local offset */
+	NHASH	= 1024,		/* must be power of two */
+	HASHMUL	= 79L,
+};
+
+int	_is2(char*),		/* in [$OS].c */
+	_is5(char*),
+	_is6(char*),
+	_is7(char*),
+	_isz(char*),
+	_is8(char*),
+	_is9(char*),
+	_isk(char*),
+	_isq(char*),
+	_isv(char*),
+	_isu(char*),
+	_read2(Biobuf*, Prog*),
+	_read5(Biobuf*, Prog*),
+	_read6(Biobuf*, Prog*),
+	_read7(Biobuf*, Prog*),
+	_readz(Biobuf*, Prog*),
+	_read8(Biobuf*, Prog*),
+	_read9(Biobuf*, Prog*),
+	_readk(Biobuf*, Prog*),
+	_readq(Biobuf*, Prog*),
+	_readv(Biobuf*, Prog*),
+	_readu(Biobuf*, Prog*);
+
+typedef struct Obj	Obj;
+typedef struct Symtab	Symtab;
+
+struct	Obj		/* functions to handle each intermediate (.$O) file */
+{
+	char	*name;				/* name of each $O file */
+	int	(*is)(char*);			/* test for each type of $O file */
+	int	(*read)(Biobuf*, Prog*);	/* read for each type of $O file*/
+};
+
+static Obj	obj[] =
+{			/* functions to identify and parse each type of obj */
+	[Obj68020]	"68020 .2",	_is2, _read2,
+	[ObjAmd64]	"amd64 .6",	_is6, _read6,
+	[ObjArm]	"arm .5",	_is5, _read5,
+	[ObjArm64]	"arm64 .7",	_is7, _read7,
+	[ObjLoong]	"loong .z",	_isz, _readz,
+	[Obj386]	"386 .8",	_is8, _read8,
+	[ObjSparc]	"sparc .k",	_isk, _readk,
+	[ObjPower]	"power .q",	_isq, _readq,
+	[ObjMips]	"mips .v",	_isv, _readv,
+	[ObjSparc64]	"sparc64 .u",	_isu, _readu,
+	[ObjPower64]	"power64 .9",	_is9, _read9,
+	[Maxobjtype]	0, 0
+};
+
+struct	Symtab
+{
+	struct	Sym 	s;
+	struct	Symtab	*next;
+};
+
+static	Symtab *hash[NHASH];
+static	Sym	*names[NNAMES];	/* working set of active names */
+
+static	int	processprog(Prog*,int);	/* decode each symbol reference */
+static	void	objreset(void);
+static	void	objlookup(int, char *, int, uint);
+static	void 	objupdate(int, int);
+
+int
+objtype(Biobuf *bp, char **name)
+{
+	int i;
+	char buf[MAXIS];
+
+	if(Bread(bp, buf, MAXIS) < MAXIS)
+		return -1;
+	Bseek(bp, -MAXIS, 1);
+	for (i = 0; i < Maxobjtype; i++) {
+		if (obj[i].is && (*obj[i].is)(buf)) {
+			if (name)
+				*name = obj[i].name;
+			return i;
+		}
+	}
+	return -1;
+}
+
+int
+isar(Biobuf *bp)
+{
+	int n;
+	char magbuf[SARMAG];
+
+	n = Bread(bp, magbuf, SARMAG);
+	if(n == SARMAG && strncmp(magbuf, ARMAG, SARMAG) == 0)
+		return 1;
+	return 0;
+}
+
+/*
+ * determine what kind of object file this is and process it.
+ * return whether or not this was a recognized intermediate file.
+ */
+int
+readobj(Biobuf *bp, int objtype)
+{
+	Prog p;
+
+	if (objtype < 0 || objtype >= Maxobjtype || obj[objtype].is == 0)
+		return 1;
+	objreset();
+	while ((*obj[objtype].read)(bp, &p))
+		if (!processprog(&p, 1))
+			return 0;
+	return 1;
+}
+
+int
+readar(Biobuf *bp, int objtype, vlong end, int doautos)
+{
+	Prog p;
+
+	if (objtype < 0 || objtype >= Maxobjtype || obj[objtype].is == 0)
+		return 1;
+	objreset();
+	while ((*obj[objtype].read)(bp, &p) && Boffset(bp) < end)
+		if (!processprog(&p, doautos))
+			return 0;
+	return 1;
+}
+
+/*
+ *	decode a symbol reference or definition
+ */
+static	int
+processprog(Prog *p, int doautos)
+{
+	if(p->kind == aNone)
+		return 1;
+	if(p->sym < 0 || p->sym >= NNAMES)
+		return 0;
+	switch(p->kind)
+	{
+	case aName:
+		if (!doautos)
+		if(p->type != 'U' && p->type != 'b')
+			break;
+		objlookup(p->sym, p->id, p->type, p->sig);
+		break;
+	case aText:
+		objupdate(p->sym, 'T');
+		break;
+	case aData:
+		objupdate(p->sym, 'D');
+		break;
+	default:
+		break;
+	}
+	return 1;
+}
+
+/*
+ * find the entry for s in the symbol array.
+ * make a new entry if it is not already there.
+ */
+static void
+objlookup(int id, char *name, int type, uint sig)
+{
+	long h;
+	char *cp;
+	Sym *s;
+	Symtab *sp;
+
+	s = names[id];
+	if(s && strcmp(s->name, name) == 0) {
+		s->type = type;
+		s->sig = sig;
+		return;
+	}
+
+	h = *name;
+	for(cp = name+1; *cp; h += *cp++)
+		h *= HASHMUL;
+	if(h < 0)
+		h = ~h;
+	h &= (NHASH-1);
+	if (type == 'U' || type == 'b' || islocal(type)) {
+		for(sp = hash[h]; sp; sp = sp->next)
+			if(strcmp(sp->s.name, name) == 0) {
+				switch(sp->s.type) {
+				case 'T':
+				case 'D':
+				case 'U':
+					if (type == 'U') {
+						names[id] = &sp->s;
+						return;
+					}
+					break;
+				case 't':
+				case 'd':
+				case 'b':
+					if (type == 'b') {
+						names[id] = &sp->s;
+						return;
+					}
+					break;
+				case 'a':
+				case 'p':
+					if (islocal(type)) {
+						names[id] = &sp->s;
+						return;
+					}
+					break;
+				default:
+					break;
+				}
+			}
+	}
+	sp = malloc(sizeof(Symtab));
+	sp->s.name = name;
+	sp->s.type = type;
+	sp->s.sig = sig;
+	sp->s.value = islocal(type) ? MAXOFF : 0;
+	names[id] = &sp->s;
+	sp->next = hash[h];
+	hash[h] = sp;
+	return;
+}
+/*
+ *	traverse the symbol lists
+ */
+void
+objtraverse(void (*fn)(Sym*, void*), void *pointer)
+{
+	int i;
+	Symtab *s;
+
+	for(i = 0; i < NHASH; i++)
+		for(s = hash[i]; s; s = s->next)
+			(*fn)(&s->s, pointer);
+}
+
+/*
+ * update the offset information for a 'a' or 'p' symbol in an intermediate file
+ */
+void
+_offset(int id, vlong off)
+{
+	Sym *s;
+
+	s = names[id];
+	if (s && s->name[0] && islocal(s->type) && s->value > off)
+		s->value = off;
+}
+
+/*
+ * update the type of a global text or data symbol
+ */
+static void 
+objupdate(int id, int type)
+{
+	Sym *s;
+
+	s = names[id];
+	if (s && s->name[0])
+		if (s->type == 'U')
+			s->type = type;
+		else if (s->type == 'b')
+			s->type = tolower(type);
+}
+
+/*
+ * look for the next file in an archive
+ */
+int
+nextar(Biobuf *bp, int offset, char *buf)
+{
+	struct ar_hdr a;
+	int i, r;
+	long arsize;
+
+	if (offset&01)
+		offset++;
+	Bseek(bp, offset, 0);
+	r = Bread(bp, &a, SAR_HDR);
+	if(r != SAR_HDR)
+		return 0;
+	if(strncmp(a.fmag, ARFMAG, sizeof(a.fmag)))
+		return -1;
+	for(i=0; i<sizeof(a.name) && i<SARNAME && a.name[i] != ' '; i++)
+		buf[i] = a.name[i];
+	buf[i] = 0;
+	arsize = strtol(a.size, 0, 0);
+	if (arsize&1)
+		arsize++;
+	return arsize + SAR_HDR;
+}
+
+static void
+objreset(void)
+{
+	int i;
+	Symtab *s, *n;
+
+	for(i = 0; i < NHASH; i++) {
+		for(s = hash[i]; s; s = n) {
+			n = s->next;
+			free(s->s.name);
+			free(s);
+		}
+		hash[i] = 0;
+	}
+	memset(names, 0, sizeof names);
+}
--- /dev/null
+++ b/sys/src/libmach/setmach.c
@@ -1,0 +1,181 @@
+#include	<u.h>
+#include	<libc.h>
+#include	<bio.h>
+#include	<mach.h>
+		/* table for selecting machine-dependent parameters */
+
+typedef	struct machtab Machtab;
+
+struct machtab
+{
+	char		*name;			/* machine name */
+	short		type;			/* executable type */
+	short		boottype;		/* bootable type */
+	int		asstype;		/* disassembler code */
+	Mach		*mach;			/* machine description */
+	Machdata	*machdata;		/* machine functions */
+};
+
+extern	Mach		mmips, msparc, m68020, mi386, mamd64,
+			marm, marm64, mmips2be, mmips2le, mpower, mpower64, msparc64, mloong;
+extern	Machdata	mipsmach, mipsmachle, sparcmach, m68020mach, i386mach,
+			armmach, arm64mach, mipsmach2le, powermach, sparc64mach, loongmach;
+
+/*
+ *	machine selection table.  machines with native disassemblers should
+ *	follow the plan 9 variant in the table; native modes are selectable
+ *	only by name.
+ */
+Machtab	machines[] =
+{
+	{	"68020",			/*68020*/
+		F68020,
+		F68020B,
+		A68020,
+		&m68020,
+		&m68020mach,	},
+	{	"68020",			/*Next 68040 bootable*/
+		F68020,
+		FNEXTB,
+		A68020,
+		&m68020,
+		&m68020mach,	},
+	{	"spim2",			/*plan 9 mips2 little endian*/
+		FMIPS2LE,
+		0,
+		AMIPS,
+		&mmips2le,
+		&mipsmach2le, 	},
+	{	"spim",				/*plan 9 mips little endian*/
+		FMIPSLE,
+		0,
+		AMIPS,
+		&mmips,
+		&mipsmachle, 	},
+	{	"mips",				/*plan 9 mips*/
+		FMIPS,
+		FMIPSB,
+		AMIPS,
+		&mmips,
+		&mipsmach, 	},
+	{	"mips2",			/*plan 9 mips2*/
+		FMIPS2BE,
+		FMIPSB,
+		AMIPS,
+		&mmips2be,
+		&mipsmach, 	},		/* shares debuggers with native mips */
+	{	"mipsco",			/*native mips - must follow plan 9*/
+		FMIPS,
+		FMIPSB,
+		AMIPSCO,
+		&mmips,
+		&mipsmach,	},
+	{	"sparc",			/*plan 9 sparc */
+		FSPARC,
+		FSPARCB,
+		ASPARC,
+		&msparc,
+		&sparcmach,	},
+	{	"sunsparc",			/*native sparc - must follow plan 9*/
+		FSPARC,
+		FSPARCB,
+		ASUNSPARC,
+		&msparc,
+		&sparcmach,	},
+	{	"386",				/*plan 9 386*/
+		FI386,
+		FI386B,
+		AI386,
+		&mi386,
+		&i386mach,	},
+	{	"86",				/*8086 - a peach of a machine*/
+		FI386,
+		FI386B,
+		AI8086,
+		&mi386,
+		&i386mach,	},
+	{	"amd64",			/*amd64*/
+		FAMD64,
+		FAMD64B,
+		AAMD64,
+		&mamd64,
+		&i386mach,	},
+	{	"arm",				/*ARM*/
+		FARM,
+		FARMB,
+		AARM,
+		&marm,
+		&armmach,	},
+	{	"arm64",			/*ARM64*/
+		FARM64,
+		FARM64B,
+		AARM64,
+		&marm64,
+		&arm64mach,	},
+	{	"loong",			/*LoongArch*/
+		FLOONG,
+		FLOONGB,
+		ALOONG,
+		&mloong,
+		&loongmach,	},
+	{	"power",			/*PowerPC*/
+		FPOWER,
+		FPOWERB,
+		APOWER,
+		&mpower,
+		&powermach,	},
+	{	"power64",			/*PowerPC*/
+		FPOWER64,
+		FPOWER64B,
+		APOWER64,
+		&mpower64,
+		&powermach,	},
+	{	"sparc64",			/*plan 9 sparc64 */
+		FSPARC64,
+		FSPARCB,			/* XXX? */
+		ASPARC64,
+		&msparc64,
+		&sparc64mach,	},
+	{	0		},		/*the terminator*/
+};
+
+/*
+ *	select a machine by executable file type
+ */
+void
+machbytype(int type)
+{
+	Machtab *mp;
+
+	for (mp = machines; mp->name; mp++){
+		if (mp->type == type || mp->boottype == type) {
+			asstype = mp->asstype;
+			machdata = mp->machdata;
+			break;
+		}
+	}
+}
+/*
+ *	select a machine by name
+ */
+int
+machbyname(char *name)
+{
+	Machtab *mp;
+
+	if (!name) {
+		asstype = AMIPS;
+		machdata = &mipsmach;
+		mach = &mmips;
+		return 1;
+	}
+	for (mp = machines; mp->name; mp++){
+		if (strcmp(mp->name, name) == 0) {
+			asstype = mp->asstype;
+			machdata = mp->machdata;
+			mach = mp->mach;
+			return 1;
+		}
+	}
+	return 0;
+}
--- /dev/null
+++ b/sys/src/libmach/z.c
@@ -1,0 +1,152 @@
+/*
+ * TODO: LoongArch
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+#include "/loong/include/ureg.h"
+
+#define	REGSIZE		sizeof(struct Ureg)
+#define	FPREGSIZE	520
+
+#define	REGOFF(x)	(uintptr)(&((struct Ureg *) 0)->x)
+#define	FP_REG(x)	(REGSIZE+16*(x))
+#define	FP_CTL(x)	(FP_REG(32)+4*(x))
+
+#define SP		REGOFF(sp)
+#define PC		REGOFF(pc)
+
+Reglist loongreglist[] =
+{
+	{"TYPE",	REGOFF(type),		RINT|RRDONLY, 'Y'},
+	{"PSR",		REGOFF(psr),		RINT|RRDONLY, 'Y'},
+	{"PC",		PC,			RINT, 'Y'},
+	{"SP",		SP,			RINT, 'Y'},
+	{"R30",		REGOFF(r30),		RINT, 'Y'},
+	{"R29",		REGOFF(r29),		RINT, 'Y'},
+	{"R28",		REGOFF(r28),		RINT, 'Y'},
+	{"R27",		REGOFF(r27),		RINT, 'Y'},
+	{"R26",		REGOFF(r26),		RINT, 'Y'},
+	{"R25",		REGOFF(r25),		RINT, 'Y'},
+	{"R24",		REGOFF(r24),		RINT, 'Y'},
+	{"R23",		REGOFF(r23),		RINT, 'Y'},
+	{"R22",		REGOFF(r22),		RINT, 'Y'},
+	{"R21",		REGOFF(r21),		RINT, 'Y'},
+	{"R20",		REGOFF(r20),		RINT, 'Y'},
+	{"R19",		REGOFF(r19),		RINT, 'Y'},
+	{"R18",		REGOFF(r18),		RINT, 'Y'},
+	{"R17",		REGOFF(r17),		RINT, 'Y'},
+	{"R16",		REGOFF(r16),		RINT, 'Y'},
+	{"R15",		REGOFF(r15),		RINT, 'Y'},
+	{"R14",		REGOFF(r14),		RINT, 'Y'},
+	{"R13",		REGOFF(r13),		RINT, 'Y'},
+	{"R12",		REGOFF(r12),		RINT, 'Y'},
+	{"R11",		REGOFF(r11),		RINT, 'Y'},
+	{"R10",		REGOFF(r10),		RINT, 'Y'},
+	{"R9",		REGOFF(r9),		RINT, 'Y'},
+	{"R8",		REGOFF(r8),		RINT, 'Y'},
+	{"R7",		REGOFF(r7),		RINT, 'Y'},
+	{"R6",		REGOFF(r6),		RINT, 'Y'},
+	{"R5",		REGOFF(r5),		RINT, 'Y'},
+	{"R4",		REGOFF(r4),		RINT, 'Y'},
+	{"R3",		REGOFF(r3),		RINT, 'Y'},
+	{"R2",		REGOFF(r2),		RINT, 'Y'},
+	{"R1",		REGOFF(r1),		RINT, 'Y'},
+	{"R0",		REGOFF(r0),		RINT, 'Y'},
+
+	{"FPSR",	FP_CTL(1),		RINT, 'X'},
+	{"FPCR",	FP_CTL(0),		RINT, 'X'},
+
+	{"F31",		FP_REG(31),		RFLT, 'F'}, /* double */
+	{"F30",		FP_REG(30),		RFLT, 'F'},
+	{"F29",		FP_REG(29),		RFLT, 'F'},
+	{"F28",		FP_REG(28),		RFLT, 'F'},
+	{"F27",		FP_REG(27),		RFLT, 'F'},
+	{"F26",		FP_REG(26),		RFLT, 'F'},
+	{"F25",		FP_REG(25),		RFLT, 'F'},
+	{"F24",		FP_REG(24),		RFLT, 'F'},
+	{"F23",		FP_REG(23),		RFLT, 'F'},
+	{"F22",		FP_REG(22),		RFLT, 'F'},
+	{"F21",		FP_REG(21),		RFLT, 'F'},
+	{"F20",		FP_REG(20),		RFLT, 'F'},
+	{"F19",		FP_REG(19),		RFLT, 'F'},
+	{"F18",		FP_REG(18),		RFLT, 'F'},
+	{"F17",		FP_REG(17),		RFLT, 'F'},
+	{"F16",		FP_REG(16),		RFLT, 'F'},
+	{"F15",		FP_REG(15),		RFLT, 'F'},
+	{"F14",		FP_REG(14),		RFLT, 'F'},
+	{"F13",		FP_REG(13),		RFLT, 'F'},
+	{"F12",		FP_REG(12),		RFLT, 'F'},
+	{"F11",		FP_REG(11),		RFLT, 'F'},
+	{"F10",		FP_REG(10),		RFLT, 'F'},
+	{"F9",		FP_REG(9),		RFLT, 'F'},
+	{"F8",		FP_REG(8),		RFLT, 'F'},
+	{"F7",		FP_REG(7),		RFLT, 'F'},
+	{"F6",		FP_REG(6),		RFLT, 'F'},
+	{"F5",		FP_REG(5),		RFLT, 'F'},
+	{"F4",		FP_REG(4),		RFLT, 'F'},
+	{"F3",		FP_REG(3),		RFLT, 'F'},
+	{"F2",		FP_REG(2),		RFLT, 'F'},
+	{"F1",		FP_REG(1),		RFLT, 'F'},
+	{"F0",		FP_REG(0),		RFLT, 'F'},
+
+	{"f31",		FP_REG(31),		RFLT, 'f'}, /* double */
+	{"f30",		FP_REG(30),		RFLT, 'f'},
+	{"f29",		FP_REG(29),		RFLT, 'f'},
+	{"f28",		FP_REG(28),		RFLT, 'f'},
+	{"f27",		FP_REG(27),		RFLT, 'f'},
+	{"f26",		FP_REG(26),		RFLT, 'f'},
+	{"f25",		FP_REG(25),		RFLT, 'f'},
+	{"f24",		FP_REG(24),		RFLT, 'f'},
+	{"f23",		FP_REG(23),		RFLT, 'f'},
+	{"f22",		FP_REG(22),		RFLT, 'f'},
+	{"f21",		FP_REG(21),		RFLT, 'f'},
+	{"f20",		FP_REG(20),		RFLT, 'f'},
+	{"f19",		FP_REG(19),		RFLT, 'f'},
+	{"f18",		FP_REG(18),		RFLT, 'f'},
+	{"f17",		FP_REG(17),		RFLT, 'f'},
+	{"f16",		FP_REG(16),		RFLT, 'f'},
+	{"f15",		FP_REG(15),		RFLT, 'f'},
+	{"f14",		FP_REG(14),		RFLT, 'f'},
+	{"f13",		FP_REG(13),		RFLT, 'f'},
+	{"f12",		FP_REG(12),		RFLT, 'f'},
+	{"f11",		FP_REG(11),		RFLT, 'f'},
+	{"f10",		FP_REG(10),		RFLT, 'f'},
+	{"f9",		FP_REG(9),		RFLT, 'f'},
+	{"f8",		FP_REG(8),		RFLT, 'f'},
+	{"f7",		FP_REG(7),		RFLT, 'f'},
+	{"f6",		FP_REG(6),		RFLT, 'f'},
+	{"f5",		FP_REG(5),		RFLT, 'f'},
+	{"f4",		FP_REG(4),		RFLT, 'f'},
+	{"f3",		FP_REG(3),		RFLT, 'f'},
+	{"f2",		FP_REG(2),		RFLT, 'f'},
+	{"f1",		FP_REG(1),		RFLT, 'f'},
+	{"f0",		FP_REG(0),		RFLT, 'f'},
+	{  0 }
+};
+
+	/* the machine description */
+Mach mloong =
+{
+	"loong",
+	MLOONG,		/* machine type */
+	loongreglist,	/* register set */
+	REGSIZE,	/* register set size */
+	FPREGSIZE,		/* fp register set size */
+	"PC",		/* name of PC */
+	"SP",		/* name of SP */
+	"R1",		/* name of link register */
+	"setSB",	/* static base register name */
+	0,		/* static base register value */
+	0x10000,	/* page size (for segment alignment) */
+	0xFFFFFFFF80000000ULL,	/* kernel base */
+	0xFFFF800000000000ULL,	/* kernel text mask */
+	0x00007FFFFFFF0000ULL,	/* user stack top */
+	4,		/* quantization of pc */
+	8,		/* szaddr */
+	8,		/* szreg */
+	4,		/* szfloat */
+	8,		/* szdouble */
+};
--- /dev/null
+++ b/sys/src/libmach/zdb.c
@@ -1,0 +1,65 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+/*
+ * LoongArch-specific debugger interface
+ */
+static	char*	loongexcep(Map*, Rgetter);
+static	int	loongfoll(Map*, uvlong, Rgetter, uvlong*);
+static	int	loonginst(Map*, uvlong, char, char*, int);
+static	int	loongdas(Map*, uvlong, char*, int);
+static	int	loonginstlen(Map*, uvlong);
+
+/*
+ *	Debugger interface
+ */
+Machdata loongmach =
+{
+	{0x00, 0x2A, 0x20, 0x00},	/* break point 0x002A0000 */
+	4,		/* break point size */
+	leswab,		/* short to local byte order */
+	leswal,		/* long to local byte order */
+	leswav,		/* long to local byte order */
+	risctrace,	/* C traceback */
+	riscframe,	/* Frame finder */
+	loongexcep,	/* print exception */
+	0,		/* breakpoint fixup */
+	leieeesftos,		/* single precision float printer */
+	leieeedftos,		/* double precision float printer */
+	loongfoll,	/* following addresses */
+	loonginst,	/* print instruction */
+	loongdas,	/* dissembler */
+	loonginstlen,	/* instruction size */
+};
+
+static char*
+loongexcep(Map *, Rgetter)
+{
+	return "???";
+}
+
+static int
+loonginst(Map *, uvlong, char, char *, int)
+{
+	return -1;
+}
+
+static int
+loongdas(Map *, uvlong, char *, int)
+{
+	return -1;
+}
+
+static int
+loonginstlen(Map*, uvlong)
+{
+	return 4;
+}
+
+static int
+loongfoll(Map *, uvlong, Rgetter, uvlong *)
+{
+	return -1;
+}
--- /dev/null
+++ b/sys/src/libmach/zobj.c
@@ -1,0 +1,137 @@
+/*
+ * zobj.c - identify and parse a LoongArch object file
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+#include "zc/z.out.h"
+#include "obj.h"
+
+typedef struct Addr	Addr;
+struct Addr
+{
+	char	type;
+	char	sym;
+	char	name;
+};
+static Addr addr(Biobuf*);
+static char type2char(int);
+static void skip(Biobuf*, int);
+
+int
+_isz(char *s)
+{
+	return  s[0] == ANAME				/* ANAME */
+		&& s[1] == D_FILE			/* type */
+		&& s[2] == 1				/* sym */
+		&& s[3] == '<';				/* name of file */
+}
+
+int
+_readz(Biobuf *bp, Prog *p)
+{
+	int as, n;
+	Addr a;
+
+	as = Bgetc(bp);			/* as */
+	if(as < 0)
+		return 0;
+	p->kind = aNone;
+	p->sig = 0;
+	if(as == ANAME || as == ASIGNAME){
+		if(as == ASIGNAME){
+			Bread(bp, &p->sig, 4);
+			p->sig = leswal(p->sig);
+		}
+		p->kind = aName;
+		p->type = type2char(Bgetc(bp));		/* type */
+		p->sym = Bgetc(bp);			/* sym */
+		n = 0;
+		for(;;) {
+			as = Bgetc(bp);
+			if(as < 0)
+				return 0;
+			n++;
+			if(as == 0)
+				break;
+		}
+		p->id = malloc(n);
+		if(p->id == 0)
+			return 0;
+		Bseek(bp, -n, 1);
+		if(Bread(bp, p->id, n) != n)
+			return 0;
+		return 1;
+	}
+	if(as == ATEXT)
+		p->kind = aText;
+	else if(as == AGLOBL)
+		p->kind = aData;
+	skip(bp, 5);		/* reg(1), lineno(4) */
+	a = addr(bp);
+	addr(bp);
+	if(a.type != D_OREG || a.name != D_STATIC && a.name != D_EXTERN)
+		p->kind = aNone;
+	p->sym = a.sym;
+	return 1;
+}
+
+static Addr
+addr(Biobuf *bp)
+{
+	Addr a;
+	long off;
+
+	a.type = Bgetc(bp);	/* a.type */
+	skip(bp,1);		/* reg */
+	a.sym = Bgetc(bp);	/* sym index */
+	a.name = Bgetc(bp);	/* sym type */
+	switch(a.type){
+	default:
+	case D_NONE:
+	case D_REG:
+	case D_FREG:
+	case D_FCCREG:
+	case D_FCSREG:
+		break;
+	case D_OREG:
+	case D_CONST:
+	case D_BRANCH:
+		off = Bgetc(bp);
+		off |= Bgetc(bp) << 8;
+		off |= Bgetc(bp) << 16;
+		off |= Bgetc(bp) << 24;
+		if(off < 0)
+			off = -off;
+		if(a.sym && (a.name==D_PARAM || a.name==D_AUTO))
+			_offset(a.sym, off);
+		break;
+	case D_SCONST:
+		skip(bp, NSNAME);
+		break;
+	case D_FCONST:
+		skip(bp, 8);
+		break;
+	}
+	return a;
+}
+
+static char
+type2char(int t)
+{
+	switch(t){
+	case D_EXTERN:		return 'U';
+	case D_STATIC:		return 'b';
+	case D_AUTO:		return 'a';
+	case D_PARAM:		return 'p';
+	default:		return UNKNOWN;
+	}
+}
+
+static void
+skip(Biobuf *bp, int n)
+{
+	while (n-- > 0)
+		Bgetc(bp);
+}
--- /dev/null
+++ b/sys/src/mkfile.proto
@@ -1,0 +1,19 @@
+#
+# common mkfile parameters shared by all architectures
+#
+
+OS=05678qvtz
+CPUS=spim arm arm64 amd64 386 power mips loong
+CFLAGS=-FTVw
+LEX=lex
+YACC=yacc
+MK=/bin/mk
+
+# recursive mk will have these set from the parent
+# this is never what we want.  clear them
+
+TARG=
+OFILES=
+HFILES=
+YFILES=
+LIB=