some comments added
[libfirm] / ir / be / ia32 / ia32_address_mode.c
index 72f204a..101de87 100644 (file)
  * @brief       This file contains functions for matching firm graphs for
  *              nodes that can be used as addressmode for x86 commands
  * @author      Matthias Braun
- * @version     $Id: ia32_transform.c 15462 2007-08-03 16:50:59Z matze $
+ * @version     $Id$
  */
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
 
 #include "ia32_address_mode.h"
+#include "ia32_transform.h"
 
 #include "irtypes.h"
 #include "irnode_t.h"
 #include "irprintf.h"
 #include "error.h"
 #include "iredges_t.h"
+#include "irgwalk.h"
 
 #include "../benode_t.h"
 
-#undef AGGRESSIVE_AM
+#define AGGRESSIVE_AM
 
-static int is_immediate_2(const ir_node *node, int *symconsts)
+/* gas/ld don't support negative symconsts :-( */
+#undef SUPPORT_NEGATIVE_SYMCONSTS
+
+static bitset_t *non_address_mode_nodes;
+
+static int do_is_immediate(const ir_node *node, int *symconsts, int negate)
 {
        ir_node *left;
        ir_node *right;
@@ -49,13 +56,19 @@ static int is_immediate_2(const ir_node *node, int *symconsts)
        case iro_Const:
                if(!tarval_is_long(get_Const_tarval(node))) {
 #ifdef DEBUG_libfirm
-                       ir_fprintf(stderr, "Optimisation warning tarval of %+F is not a "
-                                  "long.");
+                       ir_fprintf(stderr, "Optimisation warning tarval of %+F(%+F) is not "
+                                  "a long.\n", node, current_ir_graph);
 #endif
                        return 0;
                }
                return 1;
        case iro_SymConst:
+#ifndef SUPPORT_NEGATIVE_SYMCONSTS
+               /* unfortunately the assembler/linker doesn't support -symconst */
+               if(negate)
+                       return 0;
+#endif
+
                if(get_SymConst_kind(node) != symconst_addr_ent)
                        return 0;
                (*symconsts)++;
@@ -65,11 +78,14 @@ static int is_immediate_2(const ir_node *node, int *symconsts)
                return 1;
        case iro_Add:
        case iro_Sub:
+               if(bitset_is_set(non_address_mode_nodes, get_irn_idx(node)))
+                       return 0;
+
                left  = get_binop_left(node);
                right = get_binop_right(node);
-               if(!is_immediate_2(left, symconsts))
+               if(!do_is_immediate(left, symconsts, negate))
                        return 0;
-               if(!is_immediate_2(right, symconsts))
+               if(!do_is_immediate(right, symconsts, is_Sub(node) ? !negate : negate))
                        return 0;
 
                return 1;
@@ -80,13 +96,19 @@ static int is_immediate_2(const ir_node *node, int *symconsts)
        return 0;
 }
 
-static int is_immediate(ia32_address_t *addr, const ir_node *node)
+static int is_immediate_simple(const ir_node *node)
+{
+       int symconsts = 0;
+       return do_is_immediate(node, &symconsts, 0);
+}
+
+static int is_immediate(ia32_address_t *addr, const ir_node *node, int negate)
 {
        int symconsts = 0;
        if(addr->symconst_ent != NULL)
                symconsts = 1;
 
-       return is_immediate_2(node, &symconsts);
+       return do_is_immediate(node, &symconsts, negate);
 }
 
 static void eat_immediate(ia32_address_t *addr, ir_node *node, int negate)
@@ -94,14 +116,16 @@ static void eat_immediate(ia32_address_t *addr, ir_node *node, int negate)
        tarval  *tv;
        ir_node *left;
        ir_node *right;
+  long val;
 
        switch(get_irn_opcode(node)) {
        case iro_Const:
                tv = get_Const_tarval(node);
+               val = get_tarval_long(tv);
                if(negate) {
-                       addr->offset -= get_tarval_long(tv);
+                       addr->offset -= val;
                } else {
-                       addr->offset += get_tarval_long(tv);
+                       addr->offset += val;
                }
                break;
        case iro_SymConst:
@@ -110,15 +134,20 @@ static void eat_immediate(ia32_address_t *addr, ir_node *node, int negate)
                              "calculation");
                }
                addr->symconst_ent  = get_SymConst_entity(node);
+#ifndef SUPPORT_NEGATIVE_SYMCONSTS
+               assert(!negate);
+#endif
                addr->symconst_sign = negate;
                break;
        case iro_Add:
+               assert(!bitset_is_set(non_address_mode_nodes, get_irn_idx(node)));
                left  = get_Add_left(node);
                right = get_Add_right(node);
                eat_immediate(addr, left, negate);
                eat_immediate(addr, right, negate);
                break;
        case iro_Sub:
+               assert(!bitset_is_set(non_address_mode_nodes, get_irn_idx(node)));
                left  = get_Sub_left(node);
                right = get_Sub_right(node);
                eat_immediate(addr, left, negate);
