- improved rec_find_compound_ent_value(): handle missing [0] access
[libfirm] / ir / opt / ldstopt.c
index cab2682..b608dac 100644 (file)
@@ -307,6 +307,31 @@ static ir_entity *find_constant_entity(ir_node *ptr)
 
                        /* try next */
                        ptr = get_Sel_ptr(ptr);
+               } else if (is_Add(ptr)) {
+                       ir_node *l = get_Add_left(ptr);
+                       ir_node *r = get_Add_right(ptr);
+
+                       if (get_irn_mode(l) == get_irn_mode(ptr) && is_Const(r))
+                               ptr = l;
+                       else if (get_irn_mode(r) == get_irn_mode(ptr) && is_Const(l))
+                               ptr = r;
+                       else
+                               return NULL;
+
+                       /* for now, we support only one addition, reassoc should fold all others */
+                       if (! is_SymConst(ptr) && !is_Sel(ptr))
+                               return NULL;
+               } else if (is_Sub(ptr)) {
+                       ir_node *l = get_Sub_left(ptr);
+                       ir_node *r = get_Sub_right(ptr);
+
+                       if (get_irn_mode(l) == get_irn_mode(ptr) &&     is_Const(r))
+                               ptr = l;
+                       else
+                               return NULL;
+                       /* for now, we support only one substraction, reassoc should fold all others */
+                       if (! is_SymConst(ptr) && !is_Sel(ptr))
+                               return NULL;
                } else
                        return NULL;
        }
