More missing config.h
[libfirm] / ir / be / ia32 / ia32_finish.c
index d376e4a..45f616b 100644 (file)
@@ -4,6 +4,10 @@
  * $Id$
  */
 
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
 #include "irnode.h"
 #include "ircons.h"
 #include "irgmod.h"
@@ -21,6 +25,7 @@
 #include "ia32_transform.h"
 #include "ia32_dbg_stat.h"
 #include "ia32_optimize.h"
+#include "gen_ia32_regalloc_if.h"
 
 /**
  * Transforms a Sub or xSub into Neg--Add iff OUT_REG == SRC2_REG.
@@ -32,7 +37,7 @@ static void ia32_transform_sub_to_neg_add(ir_node *irn, ia32_code_gen_t *cg) {
        const arch_register_t *in1_reg, *in2_reg, *out_reg, **slots;
 
        /* Return if AM node or not a Sub or xSub */
-       if (get_ia32_op_type(irn) != ia32_Normal || !(is_ia32_Sub(irn) || is_ia32_xSub(irn)))
+       if (!(is_ia32_Sub(irn) || is_ia32_xSub(irn)) || get_ia32_op_type(irn) != ia32_Normal)
                return;
 
        noreg   = ia32_new_NoReg_gp(cg);
@@ -58,6 +63,7 @@ static void ia32_transform_sub_to_neg_add(ir_node *irn, ia32_code_gen_t *cg) {
                arch_set_irn_register(cg->arch_env, res, in2_reg);
 
                /* add to schedule */
+               sched_add_before(irn, get_Proj_pred(res));
                sched_add_before(irn, res);
 
                /* generate the add */
@@ -99,9 +105,10 @@ static void ia32_transform_lea_to_add(ir_node *irn, ia32_code_gen_t *cg) {
        int               imm = 0;
        ir_node          *res = NULL;
        ir_node          *nomem, *noreg, *base, *index, *op1, *op2;
-       char             *offs;
+       const char       *offs = NULL;
        ia32_transform_env_t tenv;
        const arch_register_t *out_reg, *base_reg, *index_reg;
+       int              imm_tp = ia32_ImmConst;
 
        /* must be a LEA */
        if (! is_ia32_Lea(irn))
@@ -123,11 +130,20 @@ static void ia32_transform_lea_to_add(ir_node *irn, ia32_code_gen_t *cg) {
        base  = get_irn_n(irn, 0);
        index = get_irn_n(irn,1);
 
-       offs  = get_ia32_am_offs(irn);
+       if (am_flav & ia32_O) {
+               offs  = get_ia32_am_offs(irn);
+
+               if (! offs) {
+                       ident *id = get_ia32_am_sc(irn);
 
-       /* offset has a explicit sign -> we need to skip + */
-       if (offs && offs[0] == '+')
-               offs++;
+                       assert(id != NULL);
+                       offs   = get_id_str(id);
+                       imm_tp = ia32_ImmSymConst;
+               }
+               /* offset has a explicit sign -> we need to skip + */
+               else if (offs[0] == '+')
+                       offs++;
+       }
 
        out_reg   = arch_get_irn_register(cg->arch_env, irn);
        base_reg  = arch_get_irn_register(cg->arch_env, base);
@@ -191,7 +207,7 @@ static void ia32_transform_lea_to_add(ir_node *irn, ia32_code_gen_t *cg) {
 
        if (imm) {
                set_ia32_cnst(res, offs);
-               set_ia32_immop_type(res, ia32_ImmConst);
+               set_ia32_immop_type(res, imm_tp);
        }
 
        SET_IA32_ORIG_NODE(res, ia32_get_old_node_name(cg, irn));
@@ -219,6 +235,7 @@ static INLINE int need_constraint_copy(ir_node *irn) {
                ! is_ia32_Conv_I2I(irn)     && \
                ! is_ia32_Conv_I2I8Bit(irn) && \
                ! is_ia32_CmpCMov(irn)      && \
+               ! is_ia32_PsiCondCMov(irn)  && \
                ! is_ia32_CmpSet(irn);
 }
 
@@ -297,10 +314,37 @@ insert_copy:
                        }
                }
 
-               /* If we have a CondJmp/CmpSet/xCmpSet with immediate, we need to    */
-               /* check if it's the right operand, otherwise we have */
-               /* to change it, as CMP doesn't support immediate as  */
-               /* left operands.                                     */
+               /* check xCmp: try to avoid unordered cmp */
+               if ((is_ia32_xCmp(irn) || is_ia32_xCmpCMov(irn) || is_ia32_xCmpSet(irn)) &&
+                       op_tp == ia32_Normal    &&
+                       ! is_ia32_ImmConst(irn) && ! is_ia32_ImmSymConst(irn))
+               {
+                       long pnc = get_ia32_pncode(irn);
+
+                       if (pnc & pn_Cmp_Uo) {
+                               ir_node *tmp;
+                               int idx1 = 2, idx2 = 3;
+
+                               if (is_ia32_xCmpCMov(irn)) {
+                                       idx1 = 0;
+                                       idx2 = 1;
+                               }
+
+                               tmp = get_irn_n(irn, idx1);
+                               set_irn_n(irn, idx1, get_irn_n(irn, idx2));
+                               set_irn_n(irn, idx2, tmp);
+
+                               set_ia32_pncode(irn, get_negated_pnc(pnc, mode_D));
+                       }
+               }
+
+               /*
+                       If we have a CondJmp/CmpSet/xCmpSet with immediate,
+                       we need to check if it's the right operand, otherwise
+                       we have to change it, as CMP doesn't support immediate
+                       as left operands.
+               */
+#if 0
                if ((is_ia32_CondJmp(irn) || is_ia32_CmpSet(irn) || is_ia32_xCmpSet(irn)) &&
                        (is_ia32_ImmConst(irn) || is_ia32_ImmSymConst(irn))                   &&
                        op_tp == ia32_AddrModeS)
@@ -308,22 +352,136 @@ insert_copy:
                        set_ia32_op_type(irn, ia32_AddrModeD);
                        set_ia32_pncode(irn, get_inversed_pnc(get_ia32_pncode(irn)));
                }
+#endif
+       }
+end: ;
+}
 
