/*
 * 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 */

BYTE = 1        /* Size of values */
WORD = 2
QUAD = 4

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)==BYTE || (x)==WORD || (x)==QUAD)



PROPERTIES

	GPR             /* any GPR */
	REG             /* any allocatable GPR */
	STACKABLE       /* a register than can be used with push/pop */

	GPR0  GPR1  GPR2  GPR3  GPR4  GPR5  GPR6  GPR7
	GPR8  GPR9  GPR10 GPR11 GPR12 GPR13 GPR14 GPR15
	GPR16 GPR17 GPR18 GPR19 GPR20 GPR21 GPR22 GPR23

	GPRGP GPRFP GPRSP GPRLR GPRPC

REGISTERS

	R0("r0")           : GPR, REG, GPR0, STACKABLE.
	R1("r1")           : GPR, REG, GPR1.
	R2("r2")           : GPR, REG, GPR2.
	R3("r3")           : GPR, REG, GPR3.
	R4("r4")           : GPR, REG, GPR4.
	R5("r5")           : GPR, REG, GPR5.
	R6("r6")           : GPR, GPR6.
	R7("r7")           : GPR, REG, GPR7.
	R8("r8")           : GPR, REG, GPR8.
	R9("r9")           : GPR, REG, GPR9.
	R10("r10")         : GPR, REG, GPR10.
	R11("r11")         : GPR, REG, GPR11.
	R12("r12")         : GPR, REG, GPR12.
	R13("r13")         : GPR, REG, GPR13.
	R14("r14")         : GPR, REG, GPR14.
	GP("r15")          : GPR, GPRGP.

	R16("r16")         : GPR, GPR16.

	R23("r23")         : GPR.
	FP("fp")           : GPR, GPRFP.
	SP("sp")           : GPR, GPRSP.
	LR("lr")           : GPR, GPRLR.
	PC("pc")           : GPR, GPRPC.
	/* r26 to r31 are special and the code generator doesn't touch them. */

	#define SCRATCH R6

TOKENS

/* Used only in instruction descriptions (to generate the correct syntax). */

	GPROFFSET          = { GPR reg; INT off; }    4    off "(" reg ")".
	GPRGPR             = { GPR reg1; GPR reg2; }  4    "(" reg1 "," reg2 ")".
	GPRINC             = { GPR reg; }             4    "(" reg ")++".
	ADDCMPB_LL         = { GPR rd; INT val; INT vs; ADDR dest; } 4 rd ",#" val ",#" vs "," dest.

/* Primitives */

	LABEL              = { ADDR adr; }            4    adr.
	CONST              = { INT val; }             4    "#" val.

/* Sign extended values. */

	/* The size refers to the *source*. */
	SIGNEX8            = { GPR reg; }             4    reg.
	SIGNEX16           = { GPR reg; }             4    reg.

/* The results of comparisons. */

	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.



SETS

	TOKEN              = LABEL + CONST.
	OP                 = TOKEN + SIGNEX8 + SIGNEX16.
	ANY                = GPR + OP.



INSTRUCTIONS

	add           GPR:wo, GPR:ro, GPR+CONST:ro.
	add           GPR:rw, GPR+CONST:ro.
	addcmpbge "addcmpb.ge" ADDCMPB_LL:rw.
	adds2         GPR:rw, GPR+CONST:ro.
	adds4         GPR:rw, GPR+CONST:ro.
	adds8         GPR:rw, GPR+CONST:ro.
	adds16        GPR:rw, GPR+CONST:ro.
	adds256       GPR:rw, GPR:rw, GPR:ro.
	and           GPR:rw, GPR+CONST:ro.
	asr           GPR:rw, GPR+CONST:ro.
	beq "b.eq"    LABEL:ro.
	bne "b.ne"    LABEL:ro.
	bgt "b.gt"    LABEL:ro.
	blt "b.lt"    LABEL:ro.
	bhi "b.hi"    LABEL:ro.
	bset          GPR:rw, GPR+CONST:ro.
	b             GPR+LABEL:ro.
	bl            GPR+LABEL:ro.
	cmp           GPR:ro, GPR+CONST:ro kills :cc.
	divs          GPR:wo, GPR:ro, GPR+CONST:ro.
	divu          GPR:wo, GPR:ro, GPR+CONST:ro.
	eor           GPR:rw, GPR+CONST:ro.
	exts          GPR:wo, GPR:ro, GPR+CONST:ro.
	exts          GPR:rw, GPR+CONST:ro.
	fadd          GPR:wo, GPR:ro, GPR:ro.
	fcmp          GPR:wo, GPR:ro, GPR:ro.
	fdiv          GPR:wo, GPR:ro, GPR:ro.
	flts          GPR:wo, GPR:ro.
	fltu          GPR:wo, GPR:ro.
	fmul          GPR:wo, GPR:ro, GPR:ro.
	fsub          GPR:wo, GPR:ro, GPR:ro.
	ftrunc        GPR:wo, GPR:ro.
	ld            GPR:wo, GPRINC:rw.
	ld            GPR:wo, GPROFFSET+GPRGPR+LABEL:ro.
	ldb           GPR:wo, GPROFFSET+GPRGPR+LABEL:ro.
	ldh           GPR:wo, GPROFFSET+GPRGPR+LABEL:ro.
	ldhs          GPR:wo, GPROFFSET+GPRGPR+LABEL:ro.
	lea           GPR:wo, LABEL:ro.
	lsl           GPR:rw, GPR+CONST:ro.
	lsl           GPR:wo, GPR:ro, GPR+CONST:ro.
	lsr           GPR:rw, GPR+CONST:ro.
	mov           GPR:wo, GPR+CONST:ro.
	mul           GPR:rw, GPR+CONST:ro.
	mvn           GPR:wo, GPR+CONST:ro.
	neg           GPR:rw, GPR+CONST:ro.
	or            GPR:rw, GPR+CONST:ro.
	pop           GPR0+GPR6+GPR16+GPRFP+GPRPC:wo.
	pop           GPR0+GPR6+GPR16+GPRFP:wo, GPRPC:wo.
	push          GPR0+GPR6+GPR16+GPRFP+GPRLR:ro.
	push          GPR0+GPR6+GPR16+GPRFP:ro, GPRLR:ro.
	rsb           GPR:rw, GPR+CONST:ro.
	sub           GPR:wo, GPR:ro, CONST+GPR:ro.
	sub           GPR:rw, GPR+CONST:ro.
	st            GPR:ro, GPRINC:rw.
	st            GPR:ro, GPROFFSET+GPRGPR+LABEL:ro.
	stb           GPR:ro, GPROFFSET+GPRGPR+LABEL:ro.
	sth           GPR:ro, GPROFFSET+GPRGPR+LABEL:ro.
	sths          GPR:ro, GPROFFSET+GPRGPR+LABEL:ro.

	invalid "invalid".
	comment "!" LABEL:ro.


  
