Commits

dtrg committed 33d0246

Skeleton of VideoCore IV support for the Raspberry Pi.

Comments (0)

Files changed (42)

mach/vc4/as/.distr

+mach0.c
+mach1.c
+mach2.c
+mach3.c
+mach4.c
+mach5.c

mach/vc4/as/binary.h

+/*
+ * VideoCore IV assembler for the ACK
+ * © 2013 David Given
+ * This file is redistributable under the terms of the 3-clause BSD license.
+ * See the file 'Copying' in the root of the distribution for the full text.
+ */
+
+#ifndef BINARY_H
+#define BINARY_H
+
+/* This grotesque nonsense allows us to use binary constants from C. */
+
+#define HEX__(n) 0x##n##LU
+#define B8__(x) \
+	((x&0x0000000FLU)?1:0) \
+	+((x&0x000000F0LU)?2:0) \
+	+((x&0x00000F00LU)?4:0) \
+	+((x&0x0000F000LU)?8:0) \
+	+((x&0x000F0000LU)?16:0) \
+	+((x&0x00F00000LU)?32:0) \
+	+((x&0x0F000000LU)?64:0) \
+	+((x&0xF0000000LU)?128:0)
+
+#define B8(d) \
+	((unsigned char)B8__(HEX__(d)))
+#define B16(dmsb,dlsb) \
+	(((unsigned short)B8(dmsb)<<8) + B8(dlsb))
+#define B32(dmsb,db2,db3,dlsb) \
+	  (((unsigned long)B8(dmsb)<<24) \
+	+ ((unsigned long)B8(db2)<<16) \
+	+ ((unsigned long)B8(db3)<<8) \
+	+ B8(dlsb))
+
+#endif

mach/vc4/as/mach0.c

+/*
+ * VideoCore IV assembler for the ACK
+ * © 2013 David Given
+ * This file is redistributable under the terms of the 3-clause BSD license.
+ * See the file 'Copying' in the root of the distribution for the full text.
+ */
+
+#define	THREE_PASS          /* branch and offset optimization */
+#define LISTING             /* enable listing facilities */
+#define RELOCATION          /* generate relocatable code */
+#define DEBUG 0
+
+#undef valu_t
+#define valu_t long
+
+#undef ADDR_T
+#define ADDR_T long
+
+#undef word_t
+#define word_t long
+
+#undef ALIGNWORD
+#define ALIGNWORD	4
+
+#undef ALIGNSECT
+#define ALIGNSECT	4
+
+#undef VALWIDTH
+#define VALWIDTH	8
+
+#define FIXUPFLAGS (RELBR | RELWR)

mach/vc4/as/mach1.c

+/*
+ * VideoCore IV assembler for the ACK
+ * © 2013 David Given
+ * This file is redistributable under the terms of the 3-clause BSD license.
+ * See the file 'Copying' in the root of the distribution for the full text.
+ */

mach/vc4/as/mach2.c

+/*
+ * VideoCore IV assembler for the ACK
+ * © 2013 David Given
+ * This file is redistributable under the terms of the 3-clause BSD license.
+ * See the file 'Copying' in the root of the distribution for the full text.
+ */
+
+%token <y_word> GPR
+
+%token <y_word> OP
+%token <y_word> OP_ONEREG
+%token <y_word> OP_ONELREG
+%token <y_word> OP_ALU
+%token <y_word> OP_MEM
+%token <y_word> OP_BREG
+%token <y_word> OP_STACK
+
+/* Other token types */
+
+/* %type <y_word> c */
+%type <y_word> e16 u8 u7 u6 u5 u4 u2 u1
+/* %type <y_word> nb ds bda bdl lia lil */

mach/vc4/as/mach3.c

+/*
+ * VideoCore IV assembler for the ACK
+ * © 2013 David Given
+ * This file is redistributable under the terms of the 3-clause BSD license.
+ * See the file 'Copying' in the root of the distribution for the full text.
+ */
+
+#include "binary.h"
+
+/* Integer registers */
+
+0,     GPR,        0,          "r0",
+0,     GPR,        1,          "r1",
+0,     GPR,        2,          "r2",
+0,     GPR,        3,          "r3",
+0,     GPR,        4,          "r4",
+0,     GPR,        5,          "r5",
+
+0,     GPR,        6,          "r6",
+0,     GPR,        6,          "fp",
+0,     GPR,        7,          "r7",
+0,     GPR,        8,          "r8",
+0,     GPR,        9,          "r9",
+0,     GPR,        10,         "r10",
+0,     GPR,        11,         "r11",
+0,     GPR,        12,         "r12",
+0,     GPR,        13,         "r13",
+0,     GPR,        14,         "r14",
+0,     GPR,        15,         "r15",
+0,     GPR,        16,         "r16",
+0,     GPR,        17,         "r17",
+0,     GPR,        18,         "r18",
+0,     GPR,        19,         "r19",
+0,     GPR,        20,         "r20",
+0,     GPR,        21,         "r21",
+0,     GPR,        22,         "r22",
+0,     GPR,        23,         "r23",
+0,     GPR,        24,         "r24",
+
+0,     GPR,        25,         "r25",
+0,     GPR,        25,         "sp",
+0,     GPR,        26,         "r26",
+0,     GPR,        26,         "lr",
+0,     GPR,        27,         "r27",
+0,     GPR,        28,         "r28",
+0,     GPR,        29,         "r29",
+0,     GPR,        30,         "r30",
+0,     GPR,        30,         "sr",
+0,     GPR,        31,         "r31",
+0,     GPR,        31,         "pc",
+
+/* Special instructions */
+
+0,     OP,                    B16(00000000,00000001),                  "nop",
+0,     OP,                    B16(00000000,00001010),                  "rti",
+
+0,     OP_ONEREG,             B16(00000000,01000000),                  "b",
+0,     OP_ONEREG,             B16(00000000,01100000),                  "bl",
+0,     OP_ONELREG,            B16(00000000,10000000),                  "tbb",
+0,     OP_ONELREG,            B16(00000000,10100000),                  "tbs",
+
+0,     OP_ALU,                B8(00000000),                            "mov",
+0,     OP_ALU,                B8(00000001),                            "cmn",
+0,     OP_ALU,                B8(00000010),                            "add",
+0,     OP_ALU,                B8(00000011),                            "bic",
+0,     OP_ALU,                B8(00000100),                            "mul",
+0,     OP_ALU,                B8(00000101),                            "eor",
+0,     OP_ALU,                B8(00000110),                            "sub",
+0,     OP_ALU,                B8(00000111),                            "and",
+0,     OP_ALU,                B8(00001000),                            "mvn",
+0,     OP_ALU,                B8(00001001),                            "ror",
+0,     OP_ALU,                B8(00001010),                            "cmp",
+0,     OP_ALU,                B8(00001011),                            "rsb",
+0,     OP_ALU,                B8(00001100),                            "btst",
+0,     OP_ALU,                B8(00001101),                            "or",
+0,     OP_ALU,                B8(00001110),                            "extu",
+0,     OP_ALU,                B8(00001111),                            "max",
+0,     OP_ALU,                B8(00010000),                            "bset",
+0,     OP_ALU,                B8(00010001),                            "min",
+0,     OP_ALU,                B8(00010010),                            "bclr",
+0,     OP_ALU,                B8(00010011),                            "adds2",
+0,     OP_ALU,                B8(00010100),                            "bchg",
+0,     OP_ALU,                B8(00010101),                            "adds4",
+0,     OP_ALU,                B8(00010110),                            "adds8",
+0,     OP_ALU,                B8(00010111),                            "adds16",
+0,     OP_ALU,                B8(00011000),                            "exts",
+0,     OP_ALU,                B8(00011001),                            "neg",
+0,     OP_ALU,                B8(00011010),                            "lsr",
+0,     OP_ALU,                B8(00011011),                            "clz",
+0,     OP_ALU,                B8(00011100),                            "lsl",
+0,     OP_ALU,                B8(00011101),                            "brev",
+0,     OP_ALU,                B8(00011110),                            "asr",
+0,     OP_ALU,                B8(00011111),                            "abs",
+
+

mach/vc4/as/mach4.c

+/*
+ * VideoCore IV assembler for the ACK
+ * © 2013 David Given
+ * This file is redistributable under the terms of the 3-clause BSD license.
+ * See the file 'Copying' in the root of the distribution for the full text.
+ */
+
+#include "binary.h"
+
+operation
+	: OP                                              { emit2($1); }
+
+	| OP_ONEREG GPR
+	{
+		emit2($1 | ($2<<0));
+	}
+
+	| OP_ONELREG GPR
+	{
+		if ($2 >= 0x10)
+			serror("cannot use r16+ here");
+		emit2($1 | ($2<<0));
+	}
+
+	| OP_ALU GPR ',' GPR
+	{
+		emit2(B16(01000000, 00000000) | ($1<<8) | ($2<<0) | ($4<<4));
+	}
+
+	| OP_ALU GPR ',' '#' u5
+	{
+		if ($1 >= 0x10)
+			serror("cannot use this ALU operation in 2op form");
+		emit2(B16(01100000, 00000000) | ($1<<9) | ($2<<0) | ($5<<4));
+	}
+	;
+
+e16
+	: expr
+	{
+		DOTVAL += 2;
+		newrelo($1.typ, RELO2 | FIXUPFLAGS);
+		DOTVAL -= 2;
+		$$ = $1.val & 0xFFFF;
+	}
+	;
+		
+u8
+	: absexp
+	{
+		if (($1 < 0) || ($1 > 0xFF))
+			serror("8-bit unsigned value out of range");
+		$$ = $1;
+	}
+	;
+	
+u7
+	: absexp
+	{
+		if (($1 < 0) || ($1 > 0x7F))
+			serror("7-bit unsigned value out of range");
+		$$ = $1;
+	}
+	;
+	
+u6
+	: absexp
+	{
+		if (($1 < 0) || ($1 > 0x3F))
+			serror("6-bit unsigned value out of range");
+		$$ = $1;
+	}
+	;
+	
+u5
+	: absexp
+	{
+		if (($1 < 0) || ($1 > 0x1F))
+			serror("5-bit unsigned value out of range");
+		$$ = $1;
+	}
+	;
+	
+u4
+	: absexp
+	{
+		if (($1 < 0) || ($1 > 0xF))
+			serror("4-bit unsigned value out of range");
+		$$ = $1;
+	}
+	;
+	
+u1
+	: absexp
+	{
+		if (($1 < 0) || ($1 > 1))
+			serror("1-bit unsigned value out of range");
+		$$ = $1;
+	}
+	;
+	
+u2
+	: absexp
+	{
+		if (($1 < 0) || ($1 > 0x3))
+			serror("2-bit unsigned value out of range");
+		$$ = $1;
+	}
+	;
+

