+ arity = get_irn_arity(node);
+ for (j = 0; j < arity; ++j) {
+ set_irn_n(copy, j, copy_to(get_irn_n(node, j), src_block, i));
+ DB((dbg, LEVEL_2, "-- pred %d is %+F\n", j, get_irn_n(copy, j)));
+ }
+ return copy;
+}
+
+
+/**
+ * Remove predecessors i and j from node and add predecessor new_pred
+ */
+static void rewire(ir_node* node, int i, int j, ir_node* new_pred)
+{
+ int arity = get_irn_arity(node);
+ ir_node **ins;
+ int k;
+ int l;
+
+ NEW_ARR_A(ir_node *, ins, arity - 1);
+
+ l = 0;
+ for (k = 0; k < i; ++k) ins[l++] = get_irn_n(node, k);
+ for (++k; k < j; ++k) ins[l++] = get_irn_n(node, k);
+ for (++k; k < arity; ++k) ins[l++] = get_irn_n(node, k);
+ ins[l++] = new_pred;
+ assert(l == arity - 1);
+ set_irn_in(node, l, ins);
+}
+
+
+/**
+ * Remove the jth predecessors from the ith predecessor of block and add it to block
+ */
+static void split_block(ir_node* block, int i, int j)
+{
+ ir_node* pred_block = get_nodes_block(get_irn_n(block, i));
+ int arity = get_irn_arity(block);
+ int new_pred_arity;
+ ir_node* phi;
+ ir_node **ins;
+ ir_node **pred_ins;
+ int k;
+
+ DB((dbg, LEVEL_1, "Splitting predecessor %d of predecessor %d of %+F\n", j, i, block));
+
+ NEW_ARR_A(ir_node*, ins, arity + 1);
+
+ for (phi = get_block_blockinfo(block)->phi; phi != NULL; phi = get_irn_link(phi)) {
+ ir_node* copy = copy_to(get_irn_n(phi, i), pred_block, j);
+
+ for (k = 0; k < i; ++k) ins[k] = get_irn_n(phi, k);
+ ins[k++] = copy;
+ for (; k < arity; ++k) ins[k] = get_irn_n(phi, k);
+ ins[k] = get_irn_n(phi, i);
+ assert(k == arity);
+ set_irn_in(phi, arity + 1, ins);
+ }
+
+ for (k = 0; k < i; ++k) ins[k] = get_irn_n(block, k);
+ ins[k++] = get_irn_n(pred_block, j);
+ for (; k < arity; ++k) ins[k] = get_irn_n(block, k);
+ ins[k] = get_irn_n(block, i);
+ assert(k == arity);
+ set_irn_in(block, arity + 1, ins);
+
+ new_pred_arity = get_irn_arity(pred_block) - 1;
+ NEW_ARR_A(ir_node*, pred_ins, new_pred_arity);
+
+ for (phi = get_block_blockinfo(pred_block)->phi; phi != NULL; phi = get_irn_link(phi)) {
+ for (k = 0; k < j; ++k) pred_ins[k] = get_irn_n(phi, k);
+ for (; k < new_pred_arity; ++k) pred_ins[k] = get_irn_n(phi, k + 1);
+ assert(k == new_pred_arity);
+ if (new_pred_arity > 1) {
+ set_irn_in(phi, new_pred_arity, pred_ins);
+ } else {
+ exchange(phi, pred_ins[0]);
+ }
+ }
+
+ for (k = 0; k < j; ++k) pred_ins[k] = get_irn_n(pred_block, k);
+ for (; k < new_pred_arity; ++k) pred_ins[k] = get_irn_n(pred_block, k + 1);
+ assert(k == new_pred_arity);
+ if (new_pred_arity > 1) {
+ set_irn_in(pred_block, new_pred_arity, pred_ins);
+ } else {
+ exchange(pred_block, get_nodes_block(pred_ins[0]));
+ }
+}
+
+
+static void prepare_path(ir_node* block, int i, const ir_node* dependency)
+{
+ ir_node* pred = get_nodes_block(get_irn_n(block, i));
+ int pred_arity;
+ int j;
+
+ DB((dbg, LEVEL_1, "Preparing predecessor %d of %+F\n", i, block));
+
+ pred_arity = get_irn_arity(pred);
+ for (j = 0; j < pred_arity; ++j) {
+ ir_node* pred_pred = get_nodes_block(get_irn_n(pred, j));
+
+ if (is_cdep_on(pred_pred, dependency)) {
+ prepare_path(pred, j, dependency);
+ split_block(block, i, j);
+ break;
+ }
+ }
+}
+
+
+static void if_conv_walker(ir_node* block, void* env)
+{
+ int arity;
+ int i;
+ opt_if_conv_info_t *opt_info = env;
+
+ /* Bail out, if there are no Phis at all */
+ if (get_block_blockinfo(block)->phi == NULL) return;
+
+restart:
+ arity = get_irn_arity(block);
+ for (i = 0; i < arity; ++i) {
+ ir_node* pred;
+ cdep* cdep;
+
+ pred = get_nodes_block(get_irn_n(block, i));
+ for (cdep = find_cdep(pred); cdep != NULL; cdep = cdep->next) {
+ const ir_node* dependency = cdep->node;
+ ir_node* projx0 = walk_to_projx(pred, dependency);
+ ir_node* cond;
+ int j;
+
+ if (projx0 == NULL) continue;
+
+ cond = get_Proj_pred(projx0);
+ if (get_irn_op(cond) != op_Cond) continue;
+
+ /* We only handle boolean decisions, no switches */
+ if (get_irn_mode(get_Cond_selector(cond)) != mode_b) continue;
+
+ for (j = i + 1; j < arity; ++j) {
+ ir_node* projx1;
+ ir_node* conds[1];
+ ir_node* vals[2];
+ ir_node* psi = NULL;
+ ir_node* psi_block;
+ ir_node* phi;
+
+ pred = get_nodes_block(get_irn_n(block, j));
+
+ if (!is_cdep_on(pred, dependency)) continue;
+
+ projx1 = walk_to_projx(pred, dependency);
+
+ if (projx1 == NULL) continue;
+
+ phi = get_block_blockinfo(block)->phi;
+ if (!opt_info->allow_ifconv(get_Cond_selector(cond), phi, i, j)) continue;
+
+ DB((dbg, LEVEL_1, "Found Cond %+F with proj %+F and %+F\n",
+ cond, projx0, projx1
+ ));
+
+ prepare_path(block, i, dependency);
+ prepare_path(block, j, dependency);
+ arity = get_irn_arity(block);
+
+ conds[0] = get_Cond_selector(cond);
+
+ psi_block = get_nodes_block(cond);
+ do {
+ ir_node* val_i = get_irn_n(phi, i);
+ ir_node* val_j = get_irn_n(phi, j);
+
+ if (val_i == val_j) {
+ psi = val_i;
+ DB((dbg, LEVEL_2, "Generating no psi, because both values are equal\n"));
+ } else {
+ /* Something is very fishy if two predecessors of a PhiM point into
+ * one block, but not at the same memory node
+ */
+ assert(get_irn_mode(phi) != mode_M);
+ if (get_Proj_proj(projx0) == pn_Cond_true) {
+ vals[0] = val_i;
+ vals[1] = val_j;
+ } else {
+ vals[0] = val_j;
+ vals[1] = val_i;
+ }
+
+ psi = new_r_Psi(
+ current_ir_graph, psi_block, 1, conds, vals, get_irn_mode(phi)
+ );
+ DB((dbg, LEVEL_2, "Generating %+F for %+F\n", psi, phi));
+ }
+
+ /* only exchange if we have a Psi */
+ if (arity == 2) {
+ exchange(phi, psi);
+ } else {
+ rewire(phi, i, j, psi);
+ }
+
+ phi = get_irn_link(phi);
+ } while (phi != NULL);
+
+ exchange(get_nodes_block(get_irn_n(block, i)), psi_block);
+ exchange(get_nodes_block(get_irn_n(block, j)), psi_block);
+
+ if (arity == 2) {
+#if 1
+ DB((dbg, LEVEL_1, "Welding block %+F and %+F\n", block, psi_block));
+ /* copy the block-info from the Psi-block to the block before merging */
+ get_block_blockinfo(psi_block)->has_pinned |= get_block_blockinfo(block)->has_pinned;
+ set_irn_link(block, get_irn_link(psi_block));
+
+ set_irn_in(block, get_irn_arity(psi_block), get_irn_in(psi_block) + 1);
+ exchange_cdep(psi_block, block);
+ exchange(psi_block, block);
+#else
+ DB((dbg, LEVEL_1, "Welding block %+F to %+F\n", block, psi_block));
+ get_block_blockinfo(psi_block)->has_pinned |= get_block_blockinfo(block)->has_pinned;
+ exchange(block, psi_block);
+#endif
+ return;
+ } else {
+ rewire(block, i, j, new_r_Jmp(current_ir_graph, psi_block));
+ goto restart;
+ }