/*
 * Simplify - do instruction simplification before CSE
 *
 * Copyright (C) 2004 Linus Torvalds
 */

///
// Instruction simplification
// --------------------------
//
// Notation
// ^^^^^^^^
// The following conventions are used to describe the simplications:
// * Uppercase letters are reserved for constants:
//   * `M` for a constant mask,
//   * `S` for a constant shift,
//   * `N` for a constant number of bits (usually other than a shift),
//   * `C` or 'K' for others constants.
// * Lowercase letters `a`, `b`, `x`, `y`, ... are used for non-constants
//   or when it doesn't matter if the pseudo is a constant or not.
// * Primes are used if needed to distinguish symbols (`M`, `M'`, ...).
// * Expressions or sub-expressions involving only constants are
//   understood to be evaluated.
// * `$mask(N)` is used for `((1 << N) -1)`
// * `$trunc(x, N)` is used for `(x & $mask(N))`
// * Expressions like `(-1 << S)`, `(-1 >> S)` and others formulae are
//   understood to be truncated to the size of the current instruction
//   (needed, since in general this size is not the same as the one used
//   by sparse for the evaluation of arithmetic operations).
// * `TRUNC(x, N)` is used for a truncation *to* a size of `N` bits
// * `ZEXT(x, N)` is used for a zero-extension *from* a size of `N` bits
// * `OP(x, C)` is used to represent some generic operation using a constant,
//   including when the constant is implicit (e.g. `TRUNC(x, N)`).
// * `MASK(x, M)` is used to respresent a 'masking' instruction:
//   - `AND(x, M)`
//   - `LSR(x, S)`, with `M` = (-1 << S)
//   - `SHL(x, S)`, with `M` = (-1 >> S)
//   - `TRUNC(x, N)`, with `M` = $mask(N)
//   - `ZEXT(x, N)`, with `M` = $mask(N)
// * `SHIFT(x, S)` is used for `LSR(x, S)` or `SHL(x, S)`.

#include <assert.h>

#include "parse.h"
#include "expression.h"
#include "linearize.h"
#include "simplify.h"
#include "flow.h"
#include "symbol.h"
#include "flowgraph.h"

///
// Utilities
// ^^^^^^^^^

///
// check if a pseudo is a power of 2
static inline bool is_pow2(pseudo_t src)
{
	if (src->type != PSEUDO_VAL)
		return false;
	return is_power_of_2(src->value);
}

///
// find the trivial parent for a phi-source
static struct basic_block *phi_parent(struct basic_block *source, pseudo_t pseudo)
{
	/* Can't go upwards if the pseudo is defined in the bb it came from.. */
	if (pseudo->type == PSEUDO_REG) {
		struct instruction *def = pseudo->def;
		if (def->bb == source)
			return source;
	}
	if (bb_list_size(source->children) != 1 || bb_list_size(source->parents) != 1)
		return source;
	return first_basic_block(source->parents);
}

///
// copy the phi-node's phisrcs into to given array
// @return: 0 if the the list contained the expected
//	number of element, a positive number if there was
//	more than expected and a negative one if less.
//
// :note: we can't reuse ptr_list_to_array() for the phi-sources
//	because any VOIDs in the phi-list must be ignored here
//	as in this context they mean 'entry has been removed'.
static int get_phisources(struct instruction *sources[], int nbr, struct instruction *insn)
{
	pseudo_t phi;
	int i = 0;

	assert(insn->opcode == OP_PHI);
	FOR_EACH_PTR(insn->phi_list, phi) {
		struct instruction *def;
		if (phi == VOID)
			continue;
		if (i >= nbr)
			return 1;
		def = phi->def;
		assert(def->opcode == OP_PHISOURCE);
		sources[i++] = def;
	} END_FOR_EACH_PTR(phi);
	return i - nbr;
}

static int if_convert_phi(struct instruction *insn)
{
	struct instruction *array[2];
	struct basic_block *parents[2];
	struct basic_block *bb, *bb1, *bb2, *source;
	struct instruction *br;
	pseudo_t p1, p2;

	bb = insn->bb;
	if (get_phisources(array, 2, insn))
		return 0;
	if (ptr_list_to_array(bb->parents, parents, 2) != 2)
		return 0;
	p1 = array[0]->phi_src;
	bb1 = array[0]->bb;
	p2 = array[1]->phi_src;
	bb2 = array[1]->bb;

	/* Only try the simple "direct parents" case */
	if ((bb1 != parents[0] || bb2 != parents[1]) &&
	    (bb1 != parents[1] || bb2 != parents[0]))
		return 0;

	/*
	 * See if we can find a common source for this..
	 */
	source = phi_parent(bb1, p1);
	if (source != phi_parent(bb2, p2))
		return 0;

	/*
	 * Cool. We now know that 'source' is the exclusive
	 * parent of both phi-nodes, so the exit at the
	 * end of it fully determines which one it is, and
	 * we can turn it into a select.
	 *
	 * HOWEVER, right now we only handle regular
	 * conditional branches. No multijumps or computed
	 * stuff. Verify that here.
	 */
	br = last_instruction(source->insns);
	if (!br || br->opcode != OP_CBR)
		return 0;

	assert(br->cond);
	assert(br->bb_false);

	/*
	 * We're in business. Match up true/false with p1/p2.
	 */
	if (br->bb_true == bb2 || br->bb_false == bb1) {
		pseudo_t p = p1;
		p1 = p2;
		p2 = p;
	}

	/*
	 * OK, we can now replace that last
	 *
	 *	br cond, a, b
	 *
	 * with the sequence
	 *
	 *	setcc cond
	 *	select pseudo, p1, p2
	 *	br cond, a, b
	 *
	 * and remove the phi-node. If it then
	 * turns out that 'a' or 'b' is entirely
	 * empty (common case), and now no longer
	 * a phi-source, we'll be able to simplify
	 * the conditional branch too.
	 */
	insert_select(source, br, insn, p1, p2);
	kill_instruction(insn);
	return REPEAT_CSE;
}

///
// detect trivial phi-nodes
// @insn: the phi-node
// @pseudo: the candidate resulting pseudo (NULL when starting)
// @return: the unique result if the phi-node is trivial, NULL otherwise
//
// A phi-node is trivial if it has a single possible result:
//	* all operands are the same
//	* the operands are themselves defined by a chain or cycle of phi-nodes
//		and the set of all operands involved contains a single value
//		not defined by these phi-nodes
//
// Since the result is unique, these phi-nodes can be removed.
static pseudo_t trivial_phi(pseudo_t pseudo, struct instruction *insn, struct pseudo_list **list)
{
	pseudo_t target = insn->target;
	pseudo_t phi;

	add_pseudo(list, target);

	FOR_EACH_PTR(insn->phi_list, phi) {
		struct instruction *def;
		pseudo_t src;

		if (phi == VOID)
			continue;
		def = phi->def;
		if (!def->bb)
			continue;
		src = def->phi_src; // bypass OP_PHISRC & get the real source
		if (src == VOID)
			continue;
		if (src == target)
			continue;
		if (!pseudo) {
			pseudo = src;
			continue;
		}
		if (src == pseudo)
			continue;
		if (DEF_OPCODE(def, src) == OP_PHI) {
			if (pseudo_in_list(*list, src))
				continue;
			if ((pseudo = trivial_phi(pseudo, def, list)))
				continue;
		}
		return NULL;
	} END_FOR_EACH_PTR(phi);

	return pseudo ? pseudo : VOID;
}

static int clean_up_phi(struct instruction *insn)
{
	struct pseudo_list *list = NULL;
	pseudo_t pseudo;

	if ((pseudo = trivial_phi(NULL, insn, &list))) {
		convert_instruction_target(insn, pseudo);
		kill_instruction(insn);
		return REPEAT_CSE;
	}

	return if_convert_phi(insn);
}

static int delete_pseudo_user_list_entry(struct pseudo_user_list **list, pseudo_t *entry, int count)
{
	struct pseudo_user *pu;

	FOR_EACH_PTR(*list, pu) {
		if (pu->userp == entry) {
			MARK_CURRENT_DELETED(pu);
			if (!--count)
				goto out;
		}
	} END_FOR_EACH_PTR(pu);
	assert(count <= 0);
out:
	if (pseudo_user_list_empty(*list))
		*list = NULL;
	return count;
}

static inline void rem_usage(pseudo_t p, pseudo_t *usep, int kill)
{
	if (has_use_list(p)) {
		delete_pseudo_user_list_entry(&p->users, usep, 1);
		if (kill && !p->users && has_definition(p))
			kill_instruction(p->def);
	}
}

static inline void remove_usage(pseudo_t p, pseudo_t *usep)
{
	rem_usage(p, usep, 1);
}

void kill_use(pseudo_t *usep)
{
	if (usep) {
		pseudo_t p = *usep;
		*usep = VOID;
		rem_usage(p, usep, 1);
	}
}

// Like kill_use() but do not (recursively) kill dead instructions
void remove_use(pseudo_t *usep)
{
	pseudo_t p = *usep;
	*usep = VOID;
	rem_usage(p, usep, 0);
}

static void kill_use_list(struct pseudo_list *list)
{
	pseudo_t p;
	FOR_EACH_PTR(list, p) {
		if (p == VOID)
			continue;
		kill_use(THIS_ADDRESS(p));
	} END_FOR_EACH_PTR(p);
}

static void kill_asm(struct instruction *insn)
{
	struct asm_constraint *con;

	FOR_EACH_PTR(insn->asm_rules->inputs, con) {
		kill_use(&con->pseudo);
	} END_FOR_EACH_PTR(con);
}

///
// kill an instruction
// @insn: the instruction to be killed
// @force: if unset, the normal case, the instruction is not killed
//	if not free of possible side-effect; if set the instruction
//	is unconditionally killed.
//
// The killed instruction is removed from its BB and the usage
// of all its operands are removed. The instruction is also
// marked as killed by setting its ->bb to NULL.
int kill_insn(struct instruction *insn, int force)
{
	if (!insn || !insn->bb)
		return 0;

	switch (insn->opcode) {
	case OP_SEL:
	case OP_RANGE:
		kill_use(&insn->src3);
		/* fall through */

	case OP_BINARY ... OP_BINCMP_END:
		kill_use(&insn->src2);
		/* fall through */

	case OP_UNOP ... OP_UNOP_END:
	case OP_SLICE:
	case OP_PHISOURCE:
	case OP_SYMADDR:
	case OP_CBR:
	case OP_SWITCH:
	case OP_COMPUTEDGOTO:
		kill_use(&insn->src1);
		break;

	case OP_PHI:
		kill_use_list(insn->phi_list);
		break;

	case OP_CALL:
		if (!force) {
			/* a "pure" function can be killed too */
			struct symbol *fntype = first_symbol(insn->fntypes);

			if (!(fntype->ctype.modifiers & MOD_PURE))
				return 0;
		}
		kill_use_list(insn->arguments);
		if (insn->func->type == PSEUDO_REG)
			kill_use(&insn->func);
		break;

	case OP_LOAD:
		if (!force && insn->is_volatile)
			return 0;
		kill_use(&insn->src);
		break;

	case OP_STORE:
		if (!force)
			return 0;
		kill_use(&insn->src);
		kill_use(&insn->target);
		break;

	case OP_ASM:
		if (!force)
			return 0;
		kill_asm(insn);
		break;

	case OP_ENTRY:
		/* ignore */
		return 0;

	case OP_BR:
	case OP_LABEL:
	case OP_SETVAL:
	case OP_SETFVAL:
	default:
		break;
	}

	insn->bb = NULL;
	return repeat_phase |= REPEAT_CSE;
}