mach/vc4/as/mach5.c

+/*
+ * VideoCore IV assembler for the ACK
+ * © 2013 David Given
+ * This file is redistributable under the terms of the 3-clause BSD license.
+ * See the file 'Copying' in the root of the distribution for the full text.
+ */
+

mach/vc4/build.mk

+arch-libem-vc4 := \
+	dummy.s
+
+arch-libend-vc4 = \
+	edata.s \
+	em_end.s \
+	end.s \
+	etext.s
+
+

mach/vc4/libem/dummy.s

+#
+/*
+ * VideoCore IV support library for the ACK
+ * © 2013 David Given
+ * This file is redistributable under the terms of the 3-clause BSD license.
+ * See the file 'Copying' in the root of the distribution for the full text.
+ */
+
+#include "videocore.h"

mach/vc4/libem/videocore.h

+#
+/*
+ * VideoCore IV support library for the ACK
+ * © 2013 David Given
+ * This file is redistributable under the terms of the 3-clause BSD license.
+ * See the file 'Copying' in the root of the distribution for the full text.
+ */
+
+! Declare segments (the order is important).
+
+.sect .text
+.sect .rom
+.sect .data
+.sect .bss
+

mach/vc4/libend/.distr

+edata.s
+em_end.s
+end.s
+etext.s

mach/vc4/libend/edata.s

+#
+/*
+ * VideoCore IV support library for the ACK
+ * © 2013 David Given
+ * This file is redistributable under the terms of the 3-clause BSD license.
+ * See the file 'Copying' in the root of the distribution for the full text.
+ */
+
+.sect .text
+.sect .rom
+.sect .data
+.sect .bss
+.define	_edata
+.sect .data
+_edata:

mach/vc4/libend/em_end.s

+#
+/*
+ * VideoCore IV support library for the ACK
+ * © 2013 David Given
+ * This file is redistributable under the terms of the 3-clause BSD license.
+ * See the file 'Copying' in the root of the distribution for the full text.
+ */
+
+.sect .text
+.sect .rom
+.sect .data
+.sect .bss
+.sect .end ! only for declaration of _end, __end and endbss.
+.define	endtext, endrom, enddata, endbss, __end
+
+	.sect .text
+endtext:
+	.sect .rom
+endrom:
+	.sect .data
+enddata:
+	.sect .end
+__end:
+endbss:

mach/vc4/libend/end.s

+#
+/*
+ * VideoCore IV support library for the ACK
+ * © 2013 David Given
+ * This file is redistributable under the terms of the 3-clause BSD license.
+ * See the file 'Copying' in the root of the distribution for the full text.
+ */
+
+.sect .text
+.sect .rom
+.sect .data
+.sect .bss
+.define	_end
+.sect .end ! only for declaration of _end, __end and endbss.
+_end:

mach/vc4/libend/etext.s

+#
+/*
+ * VideoCore IV support library for the ACK
+ * © 2013 David Given
+ * This file is redistributable under the terms of the 3-clause BSD license.
+ * See the file 'Copying' in the root of the distribution for the full text.
+ */
+
+.sect .text
+.sect .rom
+.sect .data
+.sect .bss
+.define	_etext
+.sect .text
+_etext:

mach/vc4/ncg/.distr

+mach.c
+mach.h
+table

mach/vc4/ncg/mach.c

+/*
+ * VideoCore IV code generator for the ACK
+ * © 2013 David Given
+ * This file is redistributable under the terms of the 3-clause BSD license.
+ * See the file 'Copying' in the root of the distribution for the full text.
+ */
+
+#include <stdlib.h>
+#include <limits.h>
+
+#ifndef NORCSID
+static char rcsid[]=	"$Id$" ;
+#endif
+
+int framesize;
+
+/*
+ * machine dependent back end routines for the Zilog Z80.
+ */
+
+con_part(int sz, word w)
+{
+	while (part_size % sz)
+		part_size++;
+	if (part_size == TEM_WSIZE)
+		part_flush();
+	if (sz == 1) {
+		w &= 0xFF;
+		w <<= 8*(3-part_size);
+		part_word |= w;
+	} else if (sz == 2) {
+		w &= 0xFFFF;
+		if (part_size == 0) {
+			/* Shift 8 for m68k2, 16 otherwise */
+			w <<= 4 * TEM_WSIZE;
+		}
+		part_word |= w;
+	} else {
+		assert(sz == TEM_WSIZE);
+		part_word = w;
+	}
+	part_size += sz;
+}
+
+con_mult(word sz)
+{
+
+	if (argval != 4)
+		fatal("bad icon/ucon size");
+	fprintf(codefile,".data4 %s\n", str);
+}
+
+#define CODE_GENERATOR  
+#define IEEEFLOAT  
+#define FL_MSL_AT_LOW_ADDRESS	1
+#define FL_MSW_AT_LOW_ADDRESS	1
+#define FL_MSB_AT_LOW_ADDRESS	1
+#include <con_float>
+
+prolog(full nlocals)
+{
+	int ss = nlocals + 8;
+	fprintf(codefile, "addi sp, sp, %d\n", -ss);
+	fprintf(codefile, "stw fp, %d(sp)\n", nlocals);
+	fprintf(codefile, "mfspr r0, lr\n"
+	                  "stw r0, %d(sp)\n", nlocals+4);
+	fprintf(codefile, "addi fp, sp, %d\n", nlocals);
+	
+	framesize = nlocals;
+}
+
+mes(word type)
+{
+	int argt ;
+
+	switch ( (int)type ) {
+	case ms_ext :
+		for (;;) {
+			switch ( argt=getarg(
+			    ptyp(sp_cend)|ptyp(sp_pnam)|sym_ptyp) ) {
+			case sp_cend :
+				return ;
+			default:
+				strarg(argt) ;
+				fprintf(codefile,".define %s\n",argstr) ;
+				break ;
+			}
+		}
+	default :
+		while ( getarg(any_ptyp) != sp_cend ) ;
+		break ;
+	}
+}
+
+char *segname[] = {
+	".sect .text",
+	".sect .data",
+	".sect .rom",
+	".sect .bss"
+};
+
+#ifdef REGVARS
+
+static int savedregsi[32];
+static int numsaved;
+
+/* Initialise regvar system for one function. */
+
+i_regsave()
+{
+	int i;
+	
+	fprintf(codefile, "! i_regsave()\n");
+	for (i=0; i<32; i++)
+		savedregsi[i] = INT_MAX;
+	numsaved = 0;
+}
+
+/* Mark a register as being saved. */
+
+regsave(const char* regname, full offset, int size)
+{
+	int regnum = atoi(regname+1);
+	savedregsi[regnum] = offset;
+	numsaved++;
+	
+	fprintf(codefile, "! %d is saved in %s\n", offset, regname);
+#if 0
+	fprintf(codefile, "stwu %s, -4(sp)\n", regname);
+	if (offset >= 0)
+		fprintf(codefile, "lwz %s, %d(fp)\n", regname, offset);
+#endif
+}
+
+/* Finish saving ragisters. */
+
+void saveloadregs(const char* ops, const char* opm)
+{
+	int offset = -(framesize + numsaved*4);
+	int reg = 32;
+	
+	/* Check for the possibility of a multiple. */
+	
+	do
+	{
+		reg--;
+	}
+	while ((reg > 0) && (savedregsi[reg] != INT_MAX));
+	if (reg < 31)
+	{
+		fprintf(codefile, "%s r%d, %d(fp)\n", opm, reg+1, offset);
+		offset += (31-reg)*4;
+	}
+	
+	/* Saved everything else singly. */
+	
+	while (reg > 0)
+	{
+		if (savedregsi[reg] != INT_MAX)
+		{
+			fprintf(codefile, "%s r%d, %d(fp)\n", ops, reg, offset);
+			offset += 4;
+		}
+		reg--;
+	}
+}
+
+f_regsave()
+{
+	int i;
+	fprintf(codefile, "! f_regsave()\n");
+	fprintf(codefile, "addi sp, sp, %d\n", -numsaved*4);
+	
+	saveloadregs("stw", "stmw");
+	
+	for (i=0; i<32; i++)
+		if ((savedregsi[i] != INT_MAX) && (savedregsi[i] > 0))
+			fprintf(codefile, "lwz r%d, %d(fp)\n", i, savedregsi[i]);
+}
+
+/* Restore all saved registers. */
+
+regreturn()
+{
+	fprintf(codefile, "! regreturn()\n");
+	saveloadregs("lwz", "lmw");
+}
+
+/* Calculate the score of a given register. */
+
+int regscore(full offset, int size, int type, int frequency, int totype)
+{
+	int score;
+	
+	fprintf(codefile, "! regscore(%ld, %d, %d, %d, %d)\n", offset, size, type, frequency, totype);
+	
+	if (size != 4)
+		return -1;
+	
+	/* Per use: 6 bytes (on average)
+	 * Overhead in prologue: 4 bytes, plus 4 if a parameter
+	 * Overhead in epilogue: 0 bytes
+	 */
+	 
+	score = frequency*6 - 4 - ((offset>=0) ? 4 : 0);
+	fprintf(codefile, "! local at offset %d has regvar score %d\n", offset, score);
+	return score;
+}
+
+#endif

