+ compound_graph_path *gr = rec_get_accessed_path(ptr, 0);
+ return gr;
+} /* 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 >= (int) 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);
+
+/**
+ * Update a Load that may have lost its users.
+ */
+static void handle_load_update(ir_node *load) {
+ ldst_info_t *info = get_irn_link(load);
+
+ /* do NOT touch volatile loads for now */
+ if (get_Load_volatility(load) == volatility_is_volatile)
+ return;
+
+ if (! info->projs[pn_Load_res] && ! info->projs[pn_Load_X_except]) {
+ ir_node *ptr = get_Load_ptr(load);
+ ir_node *mem = get_Load_mem(load);
+
+ /* a Load whose value is neither used nor exception checked, remove it */
+ exchange(info->projs[pn_Load_M], mem);
+ if (info->projs[pn_Load_X_regular])
+ exchange(info->projs[pn_Load_X_regular], new_r_Jmp(current_ir_graph, get_nodes_block(load)));
+ kill_node(load);
+ reduce_adr_usage(ptr);
+ }
+} /* handle_load_update */
+
+/**
+ * A use of an address node has vanished. Check if this was a Proj
+ * node and update the counters.
+ */
+static void reduce_adr_usage(ir_node *ptr) {
+ if (is_Proj(ptr)) {
+ if (get_irn_n_edges(ptr) <= 0) {
+ /* this Proj is dead now */
+ ir_node *pred = get_Proj_pred(ptr);
+
+ if (is_Load(pred)) {
+ ldst_info_t *info = get_irn_link(pred);
+ info->projs[get_Proj_proj(ptr)] = NULL;
+
+ /* this node lost its result proj, handle that */
+ handle_load_update(pred);
+ }
+ }
+ }
+} /* reduce_adr_usage */
+
+/**
+ * Check, if an already existing value of mode old_mode can be converted
+ * into the needed one new_mode without loss.
+ */
+static int can_use_stored_value(ir_mode *old_mode, ir_mode *new_mode) {
+ if (old_mode == new_mode)
+ return 1;
+
+ /* if both modes are two-complement ones, we can always convert the
+ Stored value into the needed one. */
+ if (get_mode_size_bits(old_mode) >= get_mode_size_bits(new_mode) &&
+ get_mode_arithmetic(old_mode) == irma_twos_complement &&
+ get_mode_arithmetic(new_mode) == irma_twos_complement)
+ return 1;
+ return 0;
+} /* can_use_stored_value */
+
+/**
+ * Check whether a Call is at least pure, ie. does only read memory.
+ */
+static unsigned is_Call_pure(ir_node *call) {
+ ir_type *call_tp = get_Call_type(call);
+ unsigned prop = get_method_additional_properties(call_tp);
+
+ /* check first the call type */
+ if ((prop & (mtp_property_const|mtp_property_pure)) == 0) {
+ /* try the called entity */
+ ir_node *ptr = get_Call_ptr(call);
+
+ if (is_Global(ptr)) {
+ ir_entity *ent = get_Global_entity(ptr);
+
+ prop = get_entity_additional_properties(ent);
+ }
+ }
+ return (prop & (mtp_property_const|mtp_property_pure)) != 0;
+} /* is_Call_pure */
+
+static ir_node *get_base_and_offset(ir_node *ptr, long *pOffset)
+{
+ ir_mode *mode = get_irn_mode(ptr);
+ long offset = 0;
+
+ /* TODO: long might not be enough, we should probably use some tarval thingy... */
+ for (;;) {
+ if (is_Add(ptr)) {
+ ir_node *l = get_Add_left(ptr);
+ ir_node *r = get_Add_right(ptr);
+
+ if (get_irn_mode(l) != mode || !is_Const(r))
+ break;
+
+ offset += get_tarval_long(get_Const_tarval(r));
+ ptr = l;
+ } else if (is_Sub(ptr)) {
+ ir_node *l = get_Sub_left(ptr);
+ ir_node *r = get_Sub_right(ptr);
+
+ if (get_irn_mode(l) != mode || !is_Const(r))
+ break;
+
+ offset -= get_tarval_long(get_Const_tarval(r));
+ ptr = l;
+ } else if (is_Sel(ptr)) {
+ ir_entity *ent = get_Sel_entity(ptr);
+ ir_type *tp = get_entity_owner(ent);
+
+ if (is_Array_type(tp)) {
+ int size;
+ ir_node *index;
+
+ /* only one dimensional arrays yet */
+ if (get_Sel_n_indexs(ptr) != 1)
+ break;
+ index = get_Sel_index(ptr, 0);
+ if (! is_Const(index))
+ break;
+
+ tp = get_entity_type(ent);
+ if (get_type_state(tp) != layout_fixed)
+ break;
+
+ size = get_type_size_bytes(tp);
+ offset += size * get_tarval_long(get_Const_tarval(index));
+ } else {
+ if (get_type_state(tp) != layout_fixed)
+ break;
+ offset += get_entity_offset(ent);
+ }
+ ptr = get_Sel_ptr(ptr);
+ } else
+ break;
+ }
+
+ *pOffset = offset;
+ return ptr;
+}
+
+static int try_load_after_store(ir_node *load,
+ ir_node *load_base_ptr, long load_offset, ir_node *store)
+{
+ ldst_info_t *info;
+ ir_node *store_ptr = get_Store_ptr(store);
+ long store_offset;
+ ir_node *store_base_ptr = get_base_and_offset(store_ptr, &store_offset);
+ ir_node *store_value;
+ ir_mode *store_mode;
+ ir_node *load_ptr;
+ ir_mode *load_mode;
+ long load_mode_len;
+ long store_mode_len;
+ long delta;
+ int res;
+
+ if (load_base_ptr != store_base_ptr)
+ return 0;
+
+ load_mode = get_Load_mode(load);
+ load_mode_len = get_mode_size_bytes(load_mode);
+ store_mode = get_irn_mode(get_Store_value(store));
+ store_mode_len = get_mode_size_bytes(store_mode);
+ delta = load_offset - store_offset;
+ store_value = get_Store_value(store);
+
+ if (delta != 0 || store_mode != load_mode) {
+ if (delta < 0 || delta + load_mode_len > store_mode_len)
+ return 0;
+
+ if (get_mode_arithmetic(store_mode) != irma_twos_complement ||
+ get_mode_arithmetic(load_mode) != irma_twos_complement)
+ return 0;
+
+
+ /* produce a shift to adjust offset delta */
+ if (delta > 0) {
+ ir_node *cnst;
+
+ /* FIXME: only true for little endian */
+ cnst = new_Const_long(mode_Iu, delta * 8);
+ store_value = new_r_Shr(current_ir_graph, get_nodes_block(load),
+ store_value, cnst, store_mode);
+ }
+
+ /* add an convert if needed */
+ if (store_mode != load_mode) {
+ store_value = new_r_Conv(current_ir_graph, get_nodes_block(load),
+ store_value, load_mode);
+ }
+ }
+
+ DBG_OPT_RAW(load, store_value);
+
+ info = get_irn_link(load);
+ if (info->projs[pn_Load_M])
+ exchange(info->projs[pn_Load_M], get_Load_mem(load));
+
+ res = 0;
+ /* no exception */
+ if (info->projs[pn_Load_X_except]) {
+ exchange( info->projs[pn_Load_X_except], new_Bad());
+ res |= CF_CHANGED;
+ }
+ if (info->projs[pn_Load_X_regular]) {
+ exchange( info->projs[pn_Load_X_regular], new_r_Jmp(current_ir_graph, get_nodes_block(load)));
+ res |= CF_CHANGED;
+ }
+
+ if (info->projs[pn_Load_res])
+ exchange(info->projs[pn_Load_res], store_value);
+
+ load_ptr = get_Load_ptr(load);
+ kill_node(load);
+ reduce_adr_usage(load_ptr);
+ return res | DF_CHANGED;