static inline bool has_target(struct instruction *insn)
{
	return opcode_table[insn->opcode].flags & OPF_TARGET;
}

void remove_dead_insns(struct entrypoint *ep)
{
	struct basic_block *bb;

	FOR_EACH_PTR_REVERSE(ep->bbs, bb) {
		struct instruction *insn;
		FOR_EACH_PTR_REVERSE(bb->insns, insn) {
			if (!insn->bb)
				continue;
			if (!has_target(insn))
				continue;
			if (!has_users(insn->target))
				kill_instruction(insn);
		} END_FOR_EACH_PTR_REVERSE(insn);
	} END_FOR_EACH_PTR_REVERSE(bb);
}

static inline int constant(pseudo_t pseudo)
{
	return pseudo->type == PSEUDO_VAL;
}

///
// is this same signed value when interpreted with both size?
static inline bool is_signed_constant(long long val, unsigned osize, unsigned nsize)
{
	return bits_extend(val, osize, 1) == bits_extend(val, nsize, 1);
}

///
// is @src generated by an instruction with the given opcode and size?
static inline pseudo_t is_same_op(pseudo_t src, int op, unsigned osize)
{
	struct instruction *def;

	if (src->type != PSEUDO_REG)
		return NULL;
	def = src->def;
	if (def->opcode != op)
		return NULL;
	if (def->orig_type->bit_size != osize)
		return NULL;
	return def->src;
}

static bool is_negate_of(pseudo_t p, pseudo_t ref)
{
	struct instruction *def;

	return (DEF_OPCODE(def, p) == OP_NEG) && (def->src == ref);
}

///
// replace the operand of an instruction
// @insn: the instruction
// @pp: the address of the instruction's operand
// @new: the new value for the operand
// @return: REPEAT_CSE.
static inline int replace_pseudo(struct instruction *insn, pseudo_t *pp, pseudo_t new)
{
	pseudo_t old = *pp;
	use_pseudo(insn, new, pp);
	remove_usage(old, pp);
	return REPEAT_CSE;
}

int replace_with_pseudo(struct instruction *insn, pseudo_t pseudo)
{
	convert_instruction_target(insn, pseudo);
	return kill_instruction(insn);
}

static inline int replace_with_value(struct instruction *insn, long long val)
{
	return replace_with_pseudo(insn, value_pseudo(val));
}

///
// replace a binop with an unop
// @insn: the instruction to be replaced
// @op: the instruction's new opcode
// @src: the instruction's new operand
// @return: REPEAT_CSE
static inline int replace_with_unop(struct instruction *insn, int op, pseudo_t src)
{
	insn->opcode = op;
	replace_pseudo(insn, &insn->src1, src);
	remove_usage(insn->src2, &insn->src2);
	return REPEAT_CSE;
}

///
// replace rightside's value
// @insn: the instruction to be replaced
// @op: the instruction's new opcode
// @src: the instruction's new operand
// @return: REPEAT_CSE
static inline int replace_binop_value(struct instruction *insn, int op, long long val)
{
	insn->opcode = op;
	insn->src2 = value_pseudo(val);
	return REPEAT_CSE;
}

///
// replace binop's opcode and values
// @insn: the instruction to be replaced
// @op: the instruction's new opcode
// @return: REPEAT_CSE
static inline int replace_binop(struct instruction *insn, int op, pseudo_t *pa, pseudo_t a, pseudo_t *pb, pseudo_t b)
{
	pseudo_t olda = *pa;
	pseudo_t oldb = *pb;
	insn->opcode = op;
	use_pseudo(insn, a, pa);
	use_pseudo(insn, b, pb);
	remove_usage(olda, pa);
	remove_usage(oldb, pb);
	return REPEAT_CSE;
}

///
// replace the opcode of an instruction
// @return: REPEAT_CSE
static inline int replace_opcode(struct instruction *insn, int op)
{
	insn->opcode = op;
	return REPEAT_CSE;
}

///
// create an instruction pair OUT(IN(a, b), c)
static int replace_insn_pair(struct instruction *out, int op_out, struct instruction *in, int op_in, pseudo_t a, pseudo_t b, pseudo_t c)
{
	pseudo_t old_a = in->src1;
	pseudo_t old_b = in->src2;
	pseudo_t old_1 = out->src1;
	pseudo_t old_2 = out->src2;

	use_pseudo(in, a, &in->src1);
	use_pseudo(in, b, &in->src2);
	use_pseudo(out, in->target, &out->src1);
	use_pseudo(out, c, &out->src2);

	remove_usage(old_a, &in->src1);
	remove_usage(old_b, &in->src2);
	remove_usage(old_1, &out->src1);
	remove_usage(old_2, &out->src2);

	out->opcode = op_out;
	in->opcode = op_in;
	return REPEAT_CSE;
}

///
// create an instruction pair OUT(IN(a, b), c) with swapped opcodes
static inline int swap_insn(struct instruction *out, struct instruction *in, pseudo_t a, pseudo_t b, pseudo_t c)
{
	return replace_insn_pair(out, in->opcode, in, out->opcode, a, b, c);
}

///
// create an instruction pair OUT(SELECT(a, b, c), d)
static int swap_select(struct instruction *out, struct instruction *in, pseudo_t a, pseudo_t b, pseudo_t c, pseudo_t d)
{
	use_pseudo(in, c, &in->src3);
	swap_insn(out, in, a, b, d);
	kill_use(&out->src3);
	return REPEAT_CSE;
}

static inline int def_opcode(pseudo_t p)
{
	if (p->type != PSEUDO_REG)
		return OP_BADOP;
	return p->def->opcode;
}

static unsigned int value_size(long long value)
{
	value >>= 8;
	if (!value)
		return 8;
	value >>= 8;
	if (!value)
		return 16;
	value >>= 16;
	if (!value)
		return 32;
	return 64;
}

///
// try to determine the maximum size of bits in a pseudo
//
// Right now this only follow casts and constant values, but we
// could look at things like AND instructions, etc.
static unsigned int operand_size(struct instruction *insn, pseudo_t pseudo)
{
	unsigned int size = insn->size;

	if (pseudo->type == PSEUDO_REG) {
		struct instruction *src = pseudo->def;
		if (src && src->opcode == OP_ZEXT && src->orig_type) {
			unsigned int orig_size = src->orig_type->bit_size;
			if (orig_size < size)
				size = orig_size;
		}
	}
	if (pseudo->type == PSEUDO_VAL) {
		unsigned int orig_size = value_size(pseudo->value);
		if (orig_size < size)
			size = orig_size;
	}
	return size;
}

static pseudo_t eval_op(int op, unsigned size, pseudo_t src1, pseudo_t src2)
{
	/* FIXME! Verify signs and sizes!! */
	long long left = src1->value;
	long long right = src2->value;
	unsigned long long ul, ur;
	long long res, mask, bits;

	mask = 1ULL << (size-1);
	bits = mask | (mask-1);

	if (left & mask)
		left |= ~bits;
	if (right & mask)
		right |= ~bits;
	ul = left & bits;
	ur = right & bits;

	switch (op) {
	case OP_NEG:
		res = -left;
		break;
	case OP_NOT:
		res = ~ul;
		break;

	case OP_ADD:
		res = left + right;
		break;
	case OP_SUB:
		res = left - right;
		break;
	case OP_MUL:
		res = ul * ur;
		break;
	case OP_DIVU:
		if (!ur)
			goto undef;
		res = ul / ur;
		break;
	case OP_DIVS:
		if (!right)
			goto undef;
		if (left == mask && right == -1)
			goto undef;
		res = left / right;
		break;
	case OP_MODU:
		if (!ur)
			goto undef;
		res = ul % ur;
		break;
	case OP_MODS:
		if (!right)
			goto undef;
		if (left == mask && right == -1)
			goto undef;
		res = left % right;
		break;
	case OP_SHL:
		if (ur >= size)
			goto undef;
		res = left << right;
		break;
	case OP_LSR:
		if (ur >= size)
			goto undef;
		res = ul >> ur;
		break;
	case OP_ASR:
		if (ur >= size)
			goto undef;
		res = left >> right;
		break;
       /* Logical */
	case OP_AND:
		res = left & right;
		break;
	case OP_OR:
		res = left | right;
		break;
	case OP_XOR:
		res = left ^ right;
		break;

	/* Binary comparison */
	case OP_SET_EQ:
		res = left == right;
		break;
	case OP_SET_NE:
		res = left != right;
		break;
	case OP_SET_LE:
		res = left <= right;
		break;
	case OP_SET_GE:
		res = left >= right;
		break;
	case OP_SET_LT:
		res = left < right;
		break;
	case OP_SET_GT:
		res = left > right;
		break;
	case OP_SET_B:
		res = ul < ur;
		break;
	case OP_SET_A:
		res = ul > ur;
		break;
	case OP_SET_BE:
		res = ul <= ur;
		break;
	case OP_SET_AE:
		res = ul >= ur;
		break;
	default:
		return NULL;
	}

	// Warning: this should be done with the output size which may
	// be different than the input size used here. But it differs
	// only for compares which are not concerned since only returning
	// 0 or 1 and for casts which are not handled here.
	res &= bits;

	return value_pseudo(res);

undef:
	return NULL;
}

static inline pseudo_t eval_unop(int op, unsigned size, pseudo_t src)
{
	return eval_op(op, size, src, VOID);
}

///
// Simplifications
// ^^^^^^^^^^^^^^^

///
// try to simplify MASK(OR(AND(x, M'), b), M)
// @insn: the masking instruction
// @mask: the associated mask (M)
// @ora: one of the OR's operands, guaranteed to be PSEUDO_REG
// @orb: the other OR's operand
// @return: 0 if no changes have been made, one or more REPEAT_* flags otherwise.
static int simplify_mask_or_and(struct instruction *insn, unsigned long long mask,
	pseudo_t ora, pseudo_t orb)
{
	unsigned long long omask, nmask;
	struct instruction *and = ora->def;
	pseudo_t src2 = and->src2;

	if (and->opcode != OP_AND)
		return 0;
	if (!constant(src2))
		return 0;
	omask = src2->value;
	nmask = omask & mask;
	if (nmask == 0) {
		// if (M' & M) == 0: ((a & M') | b) -> b
		return replace_pseudo(insn, &insn->src1, orb);
	}
	if (!one_use(insn->src1))
		return 0;	// can't modify anything inside the OR
	if (nmask == mask) {
		struct instruction *or = insn->src1->def;
		pseudo_t *arg = (ora == or->src1) ? &or->src1 : &or->src2;
		// if (M' & M) == M: ((a & M') | b) -> (a | b)
		return replace_pseudo(or, arg, and->src1);
	}
	if (nmask != omask && one_use(ora)) {
		// if (M' & M) != M': AND(a, M') -> AND(a, (M' & M))
		and->src2 = value_pseudo(nmask);
		return REPEAT_CSE;
	}
	return 0;
}