mach/vc4/ncg/mach.h

+/*
+ * VideoCore IV code generator for the ACK
+ * © 2013 David Given
+ * This file is redistributable under the terms of the 3-clause BSD license.
+ * See the file 'Copying' in the root of the distribution for the full text.
+ */
+
+#define ex_ap(y)        fprintf(codefile,".extern %s\n",y)
+#define in_ap(y)        /* nothing */
+
+#define newilb(x)       fprintf(codefile,"%s:\n",x)
+#define newdlb(x)       fprintf(codefile,"%s:\n",x)
+#define dlbdlb(x,y)     fprintf(codefile,"%s = %s\n",x,y)
+#define newlbss(l,x)       fprintf(codefile,".comm %s,%u\n",l,x);
+
+#define cst_fmt         "%d"
+#define off_fmt         "%d"
+#define ilb_fmt         "I%x_%x"
+#define dlb_fmt         "_%d"
+#define hol_fmt         "hol%d"
+
+#define hol_off         "%ld+hol%d"
+
+#define con_cst(x)      fprintf(codefile,".data4\t%ld\n",x)
+#define con_ilb(x)      fprintf(codefile,".data4\t%s\n",x)
+#define con_dlb(x)      fprintf(codefile,".data4\t%s\n",x)
+
+#define fmt_id(sf, st)	sprintf(st,"_%s",sf)
+
+#define modhead	".sect .text; .sect .rom; .sect .data; .sect .bss\n"
+
+#define BSS_INIT        0

mach/vc4/ncg/table

