diff options
Diffstat (limited to 'gcc/avoid-store-forwarding.cc')
-rw-r--r-- | gcc/avoid-store-forwarding.cc | 106 |
1 files changed, 83 insertions, 23 deletions
diff --git a/gcc/avoid-store-forwarding.cc b/gcc/avoid-store-forwarding.cc index 37e0953..1de6fd6 100644 --- a/gcc/avoid-store-forwarding.cc +++ b/gcc/avoid-store-forwarding.cc @@ -119,17 +119,6 @@ generate_bit_insert_sequence (store_fwd_info *store_info, rtx dest) unsigned HOST_WIDE_INT bitsize = store_size * BITS_PER_UNIT; unsigned HOST_WIDE_INT start = store_info->offset * BITS_PER_UNIT; - /* Adjust START for machines with BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN. - Given that the bytes will be reversed in this case, we need to - calculate the starting position from the end of the destination - register. */ - if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN) - { - unsigned HOST_WIDE_INT load_mode_bitsize - = (GET_MODE_BITSIZE (GET_MODE (dest))).to_constant (); - start = load_mode_bitsize - bitsize - start; - } - rtx mov_reg = store_info->mov_reg; store_bit_field (dest, bitsize, start, 0, 0, GET_MODE (mov_reg), mov_reg, false, false); @@ -242,17 +231,39 @@ process_store_forwarding (vec<store_fwd_info> &stores, rtx_insn *load_insn, int move_to_front = -1; int total_cost = 0; + int base_offset_index = -1; + + /* Find the last store that has the same offset the load, in the case that + we're eliminating the load. We will try to use it as a base register + to avoid bit inserts (see second loop below). We want the last one, as + it will be wider and we don't want to overwrite the base register if + there are many of them. */ + if (load_elim) + { + FOR_EACH_VEC_ELT_REVERSE (stores, i, it) + { + const bool has_base_offset + = known_eq (poly_uint64 (it->offset), + subreg_size_lowpart_offset (MEM_SIZE (it->store_mem), + load_size)); + if (has_base_offset) + { + base_offset_index = i; + break; + } + } + } /* Check if we can emit bit insert instructions for all forwarded stores. */ FOR_EACH_VEC_ELT (stores, i, it) { it->mov_reg = gen_reg_rtx (GET_MODE (it->store_mem)); rtx_insn *insns = NULL; - const bool has_zero_offset = it->offset == 0; - /* If we're eliminating the load then find the store with zero offset - and use it as the base register to avoid a bit insert if possible. */ - if (load_elim && has_zero_offset) + /* Check if this is a store with base offset, if we're eliminating the + load, and use it as the base register to avoid a bit insert if + possible. Load elimination is implied by base_offset_index != -1. */ + if (i == (unsigned) base_offset_index) { start_sequence (); @@ -445,9 +456,22 @@ store_forwarding_analyzer::avoid_store_forwarding (basic_block bb) return; auto_vec<store_fwd_info, 8> store_exprs; + auto_vec<rtx> store_exprs_del; rtx_insn *insn; unsigned int insn_cnt = 0; + /* We are iterating over the basic block's instructions detecting store + instructions. Upon reaching a load instruction, we check if any of the + previously detected stores could result in store forwarding. In that + case, we try to reorder the load and store instructions. + We skip this transformation when we encounter complex memory operations, + instructions that might throw an exception, instruction dependencies, + etc. This is done by clearing the vector of detected stores, while + keeping the removed stores in another vector. By doing so, we can check + if any of the removed stores operated on the load's address range, when + reaching a subsequent store that operates on the same address range, + as this would lead to incorrect values on the register that keeps the + loaded value. */ FOR_BB_INSNS (bb, insn) { if (!NONDEBUG_INSN_P (insn)) @@ -460,6 +484,10 @@ store_forwarding_analyzer::avoid_store_forwarding (basic_block bb) if (!set || insn_could_throw_p (insn)) { + unsigned int i; + store_fwd_info *it; + FOR_EACH_VEC_ELT (store_exprs, i, it) + store_exprs_del.safe_push (it->store_mem); store_exprs.truncate (0); continue; } @@ -483,6 +511,10 @@ store_forwarding_analyzer::avoid_store_forwarding (basic_block bb) || (load_mem && (!MEM_SIZE_KNOWN_P (load_mem) || !MEM_SIZE (load_mem).is_constant ()))) { + unsigned int i; + store_fwd_info *it; + FOR_EACH_VEC_ELT (store_exprs, i, it) + store_exprs_del.safe_push (it->store_mem); store_exprs.truncate (0); continue; } @@ -534,6 +566,7 @@ store_forwarding_analyzer::avoid_store_forwarding (basic_block bb) it->remove = true; removed_count++; remove_rest = true; + store_exprs_del.safe_push (it->store_mem); } } } @@ -573,23 +606,46 @@ store_forwarding_analyzer::avoid_store_forwarding (basic_block bb) it->remove = true; removed_count++; remove_rest = true; + forwardings.truncate (0); } else if (is_store_forwarding (store_mem, load_mem, &off_val)) { + unsigned int j; + rtx *del_it; + bool same_range_as_removed = false; + + /* Check if another store in the load's address range has + been deleted due to a constraint violation. In this case + we can't forward any other stores that operate in this + range, as it would lead to partial update of the register + that holds the loaded value. */ + FOR_EACH_VEC_ELT (store_exprs_del, j, del_it) + { + rtx del_store_mem = *del_it; + same_range_as_removed + = is_store_forwarding (del_store_mem, load_mem, NULL); + if (same_range_as_removed) + break; + } + /* Check if moving this store after the load is legal. */ bool write_dep = false; - for (unsigned int j = store_exprs.length () - 1; j != i; j--) + if (!same_range_as_removed) { - if (!store_exprs[j].forwarded - && output_dependence (store_mem, - store_exprs[j].store_mem)) + unsigned int j = store_exprs.length () - 1; + for (; j != i; j--) { - write_dep = true; - break; + if (!store_exprs[j].forwarded + && output_dependence (store_mem, + store_exprs[j].store_mem)) + { + write_dep = true; + break; + } } } - if (!write_dep) + if (!same_range_as_removed && !write_dep) { it->forwarded = true; it->offset = off_val; @@ -609,6 +665,7 @@ store_forwarding_analyzer::avoid_store_forwarding (basic_block bb) it->remove = true; removed_count++; remove_rest = true; + forwardings.truncate (0); } } @@ -616,9 +673,12 @@ store_forwarding_analyzer::avoid_store_forwarding (basic_block bb) process_store_forwarding (forwardings, insn, load_mem); } + /* Abort in case that we encounter a memory read/write that is not a + simple store/load, as we can't make safe assumptions about the + side-effects of this. */ if ((writes_mem && !is_simple_store) || (reads_mem && !is_simple_load)) - store_exprs.truncate (0); + return; if (removed_count) { |