/* Copyright (c) 1988 by Sozobon, Limited.  Author: Tony Andrews
 *
 * Permission is granted to anyone to use this software for any purpose
 * on any computer system, and to redistribute it freely, with the
 * following restrictions:
 * 1) No charge may be made other than reasonable charges for reproduction.
 * 2) Modified versions must be clearly marked as such.
 * 3) The authors are not responsible for any harmful consequences
 *    of using this software, even if they result from defects in it.
 */

/*
 * Routines for data flow analysis of a single instruction
 */

#include "top.h"

/*
 * idata
 *
 * For each instruction, we have some global information, as well
 * as flags indicating what the instruction does with its operands.
 * We need to know if each operand is set and/or referenced. If the
 * instruction has side-effects not directly related to its operands,
 * we need to know that as well, so "special case" code can deal with
 * that as well.
 */
struct	idata	{

	char	iflag;		/* flags regarding the entire instruction */
#define		SIDE	0x01	/* inst. has side-effects */
#define		CC	0x02	/* inst. munges condition codes */

	char	op1f;		/* flags for the first and second operands */
	char	op2f;
#define		SET	0x01	/* operand is set */
#define		REF	0x02	/* operand is referenced */

} idata[] =
{
	{ CC,		REF,		REF|SET },	/* OR */
	{ CC,		REF,		REF },		/* BTST */
	{ 0,		REF,		SET },		/* MOVEP */
	{ CC,		REF,		REF|SET },	/* BCHG */
	{ CC,		REF,		REF|SET },	/* BCLR */
	{ CC,		REF,		REF|SET },	/* BSET */
	{ CC,		REF,		REF|SET },	/* AND */
	{ CC,		REF,		REF|SET },	/* SUB */
	{ CC,		REF,		REF|SET },	/* ADD */
	{ CC,		REF,		REF|SET },	/* EOR */
	{ CC,		REF,		REF },		/* CMP */
	{ CC,		REF,		SET },		/* MOVE */
	{ CC,		REF|SET,	0 },		/* NEGX */
	{ CC,		REF,		REF },		/* CHK */
	{ 0,		REF,		SET },		/* LEA */
	{ CC,		SET,		0 },		/* CLR */
	{ CC,		REF|SET,	0 },		/* NEG */
	{ CC,		REF|SET,	0 },		/* NOT */
	{ CC,		REF|SET,	0 },		/* NBCD */
	{ CC,		REF|SET,	0 },		/* SWAP */
	{ SIDE,		REF,		0 },		/* PEA */
	{ CC,		REF|SET,	0 },		/* EXT */
	{ SIDE,		REF,		SET },		/* MOVEM */
	{ CC,		REF,		0 },		/* TST */
	{ CC,		REF|SET,	0 },		/* TAS */
	{ 0,		REF,		0 },		/* TRAP */
	{ SIDE,		REF|SET,	REF },		/* LINK */
	{ SIDE,		REF|SET,	0 },		/* UNLK */
	{ 0,		0,		0 },		/* RESET */
	{ 0,		0,		0 },		/* NOP */
	{ CC,		REF,		0 },		/* STOP */
	{ SIDE|CC,	0,		0 },		/* RTE */
	{ SIDE,		0,		0 },		/* RTS */
	{ 0,		0,		0 },		/* TRAPV */
	{ SIDE|CC,	0,		0 },		/* RTR */
	{ SIDE,		REF,		0 },		/* JSR */
	{ 0,		REF,		0 },		/* JMP */
	{ CC,		REF,		REF|SET },	/* ADDQ */
	{ 0,		SET,		0 },		/* ST */
	{ CC,		REF|SET,	REF },		/* DBT */
	{ CC,		REF,		REF|SET },	/* SUBQ */
	{ 0,		SET,		0 },		/* SF */
	{ CC,		REF|SET,	REF },		/* DBRA (dbf) */
	{ 0,		SET,		0 },		/* SHI */
	{ CC,		REF|SET,	REF },		/* DBHI */
	{ 0,		SET,		0 },		/* SLS */
	{ CC,		REF|SET,	REF },		/* DBLS */
	{ 0,		SET,		0 },		/* SCC */
	{ CC,		REF|SET,	REF },		/* DBCC */
	{ 0,		SET,		0 },		/* SCS */
	{ CC,		REF|SET,	REF },		/* DBCS */
	{ 0,		SET,		0 },		/* SNE */
	{ CC,		REF|SET,	REF },		/* DBNE */
	{ 0,		SET,		0 },		/* SEQ */
	{ CC,		REF|SET,	REF },		/* DBEQ */
	{ 0,		SET,		0 },		/* SVC */
	{ CC,		REF|SET,	REF },		/* DBVC */
	{ 0,		SET,		0 },		/* SVS */
	{ CC,		REF|SET,	REF },		/* DBVS */
	{ 0,		SET,		0 },		/* SPL */
	{ 0,		SET,		0 },		/* SMI */
	{ CC,		REF|SET,	REF },		/* DBMI */
	{ 0,		SET,		0 },		/* SGE */
	{ CC,		REF|SET,	REF },		/* DBGE */
	{ 0,		SET,		0 },		/* SLT */
	{ CC,		REF|SET,	REF },		/* DBLT */
	{ 0,		SET,		0 },		/* SGT */
	{ CC,		REF|SET,	REF },		/* DBGT */
	{ 0,		SET,		0 },		/* SLE */
	{ CC,		REF|SET,	REF },		/* DBLE */
	{ 0,		REF,		0 },		/* BRA */
	{ SIDE,		REF,		0 },		/* BSR */
	{ 0,		REF,		0 },		/* BHI */
	{ 0,		REF,		0 },		/* BLS */
	{ 0,		REF,		0 },		/* BCC */
	{ 0,		REF,		0 },		/* BCS */
	{ 0,		REF,		0 },		/* BNE */
	{ 0,		REF,		0 },		/* BEQ */
	{ 0,		REF,		0 },		/* BVC */
	{ 0,		REF,		0 },		/* BVS */
	{ 0,		REF,		0 },		/* BPL */
	{ 0,		REF,		0 },		/* BMI */
	{ 0,		REF,		0 },		/* BGE */
	{ 0,		REF,		0 },		/* BLT */
	{ 0,		REF,		0 },		/* BGT */
	{ 0,		REF,		0 },		/* BLE */
	{ CC,		REF,		SET },		/* MOVEQ */
	{ CC,		REF,		REF|SET },	/* DIVU */
	{ CC,		REF,		REF|SET },	/* SBCD */
	{ CC,		REF,		REF|SET },	/* DIVS */
	{ CC,		REF,		REF|SET },	/* SUBX */
	{ CC,		REF,		REF },		/* CMPM */
	{ CC,		REF,		REF|SET },	/* MULU */
	{ CC,		REF,		REF|SET },	/* ABCD */
	{ 0,		REF|SET,	REF|SET },	/* EXG */
	{ CC,		REF,		REF|SET },	/* MULS */
	{ CC,		REF,		REF|SET },	/* ADDX */
	{ CC,		REF,		REF|SET },	/* ASR */
	{ CC,		REF,		REF|SET },	/* LSR */
	{ CC,		REF,		REF|SET },	/* ROXR */
	{ CC,		REF,		REF|SET },	/* ROR */
	{ CC,		REF,		REF|SET },	/* ASL */
	{ CC,		REF,		REF|SET },	/* LSL */
	{ CC,		REF,		REF|SET },	/* ROXL */
	{ CC,		REF,		REF|SET },	/* ROL */
	{ 0,		0,		0 },		/* DC */
	{ 0,		0,		0 },		/* XREF */
	{ 0,		0,		0 },		/* XDEF */
};

