+ * Calls the functions to assure register constraints.
+ *
+ * @param irn The node to be checked for lowering
+ * @param walk_env The walker environment
+ */
+static void assure_constraints_walker(ir_node *irn, void *walk_env) {
+ if (is_Block(irn))
+ return;
+
+ if (mode_is_datab(get_irn_mode(irn)))
+ assure_different_constraints(irn, walk_env);
+
+ return;
+}
+
+/**
+ * Melt all copykeeps pointing to the same node
+ * (or Projs of the same node), copying the same operand.
+ */
+static void melt_copykeeps(constraint_env_t *cenv) {
+ op_copy_assoc_t *entry;
+
+ /* for all */
+ foreach_pset(cenv->op_set, entry) {
+ int idx, num_ck;
+ ir_node *cp;
+ struct obstack obst;
+ ir_node **ck_arr, **melt_arr;
+
+ obstack_init(&obst);
+
+ /* collect all copykeeps */
+ num_ck = idx = 0;
+ foreach_pset(entry->copies, cp) {
+ if (be_is_CopyKeep(cp)) {
+ obstack_grow(&obst, &cp, sizeof(cp));
+ ++num_ck;
+ }
+#ifdef KEEP_ALIVE_COPYKEEP_HACK
+ else {
+ set_irn_mode(cp, mode_ANY);
+ keep_alive(cp);
+ }
+#endif /* KEEP_ALIVE_COPYKEEP_HACK */
+ }
+
+ /* compare each copykeep with all other copykeeps */
+ ck_arr = (ir_node **)obstack_finish(&obst);
+ for (idx = 0; idx < num_ck; ++idx) {
+ ir_node *ref, *ref_mode_T;
+
+ if (ck_arr[idx]) {
+ int j, n_melt;
+ ir_node **new_ck_in;
+ ir_node *new_ck;
+ ir_node *sched_pt = NULL;
+
+ n_melt = 1;
+ ref = ck_arr[idx];
+ ref_mode_T = skip_Proj(get_irn_n(ref, 1));
+ obstack_grow(&obst, &ref, sizeof(ref));
+
+ DBG((cenv->dbg, LEVEL_1, "Trying to melt %+F:\n", ref));
+
+ /* check for copykeeps pointing to the same mode_T node as the reference copykeep */
+ for (j = 0; j < num_ck; ++j) {
+ ir_node *cur_ck = ck_arr[j];
+
+ if (j != idx && cur_ck && skip_Proj(get_irn_n(cur_ck, 1)) == ref_mode_T) {
+ obstack_grow(&obst, &cur_ck, sizeof(cur_ck));
+ pset_remove_ptr(entry->copies, cur_ck);
+ DBG((cenv->dbg, LEVEL_1, "\t%+F\n", cur_ck));
+ ck_arr[j] = NULL;
+ ++n_melt;
+ sched_remove(cur_ck);
+ }
+ }
+ ck_arr[idx] = NULL;
+
+ /* check, if we found some candidates for melting */
+ if (n_melt == 1) {
+ DBG((cenv->dbg, LEVEL_1, "\tno candidate found\n"));
+ continue;
+ }
+
+ pset_remove_ptr(entry->copies, ref);
+ sched_remove(ref);
+
+ melt_arr = (ir_node **)obstack_finish(&obst);
+ /* melt all found copykeeps */
+ NEW_ARR_A(ir_node *, new_ck_in, n_melt);
+ for (j = 0; j < n_melt; ++j) {
+ new_ck_in[j] = get_irn_n(melt_arr[j], 1);
+
+ /* now, we can kill the melted keep, except the */
+ /* ref one, we still need some information */
+ if (melt_arr[j] != ref)
+ kill_node(melt_arr[j], 0);
+ }
+
+#ifdef KEEP_ALIVE_COPYKEEP_HACK
+ new_ck = be_new_CopyKeep(entry->cls, cenv->birg->irg, get_nodes_block(ref), be_get_CopyKeep_op(ref), n_melt, new_ck_in, mode_ANY);
+ keep_alive(new_ck);
+#else
+ new_ck = be_new_CopyKeep(entry->cls, cenv->birg->irg, get_nodes_block(ref), be_get_CopyKeep_op(ref), n_melt, new_ck_in, get_irn_mode(ref));
+#endif /* KEEP_ALIVE_COPYKEEP_HACK */
+
+ /* set register class for all keeped inputs */
+ for (j = 1; j <= n_melt; ++j)
+ be_node_set_reg_class(new_ck, j, entry->cls);
+
+ pset_insert_ptr(entry->copies, new_ck);
+
+ /* find scheduling point */
+ if (get_irn_mode(ref_mode_T) == mode_T) {
+ /* walk along the Projs */
+ for (sched_pt = sched_next(ref_mode_T); is_Proj(sched_pt) || be_is_Keep(sched_pt) || be_is_CopyKeep(sched_pt); sched_pt = sched_next(sched_pt))
+ /* just walk along the schedule until a non-Proj/Keep/CopyKeep node is found*/ ;
+ }
+ else {
+ sched_pt = ref_mode_T;
+ }
+
+ sched_add_before(sched_pt, new_ck);
+ DBG((cenv->dbg, LEVEL_1, "created %+F, scheduled before %+F\n", new_ck, sched_pt));
+
+ /* finally: kill the reference copykeep */
+ kill_node(ref, 0);
+ }
+ }
+
+ obstack_free(&obst, NULL);
+ }
+}
+
+/**
+ * Walks over all nodes to assure register constraints.
+ *
+ * @param birg The birg structure containing the irg
+ */
+void assure_constraints(be_irg_t *birg) {
+ constraint_env_t cenv;
+ op_copy_assoc_t *entry;
+ dom_front_info_t *dom;
+ ir_node **nodes;
+ FIRM_DBG_REGISTER(firm_dbg_module_t *mod, "firm.be.lower.constr");
+
+ DEBUG_ONLY(cenv.dbg = mod;)
+ cenv.birg = birg;
+ cenv.op_set = new_pset(cmp_op_copy_assoc, 16);
+ obstack_init(&cenv.obst);
+
+ irg_walk_blkwise_graph(birg->irg, NULL, assure_constraints_walker, &cenv);
+
+ /* melt copykeeps, pointing to projs of */
+ /* the same mode_T node and keeping the */
+ /* same operand */
+ melt_copykeeps(&cenv);
+
+ /* introduce copies needs dominance information */
+ dom = be_compute_dominance_frontiers(birg->irg);
+
+ /* for all */
+ foreach_pset(cenv.op_set, entry) {
+ int n;
+ ir_node *cp;
+
+ n = pset_count(entry->copies);
+ nodes = alloca((n + 1) * sizeof(nodes[0]));
+
+ /* put the node in an array */
+ n = 0;
+ nodes[n++] = entry->op;
+ DBG((mod, LEVEL_1, "introduce copies for %+F ", entry->op));
+
+ /* collect all copies */
+ foreach_pset(entry->copies, cp) {
+ nodes[n++] = cp;
+ DB((mod, LEVEL_1, ", %+F ", cp));
+ }
+
+ DB((mod, LEVEL_1, "\n"));
+
+ /* introduce the copies for the operand and it's copies */
+ be_ssa_constr(dom, NULL, n, nodes);
+
+
+ /* Could be that not all CopyKeeps are really needed, */
+ /* so we transform unnecessary ones into Keeps. */
+ foreach_pset(entry->copies, cp) {
+ if (be_is_CopyKeep(cp) && get_irn_n_edges(cp) < 1) {
+ ir_node *keep;
+ int n = get_irn_arity(cp);
+
+ keep = be_new_Keep(arch_get_irn_reg_class(birg->main_env->arch_env, cp, -1),
+ birg->irg, get_nodes_block(cp), n, (ir_node **)&get_irn_in(cp)[1]);
+ sched_add_before(cp, keep);
+
+ /* Set all ins (including the block) of the CopyKeep BAD to keep the verifier happy. */
+ kill_node(cp, 1);
+ sched_remove(cp);
+ }
+ }
+
+ del_pset(entry->copies);
+ }
+
+ be_free_dominance_frontiers(dom);
+
+ del_pset(cenv.op_set);
+ obstack_free(&cenv.obst, NULL);
+}
+
+
+
+/**
+ * Calls the corresponding lowering function for the node.
+ *
+ * @param irn The node to be checked for lowering
+ * @param walk_env The walker environment
+ */
+static void lower_nodes_after_ra_walker(ir_node *irn, void *walk_env) {
+ lower_env_t *env = walk_env;
+ const arch_env_t *arch_env = env->chord_env->birg->main_env->arch_env;
+
+ if (! is_Block(irn) && ! is_Proj(irn)) {
+ if (is_Perm(arch_env, irn)) {
+ lower_perm_node(irn, walk_env);
+ }
+ }
+
+ return;
+}
+
+/**
+ * Walks over all blocks in an irg and performs lowering need to be
+ * done after register allocation (e.g. perm lowering).