///
// try to simplify MASK(OR(a, b), M)
// @insn: the masking instruction
// @mask: the associated mask (M)
// @or: the OR instruction
// @return: 0 if no changes have been made, one or more REPEAT_* flags otherwise.
static int simplify_mask_or(struct instruction *insn, unsigned long long mask, struct instruction *or)
{
	pseudo_t src1 = or->src1;
	pseudo_t src2 = or->src2;
	int rc;

	if (src1->type == PSEUDO_REG) {
		if ((rc = simplify_mask_or_and(insn, mask, src1, src2)))
			return rc;
	}
	if (src2->type == PSEUDO_REG) {
		if ((rc = simplify_mask_or_and(insn, mask, src2, src1)))
			return rc;
	} else if (src2->type == PSEUDO_VAL) {
		unsigned long long oval = src2->value;
		unsigned long long nval = oval & mask;
		// Try to simplify:
		//	MASK(OR(x, C), M)
		if (nval == 0) {
			// if (C & M) == 0: OR(x, C) -> x
			return replace_pseudo(insn, &insn->src1, src1);
		}
		if (nval == mask) {
			// if (C & M) == M: OR(x, C) -> M
			return replace_pseudo(insn, &insn->src1, value_pseudo(mask));
		}
		if (nval != oval && one_use(or->target)) {
			// if (C & M) != C: OR(x, C) -> OR(x, (C & M))
			return replace_pseudo(or, &or->src2, value_pseudo(nval));
		}
	}
	return 0;
}

///
// try to simplify MASK(SHIFT(OR(a, b), S), M)
// @sh: the shift instruction
// @or: the OR instruction
// @mask: the mask associated to MASK (M):
// @return: 0 if no changes have been made, one or more REPEAT_* flags otherwise.
static int simplify_mask_shift_or(struct instruction *sh, struct instruction *or, unsigned long long mask)
{
	unsigned long long smask = bits_mask(sh->size);
	int shift = sh->src2->value;

	if (sh->opcode == OP_LSR)
		mask <<= shift;
	else
		mask >>= shift;
	return simplify_mask_or(sh, smask & mask, or);
}

static int simplify_mask_shift(struct instruction *sh, unsigned long long mask)
{
	struct instruction *inner;

	if (!constant(sh->src2) || sh->tainted)
		return 0;
	switch (DEF_OPCODE(inner, sh->src1)) {
	case OP_OR:
		if (one_use(sh->target))
			return simplify_mask_shift_or(sh, inner, mask);
		break;
	}
	return 0;
}

static pseudo_t eval_insn(struct instruction *insn)
{
	unsigned size = insn->size;

	if (opcode_table[insn->opcode].flags & OPF_COMPARE)
		size = insn->itype->bit_size;
	return eval_op(insn->opcode, size, insn->src1, insn->src2);
}

static long long check_shift_count(struct instruction *insn, unsigned long long uval)
{
	unsigned int size = insn->size;
	long long sval = uval;

	if (insn->tainted)
		return -1;

	if (uval < size)
		return uval;

	insn->tainted = 1;
	sval = sign_extend_safe(sval, size);
	sval = sign_extend_safe(sval, bits_in_int);
	if (sval < 0)
		insn->src2 = value_pseudo(sval);
	return -1;
}

static int simplify_shift(struct instruction *insn, pseudo_t pseudo, long long value)
{
	struct instruction *def;
	unsigned long long mask, omask, nmask;
	unsigned long long nval;
	unsigned int size;
	pseudo_t src2;

	if (!value)
		return replace_with_pseudo(insn, pseudo);
	value = check_shift_count(insn, value);
	if (value < 0)
		return 0;

	size = insn->size;
	switch (insn->opcode) {
	case OP_ASR:
		if (value >= size)
			return 0;
		if (pseudo->type != PSEUDO_REG)
			break;
		def = pseudo->def;
		switch (def->opcode) {
		case OP_LSR:
		case OP_ASR:
			if (def == insn)	// cyclic DAG!
				break;
			src2 = def->src2;
			if (src2->type != PSEUDO_VAL)
				break;
			nval = src2->value;
			if (nval > insn->size || nval == 0)
				break;
			value += nval;
			if (def->opcode == OP_LSR)
				insn->opcode = OP_LSR;
			else if (value >= size)
				value = size - 1;
			goto new_value;

		case OP_ZEXT:
			// transform:
			//	zext.N	%t <- (O) %a
			//	asr.N	%r <- %t, C
			// into
			//	zext.N	%t <- (O) %a
			//	lsr.N	%r <- %t, C
			insn->opcode = OP_LSR;
			return REPEAT_CSE;
		}
		break;
	case OP_LSR:
		size = operand_size(insn, pseudo);
		if (value >= size)
			goto zero;
		switch(DEF_OPCODE(def, pseudo)) {
		case OP_AND:
			// replace (A & M) >> S
			// by      (A >> S) & (M >> S)
			if (!constant(def->src2))
				break;
			mask = bits_mask(insn->size - value) << value;
			omask = def->src2->value;
			nmask = omask & mask;
			if (nmask == 0)
				return replace_with_value(insn, 0);
			if (nmask == mask)
				return replace_pseudo(insn, &insn->src1, def->src1);
			if (!one_use(pseudo))
				break;
			def->opcode = OP_LSR;
			def->src2 = insn->src2;
			insn->opcode = OP_AND;
			insn->src2 = value_pseudo(omask >> value);
			return REPEAT_CSE;
		case OP_LSR:
			goto case_shift_shift;
		case OP_OR:
			mask = bits_mask(size);
			return simplify_mask_shift_or(insn, def, mask);
		case OP_SHL:
			// replace ((x << S) >> S)
			// by      (x & (-1 >> S))
			if (def->src2 != insn->src2)
				break;
			mask = bits_mask(insn->size - value);
			goto replace_mask;
		}
		break;
	case OP_SHL:
		if (value >= size)
			goto zero;
		switch(DEF_OPCODE(def, pseudo)) {
		case OP_AND:
			// simplify (A & M) << S
			if (!constant(def->src2))
				break;
			mask = bits_mask(insn->size) >> value;
			omask = def->src2->value;
			nmask = omask & mask;
			if (nmask == 0)
				return replace_with_value(insn, 0);
			if (nmask == mask)
				return replace_pseudo(insn, &insn->src1, def->src1);
			// do not simplify into ((A << S) & (M << S))
			break;
		case OP_LSR:
			// replace ((x >> S) << S)
			// by      (x & (-1 << S))
			if (def->src2 != insn->src2)
				break;
			mask = bits_mask(insn->size - value) << value;
			goto replace_mask;
		case OP_OR:
			mask = bits_mask(size);
			return simplify_mask_shift_or(insn, def, mask);
		case OP_SHL:
		case_shift_shift:		// also for LSR - LSR
			if (def == insn)	// cyclic DAG!
				break;
			src2 = def->src2;
			if (src2->type != PSEUDO_VAL)
				break;
			nval = src2->value;
			if (nval > insn->size)
				break;
			value += nval;
			goto new_value;
		}
		break;
	}
	return 0;

new_value:
	if (value < size) {
		insn->src2 = value_pseudo(value);
		return replace_pseudo(insn, &insn->src1, pseudo->def->src1);
	}
zero:
	return replace_with_value(insn, 0);
replace_mask:
	insn->opcode = OP_AND;
	insn->src2 = value_pseudo(mask);
	return replace_pseudo(insn, &insn->src1, def->src1);
}

static int simplify_mul_div(struct instruction *insn, long long value)
{
	unsigned long long sbit = 1ULL << (insn->size - 1);
	unsigned long long bits = sbit | (sbit - 1);

	if (value == 1)
		return replace_with_pseudo(insn, insn->src1);

	switch (insn->opcode) {
	case OP_MUL:
		if (value == 0)
			return replace_with_pseudo(insn, insn->src2);
	/* Fall through */
	case OP_DIVS:
		if (!(value & sbit))	// positive
			break;

		value |= ~bits;
		if (value == -1) {
			insn->opcode = OP_NEG;
			return REPEAT_CSE;
		}
	}

	return 0;
}

static int simplify_seteq_setne(struct instruction *insn, long long value)
{
	pseudo_t old = insn->src1;
	struct instruction *def;
	unsigned osize;
	int inverse;
	int opcode;

	if (value != 0 && value != 1)
		return 0;

	if (old->type != PSEUDO_REG)
		return 0;
	def = old->def;
	if (!def)
		return 0;

	inverse = (insn->opcode == OP_SET_NE) == value;
	if (!inverse && def->size == 1 && insn->size == 1) {
		// Replace:
		//	setne   %r <- %s, $0
		// or:
		//	seteq   %r <- %s, $1
		// by %s when boolean
		return replace_with_pseudo(insn, old);
	}
	opcode = def->opcode;
	switch (opcode) {
	case OP_AND:
		if (inverse)
			break;
		if (def->size != insn->size)
			break;
		if (def->src2->type != PSEUDO_VAL)
			break;
		if (def->src2->value != 1)
			break;
		return replace_with_pseudo(insn, old);
	case OP_FPCMP ... OP_BINCMP_END:
		// Convert:
		//	setcc.n	%t <- %a, %b
		//	setne.m %r <- %t, $0
		// into:
		//	setcc.n	%t <- %a, %b
		//	setcc.m %r <- %a, $b
		// and similar for setne/eq ... 0/1
		insn->opcode = inverse ? opcode_table[opcode].negate : opcode;
		insn->itype = def->itype;
		use_pseudo(insn, def->src1, &insn->src1);
		use_pseudo(insn, def->src2, &insn->src2);
		remove_usage(old, &insn->src1);
		return REPEAT_CSE;

	case OP_SEXT:
		if (value && (def->orig_type->bit_size == 1))
			break;
		/* Fall through */
	case OP_ZEXT:
		// Convert:
		//	*ext.m	%s <- (1) %a
		//	setne.1 %r <- %s, $0
		// into:
		//	setne.1 %s <- %a, $0
		// and same for setne/eq ... 0/1
		insn->itype = def->orig_type;
		return replace_pseudo(insn, &insn->src1, def->src);
	case OP_TRUNC:
		if (!one_use(old))
			break;
		// convert
		//	trunc.n	%s <- (o) %a
		//	setne.m %r <- %s, $0
		// into:
		//	and.o	%s <- %a, $((1 << o) - 1)
		//	setne.m %r <- %s, $0
		// and same for setne/eq ... 0/1
		osize = def->size;
		def->opcode = OP_AND;
		def->type = def->orig_type;
		def->size = def->type->bit_size;
		def->src2 = value_pseudo(bits_mask(osize));
		return REPEAT_CSE;
	}
	return 0;
}

