| /* | 
 |  * Simplify - do instruction simplification before CSE | 
 |  * | 
 |  * Copyright (C) 2004 Linus Torvalds | 
 |  */ | 
 |  | 
 | #include <assert.h> | 
 |  | 
 | #include "parse.h" | 
 | #include "expression.h" | 
 | #include "linearize.h" | 
 | #include "flow.h" | 
 |  | 
 | /* 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); | 
 | } | 
 |  | 
 | static void clear_phi(struct instruction *insn) | 
 | { | 
 | 	pseudo_t phi; | 
 |  | 
 | 	insn->bb = NULL; | 
 | 	FOR_EACH_PTR(insn->phi_list, phi) { | 
 | 		*THIS_ADDRESS(phi) = VOID; | 
 | 	} END_FOR_EACH_PTR(phi); | 
 | } | 
 |  | 
 | static int if_convert_phi(struct instruction *insn) | 
 | { | 
 | 	pseudo_t array[3]; | 
 | 	struct basic_block *parents[3]; | 
 | 	struct basic_block *bb, *bb1, *bb2, *source; | 
 | 	struct instruction *br; | 
 | 	pseudo_t p1, p2; | 
 |  | 
 | 	bb = insn->bb; | 
 | 	if (linearize_ptr_list((struct ptr_list *)insn->phi_list, (void **)array, 3) != 2) | 
 | 		return 0; | 
 | 	if (linearize_ptr_list((struct ptr_list *)bb->parents, (void **)parents, 3) != 2) | 
 | 		return 0; | 
 | 	p1 = array[0]->def->src1; | 
 | 	bb1 = array[0]->def->bb; | 
 | 	p2 = array[1]->def->src1; | 
 | 	bb2 = array[1]->def->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_BR) | 
 | 		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); | 
 | 	clear_phi(insn); | 
 | 	return REPEAT_CSE; | 
 | } | 
 |  | 
 | static int clean_up_phi(struct instruction *insn) | 
 | { | 
 | 	pseudo_t phi; | 
 | 	struct instruction *last; | 
 | 	int same; | 
 |  | 
 | 	last = NULL; | 
 | 	same = 1; | 
 | 	FOR_EACH_PTR(insn->phi_list, phi) { | 
 | 		struct instruction *def; | 
 | 		if (phi == VOID) | 
 | 			continue; | 
 | 		def = phi->def; | 
 | 		if (def->src1 == VOID || !def->bb) | 
 | 			continue; | 
 | 		if (last) { | 
 | 			if (last->src1 != def->src1) | 
 | 				same = 0; | 
 | 			continue; | 
 | 		} | 
 | 		last = def; | 
 | 	} END_FOR_EACH_PTR(phi); | 
 |  | 
 | 	if (same) { | 
 | 		pseudo_t pseudo = last ? last->src1 : VOID; | 
 | 		convert_instruction_target(insn, pseudo); | 
 | 		clear_phi(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) { | 
 | 			DELETE_CURRENT_PTR(pu); | 
 | 			if (!--count) | 
 | 				goto out; | 
 | 		} | 
 | 	} END_FOR_EACH_PTR(pu); | 
 | 	assert(count <= 0); | 
 | out: | 
 | 	pack_ptr_list((struct ptr_list **)list); | 
 | 	return count; | 
 | } | 
 |  | 
 | static inline void remove_usage(pseudo_t p, pseudo_t *usep) | 
 | { | 
 | 	if (has_use_list(p)) { | 
 | 		delete_pseudo_user_list_entry(&p->users, usep, 1); | 
 | 		if (!p->users) | 
 | 			kill_instruction(p->def); | 
 | 	} | 
 | } | 
 |  | 
 | void kill_use(pseudo_t *usep) | 
 | { | 
 | 	if (usep) { | 
 | 		pseudo_t p = *usep; | 
 | 		*usep = VOID; | 
 | 		remove_usage(p, usep); | 
 | 	} | 
 | } | 
 |  | 
 | void kill_instruction(struct instruction *insn) | 
 | { | 
 | 	if (!insn || !insn->bb) | 
 | 		return; | 
 |  | 
 | 	switch (insn->opcode) { | 
 | 	case OP_BINARY ... OP_BINCMP_END: | 
 | 		insn->bb = NULL; | 
 | 		kill_use(&insn->src1); | 
 | 		kill_use(&insn->src2); | 
 | 		repeat_phase |= REPEAT_CSE; | 
 | 		return; | 
 |  | 
 | 	case OP_NOT: case OP_NEG: | 
 | 		insn->bb = NULL; | 
 | 		kill_use(&insn->src1); | 
 | 		repeat_phase |= REPEAT_CSE; | 
 | 		return; | 
 |  | 
 | 	case OP_PHI: | 
 | 		insn->bb = NULL; | 
 | 		repeat_phase |= REPEAT_CSE; | 
 | 		return; | 
 |  | 
 | 	case OP_SYMADDR: | 
 | 		insn->bb = NULL; | 
 | 		repeat_phase |= REPEAT_CSE | REPEAT_SYMBOL_CLEANUP; | 
 | 		return; | 
 |  | 
 | 	case OP_RANGE: | 
 | 		insn->bb = NULL; | 
 | 		repeat_phase |= REPEAT_CSE; | 
 | 		kill_use(&insn->src1); | 
 | 		kill_use(&insn->src2); | 
 | 		kill_use(&insn->src3); | 
 | 		return; | 
 | 	case OP_BR: | 
 | 		insn->bb = NULL; | 
 | 		repeat_phase |= REPEAT_CSE; | 
 | 		if (insn->cond) | 
 | 			kill_use(&insn->cond); | 
 | 		return; | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 |  * Kill trivially dead instructions | 
 |  */ | 
 | static int dead_insn(struct instruction *insn, pseudo_t *src1, pseudo_t *src2, pseudo_t *src3) | 
 | { | 
 | 	struct pseudo_user *pu; | 
 | 	FOR_EACH_PTR(insn->target->users, pu) { | 
 | 		if (*pu->userp != VOID) | 
 | 			return 0; | 
 | 	} END_FOR_EACH_PTR(pu); | 
 |  | 
 | 	insn->bb = NULL; | 
 | 	kill_use(src1); | 
 | 	kill_use(src2); | 
 | 	kill_use(src3); | 
 | 	return REPEAT_CSE; | 
 | } | 
 |  | 
 | static inline int constant(pseudo_t pseudo) | 
 | { | 
 | 	return pseudo->type == PSEUDO_VAL; | 
 | } | 
 |  | 
 | static int replace_with_pseudo(struct instruction *insn, pseudo_t pseudo) | 
 | { | 
 | 	convert_instruction_target(insn, pseudo); | 
 | 	insn->bb = NULL; | 
 | 	return REPEAT_CSE; | 
 | } | 
 |  | 
 | 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 logical '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_CAST && 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 int simplify_asr(struct instruction *insn, pseudo_t pseudo, long long value) | 
 | { | 
 | 	unsigned int size = operand_size(insn, pseudo); | 
 |  | 
 | 	if (value >= size) { | 
 | 		warning(insn->pos, "right shift by bigger than source value"); | 
 | 		return replace_with_pseudo(insn, value_pseudo(0)); | 
 | 	} | 
 | 	if (!value) | 
 | 		return replace_with_pseudo(insn, pseudo); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int simplify_constant_rightside(struct instruction *insn) | 
 | { | 
 | 	long long value = insn->src2->value; | 
 |  | 
 | 	switch (insn->opcode) { | 
 | 	case OP_SUB: | 
 | 		if (value) { | 
 | 			insn->opcode = OP_ADD; | 
 | 			insn->src2 = value_pseudo(-value); | 
 | 			return REPEAT_CSE; | 
 | 		} | 
 | 	/* Fall through */ | 
 | 	case OP_ADD: | 
 | 	case OP_OR: case OP_XOR: | 
 | 	case OP_OR_BOOL: | 
 | 	case OP_SHL: | 
 | 	case OP_LSR: | 
 | 		if (!value) | 
 | 			return replace_with_pseudo(insn, insn->src1); | 
 | 		return 0; | 
 | 	case OP_ASR: | 
 | 		return simplify_asr(insn, insn->src1, value); | 
 |  | 
 | 	case OP_MULU: case OP_MULS: | 
 | 	case OP_AND_BOOL: | 
 | 		if (value == 1) | 
 | 			return replace_with_pseudo(insn, insn->src1); | 
 | 	/* Fall through */ | 
 | 	case OP_AND: | 
 | 		if (!value) | 
 | 			return replace_with_pseudo(insn, insn->src2); | 
 | 		return 0; | 
 | 	} | 
 | 	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_MULU: case OP_MULS: | 
 | 		if (!value) | 
 | 			return replace_with_pseudo(insn, insn->src1); | 
 | 		return 0; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int simplify_constant_binop(struct instruction *insn) | 
 | { | 
 | 	/* FIXME! Verify signs and sizes!! */ | 
 | 	long long left = insn->src1->value; | 
 | 	long long right = insn->src2->value; | 
 | 	unsigned long long ul, ur; | 
 | 	long long res, mask, bits; | 
 |  | 
 | 	mask = 1ULL << (insn->size-1); | 
 | 	bits = mask | (mask-1); | 
 |  | 
 | 	if (left & mask) | 
 | 		left |= ~bits; | 
 | 	if (right & mask) | 
 | 		right |= ~bits; | 
 | 	ul = left & bits; | 
 | 	ur = right & bits; | 
 |  | 
 | 	switch (insn->opcode) { | 
 | 	case OP_ADD: | 
 | 		res = left + right; | 
 | 		break; | 
 | 	case OP_SUB: | 
 | 		res = left - right; | 
 | 		break; | 
 | 	case OP_MULU: | 
 | 		res = ul * ur; | 
 | 		break; | 
 | 	case OP_MULS: | 
 | 		res = left * right; | 
 | 		break; | 
 | 	case OP_DIVU: | 
 | 		if (!ur) | 
 | 			return 0; | 
 | 		res = ul / ur; | 
 | 		break; | 
 | 	case OP_DIVS: | 
 | 		if (!right) | 
 | 			return 0; | 
 | 		res = left / right; | 
 | 		break; | 
 | 	case OP_MODU: | 
 | 		if (!ur) | 
 | 			return 0; | 
 | 		res = ul % ur; | 
 | 		break; | 
 | 	case OP_MODS: | 
 | 		if (!right) | 
 | 			return 0; | 
 | 		res = left % right; | 
 | 		break; | 
 | 	case OP_SHL: | 
 | 		res = left << right; | 
 | 		break; | 
 | 	case OP_LSR: | 
 | 		res = ul >> ur; | 
 | 		break; | 
 | 	case OP_ASR: | 
 | 		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; | 
 | 	case OP_AND_BOOL: | 
 | 		res = left && right; | 
 | 		break; | 
 | 	case OP_OR_BOOL: | 
 | 		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 0; | 
 | 	} | 
 | 	res &= bits; | 
 |  | 
 | 	replace_with_pseudo(insn, value_pseudo(res)); | 
 | 	return REPEAT_CSE; | 
 | } | 
 |  | 
 | static int simplify_binop(struct instruction *insn) | 
 | { | 
 | 	if (dead_insn(insn, &insn->src1, &insn->src2, NULL)) | 
 | 		return REPEAT_CSE; | 
 | 	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); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void 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); | 
 | } | 
 |  | 
 | static int canonical_order(pseudo_t p1, pseudo_t p2) | 
 | { | 
 | 	/* symbol/constants on the right */ | 
 | 	if (p1->type == PSEUDO_VAL) | 
 | 		return p2->type == PSEUDO_VAL; | 
 |  | 
 | 	if (p1->type == PSEUDO_SYM) | 
 | 		return p2->type == PSEUDO_SYM || p2->type == PSEUDO_VAL; | 
 |  | 
 | 	return 1; | 
 | } | 
 |  | 
 | static int simplify_commutative_binop(struct instruction *insn) | 
 | { | 
 | 	if (!canonical_order(insn->src1, insn->src2)) { | 
 | 		switch_pseudo(insn, &insn->src1, insn, &insn->src2); | 
 | 		return REPEAT_CSE; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | static inline int simple_pseudo(pseudo_t pseudo) | 
 | { | 
 | 	return pseudo->type == PSEUDO_VAL || pseudo->type == PSEUDO_SYM; | 
 | } | 
 |  | 
 | 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 (ptr_list_size((struct ptr_list *)def->target->users) != 1) | 
 | 		return 0; | 
 | 	switch_pseudo(def, &def->src1, insn, &insn->src2); | 
 | 	return REPEAT_CSE; | 
 | } | 
 |  | 
 | 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; | 
 | 	default: | 
 | 		return 0; | 
 | 	} | 
 | 	mask = 1ULL << (insn->size-1); | 
 | 	res &= mask | (mask-1); | 
 | 	 | 
 | 	replace_with_pseudo(insn, value_pseudo(res)); | 
 | 	return REPEAT_CSE; | 
 | } | 
 |  | 
 | static int simplify_unop(struct instruction *insn) | 
 | { | 
 | 	if (dead_insn(insn, &insn->src1, NULL, NULL)) | 
 | 		return REPEAT_CSE; | 
 | 	if (constant(insn->src1)) | 
 | 		return simplify_constant_unop(insn); | 
 | 	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 | REPEAT_SYMBOL_CLEANUP; | 
 | 		} | 
 | 		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) { | 
 | 		if (new == VOID) | 
 | 			return 0; | 
 | 		new = VOID; | 
 | 		warning(insn->pos, "crazy programmer"); | 
 | 	} | 
 | 	insn->offset += off->value; | 
 | 	use_pseudo(insn, new, &insn->src); | 
 | 	remove_usage(addr, &insn->src); | 
 | 	return REPEAT_CSE | REPEAT_SYMBOL_CLEANUP; | 
 | } | 
 |  | 
 | /* | 
 |  * 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 long long get_cast_value(long long val, int old_size, int new_size, int sign) | 
 | { | 
 | 	long long mask; | 
 |  | 
 | 	if (sign && new_size > old_size) { | 
 | 		mask = 1 << (old_size-1); | 
 | 		if (val & mask) | 
 | 			val |= ~(mask | (mask-1)); | 
 | 	} | 
 | 	mask = 1 << (new_size-1); | 
 | 	return val & (mask | (mask-1)); | 
 | } | 
 |  | 
 | static int simplify_cast(struct instruction *insn) | 
 | { | 
 | 	struct symbol *orig_type; | 
 | 	int orig_size, size; | 
 | 	pseudo_t src; | 
 |  | 
 | 	if (dead_insn(insn, &insn->src, NULL, NULL)) | 
 | 		return REPEAT_CSE; | 
 |  | 
 | 	orig_type = insn->orig_type; | 
 | 	if (!orig_type) | 
 | 		return 0; | 
 | 	orig_size = orig_type->bit_size; | 
 | 	size = insn->size; | 
 | 	src = insn->src; | 
 |  | 
 | 	/* A cast of a constant? */ | 
 | 	if (constant(src)) { | 
 | 		int sign = orig_type->ctype.modifiers & MOD_SIGNED; | 
 | 		long long val = get_cast_value(src->value, orig_size, size, sign); | 
 | 		src = value_pseudo(val); | 
 | 		goto simplify; | 
 | 	} | 
 |  | 
 | 	/* A cast of a "and" might be a no-op.. */ | 
 | 	if (src->type == PSEUDO_REG) { | 
 | 		struct instruction *def = src->def; | 
 | 		if (def->opcode == OP_AND && def->size >= size) { | 
 | 			pseudo_t val = def->src2; | 
 | 			if (val->type == PSEUDO_VAL) { | 
 | 				unsigned long long value = val->value; | 
 | 				if (!(value >> (size-1))) | 
 | 					goto simplify; | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if (size == orig_size) { | 
 | 		int op = (orig_type->ctype.modifiers & MOD_SIGNED) ? OP_SCAST : OP_CAST; | 
 | 		if (insn->opcode == op) | 
 | 			goto simplify; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 |  | 
 | simplify: | 
 | 	return replace_with_pseudo(insn, src); | 
 | } | 
 |  | 
 | static int simplify_select(struct instruction *insn) | 
 | { | 
 | 	pseudo_t cond, src1, src2; | 
 |  | 
 | 	if (dead_insn(insn, &insn->src1, &insn->src2, &insn->src3)) | 
 | 		return REPEAT_CSE; | 
 |  | 
 | 	cond = insn->src1; | 
 | 	src1 = insn->src2; | 
 | 	src2 = insn->src3; | 
 | 	if (constant(cond) || src1 == src2) { | 
 | 		pseudo_t *kill, take; | 
 | 		kill_use(&insn->src1); | 
 | 		take = cond->value ? src1 : src2; | 
 | 		kill = cond->value ? &insn->src3 : &insn->src2; | 
 | 		kill_use(kill); | 
 | 		replace_with_pseudo(insn, take); | 
 | 		return REPEAT_CSE; | 
 | 	} | 
 | 	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->src1 is already cond */ | 
 | 			insn->src2 = src1; /* Zero */ | 
 | 			return REPEAT_CSE; | 
 | 		} | 
 | 	} | 
 | 	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, pseudo_t cond, struct instruction *def, pseudo_t *pp) | 
 | { | 
 | 	use_pseudo(br, *pp, &br->cond); | 
 | 	remove_usage(cond, &br->cond); | 
 | 	if (def->opcode == OP_SET_EQ) { | 
 | 		struct basic_block *true = br->bb_true; | 
 | 		struct basic_block *false = br->bb_false; | 
 | 		br->bb_false = true; | 
 | 		br->bb_true = false; | 
 | 	} | 
 | 	return REPEAT_CSE; | 
 | } | 
 |  | 
 | static int simplify_branch(struct instruction *insn) | 
 | { | 
 | 	pseudo_t cond = insn->cond; | 
 |  | 
 | 	if (!cond) | 
 | 		return 0; | 
 |  | 
 | 	/* Constant conditional */ | 
 | 	if (constant(cond)) { | 
 | 		insert_branch(insn->bb, insn, cond->value ? insn->bb_true : insn->bb_false); | 
 | 		return REPEAT_CSE; | 
 | 	} | 
 |  | 
 | 	/* Same target? */ | 
 | 	if (insn->bb_true == insn->bb_false) { | 
 | 		struct basic_block *bb = insn->bb; | 
 | 		struct basic_block *target = insn->bb_false; | 
 | 		remove_bb_from_list(&target->parents, bb, 1); | 
 | 		remove_bb_from_list(&bb->children, target, 1); | 
 | 		insn->bb_false = NULL; | 
 | 		kill_use(&insn->cond); | 
 | 		insn->cond = NULL; | 
 | 		return REPEAT_CSE; | 
 | 	} | 
 |  | 
 | 	/* 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, cond, def, &def->src2); | 
 | 			if (constant(def->src2) && !def->src2->value) | 
 | 				return simplify_cond_branch(insn, cond, 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) { | 
 | 					insert_branch(insn->bb, insn, insn->bb_false); | 
 | 					return REPEAT_CSE; | 
 | 				} | 
 | 				if (val1 && val2) { | 
 | 					insert_branch(insn->bb, insn, insn->bb_true); | 
 | 					return REPEAT_CSE; | 
 | 				} | 
 | 				if (val2) { | 
 | 					struct basic_block *true = insn->bb_true; | 
 | 					struct basic_block *false = insn->bb_false; | 
 | 					insn->bb_false = true; | 
 | 					insn->bb_true = false; | 
 | 				} | 
 | 				use_pseudo(insn, def->src1, &insn->cond); | 
 | 				remove_usage(cond, &insn->cond); | 
 | 				return REPEAT_CSE; | 
 | 			} | 
 | 		} | 
 | 		if (def->opcode == OP_CAST || def->opcode == OP_SCAST) { | 
 | 			int orig_size = def->orig_type ? def->orig_type->bit_size : 0; | 
 | 			if (def->size > orig_size) { | 
 | 				use_pseudo(insn, def->src, &insn->cond); | 
 | 				remove_usage(cond, &insn->cond); | 
 | 				return REPEAT_CSE; | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	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: | 
 | 	insert_branch(insn->bb, insn, jmp->target); | 
 | 	return REPEAT_CSE; | 
 | } | 
 |  | 
 | int simplify_instruction(struct instruction *insn) | 
 | { | 
 | 	if (!insn->bb) | 
 | 		return 0; | 
 | 	switch (insn->opcode) { | 
 | 	case OP_ADD: case OP_MULS: | 
 | 	case OP_AND: case OP_OR: case OP_XOR: | 
 | 	case OP_AND_BOOL: case OP_OR_BOOL: | 
 | 		if (simplify_binop(insn)) | 
 | 			return REPEAT_CSE; | 
 | 		if (simplify_commutative_binop(insn)) | 
 | 			return REPEAT_CSE; | 
 | 		return simplify_associative_binop(insn); | 
 |  | 
 | 	case OP_MULU: | 
 | 	case OP_SET_EQ: case OP_SET_NE: | 
 | 		if (simplify_binop(insn)) | 
 | 			return REPEAT_CSE; | 
 | 		return simplify_commutative_binop(insn); | 
 |  | 
 | 	case OP_SUB: | 
 | 	case OP_DIVU: case OP_DIVS: | 
 | 	case OP_MODU: case OP_MODS: | 
 | 	case OP_SHL: | 
 | 	case OP_LSR: case OP_ASR: | 
 | 	case OP_SET_LE: case OP_SET_GE: | 
 | 	case OP_SET_LT: case OP_SET_GT: | 
 | 	case OP_SET_B:  case OP_SET_A: | 
 | 	case OP_SET_BE: case OP_SET_AE: | 
 | 		return simplify_binop(insn); | 
 |  | 
 | 	case OP_NOT: case OP_NEG: | 
 | 		return simplify_unop(insn); | 
 | 	case OP_LOAD: case OP_STORE: | 
 | 		return simplify_memop(insn); | 
 | 	case OP_SYMADDR: | 
 | 		if (dead_insn(insn, NULL, NULL, NULL)) | 
 | 			return REPEAT_CSE | REPEAT_SYMBOL_CLEANUP; | 
 | 		return replace_with_pseudo(insn, insn->symbol); | 
 | 	case OP_CAST: | 
 | 	case OP_SCAST: | 
 | 	case OP_FPCAST: | 
 | 	case OP_PTRCAST: | 
 | 		return simplify_cast(insn); | 
 | 	case OP_PHI: | 
 | 		if (dead_insn(insn, NULL, NULL, NULL)) { | 
 | 			clear_phi(insn); | 
 | 			return REPEAT_CSE; | 
 | 		} | 
 | 		return clean_up_phi(insn); | 
 | 	case OP_PHISOURCE: | 
 | 		if (dead_insn(insn, &insn->phi_src, NULL, NULL)) | 
 | 			return REPEAT_CSE; | 
 | 		break; | 
 | 	case OP_SEL: | 
 | 		return simplify_select(insn); | 
 | 	case OP_BR: | 
 | 		return simplify_branch(insn); | 
 | 	case OP_SWITCH: | 
 | 		return simplify_switch(insn); | 
 | 	case OP_RANGE: | 
 | 		return simplify_range(insn); | 
 | 	} | 
 | 	return 0; | 
 | } |