MOVES

	from GPR to GPR
		gen
			COMMENT("mov GPR->GPR")
			mov %2, %1

/* Constants */

	from CONST to GPR
		gen
			mov %2, %1

	from LABEL to GPR
		gen
			lea %2, {LABEL, %1.adr}
			sub %2, GP

/* Sign extension */

	from SIGNEX8 to GPR
		gen
			exts %2, %1.reg, {CONST, 8}

	from SIGNEX16 to GPR
		gen
			exts %2, %1.reg, {CONST, 16}

/* Miscellaneous */

	from CONST+LABEL+GPR to GPR
		gen
			move %1, %2


TESTS

	to test GPR
		gen
			cmp %1, {CONST, 0}



STACKINGRULES

	from GPR0+GPR6+GPR16 to STACK
		gen
			comment {LABEL, "push stackable"}
		    push %1

	from OP+GPR to STACK
		uses GPR0
		gen
			move %1, %a
			push %a

	from OP to STACK
		uses STACKABLE
		gen
			move %1, %a
			push %a

	from OP+GPR to STACK
		gen
			comment {LABEL, "push via scratch"}
			move %1, SCRATCH
			push SCRATCH



		
COERCIONS

	from OP
		uses REG
		gen
			move %1, %a
		yields %a
		
	from STACK
		uses REG
		gen
			pop SCRATCH
			move SCRATCH, %a
		yields %a



PATTERNS

/* Intrinsics */

	pat nop                            /* Does nothing */

	pat loc                            /* Load constant */
		yields {CONST, $1}

	pat dup $1<=QUAD                  /* Duplicate word on top of stack */
		with ANY
			yields %1 %1

	pat dup $1==(2*QUAD)              /* Duplicate word pair on top of stack */
		with ANY ANY
			yields %1 %2 %1 %2

	pat exg $1<=QUAD                  /* Exchange top two words on stack */
		with ANY ANY
			yields %1 %2

	pat exg $1==(2*QUAD)              /* Exchange top two word pairs on stack */
		with ANY ANY ANY ANY
			yields %2 %1 %4 %3

	pat stl lol $1==$2                 /* Store then load local */
		leaving
			dup QUAD
			stl $1

	pat lal sti lal loi $1==$3 && $2==$4 /* Store then load local, of a different size */
		leaving
			dup $2
			lal $1
			sti $2

	pat ste loe $1==$2                 /* Store then load external */
		leaving
			dup QUAD
			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==QUAD && $5==QUAD && $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==BYTE && $2==QUAD /* unsigned char -> signed int */
		/* nop */
	
	pat loc loc cui $1==WORD && $2==QUAD /* unsigned short -> signed int */
		/* nop */
	
	pat loc loc cii $1==BYTE && $2>BYTE /* signed char -> anything */
		with GPR
			yields {SIGNEX8, %1}
		with SIGNEX8
			yields {SIGNEX8, %1.reg}
		with SIGNEX16
			yields {SIGNEX8, %1.reg}

	pat loc loc cii $1==WORD && $2>WORD /* signed short -> anything */
		with GPR
			yields {SIGNEX16, %1}
		with SIGNEX8
			yields {SIGNEX16, %1.reg}
		with SIGNEX16
			yields {SIGNEX16, %1.reg}

	
		
