+static ir_node *transform_node_Proj_Cmp(ir_node *proj) {
+ ir_node *n = get_Proj_pred(proj);
+ ir_node *left = get_Cmp_left(n);
+ ir_node *right = get_Cmp_right(n);
+ tarval *tv = NULL;
+ int changed = 0;
+ ir_mode *mode = NULL;
+ long proj_nr = get_Proj_proj(proj);
+
+ /* we can evaluate some cases directly */
+ switch (proj_nr) {
+ case pn_Cmp_False:
+ return new_Const(mode_b, get_tarval_b_false());
+ case pn_Cmp_True:
+ return new_Const(mode_b, get_tarval_b_true());
+ case pn_Cmp_Leg:
+ if (!mode_is_float(get_irn_mode(left)))
+ return new_Const(mode_b, get_tarval_b_true());
+ break;
+ default:
+ break;
+ }
+
+ /* remove Casts */
+ if (is_Cast(left))
+ left = get_Cast_op(left);
+ if (is_Cast(right))
+ right = get_Cast_op(right);
+
+ /* Remove unnecessary conversions */
+ /* TODO handle constants */
+ if (is_Conv(left) && is_Conv(right)) {
+ ir_mode *mode = get_irn_mode(left);
+ ir_node *op_left = get_Conv_op(left);
+ ir_node *op_right = get_Conv_op(right);
+ ir_mode *mode_left = get_irn_mode(op_left);
+ ir_mode *mode_right = get_irn_mode(op_right);
+
+ if (smaller_mode(mode_left, mode) && smaller_mode(mode_right, mode)) {
+ ir_graph *irg = current_ir_graph;
+ ir_node *block = get_nodes_block(n);
+
+ if (mode_left == mode_right) {
+ left = op_left;
+ right = op_right;
+ changed |= 1;
+ DBG_OPT_ALGSIM0(n, n, FS_OPT_CMP_CONV_CONV);
+ } else if (smaller_mode(mode_left, mode_right)) {
+ left = new_r_Conv(irg, block, op_left, mode_right);
+ right = op_right;
+ changed |= 1;
+ DBG_OPT_ALGSIM0(n, n, FS_OPT_CMP_CONV);
+ } else if (smaller_mode(mode_right, mode_left)) {
+ left = op_left;
+ right = new_r_Conv(irg, block, op_right, mode_left);
+ changed |= 1;
+ DBG_OPT_ALGSIM0(n, n, FS_OPT_CMP_CONV);
+ }
+ }
+ }
+
+ /* remove operation of both sides if possible */
+ if (proj_nr == pn_Cmp_Eq || proj_nr == pn_Cmp_Lg) {
+ /*
+ * The following operations are NOT safe for floating point operations, for instance
+ * 1.0 + inf == 2.0 + inf, =/=> x == y
+ */
+ if (mode_is_int(get_irn_mode(left))) {
+ unsigned lop = get_irn_opcode(left);
+
+ if (lop == get_irn_opcode(right)) {
+ ir_node *ll, *lr, *rl, *rr;
+
+ /* same operation on both sides, try to remove */
+ switch (lop) {
+ case iro_Not:
+ case iro_Minus:
+ /* ~a CMP ~b => a CMP b, -a CMP -b ==> a CMP b */
+ left = get_unop_op(left);
+ right = get_unop_op(right);
+ changed |= 1;
+ DBG_OPT_ALGSIM0(n, n, FS_OPT_CMP_OP_OP);
+ break;
+ case iro_Add:
+ ll = get_Add_left(left);
+ lr = get_Add_right(left);
+ rl = get_Add_left(right);
+ rr = get_Add_right(right);
+
+ if (ll == rl) {
+ /* X + a CMP X + b ==> a CMP b */
+ left = lr;
+ right = rr;
+ changed |= 1;
+ DBG_OPT_ALGSIM0(n, n, FS_OPT_CMP_OP_OP);
+ } else if (ll == rr) {
+ /* X + a CMP b + X ==> a CMP b */
+ left = lr;
+ right = rl;
+ changed |= 1;
+ DBG_OPT_ALGSIM0(n, n, FS_OPT_CMP_OP_OP);
+ } else if (lr == rl) {
+ /* a + X CMP X + b ==> a CMP b */
+ left = ll;
+ right = rr;
+ changed |= 1;
+ DBG_OPT_ALGSIM0(n, n, FS_OPT_CMP_OP_OP);
+ } else if (lr == rr) {
+ /* a + X CMP b + X ==> a CMP b */
+ left = ll;
+ right = rl;
+ changed |= 1;
+ DBG_OPT_ALGSIM0(n, n, FS_OPT_CMP_OP_OP);
+ }
+ break;
+ case iro_Sub:
+ ll = get_Sub_left(left);
+ lr = get_Sub_right(left);
+ rl = get_Sub_left(right);
+ rr = get_Sub_right(right);
+
+ if (ll == rl) {
+ /* X - a CMP X - b ==> a CMP b */
+ left = lr;
+ right = rr;
+ changed |= 1;
+ DBG_OPT_ALGSIM0(n, n, FS_OPT_CMP_OP_OP);
+ } else if (lr == rr) {
+ /* a - X CMP b - X ==> a CMP b */
+ left = ll;
+ right = rl;
+ changed |= 1;
+ DBG_OPT_ALGSIM0(n, n, FS_OPT_CMP_OP_OP);
+ }
+ break;
+ case iro_Rot:
+ if (get_Rot_right(left) == get_Rot_right(right)) {
+ /* a ROT X CMP b ROT X ==> a CMP b */
+ left = get_Rot_left(left);
+ right = get_Rot_left(right);
+ changed |= 1;
+ DBG_OPT_ALGSIM0(n, n, FS_OPT_CMP_OP_OP);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* X+A == A, A+X == A, A-X == A -> X == 0 */
+ if (is_Add(left) || is_Sub(left)) {
+ ir_node *ll = get_binop_left(left);
+ ir_node *lr = get_binop_right(left);
+
+ if (lr == right && is_Add(left)) {
+ ir_node *tmp = ll;
+ ll = lr;
+ lr = tmp;
+ }
+ if (ll == right) {
+ left = lr;
+ right = create_zero_const(get_irn_mode(left));
+ changed |= 1;
+ DBG_OPT_ALGSIM0(n, n, FS_OPT_CMP_OP_OP);
+ }
+ }
+ if (is_Add(right) || is_Sub(right)) {
+ ir_node *rl = get_binop_left(right);
+ ir_node *rr = get_binop_right(right);
+
+ if (rr == left && is_Add(right)) {
+ ir_node *tmp = rl;
+ rl = rr;
+ rr = tmp;
+ }
+ if (rl == left) {
+ left = rr;
+ right = create_zero_const(get_irn_mode(left));
+ changed |= 1;
+ DBG_OPT_ALGSIM0(n, n, FS_OPT_CMP_OP_OP);
+ }
+ }
+ } /* mode_is_int(...) */
+ } /* proj_nr == pn_Cmp_Eq || proj_nr == pn_Cmp_Lg */
+
+ /* replace mode_b compares with ands/ors */
+ if (get_irn_mode(left) == mode_b) {
+ ir_graph *irg = current_ir_graph;
+ ir_node *block = get_nodes_block(n);
+ ir_node *bres;
+
+ switch (proj_nr) {
+ case pn_Cmp_Le: bres = new_r_Or( irg, block, new_r_Not(irg, block, left, mode_b), right, mode_b); break;
+ case pn_Cmp_Lt: bres = new_r_And(irg, block, new_r_Not(irg, block, left, mode_b), right, mode_b); break;
+ case pn_Cmp_Ge: bres = new_r_Or( irg, block, left, new_r_Not(irg, block, right, mode_b), mode_b); break;
+ case pn_Cmp_Gt: bres = new_r_And(irg, block, left, new_r_Not(irg, block, right, mode_b), mode_b); break;
+ case pn_Cmp_Lg: bres = new_r_Eor(irg, block, left, right, mode_b); break;
+ case pn_Cmp_Eq: bres = new_r_Not(irg, block, new_r_Eor(irg, block, left, right, mode_b), mode_b); break;
+ default: bres = NULL;
+ }
+ if (bres) {
+ DBG_OPT_ALGSIM0(n, bres, FS_OPT_CMP_TO_BOOL);
+ return bres;
+ }
+ }
+
+ if (!get_opt_reassociation())
+ return proj;
+
+ /*
+ * First step: normalize the compare op
+ * by placing the constant on the right side
+ * or moving the lower address node to the left.
+ */
+ if (!operands_are_normalized(left, right)) {
+ ir_node *t = left;
+
+ left = right;
+ right = t;
+
+ proj_nr = get_inversed_pnc(proj_nr);
+ changed |= 1;
+ }
+
+ /*
+ * Second step: Try to reduce the magnitude
+ * of a constant. This may help to generate better code
+ * later and may help to normalize more compares.
+ * Of course this is only possible for integer values.
+ */
+ if (is_Const(right)) {
+ mode = get_irn_mode(right);
+ tv = get_Const_tarval(right);
+
+ /* TODO extend to arbitrary constants */
+ if (is_Conv(left) && tarval_is_null(tv)) {
+ ir_node *op = get_Conv_op(left);
+ ir_mode *op_mode = get_irn_mode(op);
+
+ /*
+ * UpConv(x) REL 0 ==> x REL 0
+ */
+ if (get_mode_size_bits(mode) > get_mode_size_bits(op_mode) &&
+ ((proj_nr == pn_Cmp_Eq || proj_nr == pn_Cmp_Lg) ||
+ mode_is_signed(mode) || !mode_is_signed(op_mode))) {
+ tv = get_mode_null(op_mode);
+ left = op;
+ mode = op_mode;
+ changed |= 2;
+ DBG_OPT_ALGSIM0(n, n, FS_OPT_CMP_CONV);
+ }
+ }
+
+ if (tv != tarval_bad) {
+ /* the following optimization is possible on modes without Overflow
+ * on Unary Minus or on == and !=:
+ * -a CMP c ==> a swap(CMP) -c
+ *
+ * Beware: for two-complement Overflow may occur, so only == and != can
+ * be optimized, see this:
+ * -MININT < 0 =/=> MININT > 0 !!!
+ */
+ if (is_Minus(left) &&
+ (!mode_overflow_on_unary_Minus(mode) ||
+ (mode_is_int(mode) && (proj_nr == pn_Cmp_Eq || proj_nr == pn_Cmp_Lg)))) {
+ tv = tarval_neg(tv);
+
+ if (tv != tarval_bad) {
+ left = get_Minus_op(left);
+ proj_nr = get_inversed_pnc(proj_nr);
+ changed |= 2;
+ DBG_OPT_ALGSIM0(n, n, FS_OPT_CMP_OP_C);
+ }
+ } else if (is_Not(left) && (proj_nr == pn_Cmp_Eq || proj_nr == pn_Cmp_Lg)) {
+ /* Not(a) ==/!= c ==> a ==/!= Not(c) */
+ tv = tarval_not(tv);
+
+ if (tv != tarval_bad) {
+ left = get_Not_op(left);
+ changed |= 2;
+ DBG_OPT_ALGSIM0(n, n, FS_OPT_CMP_OP_C);
+ }
+ }
+
+ /* for integer modes, we have more */
+ if (mode_is_int(mode)) {
+ /* Ne includes Unordered which is not possible on integers.
+ * However, frontends often use this wrong, so fix it here */
+ if (proj_nr & pn_Cmp_Uo) {
+ proj_nr &= ~pn_Cmp_Uo;
+ set_Proj_proj(proj, proj_nr);
+ }
+
+ /* c > 0 : a < c ==> a <= (c-1) a >= c ==> a > (c-1) */
+ if ((proj_nr == pn_Cmp_Lt || proj_nr == pn_Cmp_Ge) &&
+ tarval_cmp(tv, get_mode_null(mode)) == pn_Cmp_Gt) {
+ tv = tarval_sub(tv, get_mode_one(mode));
+
+ if (tv != tarval_bad) {
+ proj_nr ^= pn_Cmp_Eq;
+ changed |= 2;
+ DBG_OPT_ALGSIM0(n, n, FS_OPT_CMP_CNST_MAGN);
+ }
+ }
+ /* c < 0 : a > c ==> a >= (c+1) a <= c ==> a < (c+1) */
+ else if ((proj_nr == pn_Cmp_Gt || proj_nr == pn_Cmp_Le) &&
+ tarval_cmp(tv, get_mode_null(mode)) == pn_Cmp_Lt) {
+ tv = tarval_add(tv, get_mode_one(mode));
+
+ if (tv != tarval_bad) {
+ proj_nr ^= pn_Cmp_Eq;
+ changed |= 2;
+ DBG_OPT_ALGSIM0(n, n, FS_OPT_CMP_CNST_MAGN);
+ }
+ }
+
+ /* the following reassociations work only for == and != */
+ if (proj_nr == pn_Cmp_Eq || proj_nr == pn_Cmp_Lg) {
+
+#if 0 /* Might be not that good in general */
+ /* a-b == 0 ==> a == b, a-b != 0 ==> a != b */
+ if (tarval_is_null(tv) && is_Sub(left)) {
+ right = get_Sub_right(left);
+ left = get_Sub_left(left);
+
+ tv = value_of(right);
+ changed = 1;
+ DBG_OPT_ALGSIM0(n, n, FS_OPT_CMP_OP_C);
+ }
+#endif