+/*
+ * VideoCore IV code generator for the ACK
+ * © 2013 David Given
+ * This file is redistributable under the terms of the 3-clause BSD license.
+ * See the file 'Copying' in the root of the distribution for the full text.
+ */
+
+EM_WSIZE = 4
+EM_PSIZE = 4
+EM_BSIZE = 8    /* two words saved in call frame */
+
+INT8 = 1        /* Size of values */
+INT16 = 2
+INT32 = 4
+INT64 = 8
+
+FP_OFFSET = 0   /* Offset of saved FP relative to our FP */
+PC_OFFSET = 4   /* Offset of saved PC relative to our FP */
+
+#define COMMENT(n) /* noop */
+
+
+#define nicesize(x) ((x)==INT8 || (x)==INT16 || (x)==INT32 || (x)==INT64)
+
+#define smalls(n) sfit(n, 16)
+#define smallu(n) ufit(n, 16)
+
+#define lo(n) (n & 0xFFFF)
+#define hi(n) ((n>>16) & 0xFFFF)
+
+/* Use these for instructions that treat the low half as signed --- his()
+ * includes a modifier to produce the correct value when the low half gets
+ * sign extended. Er, do make sure you load the low half second. */
+#define los(n) (n & 0xFFFF)
+#define his(n) ((hi(n) - (lo(n)>>15)) & 0xFFFF)
+
+#define IFFALSE {CONST, 4}
+#define IFTRUE {CONST, 12}
+#define ALWAYS {CONST, 20}
+#define DCTRZ {CONST, 34}
+
+#define LT {CONST, 0}
+#define GT {CONST, 1}
+#define EQ {CONST, 2}
+
+
+
+PROPERTIES
+
+	GPR             /* any GPR */
+	REG             /* any allocatable GPR */
+	FPR             /* any FPR */
+	FREG            /* any allocatable FPR */
+	SPR             /* any SPR */
+	CR              /* any CR */
+	
+	GPR0  GPRSP GPRFP GPR3  GPR4  GPR5  GPR6  GPR7
+	GPR8  GPR9  GPR10 GPR11 GPR12 GPR13 GPR14 GPR15
+	GPR16 GPR17 GPR18 GPR19 GPR20 GPR21 GPR22 GPR23
+	GPR24 GPR25 GPR26 GPR27 GPR28 GPR29 GPR30 GPR31
+	
+	CR0   CR1
+
+	FPR0  FPR1  FPR2  FPR3  FPR4  FPR5  FPR6  FPR7
+	FPR8  FPR9  FPR10 FPR11 FPR12 FPR13 FPR14 FPR15
+	FPR16 FPR17 FPR18 FPR19 FPR20 FPR21 FPR22 FPR23
+	FPR24 FPR25 FPR26 FPR27 FPR28 FPR29 FPR30 FPR31
+
+REGISTERS
+
+	/* Reverse order to encourage ncg to allocate them from r31 down */
+	
+	R31("r31")         : GPR, REG, GPR31 regvar.
+	R30("r30")         : GPR, REG, GPR30 regvar.
+	R29("r29")         : GPR, REG, GPR29 regvar.
+	R28("r28")         : GPR, REG, GPR28 regvar.
+	R27("r27")         : GPR, REG, GPR27 regvar.
+	R26("r26")         : GPR, REG, GPR26 regvar.
+	R25("r25")         : GPR, REG, GPR25 regvar.
+	R24("r24")         : GPR, REG, GPR24 regvar.
+	R23("r23")         : GPR, REG, GPR23 regvar.
+	R22("r22")         : GPR, REG, GPR22 regvar.
+	R21("r21")         : GPR, REG, GPR21 regvar.
+	R20("r20")         : GPR, REG, GPR20 regvar.
+	R19("r19")         : GPR, REG, GPR19 regvar.
+	R18("r18")         : GPR, REG, GPR18 regvar.
+	R17("r17")         : GPR, REG, GPR17 regvar.
+	R16("r16")         : GPR, REG, GPR16 regvar.
+	R15("r15")         : GPR, REG, GPR15 regvar.
+	R14("r14")         : GPR, REG, GPR14 regvar.
+	R13("r13")         : GPR, REG, GPR13 regvar.
+	R12("r12")         : GPR, REG, GPR12.
+	R11("r11")         : GPR, GPR11.
+	R10("r10")         : GPR, REG, GPR10.
+	R9("r9")           : GPR, REG, GPR9.
+	R8("r8")           : GPR, REG, GPR8.
+	R7("r7")           : GPR, REG, GPR7.
+	R6("r6")           : GPR, REG, GPR6.
+	R5("r5")           : GPR, REG, GPR5.
+	R4("r4")           : GPR, REG, GPR4.
+	R3("r3")           : GPR, REG, GPR3.
+	FP("fp")           : GPR, GPRFP.
+	SP("sp")           : GPR, GPRSP.
+	R0("r0")           : GPR, GPR0.
+
+	F31("f31")         : FPR, FREG, FPR31.
+	F30("f30")         : FPR, FREG, FPR30.
+	F29("f29")         : FPR, FREG, FPR29.
+	F28("f28")         : FPR, FREG, FPR28.
+	F27("f27")         : FPR, FREG, FPR27.
+	F26("f26")         : FPR, FREG, FPR26.
+	F25("f25")         : FPR, FREG, FPR25.
+	F24("f24")         : FPR, FREG, FPR24.
+	F23("f23")         : FPR, FREG, FPR23.
+	F22("f22")         : FPR, FREG, FPR22.
+	F21("f21")         : FPR, FREG, FPR21.
+	F20("f20")         : FPR, FREG, FPR20.
+	F19("f19")         : FPR, FREG, FPR19.
+	F18("f18")         : FPR, FREG, FPR18.
+	F17("f17")         : FPR, FREG, FPR17.
+	F16("f16")         : FPR, FREG, FPR16.
+	F15("f15")         : FPR, FREG, FPR15.
+	F14("f14")         : FPR, FREG, FPR14.
+	F13("f13")         : FPR, FREG, FPR13.
+	F12("f12")         : FPR, FREG, FPR12.
+	F11("f11")         : FPR, FREG, FPR11.
+	F10("f10")         : FPR, FREG, FPR10.
+	F9("f9")           : FPR, FREG, FPR9.
+	F8("f8")           : FPR, FREG, FPR8.
+	F7("f7")           : FPR, FREG, FPR7.
+	F6("f6")           : FPR, FREG, FPR6.
+	F5("f5")           : FPR, FREG, FPR5.
+	F4("f4")           : FPR, FREG, FPR4.
+	F3("f3")           : FPR, FREG, FPR3.
+	F2("f2")           : FPR, FREG, FPR2.
+	F1("f1")           : FPR, FREG, FPR1.
+	F0("f0")           : FPR, FREG, FPR0.
+
+	LR("lr")           : SPR.
+	CTR("ctr")         : SPR.
+	C0("cr0")          : CR, CR0.
+
+#define SCRATCH R11
+#define FSCRATCH F0
+
+
+TOKENS
+
+/* Used only in instruction descriptions (to generate the correct syntax). */
+
+	GPRINDIRECT        = { GPR reg; INT off; }    4    off "(" reg ")".
+	GPRINDIRECTLO      = { GPR reg; ADDR adr; }   4    ">" adr "(" reg ")". /* Warning! Do not use on labels. */
+	HILABEL            = { ADDR adr; }            4    "<" adr.
+	LOLABEL            = { ADDR adr; }            4    ">" adr.
+
+/* Primitives */
+
+	LABEL              = { ADDR adr; }            4    adr.
+	CONST              = { INT val; }             4    val.
+	LOCAL              = { INT off; }             4.
+
+/* Allows us to use regvar() to refer to registers */
+
+	GPRE               = { GPR reg; }             4    reg.
+
+/* Expression partial results */
+	
+	SUM_RC             = { GPR reg; INT off; }    4.
+	SUM_RR             = { GPR reg1; GPR reg2; }  4.
+	
+	TRISTATE_RC_S      = { GPR reg; INT val; }    4.
+	TRISTATE_RC_U      = { GPR reg; INT val; }    4.
+	TRISTATE_RR_S      = { GPR reg1; GPR reg2; }  4.
+	TRISTATE_RR_U      = { GPR reg1; GPR reg2; }  4.
+	
+	TRISTATE_FF        = { FPR reg1; FPR reg2; }  4.
+	
+	SEX_B              = { GPR reg; }             4.
+	SEX_H              = { GPR reg; }             4.
+	
+	IND_RC_B           = { GPR reg; INT off; }    4.
+	IND_RC_H           = { GPR reg; INT off; }    4.
+	IND_RC_H_S         = { GPR reg; INT off; }    4.
+	IND_RC_W           = { GPR reg; INT off; }    4.
+	IND_RR_W           = { GPR reg1; GPR reg2; }  4.
+	IND_LABEL_W        = { ADDR adr; }            4.
+	IND_RC_D           = { GPR reg; INT off; }    8.
+	IND_RR_D           = { GPR reg1; GPR reg2; }  8.
+	IND_LABEL_D        = { ADDR adr; }            8.
+	
+	NOT_R              = { GPR reg; }             4.
+	
+	AND_RR             = { GPR reg1; GPR reg2; }  4.
+	AND_RC             = { GPR reg; INT val; }  4.
+	OR_RR              = { GPR reg1; GPR reg2; }  4.
+	OR_RC              = { GPR reg; INT val; }  4.
+	XOR_RR             = { GPR reg1; GPR reg2; }  4.
+	XOR_RC             = { GPR reg; INT val; }  4.
+
+/* Floats */
+
+    FD                 = { FPR reg; }             8 reg.
+    FS                 = { FPR reg; }             4 reg.
+    
+/* Comments */
+
+	LABELI             = { ADDR msg; INT num; }   4    msg " " num.
+
+
+
+
+SETS
+
+	TOKEN              = LABEL + CONST + LOCAL.
+	GPRI               = GPR + GPRE.
+	
+	SUM_ALL            = SUM_RC + SUM_RR.
+	
+	TRISTATE_ALL       = TRISTATE_RC_S + TRISTATE_RC_U + TRISTATE_RR_S +
+	                     TRISTATE_RR_U + TRISTATE_FF.
+	
+	SEX_ALL            = SEX_B + SEX_H.
+	
+	LOGICAL_ALL        = NOT_R + AND_RR + AND_RC + OR_RR + OR_RC + XOR_RR +
+	                     XOR_RC.
+	
+	IND_ALL_W          = IND_RC_W + IND_RR_W + IND_LABEL_W.
+
+	IND_ALL_D          = IND_RC_D + IND_RR_D + IND_LABEL_D.
+	
+	OP_ALL_W           = SUM_ALL + TRISTATE_ALL + SEX_ALL + LOGICAL_ALL +
+	                     IND_ALL_W.
+
+
+INSTRUCTIONS
+
+  add             GPRI:wo, GPRI:ro, GPRI:ro.
+  addX "add."     GPRI:wo, GPRI:ro, GPRI:ro.
+  addi            GPRI:wo, GPRI:ro, CONST:ro.
+  addis           GPRI:wo, GPRI:ro, CONST+HILABEL:ro.
+  and             GPRI:wo, GPRI:ro, GPRI:ro.
+  andc            GPRI:wo, GPRI:ro, GPRI:ro.
+  andiX  "andi."  GPRI:wo, GPRI:ro, CONST:ro kills :cc.
+  andisX "andis." GPRI:wo, GPRI:ro, CONST:ro kills :cc.
+  b               LABEL:ro.
+  bc              CONST:ro, CONST:ro, LABEL:ro.
+  bcctr           CONST:ro, CONST:ro, CONST:ro.
+  bcctrl          CONST:ro, CONST:ro, CONST:ro.
+  bclr            CONST:ro, CONST:ro, CONST:ro.
+  bl              LABEL:ro.
+  cmp             CR:ro, CONST:ro, GPRI:ro, GPR:ro kills :cc.
+  cmpi            CR:ro, CONST:ro, GPRI:ro, CONST:ro kills :cc.
+  cmpl            CR:ro, CONST:ro, GPRI:ro, GPR:ro kills :cc.
+  cmpli           CR:ro, CONST:ro, GPRI:ro, CONST:ro kills :cc.
+  divw            GPRI:wo, GPRI:ro, GPRI:ro.
+  divwu           GPRI:wo, GPRI:ro, GPRI:ro.
+  eqv             GPRI:wo, GPRI:ro, GPRI:ro.
+  extsb           GPRI:wo, GPRI:ro.
+  extsh           GPRI:wo, GPRI:ro.
+  fadd            FD:wo, FD:ro, FD:ro.
+  fadds           FS:wo, FS:ro, FS:ro.
+  fcmpo           CR:wo, FD:ro, FD:ro.
+  fdiv            FD:wo, FD:ro, FD:ro.
+  fdivs           FS:wo, FS:ro, FS:ro.
+  fneg            FS+FD:wo, FS+FD:ro.
+  fmul            FD:wo, FD:ro, FD:ro.
+  fmuls           FS:wo, FS:ro, FS:ro.
+  frsp            FS:wo, FD:ro.
+  fsub            FD:wo, FD:ro, FD:ro.
+  fsubs           FS:wo, FS:ro, FS:ro.
+  fmr             FS+FD:wo, FS+FD:ro.
+  lbzx            GPRI:wo, GPR:ro, GPR:ro.
+  lbz             GPRI:wo, GPRINDIRECT+GPRINDIRECTLO:ro.
+  lfd             FD:wo, GPRINDIRECT+GPRINDIRECTLO:ro.
+  lfdu            FD:wo, GPRINDIRECT+GPRINDIRECTLO:ro.
+  lfdx            FD:wo, GPR:ro, GPR:ro.
+  lfs             FS:wo, GPRINDIRECT+GPRINDIRECTLO:ro.
+  lfsu            FS:wo, GPRINDIRECT+GPRINDIRECTLO:rw.
+  lfsx            FS:wo, GPR:ro, GPR:ro.
+  lhzx            GPRI:wo, GPR:ro, GPR:ro.
+  lhax            GPRI:wo, GPR:ro, GPR:ro.
+  lha             GPRI:wo, GPRINDIRECT+GPRINDIRECTLO:ro.
+  lhz             GPRI:wo, GPRINDIRECT+GPRINDIRECTLO:ro.
+  lwzu            GPRI:wo, GPRINDIRECT+GPRINDIRECTLO:ro.
+  lwzx            GPRI:wo, GPR:ro, GPR:ro.
+  lwz             GPRI:wo, GPRINDIRECT+GPRINDIRECTLO:ro.
+  nand            GPRI:wo, GPRI:ro, GPRI:ro.
+  neg             GPRI:wo, GPRI:ro.
+  nor             GPRI:wo, GPRI:ro, GPRI:ro.
+  mfcr            GPRI:wo.
+  mullw           GPRI:wo, GPRI:ro, GPRI:ro.
+  mfspr           GPRI:wo, SPR:ro.
+  mtspr           SPR:wo, GPRI:ro.
+  or              GPRI:wo, GPRI:ro, GPRI:ro.
+  orc             GPRI:wo, GPRI:ro, GPRI:ro.
+  ori             GPRI:wo, GPRI:ro, CONST+LOLABEL:ro.
+  orX "or."       GPRI:wo, GPRI:ro, GPRI:ro kills :cc.
+  rlwinm          GPRI:wo, GPRI:ro, CONST:ro, CONST:ro, CONST:ro.
+  slw             GPRI:wo, GPRI:ro, GPRI:ro.
+  subf            GPRI:wo, GPRI:ro, GPRI:ro.
+  sraw            GPRI:wo, GPRI:ro, GPRI:ro.
+  srawi           GPRI:wo, GPRI:ro, CONST:ro.
+  srw             GPRI:wo, GPRI:ro, GPRI:ro.
+  stb             GPRI:ro, GPRINDIRECT+GPRINDIRECTLO:rw.
+  stbx            GPRI:ro, GPR:ro, GPR:ro.
+  stfd            FD:ro, GPRINDIRECT+GPRINDIRECTLO:rw.
+  stfdu           FD:ro, GPRINDIRECT+GPRINDIRECTLO:rw.
+  stfdx           FD:ro, GPR:ro, GPR:ro.
+  stfs            FS:ro, GPRINDIRECT+GPRINDIRECTLO:rw.
+  stfsu           FS:ro, GPRINDIRECT+GPRINDIRECTLO:rw.
+  stfsx           FS:ro, GPR:ro, GPR:ro.
+  sth             GPRI:ro, GPRINDIRECT+GPRINDIRECTLO:rw.
+  sthx            GPRI:ro, GPR:ro, GPR:ro.
+  stw             GPRI:ro, GPRINDIRECT+GPRINDIRECTLO:rw.
+  stwx            GPRI:ro, GPR:ro, GPR:ro.
+  stwu            GPRI:ro, GPRINDIRECT+GPRINDIRECTLO:rw.
+  xor             GPRI:wo, GPRI:ro, GPRI:ro.
+  xori            GPRI:wo, GPRI:ro, CONST:ro.
+
+  gpr_gpr_gpr     GPRI:wo, GPRI:ro, GPRI:ro.
+  gpr_gpr_si      GPRI:wo, GPRI:ro, CONST:ro.
+  gpr_ro_gprindirect GPRI:ro, GPRINDIRECT:rw.
+  gpr_ro_gpr_gpr  GPRI:ro, GPRI:ro, GPRI:ro.
+  gpr_wo_gprindirect GPRI:wo, GPRINDIRECT:ro.
+  gpr_wo_gpr_gpr  GPRI:wo, GPRI:ro, GPRI:ro.
+  
+  invalid "invalid".
+  comment "!" LABEL+LABELI:ro.
+
+
+  
+MOVES
+
+	from GPR to GPR
+		gen
+			COMMENT("move GPR->GPR")
+			or %2, %1, %1
+
+/* GPRE exists solely to allow us to use regvar() (which can only be used in
+   an expression) as a register constant. */
+   
+	from GPR to GPRE
+		gen
+			COMMENT("move GPR->GPRE")
+			or %2, %1, %1
+		
+/* Constants */
+
+	from CONST smalls(%val) to GPR
+		gen
+			COMMENT("move CONST->GPRE")
+			addi %2, R0, {CONST, lo(%1.val)}
+		
+	from CONST to GPR
+		gen
+			COMMENT("move CONST->GPRE")
+			addis %2, R0, {CONST, hi(%1.val)}
+			ori %2, %2, {CONST, lo(%1.val)}
+		
+	from LABEL to GPR
+		gen
+			COMMENT("move LABEL->GPR")
+			addis %2, R0, {HILABEL, %1.adr}
+			ori %2, %2, {LOLABEL, %1.adr}
+	
+/* Sign extension */
+
+	from SEX_B to GPR
+		gen
+			COMMENT("move SEX_B->GPR")
+			extsb %2, %1.reg
+			
+	from SEX_H to GPR
+		gen
+			COMMENT("move SEX_H->GPR")
+			extsh %2, %1.reg
+					
+/* Register + something */
+
+	from SUM_RC smalls(%off) to GPR
+		gen	
+			COMMENT("move SUM_RC->GPR smalls")
+			addi %2, %1.reg, {CONST, lo(%1.off)}
+	
+	from SUM_RC to GPR
+		gen	
+			COMMENT("move SUM_RC->GPR large")
+			addi %2, %1.reg, {CONST, los(%1.off)}
+			addis %2, %2, {CONST, his(%1.off)}
+			
+	from SUM_RR to GPR
+		gen
+			COMMENT("move SUM_RR->GPR")
+			add %2, %1.reg1, %1.reg2
+		
+	from SUM_RR to GPR
+		gen
+			COMMENT("move SUM_RR->GPRE")
+			add %2, %1.reg1, %1.reg2
+		
+/* Read/write byte */
+
+	from IND_RC_B smalls(%off) to GPR
+		gen
+			COMMENT("move IND_RC_B->GPR small")
+			lbz %2, {GPRINDIRECT, %1.reg, %1.off}
+	
+	from IND_RC_B to GPR
+		gen
+			COMMENT("move IND_RC_B->GPR large")
+			addis SCRATCH, %1.reg, {CONST, his(%1.off)}
+			lbz %2, {GPRINDIRECT, SCRATCH, los(%1.off)}
+	
+	from GPR to IND_RC_B smalls(%off)
+		gen
+			COMMENT("move GPR->IND_RC_B small")
+			stb %1, {GPRINDIRECT, %2.reg, %2.off}
+	
+	from GPR to IND_RC_B
+		gen
+			COMMENT("move GPR->IND_RC_B large")
+			addis SCRATCH, %2.reg, {CONST, his(%2.off)}
+			stb %1, {GPRINDIRECT, SCRATCH, los(%2.off)}
+	
+/* Read/write short */
+
+	from IND_RC_H smalls(%off) to GPR
+		gen
+			COMMENT("move IND_RC_H->GPR small")
+			lhz %2, {GPRINDIRECT, %1.reg, %1.off}
+	
+	from IND_RC_H to GPR
+		gen
+			COMMENT("move IND_RC_H->GPR large")
+			addis SCRATCH, %1.reg, {CONST, his(%1.off)}
+			lhz %2, {GPRINDIRECT, SCRATCH, los(%1.off)}
+	
+	from IND_RC_H_S smalls(%off) to GPR
+		gen
+			COMMENT("move IND_RC_H_S->GPR small")
+			lha %2, {GPRINDIRECT, %1.reg, %1.off}
+	
+	from IND_RC_H_S to GPR
+		gen
+			COMMENT("move IND_RC_H_S->GPR large")
+			addis SCRATCH, %1.reg, {CONST, his(%1.off)}
+			lha %2, {GPRINDIRECT, SCRATCH, los(%1.off)}
+	
+	from GPR to IND_RC_H smalls(%off)
+		gen
+			COMMENT("move GPR->IND_RC_H small")
+			sth %1, {GPRINDIRECT, %2.reg, %2.off}
+	
+	from GPR to IND_RC_H
+		gen
+			COMMENT("move GPR->IND_RC_H large")
+			addis SCRATCH, %2.reg, {CONST, his(%2.off)}
+			sth %1, {GPRINDIRECT, SCRATCH, los(%2.off)}
+	
+/* Read word */
+
+	from IND_RC_W smalls(%off) to GPR
+		gen
+			COMMENT("move IND_RC_W->GPR small")
+			lwz %2, {GPRINDIRECT, %1.reg, %1.off}
+	
+	from IND_RC_W to GPR
+		gen
+			COMMENT("move IND_RC_W->GPR large")
+			addis %2, %1.reg, {CONST, his(%1.off)}
+			lwz %2, {GPRINDIRECT, %2, los(%1.off)}
+
+	from IND_RR_W to GPR
+		gen
+			COMMENT("move IND_RR_W->GPR")
+			lwzx %2, %1.reg1, %1.reg2
+			
+	from IND_LABEL_W to GPR
+		gen
+			COMMENT("move IND_LABEL_W->GPR")
+			move {LABEL, %1.adr}, SCRATCH
+			lwz %2, {GPRINDIRECT, SCRATCH, 0}
+		
+	from IND_RC_W smalls(%off) to FS
+		gen
+			COMMENT("move IND_RC_W->FS small")
+			lfs %2, {GPRINDIRECT, %1.reg, %1.off}
+	
+	from IND_RC_W to FS
+		gen
+			COMMENT("move IND_RC_W->FS large")
+			addis SCRATCH, %1.reg, {CONST, his(%1.off)}
+			lfs %2, {GPRINDIRECT, SCRATCH, los(%1.off)}
+
+	from IND_RR_W to FS
+		gen
+			COMMENT("move IND_RR_W->FS")
+			lfsx %2, %1.reg1, %1.reg2
+			
+	from IND_LABEL_W to FS
+		gen
+			COMMENT("move IND_LABEL_W->FS")
+			move {LABEL, %1.adr}, SCRATCH
+			lfs %2, {GPRINDIRECT, SCRATCH, 0}
+		
+/* Write word */
+
+	from GPR to IND_RC_W smalls(%off)
+		gen
+			COMMENT("move GPR->IND_RC_W small")
+			stw %1, {GPRINDIRECT, %2.reg, %2.off}
+	
+	from GPR to IND_RC_W
+		gen
+			COMMENT("move GPR->IND_RC_W large")
+			addis SCRATCH, %2.reg, {CONST, his(%2.off)}
+			stw %1, {GPRINDIRECT, SCRATCH, los(%2.off)}
+
+	from GPR to IND_RR_W
+		gen
+			COMMENT("move GPR->IND_RR_W")
+			stwx %1, %2.reg1, %2.reg2
+			
+	from GPR to IND_LABEL_W
+		gen
+			COMMENT("move GPR->IND_LABEL_D")
+			move {LABEL, %2.adr}, SCRATCH
+			stw %1, {GPRINDIRECT, SCRATCH, 0}
+			
+	from FS to IND_RC_W smalls(%off)
+		gen
+			COMMENT("move FS->IND_RC_W small")
+			stfs %1, {GPRINDIRECT, %2.reg, %2.off}
+	
+	from FS to IND_RC_W
+		gen
+			COMMENT("move FS->IND_RC_W large")
+			addis SCRATCH, %2.reg, {CONST, his(%2.off)}
+			stfs %1, {GPRINDIRECT, SCRATCH, los(%2.off)}
+
+	from FS to IND_RR_W
+		gen
+			COMMENT("move FS->IND_RR_W")
+			stfsx %1, %2.reg1, %2.reg2
+
+	from FS to IND_LABEL_W
+		gen
+			COMMENT("move FS->IND_LABEL_D")
+			move {LABEL, %2.adr}, SCRATCH
+			stfs %1, {GPRINDIRECT, SCRATCH, 0}
+			
+/* Read double */
+
+	from IND_RC_D smalls(%off) to FD
+		gen
+			COMMENT("move IND_RC_D->FD small")
+			lfd %2, {GPRINDIRECT, %1.reg, %1.off}
+	
+	from IND_RC_D to FD
+		gen
+			COMMENT("move IND_RC_D->FD large")
+			addis SCRATCH, %1.reg, {CONST, his(%1.off)}
+			lfd %2, {GPRINDIRECT, SCRATCH, los(%1.off)}
+			
+	from IND_RR_D to FD
+		gen
+			COMMENT("move IND_RR_D->FD")
+			lfdx %2, %1.reg1, %1.reg2
+
+	from IND_LABEL_D to FD
+		gen
+			COMMENT("move IND_LABEL_D->FD")
+			move {LABEL, %1.adr}, SCRATCH
+			lfd %2, {GPRINDIRECT, SCRATCH, 0}
+		
+/* Write double */
+
+	from FD to IND_RC_D smalls(%off)
+		gen
+			COMMENT("move FD->IND_RC_D small")
+			stfd %1, {GPRINDIRECT, %2.reg, %2.off}
+	
+	from FD to IND_RC_D
+		gen
+			COMMENT("move FD->IND_RC_D large")
+			addis SCRATCH, %2.reg, {CONST, his(%2.off)}
+			stfd %1, {GPRINDIRECT, SCRATCH, los(%2.off)}
+
+	from FD to IND_RR_D
+		gen
+			COMMENT("move FD->IND_RR_W")
+			stfdx %1, %2.reg1, %2.reg2
+			
+	from FD to IND_LABEL_D
+		gen
+			COMMENT("move FD->IND_LABEL_D")
+			move {LABEL, %2.adr}, SCRATCH
+			stfd %1, {GPRINDIRECT, SCRATCH, 0}
+			
+/* Extract condition code field (actually produces (CC&3)<<2) */
+
+	from CR0 to GPR
+		gen
+			COMMENT("move CR0->GPR")
+			mfcr %2
+			rlwinm %2, %2, {CONST, 4}, {CONST, 32-4}, {CONST, 31-2}
+
+/* Comparisons */
+
+	from TRISTATE_RR_S to CR0
+		gen
+			cmp %2, {CONST, 0}, %1.reg1, %1.reg2
+			
+	from TRISTATE_RR_U to CR0
+		gen
+			cmpl %2, {CONST, 0}, %1.reg1, %1.reg2
+		
+	from TRISTATE_RC_S to CR0
+		gen
+			COMMENT("move TRISTATE_RC_S->CR0 large")
+			move {CONST, %1.val}, SCRATCH
+			cmp %2, {CONST, 0}, %1.reg, SCRATCH
+			
+	from TRISTATE_RC_U smallu(%val) to CR0
+		gen
+			COMMENT("move TRISTATE_RC_U->CR0 small")
+			cmpli %2, {CONST, 0}, %1.reg, {CONST, %1.val}
+		
+	from TRISTATE_RC_U to CR0
+		gen
+			COMMENT("move TRISTATE_RC_U->CR0")
+			move {CONST, %1.val}, SCRATCH
+			cmpl %2, {CONST, 0}, %1.reg, SCRATCH
+		
+	from TRISTATE_FF to CR0
+		gen
+			COMMENT("move TRISTATE_FF->CR0")
+			fcmpo %2, {FD, %1.reg1}, {FD, %1.reg2}
+			
+	from GPR to CR0
+		gen
+			COMMENT("move GPR->CR0")
+			orX SCRATCH, %1, %1 /* alas, can't call test */
+			
+	from TRISTATE_RR_S + TRISTATE_RC_S + TRISTATE_FF to GPR
+		gen
+			COMMENT("move TRISTATE_R*_S->GPR")
+			move %1, C0
+			move C0, SCRATCH
+			move {LABEL, ".tristate_s_table"}, %2
+			lwzx %2, %2, SCRATCH				
+
+	from TRISTATE_RR_U + TRISTATE_RC_U to GPR
+		gen
+			COMMENT("move TRISTATE_R*_U->GPR")
+			move %1, C0
+			move C0, SCRATCH
+			move {LABEL, ".tristate_u_table"}, %2
+			lwzx %2, %2, SCRATCH				
+
+/* Logicals */
+
+	from NOT_R to GPR
+		gen
+			COMMENT("move NOT_R->GPR")
+			nor %2, %1.reg, %1.reg
+
+	from AND_RR to GPR
+		gen
+			COMMENT("move AND_RR->GPR")
+			and %2, %1.reg1, %1.reg2
+
+	from AND_RC smallu(%val) to GPR
+		gen
+			COMMENT("move AND_RC->GPR small")
+			andiX %2, %1.reg, {CONST, %1.val}
+
+	from AND_RC to GPR
+		gen
+			COMMENT("move AND_RC->GPR")
+			move {CONST, %1.val}, SCRATCH
+			and %2, %1.reg, SCRATCH
+
+	from OR_RR to GPR
+		gen
+			COMMENT("move OR_RR->GPR")
+			or %2, %1.reg1, %1.reg2
+
+	from OR_RC smallu(%val) to GPR
+		gen
+			COMMENT("move OR_RC->GPR small")
+			ori %2, %1.reg, {CONST, %1.val}
+
+	from OR_RC to GPR
+		gen
+			COMMENT("move OR_RC->GPR")
+			move {CONST, %1.val}, SCRATCH
+			or %2, %1.reg, SCRATCH
+
+	from XOR_RR to GPR
+		gen
+			COMMENT("move XOR_RR->GPR")
+			xor %2, %1.reg1, %1.reg2
+
+	from XOR_RC smallu(%val) to GPR
+		gen
+			COMMENT("move XOR_RC->GPR small")
+			xori %2, %1.reg, {CONST, %1.val}
+
+	from XOR_RC to GPR
+		gen
+			COMMENT("move XOR_RC->GPR")
+			move {CONST, %1.val}, SCRATCH
+			xor %2, %1.reg, SCRATCH
+
+/* Miscellaneous */
+
+	from OP_ALL_W + LABEL + CONST to GPRE
+		gen
+			move %1, %2.reg
+
+		
+TESTS
+	
+	to test GPR
+		gen
+			orX SCRATCH, %1, %1
+
+
+
+STACKINGRULES
+	
+	from GPR to STACK
+		gen
+			COMMENT("stack GPR")
+			stwu %1, {GPRINDIRECT, SP, 0-4}
+	
+	from CONST to STACK
+		uses REG
+		gen
+			COMMENT("stack CONST")
+			move %1, %a
+			stwu %a, {GPRINDIRECT, SP, 0-4}
+			
+	from LABEL to STACK
+		uses REG
+		gen
+			COMMENT("stack LABEL")
+			move %1, {GPRE, %a}
+			stwu %a, {GPRINDIRECT, SP, 0-4}
+			
+	from SEX_B to STACK
+		gen
+			COMMENT("stack SEX_B")
+			extsb SCRATCH, %1.reg
+			stwu SCRATCH, {GPRINDIRECT, SP, 0-4}
+			
+	from SEX_H to STACK
+		gen
+			COMMENT("stack SEX_H")
+			extsh SCRATCH, %1.reg
+			stwu SCRATCH, {GPRINDIRECT, SP, 0-4}
+			
+	from SUM_ALL + TRISTATE_ALL + LOGICAL_ALL to STACK
+		gen
+			move %1, {GPRE, SCRATCH}
+			stwu SCRATCH, {GPRINDIRECT, SP, 0-4}
+			
+	from IND_ALL_W to STACK
+		gen
+			move %1, SCRATCH
+			stwu SCRATCH, {GPRINDIRECT, SP, 0-4}
+			
+	from IND_ALL_D to STACK
+		gen
+			move %1, {FD, FSCRATCH}
+			stfdu {FD, FSCRATCH}, {GPRINDIRECT, SP, 0-8}
+			
+	from FD to STACK
+		gen
+			COMMENT("stack FD")
+			stfdu %1, {GPRINDIRECT, SP, 0-8}
+			
+	from FS to STACK
+		gen
+			COMMENT("stack FS")
+			stfsu %1, {GPRINDIRECT, SP, 0-4}
+			
+	from TOKEN to STACK
+		gen
+			invalid.
+			
+		
+		
+COERCIONS
+
+	from REG
+		uses REG
+		gen
+			COMMENT("coerce REG->REG")
+			move %1, %a
+		yields %a
+		
+	from CONST
+		uses REG
+		gen
+			COMMENT("coerce CONST->REG")
+			move %1, %a
+		yields %a
+		
+	from LABEL
+		uses REG
+		gen
+			COMMENT("coerce LABEL->REG")
+			move %1, {GPRE, %a}
+		yields %a
+		
+	from STACK
+		uses REG
+		gen
+			COMMENT("coerce STACK->REG")
+			lwz %a, {GPRINDIRECT, SP, 0}
+			addi SP, SP, {CONST, 4}
+		yields %a
+	
+	from SEX_B
+		uses REG
+		gen
+			COMMENT("coerce SEX_B->REG")
+			extsb %a, %1.reg
+		yields %a
+		
+	from SEX_H
+		uses REG
+		gen
+			COMMENT("coerce SEX_H->REG")
+			extsh %a, %1.reg
+		yields %a
+	
+	from SUM_ALL + TRISTATE_ALL + LOGICAL_ALL
+		uses REG
+		gen
+			move %1, {GPRE, %a}
+		yields %a
+	
+	from FS
+		uses FREG
+		gen
+			fmr {FS, %a}, %1
+		yields {FS, %a}
+		
+	from FD
+		uses FREG
+		gen
+			fmr {FD, %a}, %1
+		yields {FD, %a}
+		
+	from STACK
+		uses FREG
+		gen
+			COMMENT("coerce STACK->FD")
+			lfd {FD, %a}, {GPRINDIRECT, SP, 0}
+			addi SP, SP, {CONST, 8}
+		yields {FD, %a}
+
+	from STACK
+		uses FREG
+		gen
+			COMMENT("coerce STACK->FS")
+			lfs {FS, %a}, {GPRINDIRECT, SP, 0}
+			addi SP, SP, {CONST, 4}
+		yields {FS, %a}
+		
+	from IND_ALL_W
+		uses REG
+		gen
+			move %1, %a
+		yields %a
+		
+	from IND_ALL_W
+		uses FREG
+		gen
+			move %1, {FS, %a}
+		yields {FS, %a}
+		
+	from IND_ALL_D
+		uses FREG
+		gen
+			move %1, {FD, %a}
+		yields {FD, %a}
+		
+	
+
+
+PATTERNS
+
+/* Intrinsics */
+
+	pat loc                            /* Load constant */
+		yields {CONST, $1}
+
+	pat dup $1==INT32                  /* Duplicate word on top of stack */
+		with GPR
+			yields %1 %1
+								
+	pat dup $1==INT64                  /* Duplicate double-word on top of stack */
+		with GPR GPR
+			yields %2 %1 %2 %1
+								
+	pat exg $1==INT32                  /* Exchange top two words on stack */
+		with GPR GPR
+			yields %1 %2
+		
+	pat stl lol $1==$2                 /* Store then load local */
+		leaving
+			dup 4
+			stl $1
+			
+	pat lal sti lal loi $1==$3 && $2==$4 /* Store then load local, of a different size */
+		leaving
+			dup INT32
+			lal $1
+			sti $2
+			
+	pat ste loe $1==$2                 /* Store then load external */
+		leaving
+			dup 4
+			ste $1
+		
+		
+/* Type conversions */
+
+	pat loc loc cii loc loc cii $1==$4 && $2==$5 /* madness, generated by the C compiler */
+		leaving
+			loc $1
+			loc $2
+			cii
+			
+	pat loc loc cii loc loc cii $2==INT32 && $5==INT32 && $4<$2 /* madness, generated by the C compiler */
+		leaving
+			loc $4
+			loc $5
+			cii
+			
+	pat loc loc ciu                    /* signed X -> unsigned X */
+		leaving
+			loc $1
+			loc $2
+			cuu
+			
+	pat loc loc cuu $1==$2             /* unsigned X -> unsigned X */
+		/* nop */
+
+	pat loc loc cii $1==$2             /* signed X -> signed X */
+		/* nop */
+
+	pat loc loc cui $1==$2             /* unsigned X -> signed X */
+		/* nop */
+		
+	pat loc loc cui $1==INT8 && $2==INT32 /* unsigned char -> signed int */
+		/* nop */
+	
+	pat loc loc cui $1==INT16 && $2==INT32 /* unsigned short -> signed int */
+		/* nop */
+	
+	pat loc loc cii $1==INT8 && $2==INT32 /* signed char -> signed int */
+		with GPR
+			yields {SEX_B, %1}
+	
+	pat loc loc cii $1==2 && $2==4     /* signed char -> signed short */
+		with GPR
+			yields {SEX_H, %1}
+	
+
+		
+	
+		
+/* Local variables */
+
+	pat lal                            /* Load address of local */
+		yields {SUM_RC, FP, $1}
+
+	pat lol inreg($1)>0                /* Load from local */
+		yields {LOCAL, $1}
+		
+	pat lol                            /* Load from local */
+		leaving
+			lal $1
+			loi INT32
+
+	pat ldl                            /* Load double-word from local */
+		leaving
+			lal $1
+			loi INT32*2
+			
+	pat stl inreg($1)>0                /* Store to local */
+		with CONST + LABEL + GPR + OP_ALL_W
+			kills regvar($1), LOCAL %off==$1
+			gen
+				move %1, {GPRE, regvar($1)}
+		
+	pat stl                            /* Store to local */
+		leaving
+			lal $1
+			sti INT32
+		
+	pat sdl                            /* Store double-word to local */
+		leaving
+			lal $1
+			sti INT32*2
+			
+	pat lil inreg($1)>0                /* Load from indirected local */
+		uses REG
+		gen
+			lwz %a, {GPRINDIRECT, regvar($1), 0}
+		yields %a
+		
+	pat lil                            /* Load from indirected local */
+		leaving
+			lol $1
+			loi INT32
+			
+	pat sil                            /* Save to indirected local */
+		leaving
+			lol $1
+			sti INT32
+			
+	pat stl lol $1==$2                 /* Save then load (generated by C compiler) */
+		leaving
+			dup 4
+			stl $1
+			
+	pat zrl                             /* Zero local */
+		leaving
+			loc 0
+			stl $1
+	
+	pat inl                             /* Increment local */
+		leaving
+			lol $1
+			loc 1
+			adi 4
+			stl $1
+					
+	pat del                             /* Decrement local */
+		leaving
+			lol $1
+			loc 1
+			sbi 4
+			stl $1
+
+
+/* Global variables */
+		
+	pat lpi                            /* Load address of external function */
+		leaving
+			lae $1
+				
+	pat lae                            /* Load address of external */
+		yields {LABEL, $1}
+		
+	pat loe                            /* Load word external */
+		leaving
+			lae $1
+			loi INT32
+
+	pat ste                            /* Store word external */
+		leaving
+			lae $1
+			sti INT32
+			
+	pat lde                            /* Load double-word external */
+		leaving
+			lae $1
+			loi INT64
+			
+	pat sde                            /* Store double-word external */
+		leaving
+			lae $1
+			sti INT64
+			
+	pat zre                             /* Zero external */
+		leaving
+			loc 0
+			ste $1
+	
+	pat ine                             /* Increment external */
+		uses REG={LABEL, $1}, REG
+			gen
+				lwz %b, {GPRINDIRECT, %a, 0}
+				addi %b, %b, {CONST, 1}
+				stw %b, {GPRINDIRECT, %a, 0}
+					
+	pat dee                             /* Decrement external */
+		uses REG={LABEL, $1}, REG
+			gen
+				lwz %b, {GPRINDIRECT, %a, 0}
+				addi %b, %b, {CONST, 0-1}
+				stw %b, {GPRINDIRECT, %a, 0}
+					
+
+
+/* Structures */
+
+	pat lof                            /* Load word offsetted */
+		leaving
+			adp $1
+			loi INT32
+			
+	pat ldf                            /* Load double-word offsetted */
+		leaving
+			adp $1
+			loi INT64
+			
+	pat stf                            /* Store word offsetted */
+		leaving
+			adp $1
+			sti INT32
+			
+	pat sdf                            /* Store double-word offsetted */
+		leaving
+			adp $1
+			sti INT64
+			
+
+
+/* Loads and stores */
+
+	pat loi $1==INT8                   /* Load byte indirect */
+		with GPR
+			uses REG
+			gen
+				lbz %a, {GPRINDIRECT, %1, 0}
+			yields %a
+		with SUM_RR
+			uses reusing %1, REG
+			gen
+				lbzx %a, %1.reg1, %1.reg2
+			yields %a
+		with SUM_RC
+			uses REG
+			gen
+				move {IND_RC_B, %1.reg, %1.off}, %a
+			yields %a
+		
+	pat loi loc loc cii $1==INT16 && $2==INT16 && $3==INT32 /* Load half-word indirect and sign extend */
+		with GPR
+			uses REG
+			gen
+				lha %a, {GPRINDIRECT, %1, 0}
+			yields %a
+		with SUM_RR
+			uses reusing %1, REG
+			gen
+				lhax %a, %1.reg1, %1.reg2
+			yields %a
+		with SUM_RC
+			uses REG
+			gen
+				move {IND_RC_H_S, %1.reg, %1.off}, %a
+			yields %a
+		
+	pat loi $1==INT16                  /* Load half-word indirect */
+		with GPR
+			uses REG
+			gen
+				lhz %a, {GPRINDIRECT, %1, 0}
+			yields %a
+		with SUM_RR
+			uses reusing %1, REG
+			gen
+				lhzx %a, %1.reg1, %1.reg2
+			yields %a
+		with SUM_RC
+			uses REG
+			gen
+				move {IND_RC_H, %1.reg, %1.off}, %a
+			yields %a
+		
+	pat loi $1==INT32                  /* Load word indirect */
+		with GPR
+			yields {IND_RC_W, %1, 0}
+		with SUM_RC
+			yields {IND_RC_W, %1.reg, %1.off}
+		with SUM_RR
+			yields {IND_RR_W, %1.reg1, %1.reg2}
+		with LABEL
+			yields {IND_LABEL_W, %1.adr}
+
+	pat loi $1==INT64                  /* Load double-word indirect */
+		with GPR
+			yields {IND_RC_D, %1, 0}
+		with SUM_RC
+			yields {IND_RC_D, %1.reg, %1.off}
+		with SUM_RR
+			yields {IND_RR_D, %1.reg1, %1.reg2}
+		with LABEL
+			yields {IND_LABEL_D, %1.adr}
+
+	pat loi                            /* Load arbitrary size */
+		leaving
+			loc $1
+			los INT32
+					
+	pat los                            /* Load arbitrary size */
+		with GPR3 GPR4 STACK
+			kills ALL
+			gen
+				bl {LABEL, ".los"}
+				
+	pat sti $1==INT8                   /* Store byte indirect */
+		with GPR GPR
+			gen
+				stb %2, {GPRINDIRECT, %1, 0}
+		with SUM_RR GPR
+			gen
+				stbx %2, %1.reg1, %1.reg2
+		with SUM_RC GPR
+			gen
+				move %2, {IND_RC_B, %1.reg, %1.off}
+		with GPR SEX_B
+			gen
+				stb %2.reg, {GPRINDIRECT, %1, 0}
+		with SUM_RR SEX_B
+			gen
+				stbx %2.reg, %1.reg1, %1.reg2
+		with SUM_RC SEX_B
+			gen
+				move %2.reg, {IND_RC_B, %1.reg, %1.off}
+
+	pat sti $1==INT16                  /* Store half-word indirect */
+		with GPR GPR
+			gen
+				sth %2, {GPRINDIRECT, %1, 0}
+		with SUM_RR GPR
+			gen
+				sthx %2, %1.reg1, %1.reg2
+		with SUM_RC GPR
+			gen
+				move %2, {IND_RC_H, %1.reg, %1.off}
+		with GPR SEX_H
+			gen
+				sth %2.reg, {GPRINDIRECT, %1, 0}
+		with SUM_RR SEX_H
+			gen
+				sthx %2.reg, %1.reg1, %1.reg2
+		with SUM_RC SEX_H
+			gen
+				move %2.reg, {IND_RC_H, %1.reg, %1.off}
+
+	pat sti $1==INT32                  /* Store word indirect */
+		with GPR GPR+FS
+			gen
+				move %2, {IND_RC_W, %1, 0}
+		with SUM_RR GPR+FS
+			gen
+				move %2, {IND_RR_W, %1.reg1, %1.reg2}
+		with SUM_RC GPR+FS
+			gen
+				move %2, {IND_RC_W, %1.reg, %1.off}
+		with LABEL GPR+FS
+			gen
+				move %2, {IND_LABEL_W, %1.adr}
+
+	pat sti $1==INT64                  /* Store double-word indirect */
+		with GPR FD
+			gen
+				move %2, {IND_RC_D, %1, 0}
+		with SUM_RR FD
+			gen
+				move %2, {IND_RR_D, %1.reg1, %1.reg2}
+		with SUM_RC FD
+			gen
+				move %2, {IND_RC_D, %1.reg, %1.off}
+		with GPR GPR GPR
+			gen
+				stw %2, {GPRINDIRECT, %1, 0}
+				stw %3, {GPRINDIRECT, %1, 4}
+		with SUM_RC GPR GPR
+			gen
+				move %2, {IND_RC_W, %1.reg, %1.off}
+				move %3, {IND_RC_W, %1.reg, %1.off+4}
+		with LABEL FD
+			gen
+				move %2, {IND_LABEL_D, %1.adr}
+				
+
+	pat sti                            /* Store arbitrary size */
+		leaving
+			loc $1
+			sts INT32
+					
+	pat sts                            /* Load arbitrary size */
+		with GPR3 GPR4 STACK
+			kills ALL
+			gen
+				bl {LABEL, ".sts"}
+				
+
+
+/* Arithmetic wrappers */
+
+	pat ads $1==4                      /* Add var to pointer */
+		leaving adi $1
+	
+	pat sbs $1==4                      /* Subtract var from pointer */
+		leaving sbi $1
+		
+	pat adp                            /* Add constant to pointer */
+		leaving
+			loc $1
+			adi 4
+
+	pat adu                            /* Add unsigned */
+		leaving
+			adi $1
+			
+	pat sbu                            /* Subtract unsigned */
+		leaving
+			sbi $1
+			
+	pat inc                            /* Add 1 */
+		leaving
+			loc 1
+			adi 4
+			
+	pat dec                            /* Subtract 1 */
+		leaving
+			loc 1
+			sbi 4
+	
+	pat loc mlu $2==2                  /* Unsigned multiply by constant */
+		leaving
+			loc $1
+			mli 4
+			
+	pat mlu                            /* Unsigned multiply by var */
+		leaving
+			mli $1
+			
+	pat loc slu                        /* Shift left unsigned by constant amount */
+		leaving
+			loc $1
+			sli $2
+			
+	pat slu                            /* Shift left unsigned by variable amount */
+		leaving
+			sli $1
+
+			
+			
+/* Word arithmetic */
+
+	pat adi $1==4                      /* Add word (second + top) */
+		with REG REG
+			yields {SUM_RR, %1, %2}
+		with CONST REG
+			yields {SUM_RC, %2, %1.val}
+		with REG CONST
+			yields {SUM_RC, %1, %2.val}
+		with CONST SUM_RC
+			yields {SUM_RC, %2.reg, %2.off+%1.val}
+		with CONST LABEL
+			yields {LABEL, %2.adr+%1.val}
+			
+	pat sbi $1==4                      /* Subtract word (second - top) */
+		with REG REG
+			uses reusing %2, REG
+			gen
+				subf %a, %1, %2
+			yields %a
+		with CONST REG
+			yields {SUM_RC, %2, 0-%1.val}
+		with CONST SUM_RC
+			yields {SUM_RC, %2.reg, %2.off-%1.val}
+		with CONST LABEL
+			yields {LABEL, %2.adr+(0-%1.val)}
+				
+	pat ngi $1==4                      /* Negate word */
+		with REG
+			uses reusing %1, REG
+			gen
+				neg %a, %1
+			yields %a
+		
+	pat mli $1==4                      /* Multiply word (second * top) */
+		with REG REG
+			uses reusing %2, REG
+			gen
+				mullw %a, %2, %1
+			yields %a
+		
+	pat dvi $1==4                      /* Divide word (second / top) */
+		with REG REG
+			uses reusing %2, REG
+			gen
+				divw %a, %2, %1
+			yields %a
+					
+	pat dvu $1==4                      /* Divide unsigned word (second / top) */
+		with REG REG
+			uses reusing %2, REG
+			gen
+				divwu %a, %2, %1
+			yields %a
+
+	pat rmi $1==4                      /* Remainder word (second % top) */
+		with REG REG
+			uses REG
+			gen
+				divw %a, %2, %1
+				mullw %a, %a, %1
+				subf %a, %a, %2
+			yields %a
+								
+	pat rmu $1==4                      /* Remainder unsigned word (second % top) */
+		with REG REG
+			uses REG
+			gen
+				divwu %a, %2, %1
+				mullw %a, %a, %1
+				subf %a, %a, %2
+			yields %a
+
+	pat and $1==4                      /* AND word */
+		with GPR NOT_R
+			uses reusing %1, REG
+			gen
+				andc %a, %1, %2.reg
+			yields %a
+		with NOT_R GPR
+			uses reusing %1, REG
+			gen
+				andc %a, %2, %1.reg
+			yields %a
+		with GPR GPR
+			yields {AND_RR, %1, %2}
+		with GPR CONST
+			yields {AND_RC, %1, %2.val}
+		with CONST GPR
+			yields {AND_RC, %2, %1.val}
+		
+	pat and !defined($1)               /* AND set */
+		with STACK
+			gen
+				bl {LABEL, ".and"}
+				
+	pat ior $1==4                      /* OR word */
+		with GPR NOT_R
+			uses reusing %1, REG
+			gen
+				orc %a, %1, %2.reg
+			yields %a
+		with NOT_R GPR
+			uses reusing %2, REG
+			gen
+				orc %a, %2, %1.reg
+			yields %a
+		with GPR GPR
+			yields {OR_RR, %1, %2}
+		with GPR CONST
+			yields {OR_RC, %1, %2.val}
+		with CONST GPR
+			yields {OR_RC, %2, %1.val}
+		
+	pat ior !defined($1)               /* OR set */
+		with STACK
+			gen
+				bl {LABEL, ".ior"}
+				
+	pat xor $1==4                      /* XOR word */
+		with GPR GPR
+			yields {XOR_RR, %1, %2}
+		with GPR CONST
+			yields {XOR_RC, %1, %2.val}
+		with CONST GPR
+			yields {XOR_RC, %2, %1.val}
+	
+	pat xor !defined($1)               /* XOR set */
+		with STACK
+			gen
+				bl {LABEL, ".xor"}
+				
+	pat com $1==INT32                  /* NOT word */
+		with AND_RR
+			uses REG
+			gen
+				nand %a, %1.reg1, %1.reg2
+			yields %a
+		with OR_RR
+			uses REG
+			gen
+				nor %a, %1.reg1, %1.reg2
+			yields %a
+		with XOR_RR
+			uses REG
+			gen
+				eqv %a, %1.reg1, %1.reg2
+			yields %a
+		with GPR
+			yields {NOT_R, %1}
+				
+	pat com !defined($1)               /* NOT set */
+		with STACK
+			gen
+				bl {LABEL, ".com"}
+				
+	pat sli $1==4                      /* Shift left (second << top) */
+		with CONST GPR
+			uses reusing %2, REG