/* Local variables */

	pat lal                            /* Load address of local */
		uses REG
		gen
			sub %a, FP, GP
			add %a, {CONST, $1}
		yields %a

	pat lol                            /* Load quad from local */
		uses REG
		gen
			ld %a, {GPROFFSET, FP, $1}
		yields %a

	pat ldl                            /* Load double-word from local */
		leaving
			lol $1 + QUAD*1
			lol $1 + QUAD*0

	pat stl                            /* Store to local */
		with GPR
			gen
				st %1, {GPROFFSET, FP, $1}

	pat sdl                            /* Store double-word to local */
		leaving
			stl $1 + QUAD*0
			stl $1 + QUAD*1

	pat lil                            /* Load from indirected local */
		leaving
			lol $1
			loi QUAD
			
	pat sil                            /* Save to indirected local */
		leaving
			lol $1
			sti QUAD
			
	pat stl lol $1==$2                 /* Save then load (generated by C compiler) */
		leaving
			dup QUAD
			stl $1
			
	pat zrl                            /* Zero local */
		leaving
			loc 0
			stl $1

	pat inl                            /* Increment local in register */
		leaving
			lol $1
			loc 1
			adi QUAD
			stl $1

	pat del                            /* Decrement local in register */
		leaving
			lol $1
			loc 1
			sbi QUAD
			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 QUAD

	pat ste                            /* Store word external */
		leaving
			lae $1
			sti QUAD
			
	pat zre                             /* Zero external */
		leaving
			loc 0
			ste $1
	
	pat ine                             /* Increment external */
		leaving
            loe $1
            inc
            ste $1

	pat dee                             /* Decrement external */
		leaving
            loe $1
            dec
            ste $1

	pat lde                             /* Load double external */
		leaving
			lae $1
			loi QUAD*2

	pat sde                             /* Store double external */
		leaving
			lae $1
			sti QUAD*2


/* Structures */

	pat lof                            /* Load word offsetted */
		leaving
			adp $1
			loi QUAD

	pat ldf                            /* Load double offsetted */
		with GPR
			uses reusing %1, REG=%1, REG
			gen
				add %a, GP
				ld %b, {GPROFFSET, %a, $1+4}
				ld %a, {GPROFFSET, %a, $1+0}
			yields %a %b

	pat stf                            /* Store word offsetted */
		leaving
			adp $1
			sti QUAD

	pat sdf                            /* Store double offsetted */
		with GPR GPR GPR
			uses reusing %3, REG=%3
			gen
				add %a, GP
				st %1, {GPROFFSET, %a, $1+0}
				st %2, {GPROFFSET, %a, $1+4}


			