static int simplify_compare_constant(struct instruction *insn, long long value)
{
	unsigned size = insn->itype->bit_size;
	unsigned long long bits = bits_mask(size);
	struct instruction *def;
	pseudo_t src1, src2;
	unsigned int osize;
	int changed = 0;

	switch (insn->opcode) {
	case OP_SET_LT:
		if (!value)
			break;
		if (value == sign_bit(size))	// (x <  SMIN) --> 0
			return replace_with_pseudo(insn, value_pseudo(0));
		if (value == sign_mask(size))	// (x <  SMAX) --> (x != SMAX)
			return replace_opcode(insn, OP_SET_NE);
		if (value == sign_bit(size) + 1)// (x < SMIN + 1) --> (x == SMIN)
			return replace_binop_value(insn, OP_SET_EQ, sign_bit(size));
		if (!(value & sign_bit(size)))
			changed |= replace_binop_value(insn, OP_SET_LE, (value - 1) & bits);
		break;
	case OP_SET_LE:
		if (!value)
			break;
		if (value == sign_mask(size))	// (x <= SMAX) --> 1
			return replace_with_pseudo(insn, value_pseudo(1));
		if (value == sign_bit(size))	// (x <= SMIN) --> (x == SMIN)
			return replace_opcode(insn, OP_SET_EQ);
		if (value == sign_mask(size) - 1) // (x <= SMAX - 1) --> (x != SMAX)
			return replace_binop_value(insn, OP_SET_NE, sign_mask(size));
		if (value & sign_bit(size))
			changed |= replace_binop_value(insn, OP_SET_LT, (value + 1) & bits);
		break;
	case OP_SET_GE:
		if (!value)
			break;
		if (value == sign_bit(size))	// (x >= SMIN) --> 1
			return replace_with_pseudo(insn, value_pseudo(1));
		if (value == sign_mask(size))	// (x >= SMAX) --> (x == SMAX)
			return replace_opcode(insn, OP_SET_EQ);
		if (value == sign_bit(size) + 1)// (x >= SMIN + 1) --> (x != SMIN)
			return replace_binop_value(insn, OP_SET_NE, sign_bit(size));
		if (!(value & sign_bit(size)))
			changed |= replace_binop_value(insn, OP_SET_GT, (value - 1) & bits);
		break;
	case OP_SET_GT:
		if (!value)
			break;
		if (value == sign_mask(size))	// (x >  SMAX) --> 0
			return replace_with_pseudo(insn, value_pseudo(0));
		if (value == sign_bit(size))	// (x >  SMIN) --> (x != SMIN)
			return replace_opcode(insn, OP_SET_NE);
		if (value == sign_mask(size) - 1) // (x > SMAX - 1) --> (x == SMAX)
			return replace_binop_value(insn, OP_SET_EQ, sign_mask(size));
		if (value & sign_bit(size))
			changed |= replace_binop_value(insn, OP_SET_GE, (value + 1) & bits);
		break;

	case OP_SET_B:
		if (!value)			// (x < 0) --> 0
			return replace_with_pseudo(insn, value_pseudo(0));
		if (value == 1)			// (x < 1) --> (x == 0)
			return replace_binop_value(insn, OP_SET_EQ, 0);
		else if (value == bits)		// (x < ~0) --> (x != ~0)
			return replace_binop_value(insn, OP_SET_NE, value);
		else				// (x < y) --> (x <= (y-1))
			changed |= replace_binop_value(insn, OP_SET_BE, (value - 1) & bits);
		break;
	case OP_SET_AE:
		if (!value)			// (x >= 0) --> 1
			return replace_with_pseudo(insn, value_pseudo(1));
		if (value == 1)			// (x >= 1) --> (x != 0)
			return replace_binop_value(insn, OP_SET_NE, 0);
		else if (value == bits)		// (x >= ~0) --> (x == ~0)
			return replace_binop_value(insn, OP_SET_EQ, value);
		else				// (x >= y) --> (x > (y-1)
			changed |= replace_binop_value(insn, OP_SET_A, (value - 1) & bits);
		break;
	case OP_SET_BE:
		if (!value)			// (x <= 0) --> (x == 0)
			return replace_opcode(insn, OP_SET_EQ);
		if (value == bits)		// (x <= ~0) --> 1
			return replace_with_pseudo(insn, value_pseudo(1));
		if (value == (bits - 1))	// (x <= ~1) --> (x != ~0)
			return replace_binop_value(insn, OP_SET_NE, bits);
		if (value == (bits >> 1))	// (x u<= SMAX) --> (x s>= 0)
			changed |= replace_binop_value(insn, OP_SET_GE, 0);
		break;
	case OP_SET_A:
		if (!value)			// (x > 0) --> (x != 0)
			return replace_opcode(insn, OP_SET_NE);
		if (value == bits)		// (x > ~0) --> 0
			return replace_with_pseudo(insn, value_pseudo(0));
		if (value == (bits - 1))	// (x > ~1) --> (x == ~0)
			return replace_binop_value(insn, OP_SET_EQ, bits);
		if (value == (bits >> 1))	// (x u> SMAX) --> (x s< 0)
			changed |= replace_binop_value(insn, OP_SET_LT, 0);
		break;
	}

	src1 = insn->src1;
	src2 = insn->src2;
	value = src2->value;
	switch (DEF_OPCODE(def, src1)) {
	case OP_AND:
		if (!constant(def->src2))
			break;
		bits = def->src2->value;
		switch (insn->opcode) {
		case OP_SET_EQ:
			if ((value & bits) != value)
				return replace_with_value(insn, 0);
			if (value == bits && is_power_of_2(bits))
				return replace_binop_value(insn, OP_SET_NE, 0);
			break;
		case OP_SET_NE:
			if ((value & bits) != value)
				return replace_with_value(insn, 1);
			if (value == bits && is_power_of_2(bits))
				return replace_binop_value(insn, OP_SET_EQ, 0);
			break;
		case OP_SET_LE: case OP_SET_LT:
			value = sign_extend(value, def->size);
			if (insn->opcode == OP_SET_LT)
				value -= 1;
			if (bits & sign_bit(def->size))
				break;
			if (value < 0)
				return replace_with_value(insn, 0);
			if (value >= (long long)bits)
				return replace_with_value(insn, 1);
			if (value == 0)
				return replace_opcode(insn, OP_SET_EQ);
			break;
		case OP_SET_GT: case OP_SET_GE:
			value = sign_extend(value, def->size);
			if (insn->opcode == OP_SET_GE)
				value -= 1;
			if (bits & sign_bit(def->size))
				break;
			if (value < 0)
				return replace_with_value(insn, 1);
			if (value >= (long long)bits)
				return replace_with_value(insn, 0);
			if (value == 0)
				return replace_opcode(insn, OP_SET_NE);
			break;
		case OP_SET_B:
			if (value > bits)
				return replace_with_value(insn, 1);
			break;
		case OP_SET_BE:
			if (value >= bits)
				return replace_with_value(insn, 1);
			break;
		case OP_SET_AE:
			if (value > bits)
				return replace_with_value(insn, 0);
			break;
		case OP_SET_A:
			if (value >= bits)
				return replace_with_value(insn, 0);
			break;
		}
		break;
	case OP_OR:
		if (!constant(def->src2))
			break;
		bits = def->src2->value;
		switch (insn->opcode) {
		case OP_SET_EQ:
			if ((value & bits) != bits)
				return replace_with_value(insn, 0);
			break;
		case OP_SET_NE:
			if ((value & bits) != bits)
				return replace_with_value(insn, 1);
			break;
		case OP_SET_B:
			if (bits >= value)
				return replace_with_value(insn, 0);
			break;
		case OP_SET_BE:
			if (bits > value)
				return replace_with_value(insn, 0);
			break;
		case OP_SET_AE:
			if (bits > value)
				return replace_with_value(insn, 1);
			break;
		case OP_SET_A:
			if (bits >= value)
				return replace_with_value(insn, 1);
			break;
		case OP_SET_LT:
			value -= 1;
		case OP_SET_LE:
			if (bits & sign_bit(def->size)) {
				value = sign_extend(value, def->size);
				if (value >= -1)
					return replace_with_value(insn, 1);
			}
			break;
		case OP_SET_GE:
			value -= 1;
		case OP_SET_GT:
			if (bits & sign_bit(def->size)) {
				value = sign_extend(value, def->size);
				if (value >= -1)
					return replace_with_value(insn, 0);
			}
			break;
		}
		break;
	case OP_SEXT:				// sext(x) cmp C --> x cmp trunc(C)
		osize = def->orig_type->bit_size;
		if (is_signed_constant(value, osize, size)) {
			insn->itype = def->orig_type;
			insn->src2 = value_pseudo(zero_extend(value, osize));
			return replace_pseudo(insn, &insn->src1, def->src);
		}
		switch (insn->opcode) {
		case OP_SET_BE:
			if (value >= sign_bit(osize)) {
				insn->itype = def->orig_type;
				replace_binop_value(insn, OP_SET_GE, 0);
				return replace_pseudo(insn, &insn->src1, def->src);
			}
			break;
		case OP_SET_A:
			if (value >= sign_bit(osize)) {
				insn->itype = def->orig_type;
				replace_binop_value(insn, OP_SET_LT, 0);
				return replace_pseudo(insn, &insn->src1, def->src);
			}
			break;
		case OP_SET_LT: case OP_SET_LE:
			if (value < sign_bit(size))
				return replace_with_value(insn, 1);
			else
				return replace_with_value(insn, 0);
			break;
		case OP_SET_GE: case OP_SET_GT:
			if (value < sign_bit(size))
				return replace_with_value(insn, 0);
			else
				return replace_with_value(insn, 1);
			break;
		}
		break;
	case OP_TRUNC:
		osize = def->orig_type->bit_size;
		switch (insn->opcode) {
		case OP_SET_EQ: case OP_SET_NE:
			if (one_use(def->target)) {
				insn->itype = def->orig_type;
				def->type = def->orig_type;
				def->size = osize;
				def->src2 = value_pseudo(bits);
				return replace_opcode(def, OP_AND);
			}
			break;
		}
		break;
	case OP_ZEXT:
		osize = def->orig_type->bit_size;
		bits = bits_mask(osize);
		if (value <= bits) {
			const struct opcode_table *op = &opcode_table[insn->opcode];
			if (op->flags & OPF_SIGNED)
				insn->opcode = op->sign;
			insn->itype = def->orig_type;
			return replace_pseudo(insn, &insn->src1, def->src);
		}
		switch (insn->opcode) {
		case OP_SET_LT: case OP_SET_LE:
			if (sign_extend(value, size) > (long long)bits)
				return replace_with_value(insn, 1);
			else
				return replace_with_value(insn, 0);
			break;
		case OP_SET_GE: case OP_SET_GT:
			if (sign_extend(value, size) > (long long)bits)
				return replace_with_value(insn, 0);
			else
				return replace_with_value(insn, 1);
			break;
		case OP_SET_B: case OP_SET_BE:
			return replace_with_value(insn, 1);
		case OP_SET_AE: case OP_SET_A:
			return replace_with_value(insn, 0);
		}
		break;
	}
	return changed;
}

