+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) {
+ /* TODO: implement for big-endian */
+ if (delta < 0 || delta + load_mode_len > store_mode_len
+ || (be_get_backend_param()->byte_order_big_endian
+ && 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;
+ ir_graph *irg = get_irn_irg(load);
+
+ cnst = new_r_Const_long(irg, mode_Iu, delta * 8);
+ store_value = new_r_Shr(get_nodes_block(load),
+ store_value, cnst, store_mode);
+ }
+
+ /* add an convert if needed */
+ if (store_mode != load_mode) {
+ store_value = new_r_Conv(get_nodes_block(load), store_value, load_mode);
+ }
+ }
+
+ DBG_OPT_RAW(load, store_value);
+
+ info = (ldst_info_t*)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]) {
+ ir_graph *irg = get_irn_irg(load);
+ exchange( info->projs[pn_Load_X_except], new_r_Bad(irg));
+ res |= CF_CHANGED;
+ }
+ if (info->projs[pn_Load_X_regular]) {
+ exchange( info->projs[pn_Load_X_regular], new_r_Jmp(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;
+}
+