/* Loads and stores */

	pat loi $1==BYTE                   /* Load byte indirect */
		with LABEL
			uses REG
			gen
				ldb %a, %1
			yields %a
		with GPR
			uses reusing %1, REG
			gen
				ldb %a, {GPRGPR, %1, GP}
			yields %a

	pat loi loc loc cii $1==WORD && $2==WORD && $3==QUAD /* Load short indirect and sign extend */
		with LABEL
			uses REG
			gen
				ldhs %a, %1
			yields %a
		with GPR
			uses reusing %1, REG
			gen
				add %a, %1, GP
				ldhs %a, {GPROFFSET, %a, 0}
			yields %a
		
	pat loi $1==WORD                   /* Load short indirect */
		with LABEL
			uses REG
			gen
				ldh %a, %1
			yields %a
		with GPR
			uses reusing %1, REG
			gen
				add %a, %1, GP
				ldh %a, {GPROFFSET, %a, 0}
			yields %a

	pat loi $1==QUAD                   /* Load quad indirect */
		with LABEL
			uses REG
			gen
				ld %a, %1
			yields %a
		with GPR
			uses reusing %1, REG
			gen
				add %a, %1, GP
				ld %a, {GPROFFSET, %a, 0}
			yields %a

	pat loi $1==2*QUAD                 /* Load double-quad indirect */
		with LABEL
			uses REG, REG
			gen
				lea %b, %1
				ld %a, {GPROFFSET, %b, 0}
				ld %b, {GPROFFSET, %b, 4}
			yields %b %a
		with GPR
			uses reusing %1, REG, REG
			gen
				add %b, %1, GP
				ld %a, {GPROFFSET, %b, 0}
				ld %b, {GPROFFSET, %b, 4}
			yields %b %a

	pat loi $1==3*QUAD                 /* Load triple-quad indirect */
		with LABEL
			uses REG, REG, REG
			gen
				lea %b, %1
				ld %a, {GPROFFSET, %b, 0}
				ld %b, {GPROFFSET, %b, 4}
				ld %b, {GPROFFSET, %b, 8}
			yields %c %b %a
		with GPR
			uses reusing %1, REG, REG, REG
			gen
				add %b, %1, GP
				ld %a, {GPROFFSET, %b, 0}
				ld %b, {GPROFFSET, %b, 4}
				ld %c, {GPROFFSET, %b, 8}
			yields %c %b %a

	pat loi                            /* Load arbitrary size */
		leaving
			loc $1
			los QUAD
					
	pat los                            /* Load arbitrary size */
		leaving
			cal ".los"
				
	pat sti $1==BYTE                   /* Store byte indirect */
		with LABEL GPR
			gen
				stb %2, %1
		with LABEL SIGNEX8+SIGNEX16
			gen
				stb %2.reg, %1
		with GPR GPR
			gen
				stb %2, {GPRGPR, %1, GP}
		with GPR SIGNEX8+SIGNEX16
			gen
				stb %2.reg, {GPRGPR, %1, GP}

	pat sti $1==WORD                  /* Store half-word indirect */
		with LABEL GPR
			gen
				sth %2, %1
		with LABEL SIGNEX16
			gen
				sth %2.reg, %1
		with GPR GPR
			uses reusing %1, REG
			gen
				add %a, %1, GP
				sth %2, {GPROFFSET, %a, 0}
		with GPR SIGNEX16
			uses reusing %1, REG
			gen
				add %a, %1, GP
				sth %2.reg, {GPROFFSET, %a, 0}

	pat sti $1==QUAD                  /* Store quad indirect */
		with LABEL GPR
			gen
				st %2, %1
		with GPR GPR
			uses reusing %1, REG
			gen
				add %a, %1, GP
				st %2, {GPROFFSET, %a, 0}

	pat sti $1==2*QUAD                 /* Load double-quad indirect */
		with LABEL GPR GPR
			uses REG
			gen
				lea %a, %1
				st %2, {GPROFFSET, %a, 0}
				st %3, {GPROFFSET, %a, 4}
		with GPR GPR GPR
			uses reusing %1, REG=%1
			gen
				add %a, GP
				st %2, {GPROFFSET, %a, 0}
				st %3, {GPROFFSET, %a, 4}

	pat sti $1==3*QUAD                 /* Load triple-quad indirect */
		with LABEL GPR GPR GPR
			uses REG
			gen
				lea %a, %1
				st %2, {GPROFFSET, %a, 0}
				st %3, {GPROFFSET, %a, 4}
				st %4, {GPROFFSET, %a, 8}
		with GPR GPR GPR GPR
			uses reusing %1, REG=%1
			gen
				add %a, GP
				st %2, {GPROFFSET, %a, 0}
				st %3, {GPROFFSET, %a, 4}
				st %4, {GPROFFSET, %a, 8}

	pat sti                            /* Store arbitrary size */
		leaving
			loc $1
			sts QUAD
					
	pat sts                            /* Store arbitrary size */
		leaving
			cal ".sts"



