+
+/********************************************************************************************************
+ * _____ _ _ ____ _ _ _ _ _
+ * | __ \ | | | | / __ \ | | (_) (_) | | (_)
+ * | |__) |__ ___ _ __ | |__ ___ | | ___ | | | |_ __ | |_ _ _ __ ___ _ ______ _| |_ _ ___ _ __
+ * | ___/ _ \/ _ \ '_ \| '_ \ / _ \| |/ _ \ | | | | '_ \| __| | '_ ` _ \| |_ / _` | __| |/ _ \| '_ \
+ * | | | __/ __/ |_) | | | | (_) | | __/ | |__| | |_) | |_| | | | | | | |/ / (_| | |_| | (_) | | | |
+ * |_| \___|\___| .__/|_| |_|\___/|_|\___| \____/| .__/ \__|_|_| |_| |_|_/___\__,_|\__|_|\___/|_| |_|
+ * | | | |
+ * |_| |_|
+ ********************************************************************************************************/
+
+/**
+ * NOTE: THESE PEEPHOLE OPTIMIZATIONS MUST BE CALLED AFTER SCHEDULING AND REGISTER ALLOCATION.
+ */
+
+static int ia32_cnst_compare(ir_node *n1, ir_node *n2) {
+ return get_ia32_id_cnst(n1) == get_ia32_id_cnst(n2);
+}
+
+/**
+ * Checks for potential CJmp/CJmpAM optimization candidates.
+ */
+static ir_node *ia32_determine_cjmp_cand(ir_node *irn, is_op_func_t *is_op_func) {
+ ir_node *cand = NULL;
+ ir_node *prev = sched_prev(irn);
+
+ if (is_Block(prev)) {
+ if (get_Block_n_cfgpreds(prev) == 1)
+ prev = get_Block_cfgpred(prev, 0);
+ else
+ prev = NULL;
+ }
+
+ /* The predecessor must be a ProjX. */
+ if (prev && is_Proj(prev) && get_irn_mode(prev) == mode_X) {
+ prev = get_Proj_pred(prev);
+
+ if (is_op_func(prev))
+ cand = prev;
+ }
+
+ return cand;
+}
+
+static int is_TestJmp_cand(const ir_node *irn) {
+ return is_ia32_TestJmp(irn) || is_ia32_And(irn);
+}
+
+/**
+ * Checks if two consecutive arguments of cand matches
+ * the two arguments of irn (TestJmp).
+ */
+static int is_TestJmp_replacement(ir_node *cand, ir_node *irn) {
+ ir_node *in1 = get_irn_n(irn, 0);
+ ir_node *in2 = get_irn_n(irn, 1);
+ int i, n = get_irn_arity(cand);
+ int same_args = 0;
+
+ for (i = 0; i < n - 1; i++) {
+ if (get_irn_n(cand, i) == in1 &&
+ get_irn_n(cand, i + 1) == in2)
+ {
+ same_args = 1;
+ break;
+ }
+ }
+
+ if (same_args)
+ return ia32_cnst_compare(cand, irn);
+
+ return 0;
+}
+
+/**
+ * Tries to replace a TestJmp by a CJmp or CJmpAM (in case of And)
+ */
+static void ia32_optimize_TestJmp(ir_node *irn, ia32_code_gen_t *cg) {
+ ir_node *cand = ia32_determine_cjmp_cand(irn, is_TestJmp_cand);
+ int replace = 0;
+
+ /* we found a possible candidate */
+ replace = cand ? is_TestJmp_replacement(cand, irn) : 0;
+
+ if (replace) {
+ DBG((cg->mod, LEVEL_1, "replacing %+F by ", irn));
+
+ if (is_ia32_And(cand))
+ set_irn_op(irn, op_ia32_CJmpAM);
+ else
+ set_irn_op(irn, op_ia32_CJmp);
+
+ DB((cg->mod, LEVEL_1, "%+F\n", irn));
+ }
+}
+
+static int is_CondJmp_cand(const ir_node *irn) {
+ return is_ia32_CondJmp(irn) || is_ia32_Sub(irn);
+}
+
+/**
+ * Checks if the arguments of cand are the same of irn.
+ */
+static int is_CondJmp_replacement(ir_node *cand, ir_node *irn) {
+ int i, n = get_irn_arity(cand);
+ int same_args = 1;
+
+ for (i = 0; i < n; i++) {
+ if (get_irn_n(cand, i) != get_irn_n(irn, i)) {
+ same_args = 0;
+ break;
+ }
+ }
+
+ if (same_args)
+ return ia32_cnst_compare(cand, irn);
+
+ return 0;
+}
+
+/**
+ * Tries to replace a CondJmp by a CJmpAM
+ */
+static void ia32_optimize_CondJmp(ir_node *irn, ia32_code_gen_t *cg) {
+ ir_node *cand = ia32_determine_cjmp_cand(irn, is_CondJmp_cand);
+ int replace = 0;
+
+ /* we found a possible candidate */
+ replace = cand ? is_CondJmp_replacement(cand, irn) : 0;
+
+ if (replace) {
+ DBG((cg->mod, LEVEL_1, "replacing %+F by ", irn));
+ DBG_OPT_CJMP(irn);
+
+ set_irn_op(irn, op_ia32_CJmpAM);
+
+ DB((cg->mod, LEVEL_1, "%+F\n", irn));
+ }
+}
+
+/**
+ * Creates a Push from Store(IncSP(gp_reg_size))
+ */
+static void ia32_create_Push(ir_node *irn, ia32_code_gen_t *cg) {
+ ir_node *sp = get_irn_n(irn, 0);
+ ir_node *val, *next, *push, *bl, *proj_M, *proj_res, *old_proj_M;
+ const ir_edge_t *edge;
+
+ if (get_ia32_am_offs(irn) || !be_is_IncSP(sp))
+ return;
+
+ if (arch_get_irn_register(cg->arch_env, get_irn_n(irn, 1)) !=
+ &ia32_gp_regs[REG_GP_NOREG])
+ return;
+
+ val = get_irn_n(irn, 2);
+ if (mode_is_float(get_irn_mode(val)))
+ return;
+
+ if (be_get_IncSP_direction(sp) != be_stack_dir_expand ||
+ be_get_IncSP_offset(sp) != get_mode_size_bytes(ia32_reg_classes[CLASS_ia32_gp].mode))
+ return;
+
+ /* ok, translate into Push */
+ edge = get_irn_out_edge_first(irn);
+ old_proj_M = get_edge_src_irn(edge);
+
+ next = sched_next(irn);
+ sched_remove(irn);
+ sched_remove(sp);
+
+ bl = get_nodes_block(irn);
+ push = new_rd_ia32_Push(NULL, current_ir_graph, bl,
+ be_get_IncSP_pred(sp), val, be_get_IncSP_mem(sp));
+ proj_res = new_r_Proj(current_ir_graph, bl, push, get_irn_mode(sp), pn_ia32_Push_stack);
+ proj_M = new_r_Proj(current_ir_graph, bl, push, mode_M, pn_ia32_Push_M);
+
+ /* copy a possible constant from the store */
+ set_ia32_id_cnst(push, get_ia32_id_cnst(irn));
+ set_ia32_immop_type(push, get_ia32_immop_type(irn));
+
+ /* the push must have SP out register */
+ arch_set_irn_register(cg->arch_env, push, arch_get_irn_register(cg->arch_env, sp));
+
+ exchange(old_proj_M, proj_M);
+ exchange(sp, proj_res);
+ sched_add_before(next, push);
+ sched_add_after(push, proj_res);
+}
+
+/**
+ * Creates a Pop from IncSP(Load(sp))
+ */
+static void ia32_create_Pop(ir_node *irn, ia32_code_gen_t *cg) {
+ ir_node *old_proj_M = be_get_IncSP_mem(irn);
+ ir_node *load = skip_Proj(old_proj_M);
+ ir_node *old_proj_res = NULL;
+ ir_node *bl, *pop, *next, *proj_res, *proj_sp, *proj_M;
+ const ir_edge_t *edge;
+ const arch_register_t *reg, *sp;
+
+ if (! is_ia32_Load(load) || get_ia32_am_offs(load))
+ return;
+
+ if (arch_get_irn_register(cg->arch_env, get_irn_n(load, 1)) !=
+ &ia32_gp_regs[REG_GP_NOREG])
+ return;
+ if (arch_get_irn_register(cg->arch_env, get_irn_n(load, 0)) != cg->isa->arch_isa.sp)
+ return;
+
+ /* ok, translate into pop */
+ foreach_out_edge(load, edge) {
+ ir_node *succ = get_edge_src_irn(edge);
+ if (succ != old_proj_M) {
+ old_proj_res = succ;
+ break;
+ }
+ }
+ if (! old_proj_res) {
+ assert(0);
+ return; /* should not happen */
+ }
+
+ bl = get_nodes_block(load);
+
+ /* IncSP is typically scheduled after the load, so remove it first */
+ sched_remove(irn);
+ next = sched_next(old_proj_res);
+ sched_remove(old_proj_res);
+ sched_remove(load);
+
+ reg = arch_get_irn_register(cg->arch_env, load);
+ sp = arch_get_irn_register(cg->arch_env, irn);
+
+ pop = new_rd_ia32_Pop(NULL, current_ir_graph, bl, get_irn_n(irn, 0), get_irn_n(load, 2));
+ proj_res = new_r_Proj(current_ir_graph, bl, pop, get_irn_mode(old_proj_res), pn_ia32_Pop_res);
+ proj_sp = new_r_Proj(current_ir_graph, bl, pop, get_irn_mode(irn), pn_ia32_Pop_stack);
+ proj_M = new_r_Proj(current_ir_graph, bl, pop, mode_M, pn_ia32_Pop_M);
+
+ exchange(old_proj_M, proj_M);
+ exchange(old_proj_res, proj_res);
+ exchange(irn, proj_sp);
+
+ arch_set_irn_register(cg->arch_env, proj_res, reg);
+ arch_set_irn_register(cg->arch_env, proj_sp, sp);
+
+ sched_add_before(next, proj_sp);
+ sched_add_before(proj_sp, proj_res);
+ sched_add_before(proj_res,pop);
+}
+
+/**
+ * Tries to optimize two following IncSP.
+ */
+static void ia32_optimize_IncSP(ir_node *irn, ia32_code_gen_t *cg) {
+ ir_node *prev = be_get_IncSP_pred(irn);
+ int real_uses = get_irn_n_edges(prev);
+
+ if (be_is_IncSP(prev) && real_uses == 1) {
+ /* first IncSP has only one IncSP user, kill the first one */
+ unsigned prev_offs = be_get_IncSP_offset(prev);
+ be_stack_dir_t prev_dir = be_get_IncSP_direction(prev);
+ unsigned curr_offs = be_get_IncSP_offset(irn);
+ be_stack_dir_t curr_dir = be_get_IncSP_direction(irn);
+
+ int new_ofs = prev_offs * (prev_dir == be_stack_dir_expand ? -1 : +1) +
+ curr_offs * (curr_dir == be_stack_dir_expand ? -1 : +1);
+
+ if (new_ofs < 0) {
+ new_ofs = -new_ofs;
+ curr_dir = be_stack_dir_expand;
+ }
+ else
+ curr_dir = be_stack_dir_shrink;
+ be_set_IncSP_offset(prev, 0);
+ be_set_IncSP_offset(irn, (unsigned)new_ofs);
+ be_set_IncSP_direction(irn, curr_dir);
+
+ /* Omit the optimized IncSP */
+ be_set_IncSP_pred(irn, be_get_IncSP_pred(prev));
+ }
+}
+
+/**
+ * Performs Peephole Optimizations.
+ */
+void ia32_peephole_optimization(ir_node *irn, void *env) {
+ ia32_code_gen_t *cg = env;
+
+ if (is_ia32_TestJmp(irn))
+ ia32_optimize_TestJmp(irn, cg);
+ else if (is_ia32_CondJmp(irn))
+ ia32_optimize_CondJmp(irn, cg);
+ /* seems to be buggy when using Pushes */
+// else if (be_is_IncSP(irn))
+// ia32_optimize_IncSP(irn, cg);
+ else if (is_ia32_Store(irn))
+ ia32_create_Push(irn, cg);
+}
+
+
+