static int simplify_constant_mask(struct instruction *insn, unsigned long long mask)
{
	pseudo_t old = insn->src1;
	unsigned long long omask;
	unsigned long long nmask;
	struct instruction *def;
	int osize;

	switch (DEF_OPCODE(def, old)) {
	case OP_FPCMP ... OP_BINCMP_END:
		osize = 1;
		goto oldsize;
	case OP_OR:
		return simplify_mask_or(insn, mask, def);
	case OP_LSR:
	case OP_SHL:
		return simplify_mask_shift(def, mask);
	case OP_ZEXT:
		osize = def->orig_type->bit_size;
		/* fall through */
	oldsize:
		omask = (1ULL << osize) - 1;
		nmask = mask & omask;
		if (nmask == omask)
			// the AND mask is redundant
			return replace_with_pseudo(insn, old);
		if (nmask != mask) {
			// can use a smaller mask
			insn->src2 = value_pseudo(nmask);
			return REPEAT_CSE;
		}
		break;
	}
	return 0;
}

static int simplify_const_rightadd(struct instruction *def, struct instruction *insn)
{
	unsigned size = insn->size;
	pseudo_t src2 = insn->src2;

	switch (def->opcode) {
	case OP_SUB:
		if (constant(def->src1)) { // (C - y) + D --> eval(C+D) - y
			pseudo_t val = eval_op(OP_ADD, size, def->src1, src2);
			insn->opcode = OP_SUB;
			use_pseudo(insn, def->src2, &insn->src2);
			return replace_pseudo(insn, &insn->src1, val);
		}
		break;
	}
	return 0;
}

static int simplify_constant_rightside(struct instruction *insn)
{
	long long value = insn->src2->value;
	long long sbit = 1ULL << (insn->size - 1);
	long long bits = sbit | (sbit - 1);
	int changed = 0;

	switch (insn->opcode) {
	case OP_OR:
		if ((value & bits) == bits)
			return replace_with_pseudo(insn, insn->src2);
		goto case_neutral_zero;

	case OP_XOR:
		if ((value & bits) == bits) {
			insn->opcode = OP_NOT;
			return REPEAT_CSE;
		}
		/* fallthrough */
	case_neutral_zero:
		if (!value)
			return replace_with_pseudo(insn, insn->src1);
		return 0;

	case OP_SUB:
		insn->opcode = OP_ADD;
		insn->src2 = eval_unop(OP_NEG, insn->size, insn->src2);
		changed = REPEAT_CSE;
		/* fallthrough */
	case OP_ADD:
		if (!value)
			return replace_with_pseudo(insn, insn->src1);
		if (insn->src1->type == PSEUDO_REG)	// (x # y) + z
			changed |= simplify_const_rightadd(insn->src1->def, insn);
		return changed;
	case OP_ASR:
	case OP_SHL:
	case OP_LSR:
		return simplify_shift(insn, insn->src1, value);

	case OP_MODU: case OP_MODS:
		if (value == 1)
			return replace_with_value(insn, 0);
		return 0;

	case OP_DIVU: case OP_DIVS:
	case OP_MUL:
		return simplify_mul_div(insn, value);

	case OP_AND:
		if (!value)
			return replace_with_pseudo(insn, insn->src2);
		if ((value & bits) == bits)
			return replace_with_pseudo(insn, insn->src1);
		return simplify_constant_mask(insn, value);

	case OP_SET_NE:
	case OP_SET_EQ:
		if ((changed = simplify_seteq_setne(insn, value)))
			return changed;
		/* fallthrough */
	case OP_SET_LT: case OP_SET_LE: case OP_SET_GE: case OP_SET_GT:
	case OP_SET_B:  case OP_SET_BE: case OP_SET_AE: case OP_SET_A:
		return simplify_compare_constant(insn, value);
	}
	return 0;
}

static int simplify_const_leftsub(struct instruction *insn, struct instruction *def)
{
	unsigned size = insn->size;
	pseudo_t src1 = insn->src1;

	switch (def->opcode) {
	case OP_ADD:
		if (constant(def->src2)) { // C - (y + D) --> eval(C-D) - y
			insn->src1 = eval_op(OP_SUB, size, src1, def->src2);
			return replace_pseudo(insn, &insn->src2, def->src1);
		}
		break;
	case OP_SUB:
		if (constant(def->src1)) { // C - (D - z) --> z + eval(C-D)
			pseudo_t val = eval_op(OP_SUB, size, src1, def->src1);
			insn->opcode = OP_ADD;
			use_pseudo(insn, def->src2, &insn->src1);
			return replace_pseudo(insn, &insn->src2, val);
		}
		break;
	}
	return 0;
}

static int simplify_constant_leftside(struct instruction *insn)
{
	long long value = insn->src1->value;

	switch (insn->opcode) {
	case OP_ADD: case OP_OR: case OP_XOR:
		if (!value)
			return replace_with_pseudo(insn, insn->src2);
		return 0;

	case OP_SHL:
	case OP_LSR: case OP_ASR:
	case OP_AND:
	case OP_MUL:
		if (!value)
			return replace_with_pseudo(insn, insn->src1);
		return 0;
	case OP_SUB:
		if (!value)			// (0 - x) --> -x
			return replace_with_unop(insn, OP_NEG, insn->src2);
		if (insn->src2->type == PSEUDO_REG)
			return simplify_const_leftsub(insn, insn->src2->def);
		break;
	}
	return 0;
}

static int simplify_constant_binop(struct instruction *insn)
{
	pseudo_t res = eval_insn(insn);

	if (!res)
		return 0;

	return replace_with_pseudo(insn, res);
}

static int simplify_binop_same_args(struct instruction *insn, pseudo_t arg)
{
	switch (insn->opcode) {
	case OP_SET_NE:
	case OP_SET_LT: case OP_SET_GT:
	case OP_SET_B:  case OP_SET_A:
		if (Wtautological_compare)
			warning(insn->pos, "self-comparison always evaluates to false");
	case OP_SUB:
	case OP_XOR:
		return replace_with_value(insn, 0);

	case OP_SET_EQ:
	case OP_SET_LE: case OP_SET_GE:
	case OP_SET_BE: case OP_SET_AE:
		if (Wtautological_compare)
			warning(insn->pos, "self-comparison always evaluates to true");
		return replace_with_value(insn, 1);

	case OP_AND:
	case OP_OR:
		return replace_with_pseudo(insn, arg);

	default:
		break;
	}

	return 0;
}

static int simplify_binop(struct instruction *insn)
{
	if (constant(insn->src1)) {
		if (constant(insn->src2))
			return simplify_constant_binop(insn);
		return simplify_constant_leftside(insn);
	}
	if (constant(insn->src2))
		return simplify_constant_rightside(insn);
	if (insn->src1 == insn->src2)
		return simplify_binop_same_args(insn, insn->src1);
	return 0;
}

static int switch_pseudo(struct instruction *insn1, pseudo_t *pp1, struct instruction *insn2, pseudo_t *pp2)
{
	pseudo_t p1 = *pp1, p2 = *pp2;

	use_pseudo(insn1, p2, pp1);
	use_pseudo(insn2, p1, pp2);
	remove_usage(p1, pp1);
	remove_usage(p2, pp2);
	return REPEAT_CSE;
}

///
// check if the given pseudos are in canonical order
//
// The canonical order is VOID < UNDEF < PHI < REG < ARG < SYM < VAL
// The rationale is:
//	* VALs at right (they don't need a definition)
//	* REGs at left (they need a defining instruction)
//	* SYMs & ARGs between REGs & VALs
//	* REGs & ARGs are ordered between themselves by their internal number
//	* SYMs are ordered between themselves by address
//	* VOID, UNDEF and PHI are uninteresting (but VOID should have type 0)
static int canonical_order(pseudo_t p1, pseudo_t p2)
{
	int t1 = p1->type;
	int t2 = p2->type;

	/* symbol/constants on the right */
	if (t1 < t2)
		return 1;
	if (t1 > t2)
		return 0;
	switch (t1) {
	case PSEUDO_SYM:
		return p1->sym <= p2->sym;
	case PSEUDO_REG:
	case PSEUDO_ARG:
		return p1->nr <= p2->nr;
	default:
		return 1;
	}
}

static int canonicalize_commutative(struct instruction *insn)
{
	if (canonical_order(insn->src1, insn->src2))
		return 0;

	switch_pseudo(insn, &insn->src1, insn, &insn->src2);
	return repeat_phase |= REPEAT_CSE;
}

static int canonicalize_compare(struct instruction *insn)
{
	if (canonical_order(insn->src1, insn->src2))
		return 0;

	switch_pseudo(insn, &insn->src1, insn, &insn->src2);
	insn->opcode = opcode_table[insn->opcode].swap;
	return repeat_phase |= REPEAT_CSE;
}

static inline int simple_pseudo(pseudo_t pseudo)
{
	return pseudo->type == PSEUDO_VAL || pseudo->type == PSEUDO_SYM;
}

///
// test if, in the given BB, the ordering of 2 instructions
static bool insn_before(struct basic_block *bb, struct instruction *x, struct instruction *y)
{
	struct instruction *insn;

	FOR_EACH_PTR(bb->insns, insn) {
		if (insn == x)
			return true;
		if (insn == y)
			return false;
	} END_FOR_EACH_PTR(insn);
	return false;
}

///
// check if it safe for a pseudo to be used by an instruction
static inline bool can_move_to(pseudo_t src, struct instruction *dst)
{
	struct basic_block *bbs, *bbd;
	struct instruction *def;

	if (!one_use(dst->target))
		return false;
	if (src->type != PSEUDO_REG)
		return true;

	def = src->def;
	if (dst == def)
		return false;
	bbs = def->bb;
	bbd = dst->bb;
	if (bbs == bbd)
		return insn_before(bbs, def, dst);
	else
		return domtree_dominates(bbs, bbd);
}

static int simplify_associative_binop(struct instruction *insn)
{
	struct instruction *def;
	pseudo_t pseudo = insn->src1;

	if (!simple_pseudo(insn->src2))
		return 0;
	if (pseudo->type != PSEUDO_REG)
		return 0;
	def = pseudo->def;
	if (def == insn)
		return 0;
	if (def->opcode != insn->opcode)
		return 0;
	if (!simple_pseudo(def->src2))
		return 0;
	if (constant(def->src2) && constant(insn->src2)) {
		// (x # C) # K --> x # eval(C # K)
		insn->src2 = eval_op(insn->opcode, insn->size, insn->src2, def->src2);
		return replace_pseudo(insn, &insn->src1, def->src1);
	}
	if (!one_use(def->target))
		return 0;
	switch_pseudo(def, &def->src1, insn, &insn->src2);
	return REPEAT_CSE;
}