/*
 * chkset(op) - check to see if operand 'op' sets a register
 *
 * This given operand is set by an instruction. Depending on the
 * addressing mode used, this may set a register. If so, return
 * an appropriate mask. This only happens with register direct
 * addressing.
 */
int
chkset(op)
struct	opnd	*op;
{
	switch (M(op->amode)) {

	case REG:
		return RM(op->areg);
	case REGI:
		if (op->amode & (INC|DEC))
			return RM(op->areg);
		else
			return 0;

	default:
		return 0;
	}
}

/*
 * chkref(op) - check to see if operand 'op' references a register
 *
 * Checks for register references in source or destination
 * operands, since they can occur in either.
 */
int
chkref(op, is_src)
struct	opnd	*op;
bool	is_src;		/* is the operand a source? */
{
	switch (M(op->amode)) {

	case NONE:
	case IMM:
	case ABS:
	case PCD:
		return 0;

	case REG:
		if (is_src)
			return RM(op->areg);
		else
			return 0;

	case REGI:
	case REGID:
		return RM(op->areg);

	case REGIDX:
		return (RM(op->areg) | RM(op->ireg));

	case PCDX:
		return RM(op->ireg);

	default:
		fprintf(stderr, "illegal mode in chkref() %d\n", M(op->amode));
		exit(EXIT_FAILURE);
	}
}

