+/**
+ * Do the AddSub optimization, then Transform
+ * Constant folding on Phi
+ * Sub(0,a) -> Minus(a)
+ * Sub(Mul(a, x), a) -> Mul(a, x-1)
+ * Sub(Sub(x, y), b) -> Sub(x, Add(y,b))
+ * Sub(Add(a, x), x) -> a
+ * Sub(x, Add(x, a)) -> -a
+ * Sub(x, Const) -> Add(x, -Const)
+ */
+static ir_node *transform_node_Sub(ir_node *n) {
+ ir_mode *mode;
+ ir_node *oldn = n;
+ ir_node *a, *b, *c;
+
+ n = transform_node_AddSub(n);
+
+ a = get_Sub_left(n);
+ b = get_Sub_right(n);
+
+ mode = get_irn_mode(n);
+
+restart:
+ HANDLE_BINOP_PHI(tarval_sub, a, b, c, mode);
+
+ /* for FP these optimizations are only allowed if fp_strict_algebraic is disabled */
+ if (mode_is_float(mode) && (get_irg_fp_model(current_ir_graph) & fp_strict_algebraic))
+ return n;
+
+ if (is_Const(b) && get_irn_mode(b) != mode_P) {
+ /* a - C -> a + (-C) */
+ ir_node *cnst = const_negate(b);
+ if (cnst != NULL) {
+ ir_node *block = get_nodes_block(n);
+ dbg_info *dbgi = get_irn_dbg_info(n);
+ ir_graph *irg = get_irn_irg(n);
+
+ n = new_rd_Add(dbgi, irg, block, a, cnst, mode);
+ DBG_OPT_ALGSIM0(oldn, n, FS_OPT_SUB_TO_ADD);
+ return n;
+ }
+ }
+
+ if (is_Minus(a)) { /* (-a) - b -> -(a + b) */
+ ir_graph *irg = current_ir_graph;
+ dbg_info *dbg = get_irn_dbg_info(n);
+ ir_node *block = get_nodes_block(n);
+ ir_node *left = get_Minus_op(a);
+ ir_node *add = new_rd_Add(dbg, irg, block, left, b, mode);
+
+ n = new_rd_Minus(dbg, irg, block, add, mode);
+ DBG_OPT_ALGSIM0(oldn, n, FS_OPT_SUB_TO_ADD);
+ return n;
+ } else if (is_Minus(b)) { /* a - (-b) -> a + b */
+ ir_graph *irg = current_ir_graph;
+ dbg_info *dbg = get_irn_dbg_info(n);
+ ir_node *block = get_nodes_block(n);
+ ir_node *right = get_Minus_op(b);
+
+ n = new_rd_Add(dbg, irg, block, a, right, mode);
+ DBG_OPT_ALGSIM0(oldn, n, FS_OPT_SUB_MINUS);
+ return n;
+ } else if (is_Sub(b)) { /* a - (b - c) -> a + (c - b) */
+ ir_graph *irg = current_ir_graph;
+ dbg_info *s_dbg = get_irn_dbg_info(b);
+ ir_node *s_block = get_nodes_block(b);
+ ir_node *s_left = get_Sub_right(b);
+ ir_node *s_right = get_Sub_left(b);
+ ir_mode *s_mode = get_irn_mode(b);
+ ir_node *sub = new_rd_Sub(s_dbg, irg, s_block, s_left, s_right, s_mode);
+ dbg_info *a_dbg = get_irn_dbg_info(n);
+ ir_node *a_block = get_nodes_block(n);
+
+ n = new_rd_Add(a_dbg, irg, a_block, a, sub, mode);
+ DBG_OPT_ALGSIM0(oldn, n, FS_OPT_SUB_TO_ADD);
+ return n;
+ } else if (is_Mul(b)) { /* a - (b * C) -> a + (b * -C) */
+ ir_node *m_right = get_Mul_right(b);
+ if (is_Const(m_right)) {
+ ir_node *cnst2 = const_negate(m_right);
+ if (cnst2 != NULL) {
+ ir_graph *irg = current_ir_graph;
+ dbg_info *m_dbg = get_irn_dbg_info(b);
+ ir_node *m_block = get_nodes_block(b);
+ ir_node *m_left = get_Mul_left(b);
+ ir_mode *m_mode = get_irn_mode(b);
+ ir_node *mul = new_rd_Mul(m_dbg, irg, m_block, m_left, cnst2, m_mode);
+ dbg_info *a_dbg = get_irn_dbg_info(n);
+ ir_node *a_block = get_nodes_block(n);
+
+ n = new_rd_Add(a_dbg, irg, a_block, a, mul, mode);
+ DBG_OPT_ALGSIM0(oldn, n, FS_OPT_SUB_TO_ADD);
+ return n;
+ }
+ }
+ }
+
+ /* Beware of Sub(P, P) which cannot be optimized into a simple Minus ... */
+ if (mode_is_num(mode) && mode == get_irn_mode(a) && is_Const(a) && is_Const_null(a)) {
+ n = new_rd_Minus(
+ get_irn_dbg_info(n),
+ current_ir_graph,
+ get_irn_n(n, -1),
+ b,
+ mode);
+ DBG_OPT_ALGSIM0(oldn, n, FS_OPT_SUB_0_A);
+ return n;
+ }
+ if (is_Add(a)) {
+ if (mode_wrap_around(mode)) {
+ ir_node *left = get_Add_left(a);
+ ir_node *right = get_Add_right(a);
+
+ /* FIXME: Does the Conv's work only for two complement or generally? */
+ if (left == b) {
+ if (mode != get_irn_mode(right)) {
+ /* This Sub is an effective Cast */
+ right = new_r_Conv(get_irn_irg(n), get_irn_n(n, -1), right, mode);
+ }
+ n = right;
+ DBG_OPT_ALGSIM1(oldn, a, b, n, FS_OPT_ADD_SUB);
+ return n;
+ } else if (right == b) {
+ if (mode != get_irn_mode(left)) {
+ /* This Sub is an effective Cast */
+ left = new_r_Conv(get_irn_irg(n), get_irn_n(n, -1), left, mode);
+ }
+ n = left;
+ DBG_OPT_ALGSIM1(oldn, a, b, n, FS_OPT_ADD_SUB);
+ return n;
+ }
+ }
+ }
+ if (is_Add(b)) {
+ if (mode_wrap_around(mode)) {
+ ir_node *left = get_Add_left(b);
+ ir_node *right = get_Add_right(b);
+
+ /* FIXME: Does the Conv's work only for two complement or generally? */
+ if (left == a) {
+ ir_mode *r_mode = get_irn_mode(right);
+
+ n = new_r_Minus(get_irn_irg(n), get_irn_n(n, -1), right, r_mode);
+ if (mode != r_mode) {
+ /* This Sub is an effective Cast */
+ n = new_r_Conv(get_irn_irg(n), get_irn_n(n, -1), n, mode);
+ }
+ DBG_OPT_ALGSIM1(oldn, a, b, n, FS_OPT_ADD_SUB);
+ return n;
+ } else if (right == a) {
+ ir_mode *l_mode = get_irn_mode(left);
+
+ n = new_r_Minus(get_irn_irg(n), get_irn_n(n, -1), left, l_mode);
+ if (mode != l_mode) {
+ /* This Sub is an effective Cast */
+ n = new_r_Conv(get_irn_irg(n), get_irn_n(n, -1), n, mode);
+ }
+ DBG_OPT_ALGSIM1(oldn, a, b, n, FS_OPT_ADD_SUB);
+ return n;
+ }
+ }
+ }
+ if (mode_is_int(mode) && is_Conv(a) && is_Conv(b)) {
+ ir_mode *mode = get_irn_mode(a);
+
+ if (mode == get_irn_mode(b)) {
+ ir_mode *ma, *mb;
+ ir_node *op_a = get_Conv_op(a);
+ ir_node *op_b = get_Conv_op(b);
+
+ /* check if it's allowed to skip the conv */
+ ma = get_irn_mode(op_a);
+ mb = get_irn_mode(op_b);
+
+ if (mode_is_reference(ma) && mode_is_reference(mb)) {
+ /* SubInt(ConvInt(aP), ConvInt(bP)) -> SubInt(aP,bP) */
+ a = op_a; b = op_b;
+ set_Sub_left(n, a);
+ set_Sub_right(n, b);
+
+ goto restart;
+ }
+ }
+ }
+ /* do NOT execute this code if reassociation is enabled, it does the inverse! */
+ if (!is_reassoc_running() && is_Mul(a)) {
+ ir_node *ma = get_Mul_left(a);
+ ir_node *mb = get_Mul_right(a);
+
+ if (ma == b) {
+ ir_node *blk = get_irn_n(n, -1);
+ n = new_rd_Mul(
+ get_irn_dbg_info(n),
+ current_ir_graph, blk,
+ ma,
+ new_rd_Sub(
+ get_irn_dbg_info(n),
+ current_ir_graph, blk,
+ mb,
+ new_r_Const_long(current_ir_graph, blk, mode, 1),
+ mode),
+ mode);
+ DBG_OPT_ALGSIM0(oldn, n, FS_OPT_SUB_MUL_A_X_A);
+ return n;
+ } else if (mb == b) {
+ ir_node *blk = get_irn_n(n, -1);
+ n = new_rd_Mul(
+ get_irn_dbg_info(n),
+ current_ir_graph, blk,
+ mb,
+ new_rd_Sub(
+ get_irn_dbg_info(n),
+ current_ir_graph, blk,
+ ma,
+ new_r_Const_long(current_ir_graph, blk, mode, 1),
+ mode),
+ mode);
+ DBG_OPT_ALGSIM0(oldn, n, FS_OPT_SUB_MUL_A_X_A);
+ return n;
+ }
+ }
+ if (is_Sub(a)) { /* (x - y) - b -> x - (y + b) */
+ ir_node *x = get_Sub_left(a);
+ ir_node *y = get_Sub_right(a);
+ ir_node *blk = get_irn_n(n, -1);
+ ir_mode *m_b = get_irn_mode(b);
+ ir_mode *m_y = get_irn_mode(y);
+ ir_mode *add_mode;
+ ir_node *add;
+
+ /* Determine the right mode for the Add. */
+ if (m_b == m_y)
+ add_mode = m_b;
+ else if (mode_is_reference(m_b))
+ add_mode = m_b;
+ else if (mode_is_reference(m_y))
+ add_mode = m_y;
+ else {
+ /*
+ * Both modes are different but none is reference,
+ * happens for instance in SubP(SubP(P, Iu), Is).
+ * We have two possibilities here: Cast or ignore.
+ * Currently we ignore this case.
+ */
+ return n;
+ }
+
+ add = new_r_Add(current_ir_graph, blk, y, b, add_mode);
+
+ n = new_rd_Sub(get_irn_dbg_info(n), current_ir_graph, blk, x, add, mode);
+ DBG_OPT_ALGSIM0(oldn, n, FS_OPT_SUB_SUB_X_Y_Z);
+ return n;
+ }
+
+ if (get_mode_arithmetic(mode) == irma_twos_complement) {
+ if (is_Const(a) && is_Not(b)) {
+ /* c - ~X = X + (c+1) */
+ tarval *tv = get_Const_tarval(a);
+
+ tv = tarval_add(tv, get_mode_one(mode));
+ if (tv != tarval_bad) {
+ ir_node *blk = get_irn_n(n, -1);
+ ir_node *c = new_r_Const(current_ir_graph, blk, mode, tv);
+ n = new_rd_Add(get_irn_dbg_info(n), current_ir_graph, blk, get_Not_op(b), c, mode);
+ DBG_OPT_ALGSIM0(oldn, n, FS_OPT_SUB_C_NOT_X);
+ return n;
+ }
+ }
+ }
+ return n;
+} /* transform_node_Sub */
+
+/**
+ * Several transformation done on n*n=2n bits mul.
+ * These transformations must be done here because new nodes may be produced.
+ */
+static ir_node *transform_node_Mul2n(ir_node *n, ir_mode *mode) {
+ ir_node *oldn = n;
+ ir_node *a = get_Mul_left(n);
+ ir_node *b = get_Mul_right(n);
+ tarval *ta = value_of(a);
+ tarval *tb = value_of(b);
+ ir_mode *smode = get_irn_mode(a);
+
+ if (ta == get_mode_one(smode)) {
+ ir_node *blk = get_irn_n(n, -1);
+ n = new_rd_Conv(get_irn_dbg_info(n), current_ir_graph, blk, b, mode);
+ DBG_OPT_ALGSIM1(oldn, a, b, n, FS_OPT_NEUTRAL_1);
+ return n;
+ }
+ else if (ta == get_mode_minus_one(smode)) {
+ ir_node *blk = get_irn_n(n, -1);
+ n = new_rd_Minus(get_irn_dbg_info(n), current_ir_graph, blk, b, smode);
+ n = new_rd_Conv(get_irn_dbg_info(n), current_ir_graph, blk, n, mode);
+ DBG_OPT_ALGSIM1(oldn, a, b, n, FS_OPT_MUL_MINUS_1);
+ return n;
+ }
+ if (tb == get_mode_one(smode)) {
+ ir_node *blk = get_irn_n(a, -1);
+ n = new_rd_Conv(get_irn_dbg_info(n), current_ir_graph, blk, a, mode);
+ DBG_OPT_ALGSIM1(oldn, a, b, n, FS_OPT_NEUTRAL_1);
+ return n;
+ }
+ else if (tb == get_mode_minus_one(smode)) {
+ ir_node *blk = get_irn_n(n, -1);
+ n = new_rd_Minus(get_irn_dbg_info(n), current_ir_graph, blk, a, smode);
+ n = new_rd_Conv(get_irn_dbg_info(n), current_ir_graph, blk, n, mode);
+ DBG_OPT_ALGSIM1(oldn, a, b, n, FS_OPT_MUL_MINUS_1);
+ return n;
+ }
+ return n;
+}