static int simplify_add_one_side(struct instruction *insn, pseudo_t *p1, pseudo_t *p2)
{
	struct instruction *defr = NULL;
	struct instruction *def;
	pseudo_t src1 = *p1;
	pseudo_t src2 = *p2;

	switch (DEF_OPCODE(def, src1)) {
	case OP_MUL:
		if (DEF_OPCODE(defr, *p2) == OP_MUL) {
			if (defr->src2 == def->src2 && can_move_to(def->src2, defr)) {
				// ((x * z) + (y * z)) into ((x + y) * z)
				swap_insn(insn, defr, def->src1, defr->src1, def->src2);
				return REPEAT_CSE;
			}
			if (defr->src1 == def->src1 && can_move_to(def->src1, defr)) {
				// ((z * x) + (z * y)) into ((x + y) * z)
				swap_insn(insn, defr, def->src2, defr->src2, def->src1);
				return REPEAT_CSE;
			}
			if (defr->src1 == def->src2 && can_move_to(def->src1, defr)) {
				// ((x * z) + (z * y)) into ((x + y) * z)
				swap_insn(insn, defr, def->src1, defr->src2, def->src2);
				return REPEAT_CSE;
			}
		}
		break;

	case OP_NEG:				// (-x + y) --> (y - x)
		return replace_binop(insn, OP_SUB, &insn->src1, src2, &insn->src2, def->src);

	case OP_SUB:
		if (def->src2 == src2)		// (x - y) + y --> x
			return replace_with_pseudo(insn, def->src1);
		break;
	}
	return 0;
}

static int simplify_add(struct instruction *insn)
{
	return simplify_add_one_side(insn, &insn->src1, &insn->src2) ||
	       simplify_add_one_side(insn, &insn->src2, &insn->src1);
}

static int simplify_sub(struct instruction *insn)
{
	pseudo_t src1 = insn->src1;
	pseudo_t src2 = insn->src2;
	struct instruction *def;

	switch (DEF_OPCODE(def, src1)) {
	case OP_ADD:
		if (def->src1 == src2)		// (x + y) - x --> y
			return replace_with_pseudo(insn, def->src2);
		if (def->src2 == src2)		// (x + y) - y --> x
			return replace_with_pseudo(insn, def->src1);
		break;
	}

	switch (DEF_OPCODE(def, src2)) {
	case OP_ADD:
		if (src1 == def->src1)		// x - (x + z) --> -z
			return replace_with_unop(insn, OP_NEG, def->src2);
		if (src1 == def->src2)		// x - (y + x) --> -y
			return replace_with_unop(insn, OP_NEG, def->src1);
		break;
	case OP_NEG:				// (x - -y) --> (x + y)
		insn->opcode = OP_ADD;
		return replace_pseudo(insn, &insn->src2, def->src);
	}

	return 0;
}

static int simplify_compare(struct instruction *insn)
{
	pseudo_t src1 = insn->src1;
	pseudo_t src2 = insn->src2;
	struct instruction *def = NULL;
	unsigned int osize;
	pseudo_t src;

	switch (DEF_OPCODE(def, src1)) {
	case OP_SEXT: case OP_ZEXT:
		osize = def->orig_type->bit_size;
		if ((src = is_same_op(src2, def->opcode, osize))) {
			const struct opcode_table *op = &opcode_table[insn->opcode];
			if ((def->opcode == OP_ZEXT) && (op->flags & OPF_SIGNED))
				insn->opcode = op->sign;
			insn->itype = def->orig_type;
			replace_pseudo(insn, &insn->src1, def->src);
			return replace_pseudo(insn, &insn->src2, src);
		}
		break;
	}
	return 0;
}

static int simplify_and_one_side(struct instruction *insn, pseudo_t *p1, pseudo_t *p2)
{
	struct instruction *def, *defr = NULL;
	pseudo_t src1 = *p1;

	switch (DEF_OPCODE(def, src1)) {
	case OP_NOT:
		if (def->src == *p2)
			return replace_with_value(insn, 0);
		break;
	case OP_BINCMP ... OP_BINCMP_END:
		if (DEF_OPCODE(defr, *p2) == opcode_negate(def->opcode)) {
			if (def->src1 == defr->src1 && def->src2 == defr->src2)
				return replace_with_value(insn, 0);
		}
		if (def->opcode == OP_SET_GE && is_zero(def->src2)) {
			switch (DEF_OPCODE(defr, *p2)) {
			case OP_SET_LE:
				if (!is_positive(defr->src2, defr->itype->bit_size))
					break;
				// (x >= 0) && (x <= C) --> (x u<= C)
				insn->itype = defr->itype;
				replace_binop(insn, OP_SET_BE, &insn->src1, defr->src1, &insn->src2, defr->src2);
				return REPEAT_CSE;
			}
		}
		break;
	case OP_OR:
		if (DEF_OPCODE(defr, *p2) == OP_OR) {
			if (defr->src2 == def->src2 && can_move_to(def->src2, defr)) {
				// ((x | z) & (y | z)) into ((x & y) | z)
				swap_insn(insn, defr, def->src1, defr->src1, def->src2);
				return REPEAT_CSE;
			}
			if (defr->src1 == def->src1 && can_move_to(def->src1, defr)) {
				// ((z | x) & (z | y)) into ((x & y) | z)
				swap_insn(insn, defr, def->src2, defr->src2, def->src1);
				return REPEAT_CSE;
			}
			if (defr->src1 == def->src2 && can_move_to(def->src1, defr)) {
				// ((x | z) & (z | y)) into ((x & y) | z)
				swap_insn(insn, defr, def->src1, defr->src2, def->src2);
				return REPEAT_CSE;
			}
		}
		break;
	case OP_SHL: case OP_LSR: case OP_ASR:
		if (DEF_OPCODE(defr, *p2) == def->opcode && defr->src2 == def->src2) {
			if (can_move_to(def->src1, defr)) {
				// SHIFT(x, s) & SHIFT(y, s) --> SHIFT((x & y), s)
				swap_insn(insn, defr, def->src1, defr->src1, def->src2);
				return REPEAT_CSE;
			}
		}
		break;
	}
	return 0;
}

static int simplify_and(struct instruction *insn)
{
	return simplify_and_one_side(insn, &insn->src1, &insn->src2) ||
	       simplify_and_one_side(insn, &insn->src2, &insn->src1);
}

static int simplify_ior_one_side(struct instruction *insn, pseudo_t *p1, pseudo_t *p2)
{
	struct instruction *def, *defr = NULL;
	pseudo_t src1 = *p1;

	switch (DEF_OPCODE(def, src1)) {
	case OP_AND:
		if (DEF_OPCODE(defr, *p2) == OP_AND) {
			if (defr->src2 == def->src2 && can_move_to(def->src2, defr)) {
				// ((x & z) | (y & z)) into ((x | y) & z)
				swap_insn(insn, defr, def->src1, defr->src1, def->src2);
				return REPEAT_CSE;
			}
			if (defr->src1 == def->src1 && can_move_to(def->src1, defr)) {
				// ((z & x) | (z & y)) into ((x | y) & z)
				swap_insn(insn, defr, def->src2, defr->src2, def->src1);
				return REPEAT_CSE;
			}
			if (defr->src1 == def->src2 && can_move_to(def->src1, defr)) {
				// ((x & z) | (z & y)) into ((x | y) & z)
				swap_insn(insn, defr, def->src1, defr->src2, def->src2);
				return REPEAT_CSE;
			}
		}
		break;
	case OP_NOT:
		if (def->src == *p2)
			return replace_with_value(insn, bits_mask(insn->size));
		break;
	case OP_BINCMP ... OP_BINCMP_END:
		if (DEF_OPCODE(defr, *p2) == opcode_negate(def->opcode)) {
			if (def->src1 == defr->src1 && def->src2 == defr->src2)
				return replace_with_value(insn, 1);
		}
		break;
	case OP_SHL: case OP_LSR: case OP_ASR:
		if (DEF_OPCODE(defr, *p2) == def->opcode && defr->src2 == def->src2) {
			if (can_move_to(def->src1, defr)) {
				// SHIFT(x, s) | SHIFT(y, s) --> SHIFT((x | y), s)
				swap_insn(insn, defr, def->src1, defr->src1, def->src2);
				return REPEAT_CSE;
			}
		}
		break;
	}
	return 0;
}

static int simplify_ior(struct instruction *insn)
{
	return simplify_ior_one_side(insn, &insn->src1, &insn->src2) ||
	       simplify_ior_one_side(insn, &insn->src2, &insn->src1);
}

static int simplify_xor_one_side(struct instruction *insn, pseudo_t *p1, pseudo_t *p2)
{
	struct instruction *def, *defr = NULL;
	pseudo_t src1 = *p1;

	switch (DEF_OPCODE(def, src1)) {
	case OP_AND:
		if (DEF_OPCODE(defr, *p2) == OP_AND) {
			if (defr->src2 == def->src2 && can_move_to(def->src2, defr)) {
				// ((x & z) ^ (y & z)) into ((x ^ y) & z)
				swap_insn(insn, defr, def->src1, defr->src1, def->src2);
				return REPEAT_CSE;
			}
			if (defr->src1 == def->src1 && can_move_to(def->src1, defr)) {
				// ((z & x) ^ (z & y)) into ((x ^ y) & z)
				swap_insn(insn, defr, def->src2, defr->src2, def->src1);
				return REPEAT_CSE;
			}
			if (defr->src1 == def->src2 && can_move_to(def->src1, defr)) {
				// ((x & z) ^ (z & y)) into ((x ^ y) & z)
				swap_insn(insn, defr, def->src1, defr->src2, def->src2);
				return REPEAT_CSE;
			}
		}
		break;
	case OP_NOT:
		if (def->src == *p2)
			return replace_with_value(insn, bits_mask(insn->size));
		break;
	case OP_BINCMP ... OP_BINCMP_END:
		if (DEF_OPCODE(defr, *p2) == opcode_negate(def->opcode)) {
			if (def->src1 == defr->src1 && def->src2 == defr->src2)
				return replace_with_value(insn, 1);
		}
		break;
	case OP_SHL: case OP_LSR: case OP_ASR:
		if (DEF_OPCODE(defr, *p2) == def->opcode && defr->src2 == def->src2) {
			if (can_move_to(def->src1, defr)) {
				// SHIFT(x, s) ^ SHIFT(y, s) --> SHIFT((x ^ y), s)
				swap_insn(insn, defr, def->src1, defr->src1, def->src2);
				return REPEAT_CSE;
			}
		}
		break;
	}
	return 0;
}

static int simplify_xor(struct instruction *insn)
{
	return simplify_xor_one_side(insn, &insn->src1, &insn->src2) ||
	       simplify_xor_one_side(insn, &insn->src2, &insn->src1);
}

static int simplify_constant_unop(struct instruction *insn)
{
	long long val = insn->src1->value;
	long long res, mask;

	switch (insn->opcode) {
	case OP_NOT:
		res = ~val;
		break;
	case OP_NEG:
		res = -val;
		break;
	case OP_SEXT:
		mask = 1ULL << (insn->orig_type->bit_size-1);
		if (val & mask)
			val |= ~(mask | (mask-1));
		/* fall through */
	case OP_ZEXT:
	case OP_TRUNC:
		res = val;
		break;
	default:
		return 0;
	}
	mask = 1ULL << (insn->size-1);
	res &= mask | (mask-1);
	
	return replace_with_value(insn, res);
}

