fix fehler69
[libfirm] / ir / be / ia32 / ia32_optimize.c
index afb9f74..e5790fb 100644 (file)
@@ -30,6 +30,7 @@
 #include "irnode.h"
 #include "irprog_t.h"
 #include "ircons.h"
+#include "irtools.h"
 #include "firm_types.h"
 #include "iredges.h"
 #include "tv.h"
@@ -372,14 +373,14 @@ static int is_addr_candidate(const ir_node *irn)
        right = get_irn_n(irn, 3);
 
        if (pred_is_specific_nodeblock(block, left, is_ia32_Ld)) {
-               n         = ia32_get_irn_n_edges(left);
+               n = ia32_get_irn_n_edges(left);
                /* load with only one user: don't create LEA */
                if(n == 1)
                        return 0;
        }
 
        if (pred_is_specific_nodeblock(block, right, is_ia32_Ld)) {
-               n         = ia32_get_irn_n_edges(right);
+               n = ia32_get_irn_n_edges(right);
                if(n == 1)
                        return 0;
        }
@@ -406,7 +407,8 @@ static int is_addr_candidate(const ir_node *irn)
 static ia32_am_cand_t is_am_candidate(heights_t *h, const ir_node *block, ir_node *irn) {
        ir_node *in, *load, *other, *left, *right;
        int      is_cand = 0, cand;
-       int arity;
+       int      arity;
+       int      is_binary;
 
        if (is_ia32_Ld(irn) || is_ia32_St(irn) ||
                is_ia32_vfild(irn) || is_ia32_vfist(irn) ||
@@ -416,9 +418,10 @@ static ia32_am_cand_t is_am_candidate(heights_t *h, const ir_node *block, ir_nod
        if(get_ia32_frame_ent(irn) != NULL)
                return IA32_AM_CAND_NONE;
 
-       left  = get_irn_n(irn, 2);
-       arity = get_irn_arity(irn);
-       if(get_ia32_am_arity(irn) == ia32_am_binary) {
+       left      = get_irn_n(irn, 2);
+       arity     = get_irn_arity(irn);
+       is_binary = get_ia32_am_arity(irn) == ia32_am_binary;
+       if(is_binary) {
                /* binary op */
                right = get_irn_n(irn, 3);
        } else {
@@ -449,7 +452,7 @@ static ia32_am_cand_t is_am_candidate(heights_t *h, const ir_node *block, ir_nod
                }
 
                /* If there is a data dependency of other irn from load: cannot use AM */
-               if (is_cand && get_nodes_block(other) == block) {
+               if (is_cand && is_binary && get_nodes_block(other) == block) {
                        other   = skip_Proj(other);
                        is_cand = heights_reachable_in_block(h, other, load) ? 0 : is_cand;
                        /* this could happen in loops */
@@ -474,11 +477,15 @@ static ia32_am_cand_t is_am_candidate(heights_t *h, const ir_node *block, ir_nod
                other = left;
 
                /* 8bit Loads are not supported, they cannot be used with every register */
-               if (get_mode_size_bits(get_ia32_ls_mode(load)) < 16)
+               /* 8bit Loads are not supported (for binary ops),
+                * they cannot be used with every register */
+               if (get_ia32_am_arity(irn) == ia32_am_binary &&
+                               get_mode_size_bits(get_ia32_ls_mode(load)) < 16) {
                        is_cand = 0;
+               }
 
                /* If there is a data dependency of other irn from load: cannot use load */
-               if (is_cand && get_nodes_block(other) == block) {
+               if (is_cand && is_binary && get_nodes_block(other) == block) {
                        other   = skip_Proj(other);
                        is_cand = heights_reachable_in_block(h, other, load) ? 0 : is_cand;
                        /* this could happen in loops */
@@ -1156,15 +1163,19 @@ static void optimize_load_conv(ir_node *node)
 
 static void optimize_conv_conv(ir_node *node)
 {
-       ir_node *pred;
-       ir_mode *pred_mode;
-       ir_mode *conv_mode;
+       ir_node *pred_proj, *pred, *result_conv;
+       ir_mode *pred_mode, *conv_mode;
 
        if (!is_ia32_Conv_I2I(node) && !is_ia32_Conv_I2I8Bit(node))
                return;
 
        assert(n_ia32_Conv_I2I_val == n_ia32_Conv_I2I8Bit_val);
-       pred = get_irn_n(node, n_ia32_Conv_I2I_val);
+       pred_proj = get_irn_n(node, n_ia32_Conv_I2I_val);
+       if(is_Proj(pred_proj))
+               pred = get_Proj_pred(pred_proj);
+       else
+               pred = pred_proj;
+
        if(!is_ia32_Conv_I2I(pred) && !is_ia32_Conv_I2I8Bit(pred))
                return;
 
@@ -1172,16 +1183,43 @@ static void optimize_conv_conv(ir_node *node)
         * so we only need the 2nd conv if it shrinks the mode */
        conv_mode = get_ia32_ls_mode(node);
        pred_mode = get_ia32_ls_mode(pred);
-       if(get_mode_size_bits(conv_mode) < get_mode_size_bits(pred_mode))
-               return;
+       /* if 2nd conv is smaller then first conv, then we can always take the 2nd
+        * conv */
+       if(get_mode_size_bits(conv_mode) <= get_mode_size_bits(pred_mode)) {
+               if(get_irn_n_edges(pred_proj) == 1) {
+                       result_conv = pred_proj;
+                       set_ia32_ls_mode(pred, conv_mode);
+               } else {
+                       /* TODO: construct syncs/stuff here but we'll probably end up with
+                        * 2 statements anyway */
+                       if(get_irn_mode(pred) == mode_T) {
+                               return;
+                       }
 
-       /* we can't eliminate an upconv signed->unsigned  */
-       if (get_mode_size_bits(conv_mode) != get_mode_size_bits(pred_mode) &&
-               !get_mode_sign(conv_mode) && get_mode_sign(pred_mode))
-               return;
+                       result_conv = exact_copy(pred);
+                       set_ia32_ls_mode(result_conv, conv_mode);
+               }
+       } else {
+               /* if both convs have the same sign, then we can take the smaller one */
+               if(get_mode_sign(conv_mode) == get_mode_sign(pred_mode)) {
+                       result_conv = pred_proj;
+               } else {
+                       /* no optimisation possible if smaller conv is sign-extend */
+                       if(mode_is_signed(pred_mode)) {
+                               return;
+                       }
+                       /* we can take the smaller conv if it is unsigned */
+                       result_conv = pred_proj;
+               }
+       }
 
        /* kill the conv */
-       exchange(node, pred);
+       exchange(node, result_conv);
+
+       if(get_irn_n_edges(pred) == 0) {
+               be_kill_node(pred);
+       }
+       optimize_conv_conv(result_conv);
 }
 
 static void optimize_node(ir_node *node, void *env)
@@ -1426,20 +1464,29 @@ static void optimize_am(ir_node *irn, void *env) {
                assert(cand & IA32_AM_CAND_RIGHT);
                load = get_Proj_pred(right);
 
+               if(get_irn_n_edges(right) > 1) {
+                       source_possible = 0;
+               }
+#if 1
+               /* TODO: this isn't really needed, but the code below is buggy
+                  as heights won't get recomputed when the graph is reconstructed
+                  so we can only transform loads with the result proj only */
                if(get_irn_n_edges(load) > 1) {
                        source_possible = 0;
                }
+#endif
        }
 
        if (source_possible) {
                ir_mode *ls_mode = get_ia32_ls_mode(load);
-               if(get_mode_size_bits(ls_mode) != 32
-                               || ls_mode == mode_D)
+               if(get_mode_size_bits(ls_mode) != 32 || ls_mode == mode_D)
                        source_possible = 0;
 
        }
 
        if (source_possible) {
+               const ia32_attr_t *attr_load = get_ia32_attr_const(load);
+               ia32_attr_t       *attr_irn  = get_ia32_attr(irn);
                addr_b = get_irn_n(load, 0);
                addr_i = get_irn_n(load, 1);
 
@@ -1451,7 +1498,16 @@ static void optimize_am(ir_node *irn, void *env) {
                set_ia32_am_flavour(irn, get_ia32_am_flavour(load));
                set_ia32_op_type(irn, ia32_AddrModeS);
                set_ia32_frame_ent(irn, get_ia32_frame_ent(load));
-               set_ia32_ls_mode(irn, get_ia32_ls_mode(load));
+               attr_irn->data.need_64bit_stackent
+                       = attr_load->data.need_64bit_stackent;
+               attr_irn->data.need_32bit_stackent
+                       = attr_load->data.need_32bit_stackent;
+
+               /* set ls_mode if not already present (conv nodes already have ls_mode
+                  set) */
+               if(get_ia32_ls_mode(irn) == NULL) {
+                       set_ia32_ls_mode(irn, get_ia32_ls_mode(load));
+               }
 
                set_ia32_am_sc(irn, get_ia32_am_sc(load));
                if (is_ia32_am_sc_sign(load))
@@ -1488,17 +1544,21 @@ static void optimize_am(ir_node *irn, void *env) {
                        ir_node *res_proj;
                        ir_mode *mode = get_irn_mode(irn);
 
-                       assert(mode != mode_T);
-
-                       res_proj = new_rd_Proj(get_irn_dbg_info(irn), irg,
-                                              get_nodes_block(irn), new_Unknown(mode_T),
-                                              mode, 0);
-                       set_irn_mode(irn, mode_T);
-                       edges_reroute(irn, res_proj, irg);
-                       set_Proj_pred(res_proj, irn);
+                       if(mode != mode_T) {
+                               res_proj = new_rd_Proj(get_irn_dbg_info(irn), irg,
+                                                                          get_nodes_block(irn),
+                                                                          new_Unknown(mode_T), mode, 0);
+                               set_irn_mode(irn, mode_T);
+                               edges_reroute(irn, res_proj, irg);
+                               set_Proj_pred(res_proj, irn);
 
-                       set_Proj_pred(mem_proj, irn);
-                       set_Proj_proj(mem_proj, 1);
+                               set_Proj_pred(mem_proj, irn);
+                               set_Proj_proj(mem_proj, 1);
+                       } else {
+                               /* hacky: we need some proj number which is not used yet... */
+                               set_Proj_proj(mem_proj, -1);
+                               set_Proj_pred(mem_proj, irn);
+                       }
                }
 
                try_kill(load);