/* Arithmetic wrappers */

	pat ads                            /* Add var to pointer */
		leaving adi $1
	
	pat sbs                            /* Subtract var from pointer */
		leaving sbi $1
		
	pat adp                            /* Add constant to pointer */
		leaving
			loc $1
			adi QUAD

	pat adu                            /* Add unsigned */
		leaving
			adi $1
			
	pat sbu                            /* Subtract unsigned */
		leaving
			sbi $1
			
	pat inc                            /* Add 1 */
		leaving
			loc 1
			adi QUAD
			
	pat dec                            /* Subtract 1 */
		leaving
			loc 1
			sbi QUAD
	
	pat loc mlu                        /* Unsigned multiply by constant */
		leaving
			loc $1
			mli QUAD
			
	pat mlu                            /* Unsigned multiply by var */
		leaving
			mli QUAD
			
	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 loc adi $1==0                 /* Add nothing */
		/* nop */

	pat adi $1==QUAD                  /* Add word (second + top) */
		with GPR+CONST GPR
			uses reusing %2, REG=%2
			gen
				add %a, %1
			yields %a
		with GPR GPR+CONST
			uses reusing %1, REG=%1
			gen
				add %a, %2
			yields %a

	pat loc sbi $1==0                 /* Subtract nothing */
		/* nop */

	pat sbi $1==QUAD                  /* Subtract word (second - top) */
		with GPR+CONST GPR
			uses reusing %2, REG=%2
			gen
				sub %a, %1
			yields %a

	pat mli $1==QUAD                  /* Multiply word (second * top) */
		with GPR+CONST GPR
			uses reusing %2, REG=%2
			gen
				mul %a, %1
			yields %a
		with GPR GPR+CONST
			uses reusing %1, REG=%1
			gen
				mul %a, %2
			yields %a

	pat mlu
		leaving
			mli $1

	pat dvi $1==QUAD                  /* Divide word (second / top) */
		with GPR GPR
			uses reusing %2, REG
			gen
				divs %a, %2, %1
			yields %a

	pat dvu $1==QUAD                  /* Divide unsigned word (second / top) */
		with GPR GPR
			uses reusing %2, REG
			gen
				divu %a, %2, %1
			yields %a

	pat rmu $1==QUAD                  /* Remainder unsigned word (second % top) */
		with GPR GPR
			uses REG
			gen
				divu %a, %2, %1
                mul %a, %1
                rsb %a, %2
			yields %a

	pat rmi $1==QUAD                  /* Remainder signed word (second % top) */
		with GPR GPR
			uses REG
			gen
				divs %a, %2, %1
                mul %a, %1
                rsb %a, %2
			yields %a

	pat ngi $1==QUAD                  /* Negate word */
		with GPR
			uses reusing %1, REG=%1
			gen
				neg %a, %a
			yields %a

	pat and $1==QUAD                  /* AND word */
		with GPR+CONST GPR
			uses reusing %2, REG=%2
			gen
				and %a, %1
			yields %a
		with GPR GPR+CONST
			uses reusing %1, REG=%1
			gen
				and %a, %2
			yields %a

	pat ior $1==QUAD                  /* OR word */
		with GPR+CONST GPR
			uses reusing %2, REG=%2
			gen
				or %a, %1
			yields %a
		with GPR GPR+CONST
			uses reusing %1, REG=%1
			gen
				or %a, %2
			yields %a

	pat xor $1==QUAD                  /* XOR word */
		with GPR+CONST GPR
			uses reusing %2, REG=%2
			gen
				eor %a, %1
			yields %a
		with GPR GPR+CONST
			uses reusing %1, REG=%1
			gen
				eor %a, %2
			yields %a

	pat com $1==QUAD                  /* Complement */
		with GPR
			uses reusing %1, REG=%1
			gen
                mvn %a, %1
            yields %a

	pat dvi $1==QUAD                  /* Divide word (second / top) */
		with GPR GPR
			uses reusing %2, REG
			gen
				divs %a, %2, %1
			yields %a

	pat dvu $1==QUAD                  /* Divide unsigned word (second / top) */
		with GPR GPR
			uses reusing %2, REG
			gen
				divu %a, %2, %1
			yields %a

	pat rmu $1==QUAD                  /* Remainder unsigned word (second % top) */
		with GPR GPR
			uses REG
			gen
				divu %a, %2, %1
                mul %a, %1
                sub %a, %2
			yields %a

	pat rmi $1==QUAD                  /* Remainder signed word (second % top) */
		with GPR GPR
			uses REG
			gen
				divs %a, %2, %1
                mul %a, %1
                sub %a, %2
			yields %a

#if 0
	pat mli $1==4                      /* Multiply word (second * top) */
		with REG REG
			uses reusing %2, REG
			gen
				mullw %a, %2, %1
			yields %a
		

	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==QUAD                  /* 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"}
#endif
				
	pat sli $1==4                      /* Shift left (second << top) */
		with CONST+GPR GPR
			uses reusing %2, REG=%2
			gen
            	lsl %a, %1
			yields %a

	pat sri $1==4                      /* Shift right signed (second >> top) */
		with CONST+GPR GPR
			uses reusing %2, REG=%2
			gen
				asr %2, %1
			yields %a

	pat sru $1==4                      /* Shift right unsigned (second >> top) */
		with CONST+GPR GPR
			uses reusing %2, REG=%2
			gen
				lsr %2, %1
			yields %a



/* Special arithmetic */

	pat loc sli adi $1==1 && $2==QUAD && $3==QUAD /* Shift and add (second + top<<1) */
		with GPR+CONST GPR
			uses reusing %2, REG=%2
			gen
				adds2 %a, %1
			yields %a

	pat loc sli adi $1==2 && $2==QUAD && $3==QUAD /* Shift and add (second + top<<2) */
		with GPR+CONST GPR
			uses reusing %2, REG=%2
			gen
				adds4 %a, %1
			yields %a

	pat loc sli adi $1==3 && $2==QUAD && $3==QUAD /* Shift and add (second + top<<3) */
		with GPR+CONST GPR
			uses reusing %2, REG=%2
			gen
				adds8 %a, %1
			yields %a

	pat loc sli adi $1==4 && $2==QUAD && $3==QUAD /* Shift and add (second + top<<4) */
		with GPR+CONST GPR
			uses reusing %2, REG=%2
			gen
				adds16 %a, %1
			yields %a

	pat loc sli adi $1==8 && $2==QUAD && $3==QUAD /* Shift and add (second + top<<8) */
		with GPR GPR
			uses reusing %2, REG
			gen
				adds256 %a, %2, %1
			yields %a

	pat loc sli ads
		leaving
			loc $1
			sli $2
			adi $3