static int simplify_unop(struct instruction *insn)
{
	struct instruction *def;
	pseudo_t src = insn->src;

	if (constant(src))
		return simplify_constant_unop(insn);

	switch (insn->opcode) {
	case OP_NOT:
		switch (DEF_OPCODE(def, src)) {
		case OP_ADD:
			if (!constant(def->src2))
				break;
			insn->opcode = OP_SUB;	// ~(x + C) --> ~C - x
			src = eval_unop(OP_NOT, insn->size, def->src2);
			use_pseudo(insn, def->src1, &insn->src2);
			return replace_pseudo(insn, &insn->src1, src);
		case OP_NEG:
			insn->opcode = OP_SUB;	// ~(-x) --> x - 1
			insn->src2 = value_pseudo(1);
			return replace_pseudo(insn, &insn->src1, def->src);
		case OP_NOT:			// ~(~x) --> x
			return replace_with_pseudo(insn, def->src);
		case OP_SUB:
			if (!constant(def->src1))
				break;
			insn->opcode = OP_ADD;	// ~(C - x) --> x + ~C
			insn->src2 = eval_unop(OP_NOT, insn->size, def->src1);
			return replace_pseudo(insn, &insn->src1, def->src2);
		case OP_XOR:
			if (!constant(def->src2))
				break;
			insn->opcode = OP_XOR;	// ~(x ^ C) --> x ^ ~C
			insn->src2 = eval_unop(OP_NOT, insn->size, def->src2);
			return replace_pseudo(insn, &insn->src1, def->src1);
		}
		break;
	case OP_NEG:
		switch (DEF_OPCODE(def, src)) {
		case OP_ADD:
			if (!constant(def->src2))
				break;
			insn->opcode = OP_SUB;	// -(x + C) --> (-C - x)
			src = eval_unop(OP_NEG, insn->size, def->src2);
			use_pseudo(insn, def->src1, &insn->src2);
			return replace_pseudo(insn, &insn->src1, src);
		case OP_NEG:			// -(-x) --> x
			return replace_with_pseudo(insn, def->src);
		case OP_NOT:
			insn->opcode = OP_ADD;	// -(~x) --> x + 1
			insn->src2 = value_pseudo(1);
			return replace_pseudo(insn, &insn->src1, def->src);
		case OP_SUB:
			insn->opcode = OP_SUB;		// -(x - y) --> y - x
			use_pseudo(insn, def->src1, &insn->src2);
			return replace_pseudo(insn, &insn->src1, def->src2);
		}
		break;
	default:
		return 0;
	}
	return 0;
}

static int simplify_one_memop(struct instruction *insn, pseudo_t orig)
{
	pseudo_t addr = insn->src;
	pseudo_t new, off;

	if (addr->type == PSEUDO_REG) {
		struct instruction *def = addr->def;
		if (def->opcode == OP_SYMADDR && def->src) {
			kill_use(&insn->src);
			use_pseudo(insn, def->src, &insn->src);
			return REPEAT_CSE;
		}
		if (def->opcode == OP_ADD) {
			new = def->src1;
			off = def->src2;
			if (constant(off))
				goto offset;
			new = off;
			off = def->src1;
			if (constant(off))
				goto offset;
			return 0;
		}
	}
	return 0;

offset:
	/* Invalid code */
	if (new == orig || new == addr) {
		if (new == VOID)
			return 0;
		/*
		 * If some BB have been removed it is possible that this
		 * memop is in fact part of a dead BB. In this case
		 * we must not warn since nothing is wrong.
		 * If not part of a dead BB this will be redone after
		 * the BBs have been cleaned up.
		 */
		if (repeat_phase & REPEAT_CFG_CLEANUP)
			return 0;
		warning(insn->pos, "crazy programmer");
		replace_pseudo(insn, &insn->src, VOID);
		return 0;
	}
	insn->offset += off->value;
	replace_pseudo(insn, &insn->src, new);
	return REPEAT_CSE;
}

///
// simplify memops instructions
//
// :note: We walk the whole chain of adds/subs backwards.
//	That's not only more efficient, but it allows us to find loops.
static int simplify_memop(struct instruction *insn)
{
	int one, ret = 0;
	pseudo_t orig = insn->src;

	do {
		one = simplify_one_memop(insn, orig);
		ret |= one;
	} while (one);
	return ret;
}

static int simplify_cast(struct instruction *insn)
{
	unsigned long long mask;
	struct instruction *def, *def2;
	pseudo_t src = insn->src;
	pseudo_t val;
	int osize;
	int size;

	/* A cast of a constant? */
	if (constant(src))
		return simplify_constant_unop(insn);

	// can merge with the previous instruction?
	size = insn->size;
	def = src->def;
	switch (def_opcode(src)) {
	case OP_AND:
		val = def->src2;
		if (val->type != PSEUDO_VAL)
			break;
		/* A cast of a AND might be a no-op.. */
		switch (insn->opcode) {
		case OP_TRUNC:
			if (!one_use(src))
				break;
			def->opcode = OP_TRUNC;
			def->orig_type = def->type;
			def->type = insn->type;
			def->size = size;

			insn->opcode = OP_AND;
			mask = val->value;
			mask &= (1ULL << size) - 1;
			insn->src2 = value_pseudo(mask);
			return REPEAT_CSE;

		case OP_SEXT:
			if (val->value & (1 << (def->size - 1)))
				break;
			// OK, sign bit is 0
		case OP_ZEXT:
			if (!one_use(src))
				break;
			// transform:
			//	and.n	%b <- %a, M
			//	*ext.m	%c <- (n) %b
			// into:
			//	zext.m	%b <- %a
			//	and.m	%c <- %b, M
			// For ZEXT, the mask will always be small
			// enough. For SEXT, it can only be done if
			// the mask force the sign bit to 0.
			def->opcode = OP_ZEXT;
			def->orig_type = insn->orig_type;
			def->type = insn->type;
			def->size = insn->size;
			insn->opcode = OP_AND;
			insn->src2 = val;
			return REPEAT_CSE;
		}
		break;
	case OP_FPCMP ... OP_BINCMP_END:
		switch (insn->opcode) {
		case OP_SEXT:
			if (insn->size == 1)
				break;
			/* fall through */
		case OP_ZEXT:
		case OP_TRUNC:
			// simplify:
			//	setcc.n	%t <- %a, %b
			//	zext.m	%r <- (n) %t
			// into:
			//	setcc.m	%r <- %a, %b
			// and same for s/zext/trunc/
			insn->opcode = def->opcode;
			insn->itype = def->itype;
			use_pseudo(insn, def->src2, &insn->src2);
			return replace_pseudo(insn, &insn->src1, def->src1);
		}
		break;
	case OP_NOT:
		switch (insn->opcode) {
		case OP_TRUNC:
			if (one_use(src)) {
				// TRUNC(NOT(x)) --> NOT(TRUNC(x))
				insn->opcode = OP_NOT;
				def->orig_type = def->type;
				def->opcode = OP_TRUNC;
				def->type = insn->type;
				def->size = insn->size;
				return REPEAT_CSE;
			}
			break;
		}
		break;
	case OP_OR:
		switch (insn->opcode) {
		case OP_TRUNC:
			mask = bits_mask(insn->size);
			return simplify_mask_or(insn, mask, def);
		}
		break;
	case OP_LSR:
	case OP_SHL:
		if (insn->opcode != OP_TRUNC)
			break;
		mask = bits_mask(insn->size);
		return simplify_mask_shift(def, mask);
	case OP_TRUNC:
		switch (insn->opcode) {
		case OP_TRUNC:
			insn->orig_type = def->orig_type;
			return replace_pseudo(insn, &insn->src1, def->src);
		case OP_SEXT:
			if (size != def->orig_type->bit_size)
				break;
			if (DEF_OPCODE(def2, def->src) != OP_LSR)
				break;
			if (def2->src2 != value_pseudo(size - def->size))
				break;
			// SEXT(TRUNC(LSR(x, N))) --> ASR(x, N)
			insn->opcode = OP_ASR;
			insn->src2 = def2->src2;
			return replace_pseudo(insn, &insn->src1, def2->src1);
		case OP_ZEXT:
			if (size != def->orig_type->bit_size)
				break;
			insn->opcode = OP_AND;
			insn->src2 = value_pseudo((1ULL << def->size) - 1);
			return replace_pseudo(insn, &insn->src1, def->src);
		}
		break;
	case OP_ZEXT:
		switch (insn->opcode) {
		case OP_SEXT:
			insn->opcode = OP_ZEXT;
			/* fall through */
		case OP_ZEXT:
			insn->orig_type = def->orig_type;
			return replace_pseudo(insn, &insn->src, def->src);
		}
		/* fall through */
	case OP_SEXT:
		switch (insn->opcode) {
		case OP_TRUNC:
			osize = def->orig_type->bit_size;
			if (size == osize)
				return replace_with_pseudo(insn, def->src);
			if (size > osize)
				insn->opcode = def->opcode;
			insn->orig_type = def->orig_type;
			return replace_pseudo(insn, &insn->src, def->src);
		}
		switch (insn->opcode) {
		case OP_SEXT:
			insn->orig_type = def->orig_type;
			return replace_pseudo(insn, &insn->src, def->src);
		}
		break;
	}

	return 0;
}