@@ -131,24 +160,18 @@ static void eat_immediate(ia32_address_t *addr, ir_node *node, int negate)
 
 static ir_node *eat_immediates(ia32_address_t *addr, ir_node *node, int force)
 {
+       if(!force && bitset_is_set(non_address_mode_nodes, get_irn_idx(node)))
+               return node;
+
        if(is_Add(node)) {
                ir_node *left  = get_Add_left(node);
                ir_node *right = get_Add_right(node);
 
-#if 0
-#ifndef AGGRESSIVE_AM
-               if(!force && get_irn_n_edges(node) > 1)
-                       return node;
-#else
-               (void) force;
-#endif
-#endif
-
-               if(is_immediate(addr, left)) {
+               if(is_immediate(addr, left, 0)) {
                        eat_immediate(addr, left, 0);
                        return eat_immediates(addr, right, 0);
                }
-               if(is_immediate(addr, right)) {
+               if(is_immediate(addr, right, 0)) {
                        eat_immediate(addr, right, 0);
                        return eat_immediates(addr, left, 0);
                }
@@ -156,14 +179,7 @@ static ir_node *eat_immediates(ia32_address_t *addr, ir_node *node, int force)
                ir_node *left  = get_Sub_left(node);
                ir_node *right = get_Sub_right(node);
 
-#if 0
-#ifndef AGGRESSIVE_AM
-               if(!force && get_irn_n_edges(node) > 1)
-                       return node;
-#endif
-#endif
-
-               if(is_immediate(addr, right)) {
+               if(is_immediate(addr, right, 1)) {
                        eat_immediate(addr, right, 1);
                        return eat_immediates(addr, left, 0);
                }
@@ -194,6 +210,8 @@ static int eat_shl(ia32_address_t *addr, ir_node *node)
        if(val == 0) {
                ir_fprintf(stderr, "Optimisation warning: unoptimized Shl(,0) found\n");
        }
+       if(bitset_is_set(non_address_mode_nodes, get_irn_idx(node)))
+               return 0;
 
 #ifndef AGGRESSIVE_AM
        if(get_irn_n_edges(node) > 1)
@@ -205,12 +223,60 @@ static int eat_shl(ia32_address_t *addr, ir_node *node)
        return 1;
 }
 
+/**
+ * Returns non-zero if a value of a given mode can be stored in GP registers.
+ */
+static INLINE int mode_needs_gp_reg(ir_mode *mode) {
+       if(mode == mode_fpcw)
+               return 0;
+       if(get_mode_size_bits(mode) > 32)
+               return 0;
+       return mode_is_int(mode) || mode_is_reference(mode) || mode == mode_b;
+}
+
+/**
+ * Check, if a given done is a Down-Conv, ie a integer Conv
+ * from a mode with mode to a mode with lesser bits.
+ * Moreover, we return only true if the node has not more than 1 user.
+ */
+static int is_downconv(const ir_node *node)
+{
+       ir_mode *src_mode;
+       ir_mode *dest_mode;
+
+       if(!is_Conv(node))
+               return 0;
+
+       /* we only want to skip the conv when we're the only user
+        * (not optimal but for now...)
+        */
+       if(get_irn_n_edges(node) > 1)
+               return 0;
+
+       src_mode  = get_irn_mode(get_Conv_op(node));
+       dest_mode = get_irn_mode(node);
+       return mode_needs_gp_reg(src_mode)
+               && mode_needs_gp_reg(dest_mode)
+               && get_mode_size_bits(dest_mode) < get_mode_size_bits(src_mode);
+}
+
+/**
+ * Skip all Donw-Conv's on a given node.
+ */
+static ir_node *skip_downconv(ir_node *node)
+{
+       while(is_downconv(node))
+               node = get_Conv_op(node);
+
+       return node;
+}
+
 void ia32_create_address_mode(ia32_address_t *addr, ir_node *node, int force)
 {
        int      res = 0;
        ir_node *eat_imms;
 
-       if(is_immediate(addr, node)) {
+       if(is_immediate(addr, node, 0)) {
                eat_immediate(addr, node, 0);
                return;
        }
@@ -222,8 +288,17 @@ void ia32_create_address_mode(ia32_address_t *addr, ir_node *node, int force)
        }
 #endif
 
+       if(!force && bitset_is_set(non_address_mode_nodes, get_irn_idx(node))) {
+               addr->base = node;
+               return;
+       }
+
        eat_imms = eat_immediates(addr, node, force);
        if(eat_imms != node) {
+               if(force) {
+                       eat_imms = skip_downconv(eat_imms);
+               }
+
                res  = 1;
                node = eat_imms;
 #ifndef AGGRESSIVE_AM
@@ -232,13 +307,17 @@ void ia32_create_address_mode(ia32_address_t *addr, ir_node *node, int force)
                        return;
                }
 #endif
+               if(bitset_is_set(non_address_mode_nodes, get_irn_idx(node))) {
+                       addr->base = node;
+                       return;
+               }
        }
 
        /* starting point Add, Sub or Shl, FrameAddr */
        if(is_Shl(node)) {
                if(eat_shl(addr, node))
                        return;
-       } else if(is_immediate(addr, node)) {
+       } else if(is_immediate(addr, node, 0)) {
                eat_immediate(addr, node, 0);
                return;
        } else if(be_is_FrameAddr(node)) {
@@ -251,22 +330,30 @@ void ia32_create_address_mode(ia32_address_t *addr, ir_node *node, int force)
        } else if(is_Add(node)) {
                ir_node *left  = get_Add_left(node);
                ir_node *right = get_Add_right(node);
-               assert(force || !is_immediate(addr, left));
-               assert(force || !is_immediate(addr, right));
+
+               if(force) {
+                       left  = skip_downconv(left);
+                       right = skip_downconv(right);
+               }
+
+               assert(force || !is_immediate(addr, left, 0));
+               assert(force || !is_immediate(addr, right, 0));
 
                if(is_Shl(left) && eat_shl(addr, left)) {
                        left = NULL;
                } else if(is_Shl(right) && eat_shl(addr, right)) {
                        right = NULL;
                }
-               if(left != NULL && be_is_FrameAddr(left)) {
+               if(left != NULL && be_is_FrameAddr(left)
+                               && !bitset_is_set(non_address_mode_nodes, get_irn_idx(left))) {
                        assert(addr->base == NULL);
                        assert(addr->frame_entity == NULL);
                        addr->base         = be_get_FrameAddr_frame(left);
                        addr->use_frame    = 1;
                        addr->frame_entity = be_get_FrameAddr_entity(left);
                        left               = NULL;
-               } else if(right != NULL && be_is_FrameAddr(right)) {
+               } else if(right != NULL && be_is_FrameAddr(right)
+                               && !bitset_is_set(non_address_mode_nodes, get_irn_idx(right))) {
                        assert(addr->base == NULL);
                        assert(addr->frame_entity == NULL);
                        addr->base         = be_get_FrameAddr_frame(right);
@@ -299,37 +386,67 @@ void ia32_create_address_mode(ia32_address_t *addr, ir_node *node, int force)
 }
 
 
-typedef struct mark_evn_t mark_evn_t;
-struct mark_evn_t {
-       bitset_t *non_address_nodes;
-};
 
-
-static void mark_non_address_nodes(mark_evn_t *env, ir_node *node)
+static void mark_non_address_nodes(ir_node *node, void *env)
 {
-       bitset_set(env->non_address_nodes, get_irn_idx(node));
-
-       if(is_Load(node)) {
-               ir_node *ptr = get_Load_ptr(node);
-               ir_node *mem = get_Load_mem(node);
-
-               mark_non_address_nodes(env, mem);
-               (void) ptr;
-       } else if(is_Store(node)) {
-               ir_node *val = get_Store_value(node);
-               ir_node *ptr = get_Store_ptr(node);
-               ir_node *mem = get_Store_mem(node);
-
-               mark_non_address_nodes(env, val);
-               mark_non_address_nodes(env, mem);
-               (void) ptr;
-       } else {
-               int i;
-               int arity = get_irn_arity(node);
+       int i, arity;
+       ir_node *ptr;
+       ir_node *mem;
+       ir_node *val;
+       ir_node *left;
+       ir_node *right;
+       (void) env;
+
+       switch(get_irn_opcode(node)) {
+       case iro_Load:
+               ptr = get_Load_ptr(node);
+               mem = get_Load_mem(node);
+
+               bitset_set(non_address_mode_nodes, get_irn_idx(mem));
+               break;
+
+       case iro_Store:
+               val = get_Store_value(node);
+               ptr = get_Store_ptr(node);
+               mem = get_Store_mem(node);
+
+               bitset_set(non_address_mode_nodes, get_irn_idx(val));
+               bitset_set(non_address_mode_nodes, get_irn_idx(mem));
+               break;
+
+       case iro_Add:
+               left  = get_Add_left(node);
+               right = get_Add_right(node);
+               /* if we can do source address mode then we will never fold the add
+                * into address mode */
+               if(!mode_is_float(get_irn_mode(node)) && (is_immediate_simple(right) ||
+                        (!use_source_address_mode(get_nodes_block(node), left, right)
+                    && !use_source_address_mode(get_nodes_block(node), right, left))))
+               {
+                   break;
+               }
+               bitset_set(non_address_mode_nodes, get_irn_idx(node));
+               /* fallthrough */
+
+       default:
+               arity = get_irn_arity(node);
 
                for(i = 0; i < arity; ++i) {
                        ir_node *in = get_irn_n(node, i);
-                       mark_non_address_nodes(env, in);
+                       bitset_set(non_address_mode_nodes, get_irn_idx(in));
                }
+               break;
        }
 }
+
+void calculate_non_address_mode_nodes(ir_graph *irg)
+{
+       non_address_mode_nodes = bitset_malloc(get_irg_last_idx(irg));
+
+       irg_walk_graph(irg, NULL, mark_non_address_nodes, NULL);
+}
+
+void free_non_address_mode_nodes(void)
+{
+       bitset_free(non_address_mode_nodes);
+}