/* Arrays */

	pat aar $1==QUAD                  /* Index array */
		with STACK
			uses GPR0
			gen
				bl {LABEL, ".aar4stack"}
			yields R0
		with GPR0 GPR1 GPR2
			uses GPR0
			gen
				bl {LABEL, ".aar4"}
			yields R0

	pat lae lar $2==QUAD && nicesize(rom($1, 3)) /* Load array */
		leaving
			lae $1
			aar QUAD
			loi rom($1, 3)

	pat lar $1==QUAD                  /* Load array */
		with STACK
			uses GPR0
			gen
				bl {LABEL, ".lar4stack"}
			yields R0
		with GPR0 GPR1 GPR2
			uses GPR0
			gen
				bl {LABEL, ".lar4"}
			yields R0

	pat lae sar $2==QUAD && nicesize(rom($1, 3)) /* Store array */
		leaving
			lae $1
			aar QUAD
			sti rom($1, 3)

	pat sar $1==QUAD                  /* Store array */
		with STACK
			uses GPR0
			gen
				bl {LABEL, ".sar4stack"}
			yields R0
		with GPR0 GPR1 GPR2
			uses GPR0
			gen
				bl {LABEL, ".sar4"}


			
/* Sets */

	pat set $1==QUAD                   /* Create quad with one bit set */
		with GPR
			uses reusing %1, REG
			gen
				bset %a, %1
			yields %a

	pat set defined($1)                /* Any other set */
		leaving
			loc $1
			cal ".set"

	pat set !defined($1)               /* Create structure with set bit (variable) */
		leaving
			cal ".set"

	pat inn defined($1)                /* Test for set bit */
		leaving
			set $1
			and $1

	pat inn !defined($1)               /* Test for set bit (variable) */
		leaving
			cal ".inn"

	pat ior !nicesize($1)              /* OR set */
		leaving
			cal ".ior"

	pat ior !defined($1)               /* OR set */
		leaving
			cal ".ior"

	pat and !nicesize($1)              /* AND set */
		leaving
			loc $1
			cal ".and"

	pat and !defined($1)               /* AND set */
		leaving
			cal ".and"
				
			
			
/* Boolean resolutions */

	proc cm_t example teq
		with GPR GPR
			uses reusing %1, REG
			gen
				cmp %1, %2
				mov %a, {CONST, 0}
				add[1] %a, {CONST, 1}
			yields %a

	pat cmu teq call cm_t("add.eq")        /* top = (second == top) */
	pat cmu tne call cm_t("add.ne")        /* top = (second != top) */
	pat cmu tlt call cm_t("add.lo")        /* top = unsigned (second < top) */
	pat cmu tle call cm_t("add.ls")        /* top = unsigned (second <= top) */
	pat cmu tgt call cm_t("add.hi")        /* top = unsigned (second < top) */
	pat cmu tge call cm_t("add.hs")        /* top = unsigned (second >= top) */
	pat cmi teq call cm_t("add.eq")        /* top = (second == top) */
	pat cmi tne call cm_t("add.ne")        /* top = (second != top) */
	pat cmi tlt call cm_t("add.lt")        /* top = signed (second < top) */
	pat cmi tle call cm_t("add.le")        /* top = signed (second <= top) */
	pat cmi tgt call cm_t("add.gt")        /* top = signed (second < top) */
	pat cmi tge call cm_t("add.ge")        /* top = signed (second >= top) */

	proc cmf_t example teq
		with GPR GPR
			uses reusing %1, REG
			gen
				fcmp %a, %1, %2
				mov %a, {CONST, 0}
				add[1] %a, {CONST, 1}
			yields %a

	pat cmf teq call cmf_t("add.eq")        /* top = float (second == top) */
	pat cmf tne call cmf_t("add.ne")        /* top = float (second != top) */
	pat cmf tlt call cmf_t("add.lo")        /* top = float (second < top) */
	pat cmf tle call cmf_t("add.ls")        /* top = float (second <= top) */
	pat cmf tgt call cmf_t("add.hi")        /* top = float (second > top) */
	pat cmf tge call cmf_t("add.hs")        /* top = float (second >= top) */

	proc fallback_t example teq
		with GPR
			uses reusing %1, REG
			gen
				cmp %1, {CONST, 0}
				mov %a, {CONST, 0}
				add[1] %a, {CONST, 1}
			yields %a

	pat teq call fallback_t("add.eq")       /* top = float (top == 0) */
	pat tne call fallback_t("add.ne")       /* top = float (top != 0) */
	pat tlt call fallback_t("add.lo")       /* top = float (top < 0) */
	pat tle call fallback_t("add.ls")       /* top = float (top <= 0) */
	pat tgt call fallback_t("add.hi")       /* top = float (top > 0) */
	pat tge call fallback_t("add.hs")       /* top = float (top >= 0) */