static int simplify_select(struct instruction *insn)
{
	pseudo_t cond, src1, src2;
	struct instruction *def;

	cond = insn->src1;
	src1 = insn->src2;
	src2 = insn->src3;

	if (constant(cond))
		return replace_with_pseudo(insn, cond->value ? src1 : src2);
	if (src1 == src2)
		return replace_with_pseudo(insn, src1);

	if (constant(src1) && constant(src2)) {
		long long val1 = src1->value;
		long long val2 = src2->value;

		/* The pair 0/1 is special - replace with SETNE/SETEQ */
		if ((val1 | val2) == 1) {
			int opcode = OP_SET_EQ;
			if (val1) {
				src1 = src2;
				opcode = OP_SET_NE;
			}
			insn->opcode = opcode;
			insn->itype = insn->type;
			/* insn->src1 is already cond */
			insn->src2 = src1; /* Zero */
			return REPEAT_CSE;
		}
	}
	if (cond == src2 && is_zero(src1))			// SEL(x, 0, x) --> 0
		return replace_with_pseudo(insn, src1);
	if (cond == src1 && is_zero(src2))			// SEL(x, x, 0) --> x
		return replace_with_pseudo(insn, cond);

	switch (DEF_OPCODE(def, cond)) {
	case OP_SET_EQ:
		if (src1 == def->src1 && src2 == def->src2)
			return replace_with_pseudo(insn, src2); // SEL(x==y,x,y) --> y
		if (src2 == def->src1 && src1 == def->src2)
			return replace_with_pseudo(insn, src2); // SEL(y==x,x,y) --> y
		break;
	case OP_SET_NE:
		if (src1 == def->src1 && src2 == def->src2)
			return replace_with_pseudo(insn, src1); // SEL(x!=y,x,y) --> x
		if (src2 == def->src1 && src1 == def->src2)
			return replace_with_pseudo(insn, src1); // SEL(y!=x,x,y) --> x
		break;
	case OP_SET_LE: case OP_SET_LT:
	case OP_SET_BE: case OP_SET_B:
		if (!one_use(cond))
			break;
		// SEL(x {<,<=} y, a, b) --> SEL(x {>=,>} y, b, a)
		def->opcode = opcode_negate(def->opcode);
		return switch_pseudo(insn, &insn->src2, insn, &insn->src3);
	case OP_SET_GT:
		if (one_use(cond) && is_zero(def->src2)) {
			if (is_negate_of(src2, src1))
				// SEL(x > 0, a, -a) --> SEL(x >= 0, a, -a)
				return replace_opcode(def, OP_SET_GE);
		}
		break;
	case OP_SEL:
		if (constant(def->src2) && constant(def->src3)) {
			// Is the def of the conditional another select?
			// And if that one results in a "zero or not", use the
			// original conditional instead.
			//	SEL(SEL(x, C, 0), y, z) --> SEL(x, y, z)
			//	SEL(SEL(x, C, 0), C, 0) --> SEL(x, C, 0) == cond
			//	SEL(SEL(x, 0, C), y, z) --> SEL(x, z, y)
			//	SEL(SEL(x, C1, C2), y, z) --> y
			if (!def->src3->value) {
				if ((src1 == def->src2) && (src2 == def->src3))
					return replace_with_pseudo(insn, cond);
				return replace_pseudo(insn, &insn->cond, def->cond);
			}
			if (!def->src2->value) {
				switch_pseudo(insn, &insn->src2, insn, &insn->src3);
				return replace_pseudo(insn, &insn->cond, def->cond);
			}
			// both values must be non-zero
			return replace_with_pseudo(insn, src1);
		}
	case OP_AND:
		if (is_pow2(def->src2) && is_pow2(src1) && is_zero(src2) && insn->size == def->size && one_use(cond)) {
			unsigned s1 = log2_exact(def->src2->value);
			unsigned s2 = log2_exact(insn->src2->value);
			unsigned shift;

			if (s1 == s2)
				return replace_with_pseudo(insn, cond);

			// SEL(x & A, B, 0) --> SHIFT(x & A, S)
			insn->opcode = (s1 < s2) ? OP_SHL : OP_LSR;
			shift = (s1 < s2) ? (s2 - s1) : (s1 - s2);
			insn->src2 = value_pseudo(shift);
			return REPEAT_CSE;
		}
		break;
	}

	switch (DEF_OPCODE(def, src1)) {
	case OP_ADD: case OP_OR: case OP_XOR:
		if ((def->src1 == src2) && can_move_to(cond, def)) {
			// SEL(x, OP(y,z), y) --> OP(SEL(x, z, 0), y)
			swap_select(insn, def, cond, def->src2, value_pseudo(0), src2);
			return REPEAT_CSE;
		}
		if ((def->src2 == src2) && can_move_to(cond, def)) {
			// SEL(x, OP(z,y), y) --> OP(SEL(x, z, 0), y)
			swap_select(insn, def, cond, def->src1, value_pseudo(0), src2);
			return REPEAT_CSE;
		}
		break;
	}

	switch (DEF_OPCODE(def, src2)) {
	case OP_ADD: case OP_OR: case OP_XOR:
		if ((def->src1 == src1) && can_move_to(cond, def)) {
			// SEL(x, y, OP(y,z)) --> OP(SEL(x, 0, z), y)
			swap_select(insn, def, cond, value_pseudo(0), def->src2, src1);
			return REPEAT_CSE;
		}
		if ((def->src2 == src1) && can_move_to(cond, def)) {
			// SEL(x, y, OP(z,y)) --> OP(SEL(x, 0, z), y)
			swap_select(insn, def, cond, value_pseudo(0), def->src1, src1);
			return REPEAT_CSE;
		}
		break;
	}
	return 0;
}

static int is_in_range(pseudo_t src, long long low, long long high)
{
	long long value;

	switch (src->type) {
	case PSEUDO_VAL:
		value = src->value;
		return value >= low && value <= high;
	default:
		return 0;
	}
}

static int simplify_range(struct instruction *insn)
{
	pseudo_t src1, src2, src3;

	src1 = insn->src1;
	src2 = insn->src2;
	src3 = insn->src3;
	if (src2->type != PSEUDO_VAL || src3->type != PSEUDO_VAL)
		return 0;
	if (is_in_range(src1, src2->value, src3->value)) {
		kill_instruction(insn);
		return REPEAT_CSE;
	}
	return 0;
}

///
// simplify SET_NE/EQ $0 + BR
static int simplify_cond_branch(struct instruction *br, struct instruction *def, pseudo_t newcond)
{
	replace_pseudo(br, &br->cond, newcond);
	if (def->opcode == OP_SET_EQ) {
		struct basic_block *tmp = br->bb_true;
		br->bb_true = br->bb_false;
		br->bb_false = tmp;
	}
	return REPEAT_CSE;
}

static int simplify_branch(struct instruction *insn)
{
	pseudo_t cond = insn->cond;

	/* Constant conditional */
	if (constant(cond))
		return convert_to_jump(insn, cond->value ? insn->bb_true : insn->bb_false);

	/* Same target? */
	if (insn->bb_true == insn->bb_false)
		return convert_to_jump(insn, insn->bb_true);

	/* Conditional on a SETNE $0 or SETEQ $0 */
	if (cond->type == PSEUDO_REG) {
		struct instruction *def = cond->def;

		if (def->opcode == OP_SET_NE || def->opcode == OP_SET_EQ) {
			if (constant(def->src1) && !def->src1->value)
				return simplify_cond_branch(insn, def, def->src2);
			if (constant(def->src2) && !def->src2->value)
				return simplify_cond_branch(insn, def, def->src1);
		}
		if (def->opcode == OP_SEL) {
			if (constant(def->src2) && constant(def->src3)) {
				long long val1 = def->src2->value;
				long long val2 = def->src3->value;
				if (!val1 && !val2)
					return convert_to_jump(insn, insn->bb_false);
				if (val1 && val2)
					return convert_to_jump(insn, insn->bb_true);
				if (val2) {
					struct basic_block *tmp = insn->bb_true;
					insn->bb_true = insn->bb_false;
					insn->bb_false = tmp;
				}
				return replace_pseudo(insn, &insn->cond, def->src1);
			}
		}
		if (def->opcode == OP_SEXT || def->opcode == OP_ZEXT)
			return replace_pseudo(insn, &insn->cond, def->src);
	}
	return 0;
}

static int simplify_switch(struct instruction *insn)
{
	pseudo_t cond = insn->cond;
	long long val;
	struct multijmp *jmp;

	if (!constant(cond))
		return 0;
	val = insn->cond->value;

	FOR_EACH_PTR(insn->multijmp_list, jmp) {
		/* Default case */
		if (jmp->begin > jmp->end)
			goto found;
		if (val >= jmp->begin && val <= jmp->end)
			goto found;
	} END_FOR_EACH_PTR(jmp);
	warning(insn->pos, "Impossible case statement");
	return 0;

found:
	return convert_to_jump(insn, jmp->target);
}

static struct basic_block *is_label(pseudo_t pseudo)
{
	struct instruction *def;

	if (DEF_OPCODE(def, pseudo) != OP_LABEL)
		return NULL;
	return def->bb_true;
}

static int simplify_cgoto(struct instruction *insn)
{
	struct basic_block *target, *bb = insn->bb;
	struct basic_block *bbt, *bbf;
	struct instruction *def;
	struct multijmp *jmp;

	switch (DEF_OPCODE(def, insn->src)) {
	case OP_SEL:	// CGOTO(SEL(x, L1, L2)) --> CBR x, L1, L2
		if ((bbt = is_label(def->src2)) && (bbf = is_label(def->src3))) {
			insn->opcode = OP_CBR;
			insn->bb_true = bbt;
			insn->bb_false = bbf;
			return replace_pseudo(insn, &insn->src1, def->cond);
		}
		break;
	case OP_LABEL:
		target = def->bb_true;
		if (!target->ep)
			return 0;
		FOR_EACH_PTR(insn->multijmp_list, jmp) {
			if (jmp->target == target)
				continue;
			remove_bb_from_list(&jmp->target->parents, bb, 1);
			remove_bb_from_list(&bb->children, jmp->target, 1);
			DELETE_CURRENT_PTR(jmp);
		} END_FOR_EACH_PTR(jmp);
		kill_use(&insn->src);
		insn->opcode = OP_BR;
		insn->bb_true = target;
		return REPEAT_CSE|REPEAT_CFG_CLEANUP;
	}
	return 0;
}

static int simplify_setval(struct instruction *insn)
{
	struct expression *val = insn->val;

	switch (val->type) {
	case EXPR_LABEL:
		insn->opcode = OP_LABEL;
		insn->bb_true = val->symbol->bb_target;
		return REPEAT_CSE;
	default:
		break;
	}
	return 0;
}

int simplify_instruction(struct instruction *insn)
{
	unsigned flags;
	int changed = 0;

	flags = opcode_table[insn->opcode].flags;
	if (flags & OPF_TARGET) {
		if (!has_users(insn->target))
			return kill_instruction(insn);
	}
	if (flags & OPF_COMMU)
		canonicalize_commutative(insn) ;
	if (flags & OPF_COMPARE)
		canonicalize_compare(insn) ;
	if (flags & OPF_BINOP) {
		if ((changed = simplify_binop(insn)))
			return changed;
	}
	if (flags & OPF_ASSOC) {
		if ((changed = simplify_associative_binop(insn)))
			return changed;
	}
	if (flags & OPF_UNOP) {
		if ((changed = simplify_unop(insn)))
			return changed;
	}

	switch (insn->opcode) {
	case OP_ADD: return simplify_add(insn);
	case OP_SUB: return simplify_sub(insn);
	case OP_AND: return simplify_and(insn);
	case OP_OR:  return simplify_ior(insn);
	case OP_XOR: return simplify_xor(insn);
	case OP_MUL:
	case OP_SHL:
	case OP_LSR:
	case OP_ASR:
	case OP_NOT:
	case OP_NEG:
	case OP_DIVU:
	case OP_DIVS:
	case OP_MODU:
	case OP_MODS:
		break;
	case OP_BINCMP ... OP_BINCMP_END:
		return simplify_compare(insn);
	case OP_LOAD:
	case OP_STORE:
		return simplify_memop(insn);
	case OP_SYMADDR:
		return replace_with_pseudo(insn, insn->src);
	case OP_SEXT: case OP_ZEXT:
	case OP_TRUNC:
		return simplify_cast(insn);
	case OP_FNEG:
	case OP_FCVTU: case OP_FCVTS:
	case OP_UCVTF: case OP_SCVTF:
	case OP_FCVTF:
	case OP_PTRCAST:
		break;
	case OP_UTPTR:
	case OP_PTRTU:
		return replace_with_pseudo(insn, insn->src);
	case OP_SLICE:
		break;
	case OP_SETVAL:
		return simplify_setval(insn);
	case OP_LABEL:
	case OP_SETFVAL:
		break;
	case OP_PHI:
		return clean_up_phi(insn);
	case OP_PHISOURCE:
		break;
	case OP_SEL:
		return simplify_select(insn);
	case OP_CBR:
		return simplify_branch(insn);
	case OP_SWITCH:
		return simplify_switch(insn);
	case OP_COMPUTEDGOTO:
		return simplify_cgoto(insn);
	case OP_RANGE:
		return simplify_range(insn);
	case OP_FADD:
	case OP_FSUB:
	case OP_FMUL:
	case OP_FDIV:
		break;
	}
	return 0;
}