@@ -331,8 +356,10 @@ static long get_Sel_array_index_long(ir_node *n, int dim) {
  */
 static compound_graph_path *rec_get_accessed_path(ir_node *ptr, int depth) {
        compound_graph_path *res = NULL;
-       ir_entity           *root, *field;
-       int                 path_len, pos;
+       ir_entity           *root, *field, *ent;
+       int                 path_len, pos, idx;
+       tarval              *tv;
+       ir_type             *tp;
 
        if (is_SymConst(ptr)) {
                /* a SymConst. If the depth is 0, this is an access to a global
@@ -342,10 +369,11 @@ static compound_graph_path *rec_get_accessed_path(ir_node *ptr, int depth) {
                assert(get_SymConst_kind(ptr) == symconst_addr_ent);
                root = get_SymConst_entity(ptr);
                res = (depth == 0) ? NULL : new_compound_graph_path(get_entity_type(root), depth);
-       } else {
-               assert(is_Sel(ptr));
+       } else if (is_Sel(ptr)) {
                /* it's a Sel, go up until we find the root */
                res = rec_get_accessed_path(get_Sel_ptr(ptr), depth+1);
+               if (res == NULL)
+                       return NULL;
 
                /* fill up the step in the path at the current position */
                field    = get_Sel_entity(ptr);
@@ -357,17 +385,314 @@ static compound_graph_path *rec_get_accessed_path(ir_node *ptr, int depth) {
                        assert(get_Sel_n_indexs(ptr) == 1 && "multi dim arrays not implemented");
                        set_compound_graph_path_array_index(res, pos, get_Sel_array_index_long(ptr, 0));
                }
+       } else if (is_Add(ptr)) {
+               ir_node *l = get_Add_left(ptr);
+               ir_node *r = get_Add_right(ptr);
+               ir_mode *mode;
+
+               if (is_Const(r)) {
+                       ptr = l;
+                       tv  = get_Const_tarval(r);
+               } else {
+                       ptr = r;
+                       tv  = get_Const_tarval(l);
+               }
+ptr_arith:
+               mode = get_tarval_mode(tv);
+
+               /* ptr must be a Sel or a SymConst, this was checked in find_constant_entity() */
+               if (is_Sel(ptr)) {
+                       field = get_Sel_entity(ptr);
+               } else {
+                       field = get_SymConst_entity(ptr);
+               }
+               idx = 0;
+               for (ent = field;;) {
+                       unsigned size;
+                       tarval   *sz, *tv_index, *tlower, *tupper;
+                       long     index;
+                       ir_node  *bound;
+
+                       tp = get_entity_type(ent);
+                       if (! is_Array_type(tp))
+                               break;
+                       ent = get_array_element_entity(tp);
+                       size = get_type_size_bytes(get_entity_type(ent));
+                       sz   = new_tarval_from_long(size, mode);
+
+                       tv_index = tarval_div(tv, sz);
+                       tv       = tarval_mod(tv, sz);
+
+                       if (tv_index == tarval_bad || tv == tarval_bad)
+                               return NULL;
+
+                       assert(get_array_n_dimensions(tp) == 1 && "multiarrays not implemented");
+                       bound  = get_array_lower_bound(tp, 0);
+                       tlower = computed_value(bound);
+                       bound  = get_array_upper_bound(tp, 0);
+                       tupper = computed_value(bound);
+
+                       if (tlower == tarval_bad || tupper == tarval_bad)
+                               return NULL;
+
+                       if (tarval_cmp(tv_index, tlower) & pn_Cmp_Lt)
+                               return NULL;
+                       if (tarval_cmp(tupper, tv_index) & pn_Cmp_Lt)
+                               return NULL;
+
+                       /* ok, bounds check finished */
+                       index = get_tarval_long(tv_index);
+                       ++idx;
+               }
+               if (! tarval_is_null(tv)) {
+                       /* access to some struct/union member */
+                       return NULL;
+               }
+
+               /* should be at least ONE array */
+               if (idx == 0)
+                       return NULL;
+
+               res = rec_get_accessed_path(ptr, depth + idx);
+               if (res == NULL)
+                       return NULL;
+
+               path_len = get_compound_graph_path_length(res);
+               pos      = path_len - depth - idx;
+
+               for (ent = field;;) {
+                       unsigned size;
+                       tarval   *sz, *tv_index;
+                       long     index;
+
+                       tp = get_entity_type(ent);
+                       if (! is_Array_type(tp))
+                               break;
+                       ent = get_array_element_entity(tp);
+                       set_compound_graph_path_node(res, pos, ent);
+
+                       size = get_type_size_bytes(get_entity_type(ent));
+                       sz   = new_tarval_from_long(size, mode);
+
+                       tv_index = tarval_div(tv, sz);
+                       tv       = tarval_mod(tv, sz);
+
+                       if (tv_index == tarval_bad || tv == tarval_bad)
+                               return NULL;
+
+                       /* bounds already checked above */
+                       index = get_tarval_long(tv_index);
+                       set_compound_graph_path_array_index(res, pos, index);
+                       ++pos;
+               }
+               if (! tarval_is_null(tv)) {
+                       /* hmm, wrong access */
+                       return NULL;
+               }
+       } else if (is_Sub(ptr)) {
+               ir_node *l = get_Sub_left(ptr);
+               ir_node *r = get_Sub_right(ptr);
+
+               ptr = l;
+               tv  = get_Const_tarval(r);
+               tv  = tarval_neg(tv);
+               goto ptr_arith;
        }
        return res;
 }  /* rec_get_accessed_path */
 
-/** Returns an access path or NULL.  The access path is only
- *  valid, if the graph is in phase_high and _no_ address computation is used.
+/**
+ * Returns an access path or NULL.  The access path is only
+ * valid, if the graph is in phase_high and _no_ address computation is used.
  */
 static compound_graph_path *get_accessed_path(ir_node *ptr) {
        return rec_get_accessed_path(ptr, 0);
 }  /* get_accessed_path */
 
+typedef struct path_entry {
+       ir_entity         *ent;
+       struct path_entry *next;
+       long              index;
+} path_entry;
+
+static ir_node *rec_find_compound_ent_value(ir_node *ptr, path_entry *next) {
+       path_entry       entry, *p;
+       ir_entity        *ent, *field;
+       ir_initializer_t *initializer;
+       tarval           *tv;
+       ir_type          *tp;
+       unsigned         n;
+
+       entry.next = next;
+       if (is_SymConst(ptr)) {
+               /* found the root */
+               ent         = get_SymConst_entity(ptr);
+               initializer = get_entity_initializer(ent);
+               for (p = next; p != NULL;) {
+                       if (initializer->kind != IR_INITIALIZER_COMPOUND)
+                               return NULL;
+                       n  = get_initializer_compound_n_entries(initializer);
+                       tp = get_entity_type(ent);
+
+                       if (is_Array_type(tp)) {
+                               ent = get_array_element_entity(tp);
+                               if (ent != p->ent) {
+                                       /* a missing [0] */
+                                       if (0 >= n)
+                                               return NULL;
+                                       initializer = get_initializer_compound_value(initializer, 0);
+                                       continue;
+                               }
+                       }
+                       if (p->index >= n)
+                               return NULL;
+                       initializer = get_initializer_compound_value(initializer, p->index);
+
+                       ent = p->ent;
+                       p   = p->next;
+               }
+               tp = get_entity_type(ent);
+               while (is_Array_type(tp)) {
+                       ent = get_array_element_entity(tp);
+                       tp = get_entity_type(ent);
+                       /* a missing [0] */
+                       n  = get_initializer_compound_n_entries(initializer);
+                       if (0 >= n)
+                               return NULL;
+                       initializer = get_initializer_compound_value(initializer, 0);
+               }
+
+               switch (initializer->kind) {
+               case IR_INITIALIZER_CONST:
+                       return get_initializer_const_value(initializer);
+               case IR_INITIALIZER_TARVAL:
+               case IR_INITIALIZER_NULL:
+               default:
+                       return NULL;
+               }
+       } else if (is_Sel(ptr)) {
+               entry.ent = field = get_Sel_entity(ptr);
+               tp = get_entity_owner(field);
+               if (is_Array_type(tp)) {
+                       assert(get_Sel_n_indexs(ptr) == 1 && "multi dim arrays not implemented");
+                       entry.index = get_Sel_array_index_long(ptr, 0) - get_array_lower_bound_int(tp, 0);
+               } else {
+                       int i, n_members = get_compound_n_members(tp);
+                       for (i = 0; i < n_members; ++i) {
+                               if (get_compound_member(tp, i) == field)
+                                       break;
+                       }
+                       if (i >= n_members) {
+                               /* not found: should NOT happen */
+                               return NULL;
+                       }
+                       entry.index = i;
+               }
+               return rec_find_compound_ent_value(get_Sel_ptr(ptr), &entry);
+       }  else if (is_Add(ptr)) {
+               ir_node  *l = get_Add_left(ptr);
+               ir_node  *r = get_Add_right(ptr);
+               ir_mode  *mode;
+               unsigned pos;
+
+               if (is_Const(r)) {
+                       ptr = l;
+                       tv  = get_Const_tarval(r);
+               } else {
+                       ptr = r;
+                       tv  = get_Const_tarval(l);
+               }
+ptr_arith:
+               mode = get_tarval_mode(tv);
+
+               /* ptr must be a Sel or a SymConst, this was checked in find_constant_entity() */
+               if (is_Sel(ptr)) {
+                       field = get_Sel_entity(ptr);
+               } else {
+                       field = get_SymConst_entity(ptr);
+               }
+
+               /* count needed entries */
+               pos = 0;
+               for (ent = field;;) {
+                       tp = get_entity_type(ent);
+                       if (! is_Array_type(tp))
+                               break;
+                       ent = get_array_element_entity(tp);
+                       ++pos;
+               }
+               /* should be at least ONE entry */
+               if (pos == 0)
+                       return NULL;
+
+               /* allocate the right number of entries */
+               NEW_ARR_A(path_entry, p, pos);
+
+               /* fill them up */
+               pos = 0;
+               for (ent = field;;) {
+                       unsigned size;
+                       tarval   *sz, *tv_index, *tlower, *tupper;
+                       long     index;
+                       ir_node  *bound;
+
+                       tp = get_entity_type(ent);
+                       if (! is_Array_type(tp))
+                               break;
+                       ent = get_array_element_entity(tp);
+                       p[pos].ent  = ent;
+                       p[pos].next = &p[pos + 1];
+
+                       size = get_type_size_bytes(get_entity_type(ent));
+                       sz   = new_tarval_from_long(size, mode);
+
+                       tv_index = tarval_div(tv, sz);
+                       tv       = tarval_mod(tv, sz);
+
+                       if (tv_index == tarval_bad || tv == tarval_bad)
+                               return NULL;
+
+                       assert(get_array_n_dimensions(tp) == 1 && "multiarrays not implemented");
+                       bound  = get_array_lower_bound(tp, 0);
+                       tlower = computed_value(bound);
+                       bound  = get_array_upper_bound(tp, 0);
+                       tupper = computed_value(bound);
+
+                       if (tlower == tarval_bad || tupper == tarval_bad)
+                               return NULL;
+
+                       if (tarval_cmp(tv_index, tlower) & pn_Cmp_Lt)
+                               return NULL;
+                       if (tarval_cmp(tupper, tv_index) & pn_Cmp_Lt)
+                               return NULL;
+
+                       /* ok, bounds check finished */
+                       index = get_tarval_long(tv_index);
+                       p[pos].index = index;
+                       ++pos;
+               }
+               if (! tarval_is_null(tv)) {
+                       /* hmm, wrong access */
+                       return NULL;
+               }
+               p[pos - 1].next = next;
+               return rec_find_compound_ent_value(ptr, p);
+       } else if (is_Sub(ptr)) {
+               ir_node *l = get_Sub_left(ptr);
+               ir_node *r = get_Sub_right(ptr);
+
+               ptr = l;
+               tv  = get_Const_tarval(r);
+               tv  = tarval_neg(tv);
+               goto ptr_arith;
+       }
+       return NULL;
+}
+
+static ir_node *find_compound_ent_value(ir_node *ptr) {
+       return rec_find_compound_ent_value(ptr, NULL);
+}
+
 /* forward */
 static void reduce_adr_usage(ir_node *ptr);
 
@@ -778,30 +1103,22 @@ static unsigned optimize_load(ir_node *load)
                                        reduce_adr_usage(ptr);
                                        return res;
                                } else {
-                                       compound_graph_path *path = get_accessed_path(ptr);
-
-                                       if (path && !ent->has_initializer) {
-                                               ir_node *c;
-
-                                               assert(is_proper_compound_graph_path(path, get_compound_graph_path_length(path)-1));
-                                               /*
-                                               {
-                                                       int j;
-                                                       for (j = 0; j < get_compound_graph_path_length(path); ++j) {
-                                                               ir_entity *node = get_compound_graph_path_node(path, j);
-                                                               fprintf(stdout, ".%s", get_entity_name(node));
-                                                               if (is_Array_type(get_entity_owner(node)))
-                                                                       fprintf(stdout, "[%d]", get_compound_graph_path_array_index(path, j));
-                                                       }
-                                                       printf("\n");
-                                               }
-                                               */
-
-                                               c = get_compound_ent_value_by_path(ent, path);
-                                               free_compound_graph_path(path);
+                                       ir_node *c = NULL;
+                                       if (ent->has_initializer) {
+                                               /* new style initializer */
+                                               c = find_compound_ent_value(ptr);
+                                       } else {
+                                               /* old style initializer */
+                                               compound_graph_path *path = get_accessed_path(ptr);
 
-                                               /* printf("  cons: "); DDMN(c); */
+                                               if (path) {
+                                                       assert(is_proper_compound_graph_path(path, get_compound_graph_path_length(path)-1));
 
+                                                       c = get_compound_ent_value_by_path(ent, path);
+                                                       free_compound_graph_path(path);
+                                               }
+                                       }
+                                       if (c != NULL) {
                                                if (info->projs[pn_Load_M]) {
                                                        exchange(info->projs[pn_Load_M], mem);
                                                        res |= DF_CHANGED;
@@ -819,8 +1136,8 @@ static unsigned optimize_load(ir_node *load)
                                                Reflectiontest.
                                                printf(">>>>>>>>>>>>> Found access to constant entity %s in function %s\n", get_entity_name(ent),
                                                get_entity_name(get_irg_entity(current_ir_graph)));
-                                               printf("  load: "); DDMN(load);
-                                               printf("  ptr:  "); DDMN(ptr);
+                                               ir_printf("  load: %+F\n", load);
+                                               ir_printf("  ptr:  %+F\n", ptr);
                                                */
                                        }
                                }