/* Simple branches */

	proc anyz example zeq
		with GPR STACK
			kills ALL
			gen
				cmp %1, {CONST, 0}
				beq[1] {LABEL, $1}

	pat zeq call anyz("b.eq")          /* Branch if signed top == 0 */
	pat zne call anyz("b.ne")          /* Branch if signed top != 0 */
	pat zgt call anyz("b.gt")          /* Branch if signed top > 0 */
	pat zlt call anyz("b.lt")          /* Branch if signed top < 0 */
	pat zge call anyz("b.ge")          /* Branch if signed top >= 0 */
	pat zle call anyz("b.le")          /* Branch if signed top <= 0 */

	proc anyb example beq
		with GPR+CONST GPR STACK
			kills ALL
			gen
				cmp %2, %1
				beq[1] {LABEL, $1}

	pat beq call anyb("b.eq")          /* Branch if signed second == top */
	pat bne call anyb("b.ne")          /* Branch if signed second != top */
	pat bgt call anyb("b.gt")          /* Branch if signed second > top */
	pat bge call anyb("b.ge")          /* Branch if signed second >= top */
	pat blt call anyb("b.lt")          /* Branch if signed second < top */
	pat ble call anyb("b.le")          /* Branch if signed second <= top */

	proc cmu_z example cmu zeq
		with GPR+CONST GPR STACK
			kills ALL
			gen
				cmp %2, %1
				beq[1] {LABEL, $2}

	pat cmu zeq call cmu_z("b.eq")   /* Branch if unsigned second == top */
	pat cmu zne call cmu_z("b.ne")   /* Branch if unsigned second != top */
	pat cmu zgt call cmu_z("b.hi")   /* Branch if unsigned second > top */
	pat cmu zlt call cmu_z("b.lo")   /* Branch if unsigned second < top */
	pat cmu zge call cmu_z("b.hs")   /* Branch if unsigned second >= top */
	pat cmu zle call cmu_z("b.ls")   /* Branch if unsigned second <= top */
	pat cmi zeq call cmu_z("b.eq")   /* Branch if signed second == top */
	pat cmi zne call cmu_z("b.ne")   /* Branch if signed second != top */
	pat cmi zgt call cmu_z("b.gt")   /* Branch if signed second > top */
	pat cmi zlt call cmu_z("b.lt")   /* Branch if signed second < top */
	pat cmi zge call cmu_z("b.ge")   /* Branch if signed second >= top */
	pat cmi zle call cmu_z("b.le")   /* Branch if signed second <= top */

	proc cmf_z example cmu zeq
		with GPR GPR STACK
			kills ALL
			gen
				fcmp %2, %2, %1
				beq[1] {LABEL, $2}

	pat cmf zeq call cmf_z("b.eq")   /* Branch if float second == top */
	pat cmf zne call cmf_z("b.ne")   /* Branch if float second != top */
	pat cmf zgt call cmf_z("b.gt")   /* Branch if float second > top */
	pat cmf zlt call cmf_z("b.lt")   /* Branch if float second < top */
	pat cmf zge call cmf_z("b.ge")   /* Branch if float second >= top */
	pat cmf zle call cmf_z("b.le")   /* Branch if float second <= top */

	pat cmp                            /* Compare pointers */
		leaving
			cmu QUAD
			
	pat cms $1==QUAD                  /* Compare blocks (word sized) */
		leaving
			cmi QUAD



			

/* Other branching and labelling */

#if 0
	pat lab topeltsize($1)<=4 && !fallthrough($1)
		gen
			labeldef $1
			yields R0
			
	pat lab topeltsize($1)<=4 && fallthrough($1)
		with GPR0
			gen
				labeldef $1
			yields %1
			
	pat lab topeltsize($1)>4
		with STACK
			kills ALL
			gen
				labeldef $1

        pat bra topeltsize($1)<=4          /* Unconditional jump with TOS register */
                with GPR0 STACK
                gen
                        b {LABEL, $1}

	pat bra topeltsize($1)>4           /* Unconditional jump without TOS register */
		with STACK
			gen
				b {LABEL, $1}
#endif

	pat lab
		with STACK
			kills ALL
			gen
				labeldef $1

	pat bra
		with STACK
			kills ALL
			gen
				b {LABEL, $1}
			

				
						
/* Miscellaneous */

	pat cal                            /* Call procedure */
		with STACK
			kills ALL
			gen
				bl {LABEL, $1}

	pat cai                            /* Call procedure indirect */
		with GPR STACK
			kills ALL
			gen
				bl %1

	pat lfr $1==QUAD                  /* Load function result, word */
		yields R0
		
	pat lfr $1==QUAD*2                /* Load function result, word */
		yields R1 R0
		
	pat ret $1==0                      /* Return from procedure */
		gen
			mov SP, FP
			pop FP, PC

	pat ret $1==QUAD                  /* Return from procedure, word */
		with GPR0
			gen
				mov SP, FP
				pop FP, PC

	pat ret $1==QUAD*2                /* Return from procedure, word */
		with GPR GPR
			gen
				move %1, R0
				move %2, R1
				mov SP, FP
				pop FP, PC

	pat blm                            /* Block move constant length */
		leaving
			loc $1
			bls

	pat bls                            /* Block move variable length */
		with STACK
			kills ALL
			gen
				bl {LABEL, "_memmove"}

	pat csa                            /* Array-lookup switch */
		with GPR0 GPR1 STACK
			kills ALL
			gen
				b {LABEL, ".csa"}

	pat csb                            /* Table-lookup switch */
		with GPR0 GPR1 STACK
			kills ALL
			gen
				bl {LABEL, ".csb"}

				