-               /* check if there is a sub which need to be transformed */
-               ia32_transform_sub_to_neg_add(irn, cg);
+/**
+ * Following Problem:
+ * We have a source address mode node with base or index register equal to
+ * result register. The constraint handler will insert a copy from the
+ * remaining input operand to the result register -> base or index is
+ * broken then.
+ * Solution: Turn back this address mode into explicit Load + Operation.
+ */
+static void fix_am_source(ir_node *irn, void *env) {
+       ia32_code_gen_t *cg = env;
+       ir_node *base, *index, *noreg;
+       const arch_register_t *reg_base, *reg_index;
+       const ia32_register_req_t **reqs;
+       int n_res, i;
 
-               /* transform a LEA into an Add if possible */
-               ia32_transform_lea_to_add(irn, cg);
-       }
-end:
+       /* check only ia32 nodes with source address mode */
+       if (! is_ia32_irn(irn) || get_ia32_op_type(irn) != ia32_AddrModeS)
+               return;
+       /* no need to fix unary operations */
+       if (get_irn_arity(irn) == 4)
+               return;
+
+       base  = get_irn_n(irn, 0);
+       index = get_irn_n(irn, 1);
+
+       reg_base  = arch_get_irn_register(cg->arch_env, base);
+       reg_index = arch_get_irn_register(cg->arch_env, index);
+       reqs      = get_ia32_out_req_all(irn);
+
+       noreg = ia32_new_NoReg_gp(cg);
 
