/* PDP-11 desciptor table for ACK target optimizer */

/* Tunable constants: */

MAXOP		2;
LABEL_STARTER	'I';
OPC_TERMINATOR	' ';

%%;

/* useful addressing modes: */

ZERO		{strcmp(VAL,"$00") == 0			};
ONE		{strcmp(VAL,"$01") == 0			};
CONST		{VAL[0] == '$'				};	/* constant */
A,B		{no_side_effects(VAL)			};
X,Y		{TRUE					};
REG		{is_register(VAL)			};	/* register */
SREG		{is_scratchreg(VAL)			};	/* scratch reg */
M_ONE		{strcmp(VAL,"$-1") == 0			};	/* -1	*/
LAB,L1,L2	{VAL[0] == 'I'				};	/* label */

%%;

/* optimization patterns: */

add ZERO,A  {carry_dead(REST)}	->	;
add ONE, X  {carry_dead(REST)}	->	inc X;
sub ONE, X  {carry_dead(REST)}	->	dec X;

mov A,A				->	tst A;

/* tst-elimination */
tst (sp)+ : tst X		->	mov X,(sp)+;
tst (sp)+ : mov X,-(sp) 	->	mov X,(sp);
mov A,X : tst A			->	mov A,X;
mov X,A : tst A			->	mov X,A;

/* register subsumption */
mov REG,A : ANY A,X 		->	mov REG,A : ANY REG,X;
mov REG,A : ANY *A 		->	mov REG,A : ANY *REG;
mov REG,A : ANY *A,X 		->	mov REG,A : ANY *REG,X;
mov REG,A : ANY X,*A 		->	mov REG,A : ANY X,*REG;

/* compare with -1 */
cmp SREG,M_ONE : jeq LAB	->	inc SREG : jeq LAB;
cmp SREG,M_ONE : jne LAB	->	inc SREG : jne LAB;

/* skip over jump */
jeq L1 : jbr L2: labdef L1	->	jne L2 : labdef L1;
jge L1 : jbr L2: labdef L1	->	jlt L2 : labdef L1;
jgt L1 : jbr L2: labdef L1	->	jle L2 : labdef L1;
jlt L1 : jbr L2: labdef L1	->	jge L2 : labdef L1;
jle L1 : jbr L2: labdef L1	->	jgt L2 : labdef L1;
jne L1 : jbr L2: labdef L1	->	jeq L2 : labdef L1;

/* byte-test */
clr SREG : bisb X,SREG : tst SREG : jeq LAB
				->	tstb X : jeq LAB;
clr SREG : bisb X,SREG : tst SREG : jne LAB
				->	tstb X : jne LAB;

%%;

/* auxiliary routines: */

int no_side_effects(s)
	register char *s;
{

	for(;;) {
		switch(*s++) {
			case '\0': return TRUE;
			case '-':  if (*s == '(') return FALSE; break;
			case ')':  if (*s == '+') return FALSE; break;
		}
	}
	/* NOTREACHED */
}

int is_register(s)
	register char *s;
{
	return *s++ == 'r' && *s >= '0' && *s <= '5';
}

int is_scratchreg(s)
	register char *s;
{
	return *s++ == 'r' && (*s == '0' || *s == '1' || *s == '3');
}

int carry_dead(s)
	char *s;
{
	switch(*s++) {
		case 'a':	/* adc and adcb */
			return *s++ != 'd' || *s != 'c';
		case 'b':	/* bcc, bcs, bhi, bhis, blo, blos */
			return *s != 'c' && *s != 'h' &&
				(*s++ != 'l' || *s != 'o');
		case 's':	/* sbc and sbcb */
			return *s++ != 'b' || *s != 'c';
		default:
			return TRUE;
	}
}