/* EM specials */

	pat fil                            /* Set current filename */
		leaving
			lae $1
			ste ".filename"
			
	pat lin                            /* Set current line number */
		leaving
			loc $1
			ste ".linenumber"

	pat lni                            /* Increment line number */
		leaving
			ine ".linenumber"			
			
	pat lim                            /* Load EM trap ignore mask */
		leaving
			lde ".ignmask"
			
	pat sim                            /* Store EM trap ignore mask */
		leaving
			ste ".ignmask"
			
	pat trp                            /* Raise EM trap */
		leaving
			cal ".trap"
				
	pat sig                            /* Set trap handler */
		leaving
			ste ".trppc"
			
	pat rtt                            /* Return from trap */
		leaving
			ret 0
			
	pat lxl $1==0                      /* Load FP */
		leaving
			lor 0
		
	pat lxl $1==1                      /* Load caller's FP */
		leaving
			lxl 0
			dch
			
	pat dch                            /* FP -> caller FP */
		with GPR
			uses reusing %1, REG
			gen
				ld %a, {GPROFFSET, %1, FP_OFFSET}
				sub %a, GP
			yields %a

	pat lpb                            /* Convert FP to argument address */
		leaving
			adp EM_BSIZE
			
	pat lxa                            /* Load caller's SP */
		leaving
			lxl $1
			lpb
			
	pat gto                            /* longjmp */
		uses REG, REG
		gen
			move {LABEL, $1}, %a
			ld %b, {GPROFFSET, %a, 8}
			add FP, %b, GP
			ld %b, {GPROFFSET, %a, 4}
			add SP, %b, GP
			ld %b, {GPROFFSET, %a, 0}
			add %b, GP
			b %b

#if 0
			
	pat gto                            /* longjmp */
		with STACK
			gen
				ld {LABEL, $1+2}
				wspec {CONST, 1}
				ld {LABEL, $1+4}
				wspec {CONST, 0}
				ld {LABEL, $1+0}
				wspec {CONST, 2}
			
	pat str $1==1                      /* Store special GPRister */
		with GPR0
			gen
				wspec {CONST, $1}
				
#endif

	pat lor $1==0                      /* Load FP */
		uses REG
		gen
			move FP, %a
		yields %a
		
	pat lor $1==1                      /* Load SP */
		uses REG
		gen
			move SP, %a
		yields %a
		
	pat lor $1==2                      /* Load HP */
		leaving
			loe ".reghp"
			
	pat str $1==0                      /* Store FP */
		with GPR
			gen
				sub FP, %1, GP

	pat str $1==1                      /* Store SP */
		with GPR
			gen
				sub SP, %1, GP

	pat str $1==2                      /* Store HP */
		leaving
			ste ".reghp"

	pat ass                            /* Adjust stack by variable amount */
		with CONST+GPR STACK
		gen
			add SP, %1

	pat asp $1==QUAD                   /* Adjust stack by constant amount */
		with GPR
			/* silently ignore GPR */
		with STACK
			gen
				pop SCRATCH

	pat asp $1==(2*QUAD)               /* Adjust stack by constant amount */
		with GPR GPR
			/* silently ignore GPR */
		with STACK
			gen
				add SP, {CONST, 2*QUAD}

	pat asp                            /* Adjust stack by constant amount */
		leaving
			loc $1
			ass



/* Floating point */

	pat ngf                            /* Negate float */
		leaving
			loc 0
			exg QUAD
			sbf QUAD

	proc simple_f example adf
		with GPR GPR
			uses reusing %1, REG
			gen
				fadd[1] %a, %2, %1
			yields %a

	pat adf call simple_f("fadd")      /* Float subtract (second + top) */
	pat sbf call simple_f("fsub")      /* Float subtract (second - top) */
	pat mlf call simple_f("fmul")      /* Float multiply (second * top) */
	pat dvf call simple_f("fdiv")      /* Float divide (second / top) */

	pat loc loc cff $1==$2 && $1==QUAD /* Convert float to float */
		leaving
			nop
                                        
	pat loc loc cfi $1==$2 && $1==QUAD /* Convert float -> integer */
		with GPR
			uses reusing %1, REG
			gen
				ftrunc %a, %1
			yields %a

	pat loc loc cfu $1==$2 && $1==QUAD /* Convert float -> unsigned */
		with GPR
			uses reusing %1, REG
			gen
				ftrunc %a, %1
			yields %a

	pat loc loc cif $1==$2 && $1==QUAD /* Convert integer -> float */
		with GPR
			uses reusing %1, REG
			gen
				flts %a, %1
			yields %a

	pat loc loc cuf $1==$2 && $1==QUAD /* Convert unsigned -> float */
		with GPR
			uses reusing %1, REG
			gen
				fltu %a, %1
			yields %a

	pat fef                            /* Split float */
		leaving
			loc 0
			loc 0
#if 0
			cal ".cuf"
			lfr QUAD*2
#endif

	pat fif                            /* Multiply float and split (?) */
		leaving
			mlf QUAD
			fef

	pat zrf                            /* Load a floating zero */
		leaving
			loc 0