-       /* check for peephole optimization */
-       ia32_peephole_optimization(irn, cg);
+       n_res = get_ia32_n_res(irn);
+
+       for (i = 0; i < n_res; i++) {
+               if (arch_register_req_is(&(reqs[i]->req), should_be_same)) {
+                       /* get in and out register */
+                       const arch_register_t *out_reg  = get_ia32_out_reg(irn, i);
+
+                       /*
+                               there is a constraint for the remaining operand
+                               and the result register is equal to base or index register
+                       */
+                       if (reqs[i]->same_pos == 2 &&
+                               (REGS_ARE_EQUAL(out_reg, reg_base) || REGS_ARE_EQUAL(out_reg, reg_index)))
+                       {
+                               /* turn back address mode */
+                               ir_node               *in_node = get_irn_n(irn, 2);
+                               const arch_register_t *in_reg  = arch_get_irn_register(cg->arch_env, in_node);
+                               ir_node               *block   = get_nodes_block(irn);
+                               ir_mode               *ls_mode = get_ia32_ls_mode(irn);
+                               ir_node *load;
+                               int pnres;
+
+                               if (arch_register_get_class(in_reg) == &ia32_reg_classes[CLASS_ia32_gp]) {
+                                       load  = new_rd_ia32_Load(NULL, cg->irg, block, base, index, get_irn_n(irn, 4));
+                                       pnres = pn_ia32_Load_res;
+                               }
+                               else if (arch_register_get_class(in_reg) == &ia32_reg_classes[CLASS_ia32_xmm]) {
+                                       load  = new_rd_ia32_xLoad(NULL, cg->irg, block, base, index, get_irn_n(irn, 4));
+                                       pnres = pn_ia32_xLoad_res;
+                               }
+                               else {
+                                       assert(0 && "cannot turn back address mode for this register class");
+                               }
+
+                               /* copy address mode information to load */
+                               set_ia32_ls_mode(load, ls_mode);
+                               set_ia32_am_flavour(load, get_ia32_am_flavour(irn));
+                               set_ia32_op_type(load, ia32_AddrModeS);
+                               set_ia32_am_support(load, ia32_am_Source);
+                               set_ia32_am_scale(load, get_ia32_am_scale(irn));
+                               set_ia32_am_sc(load, get_ia32_am_sc(irn));
+                               add_ia32_am_offs(load, get_ia32_am_offs(irn));
+                               set_ia32_frame_ent(load, get_ia32_frame_ent(irn));
+
+                               if (is_ia32_use_frame(irn))
+                                       set_ia32_use_frame(load);
+
+                               /* insert the load into schedule */
+                               sched_add_before(irn, load);
+
+                               DBG((cg->mod, LEVEL_3, "irg %+F: build back AM source for node %+F, inserted load %+F\n", cg->irg, irn, load));
+
+                               load = new_r_Proj(cg->irg, block, load, ls_mode, pnres);
+                               arch_set_irn_register(cg->arch_env, load, out_reg);
+
+                               /* insert the load result proj into schedule */
+                               sched_add_before(irn, load);
+
+                               /* set the new input operand */
+                               set_irn_n(irn, 3, load);
+
+                               /* this is a normal node now */
+                               set_irn_n(irn, 0, noreg);
+                               set_irn_n(irn, 1, noreg);
+                               set_ia32_op_type(irn, ia32_Normal);
+
+                               break;
+                       }
+               }
+       }
 }
 
 static void ia32_finish_irg_walker(ir_node *block, void *env) {
        ir_node *irn, *next;
 
+       /* first: turn back AM source if necessary */
+       for (irn = sched_first(block); ! sched_is_end(irn); irn = next) {
+               next = sched_next(irn);
+               fix_am_source(irn, env);
+       }
+
+       for (irn = sched_first(block); ! sched_is_end(irn); irn = next) {
+               ia32_code_gen_t *cg = env;
+
+               next = sched_next(irn);
+
+               /* check if there is a sub which need to be transformed */
+               ia32_transform_sub_to_neg_add(irn, cg);
+
+               /* transform a LEA into an Add if possible */
+               ia32_transform_lea_to_add(irn, cg);
+       }
+
+       /* second: insert copies and finish irg */
        for (irn = sched_first(block); ! sched_is_end(irn); irn = next) {
                next = sched_next(irn);
                ia32_finish_node(irn, env);