/*
 * chkside(ip, type) - check for side-effects of 'ip'
 *
 * Return a mask of registers set or referenced (depending on 'type')
 * by the given instruction. For example, "pea" sets and references
 * the stack pointer.
 */
int
chkside(ip, type)
INST	*ip;
int	type;
{
	switch (ip->opcode) {
	case PEA:		/* refs/sets the stack pointer */
		return RM(SP);

	case LINK:		/* refs/sets SP */
		return RM(SP);

	case UNLK:
		if (type == SET)
			return RM(SP);
		else
			return 0;

	case RTE:
	case RTS:
	case RTR:
		return RM(SP);

	case JSR:
	case BSR:
		/*
		 * We have to account, here, for what the called
		 * routine might do. Registers D0-3 and A0-3 may
		 * be munged.
		 */
		if (type == SET)
			return	RM(A0)|RM(A1)|RM(A2)|RM(A3)|
				RM(D0)|RM(D1)|RM(D2)|RM(D3)|RM(SP);
		else
			return	RM(SP);


	case MOVEM:
		/*
		 * We should really check for a register mask spec.
		 * here and parse it. The simple solution is to assume
		 * that all the registers used for register variables
		 * are referenced or modified.
		 */
		return RM(A4)|RM(A5)|RM(A7)|RM(D4)|RM(D5)|RM(D6)|RM(D7);

	default:
		fprintf(stderr, "chkside() - unknown opcode\n");
		exit(EXIT_FAILURE);
	}
}

/*
 * reg_set(ip) - return mask of regs set by 'ip'
 */
int
reg_set(ip)
INST	*ip;
{
	int	mask = 0;	/* build up a register mask */

	if (idata[ip->opcode].op1f & SET)
		mask |= chkset(&ip->src);
	if (idata[ip->opcode].op1f & REF) {
		if ((ip->src.amode & (INC|DEC)) != 0)
			mask |= RM(ip->src.areg);
	}

	if (idata[ip->opcode].op2f & SET)
		mask |= chkset(&ip->dst);
	if (idata[ip->opcode].op2f & REF) {
		if ((ip->dst.amode & (INC|DEC)) != 0)
			mask |= RM(ip->dst.areg);
	}

	if (idata[ip->opcode].iflag & SIDE)
		mask |= chkside(ip, SET);

	return mask;
}

/*
 * reg_ref(ip) - return mask of regs referenced by 'ip'
 */
int
reg_ref(ip)
INST	*ip;
{
	int	mask = 0;	/* build up a register mask */

	mask |= chkref(&ip->src, idata[ip->opcode].op1f & REF);
	mask |= chkref(&ip->dst, idata[ip->opcode].op2f & REF);
	if (idata[ip->opcode].iflag & SIDE)
		mask |= chkside(ip, REF);

	return mask;
}

/*
 * sets(ip, reg) - is 'reg' set by the instruction 'ip'?
 */
bool
sets(ip, reg)
INST	*ip;
int	reg;
{
	return ((reg_set(ip) & RM(reg)) != 0);
}

/*
 * refs(ip, reg) - is 'reg' referenced by the instruction 'ip'?
 */
bool
refs(ip, reg)
INST	*ip;
int	reg;
{
	return ((reg_ref(ip) & RM(reg)) != 0);
}

/*
 * uses(ip, ref) - is 'reg' used by the instruction 'ip'?
 */
bool
uses(ip, reg)
INST	*ip;
int	reg;
{
	return sets(ip, reg) || refs(ip, reg);
}
