Branch data Line data Source code
1 : : /* Avoid store forwarding optimization pass.
2 : : Copyright (C) 2024-2025 Free Software Foundation, Inc.
3 : : Contributed by VRULL GmbH.
4 : :
5 : : This file is part of GCC.
6 : :
7 : : GCC is free software; you can redistribute it and/or modify it
8 : : under the terms of the GNU General Public License as published by
9 : : the Free Software Foundation; either version 3, or (at your option)
10 : : any later version.
11 : :
12 : : GCC is distributed in the hope that it will be useful, but
13 : : WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : General Public License for more details.
16 : :
17 : : You should have received a copy of the GNU General Public License
18 : : along with GCC; see the file COPYING3. If not see
19 : : <http://www.gnu.org/licenses/>. */
20 : :
21 : : #include "avoid-store-forwarding.h"
22 : : #include "config.h"
23 : : #include "system.h"
24 : : #include "coretypes.h"
25 : : #include "backend.h"
26 : : #include "target.h"
27 : : #include "rtl.h"
28 : : #include "alias.h"
29 : : #include "rtlanal.h"
30 : : #include "cfgrtl.h"
31 : : #include "tree-pass.h"
32 : : #include "cselib.h"
33 : : #include "predict.h"
34 : : #include "insn-config.h"
35 : : #include "expmed.h"
36 : : #include "recog.h"
37 : : #include "regset.h"
38 : : #include "df.h"
39 : : #include "expr.h"
40 : : #include "memmodel.h"
41 : : #include "emit-rtl.h"
42 : : #include "vec.h"
43 : :
44 : : /* This pass tries to detect and avoid cases of store forwarding.
45 : : On many processors there is a large penalty when smaller stores are
46 : : forwarded to larger loads. The idea used to avoid the stall is to move
47 : : the store after the load and in addition emit a bit insert sequence so
48 : : the load register has the correct value. For example the following:
49 : :
50 : : strb w2, [x1, 1]
51 : : ldr x0, [x1]
52 : :
53 : : Will be transformed to:
54 : :
55 : : ldr x0, [x1]
56 : : strb w2, [x1]
57 : : bfi x0, x2, 0, 8
58 : : */
59 : :
60 : : namespace {
61 : :
62 : : const pass_data pass_data_avoid_store_forwarding =
63 : : {
64 : : RTL_PASS, /* type. */
65 : : "avoid_store_forwarding", /* name. */
66 : : OPTGROUP_NONE, /* optinfo_flags. */
67 : : TV_AVOID_STORE_FORWARDING, /* tv_id. */
68 : : 0, /* properties_required. */
69 : : 0, /* properties_provided. */
70 : : 0, /* properties_destroyed. */
71 : : 0, /* todo_flags_start. */
72 : : TODO_df_finish /* todo_flags_finish. */
73 : : };
74 : :
75 : : class pass_rtl_avoid_store_forwarding : public rtl_opt_pass
76 : : {
77 : : public:
78 : 285689 : pass_rtl_avoid_store_forwarding (gcc::context *ctxt)
79 : 571378 : : rtl_opt_pass (pass_data_avoid_store_forwarding, ctxt)
80 : : {}
81 : :
82 : : /* opt_pass methods: */
83 : 1481340 : virtual bool gate (function *) final override
84 : : {
85 : 1481340 : return flag_avoid_store_forwarding && optimize >= 1;
86 : : }
87 : :
88 : : virtual unsigned int execute (function *) final override;
89 : : }; // class pass_rtl_avoid_store_forwarding
90 : :
91 : : /* Handler for finding and avoiding store forwardings. */
92 : :
93 : : class store_forwarding_analyzer
94 : : {
95 : : public:
96 : : unsigned int stats_sf_detected = 0;
97 : : unsigned int stats_sf_avoided = 0;
98 : :
99 : : bool is_store_forwarding (rtx store_mem, rtx load_mem,
100 : : HOST_WIDE_INT *off_val);
101 : : bool process_store_forwarding (vec<store_fwd_info> &, rtx_insn *load_insn,
102 : : rtx load_mem);
103 : : void avoid_store_forwarding (basic_block);
104 : : void update_stats (function *);
105 : : };
106 : :
107 : : /* Return a bit insertion sequence that would make DEST have the correct value
108 : : if the store represented by STORE_INFO were to be moved after DEST. */
109 : :
110 : : static rtx_insn *
111 : 6 : generate_bit_insert_sequence (store_fwd_info *store_info, rtx dest)
112 : : {
113 : : /* Memory size should be a constant at this stage. */
114 : 6 : unsigned HOST_WIDE_INT store_size
115 : 6 : = MEM_SIZE (store_info->store_mem).to_constant ();
116 : :
117 : 6 : start_sequence ();
118 : :
119 : 6 : unsigned HOST_WIDE_INT bitsize = store_size * BITS_PER_UNIT;
120 : 6 : unsigned HOST_WIDE_INT start = store_info->offset * BITS_PER_UNIT;
121 : :
122 : 6 : rtx mov_reg = store_info->mov_reg;
123 : 6 : store_bit_field (dest, bitsize, start, 0, 0, GET_MODE (mov_reg), mov_reg,
124 : : false, false);
125 : :
126 : 6 : rtx_insn *insns = get_insns ();
127 : 6 : unshare_all_rtl_in_chain (insns);
128 : 6 : end_sequence ();
129 : :
130 : 25 : for (rtx_insn *insn = insns; insn; insn = NEXT_INSN (insn))
131 : 19 : if (contains_mem_rtx_p (PATTERN (insn))
132 : 19 : || recog_memoized (insn) < 0)
133 : 0 : return NULL;
134 : :
135 : : return insns;
136 : : }
137 : :
138 : : /* Return true iff a store to STORE_MEM would write to a sub-region of bytes
139 : : from what LOAD_MEM would read. If true also store the relative byte offset
140 : : of the store within the load to OFF_VAL. */
141 : :
142 : 68 : bool store_forwarding_analyzer::
143 : : is_store_forwarding (rtx store_mem, rtx load_mem, HOST_WIDE_INT *off_val)
144 : : {
145 : 68 : poly_int64 load_offset, store_offset;
146 : 68 : rtx load_base = strip_offset (XEXP (load_mem, 0), &load_offset);
147 : 68 : rtx store_base = strip_offset (XEXP (store_mem, 0), &store_offset);
148 : 68 : poly_int64 off_diff = store_offset - load_offset;
149 : :
150 : 68 : HOST_WIDE_INT off_val_tmp = 0;
151 : 68 : bool is_off_diff_constant = off_diff.is_constant (&off_val_tmp);
152 : 68 : if (off_val)
153 : 63 : *off_val = off_val_tmp;
154 : :
155 : 68 : return (MEM_SIZE (load_mem).is_constant ()
156 : 68 : && rtx_equal_p (load_base, store_base)
157 : 43 : && known_subrange_p (store_offset, MEM_SIZE (store_mem),
158 : 43 : load_offset, MEM_SIZE (load_mem))
159 : 68 : && is_off_diff_constant);
160 : : }
161 : :
162 : : /* Given a list of small stores that are forwarded to LOAD_INSN, try to
163 : : rearrange them so that a store-forwarding penalty doesn't occur.
164 : : The stores must be given in reverse program order, starting from the
165 : : one closer to LOAD_INSN. */
166 : :
167 : 8 : bool store_forwarding_analyzer::
168 : : process_store_forwarding (vec<store_fwd_info> &stores, rtx_insn *load_insn,
169 : : rtx load_mem)
170 : : {
171 : 8 : machine_mode load_mem_mode = GET_MODE (load_mem);
172 : : /* Memory sizes should be constants at this stage. */
173 : 8 : HOST_WIDE_INT load_size = MEM_SIZE (load_mem).to_constant ();
174 : :
175 : : /* If the stores cover all the bytes of the load, then we can eliminate
176 : : the load entirely and use the computed value instead.
177 : : We can also eliminate stores on addresses that are overwritten
178 : : by later stores. */
179 : :
180 : 8 : sbitmap forwarded_bytes = sbitmap_alloc (load_size);
181 : 8 : bitmap_clear (forwarded_bytes);
182 : :
183 : 8 : unsigned int i;
184 : 8 : store_fwd_info* it;
185 : 8 : auto_vec<store_fwd_info> redundant_stores;
186 : 8 : auto_vec<int> store_ind_to_remove;
187 : 18 : FOR_EACH_VEC_ELT (stores, i, it)
188 : : {
189 : 10 : HOST_WIDE_INT store_size = MEM_SIZE (it->store_mem).to_constant ();
190 : 10 : if (bitmap_all_bits_in_range_p (forwarded_bytes, it->offset,
191 : 10 : it->offset + store_size - 1))
192 : : {
193 : 0 : redundant_stores.safe_push (*it);
194 : 0 : store_ind_to_remove.safe_push (i);
195 : 0 : continue;
196 : : }
197 : 10 : bitmap_set_range (forwarded_bytes, it->offset, store_size);
198 : : }
199 : :
200 : 8 : bitmap_not (forwarded_bytes, forwarded_bytes);
201 : 8 : bool load_elim = bitmap_empty_p (forwarded_bytes);
202 : :
203 : 8 : stats_sf_detected++;
204 : :
205 : 8 : if (dump_file)
206 : : {
207 : 0 : fprintf (dump_file, "Store forwarding detected:\n");
208 : :
209 : 0 : FOR_EACH_VEC_ELT (stores, i, it)
210 : : {
211 : 0 : fprintf (dump_file, "From: ");
212 : 0 : print_rtl_single (dump_file, it->store_insn);
213 : : }
214 : :
215 : 0 : fprintf (dump_file, "To: ");
216 : 0 : print_rtl_single (dump_file, load_insn);
217 : :
218 : 0 : if (load_elim)
219 : 0 : fprintf (dump_file, "(Load elimination candidate)\n");
220 : : }
221 : :
222 : : /* Remove redundant stores from the vector. Although this is quadratic,
223 : : there doesn't seem to be much point optimizing it. The number of
224 : : redundant stores is expected to be low and the length of the list is
225 : : limited by a --param. The dependence checking that we did earlier is
226 : : also quadratic in the size of this list. */
227 : 8 : store_ind_to_remove.reverse ();
228 : 8 : for (int i : store_ind_to_remove)
229 : 0 : stores.ordered_remove (i);
230 : :
231 : 8 : rtx load = single_set (load_insn);
232 : 8 : rtx dest;
233 : :
234 : 8 : if (load_elim)
235 : 4 : dest = gen_reg_rtx (load_mem_mode);
236 : : else
237 : 4 : dest = SET_DEST (load);
238 : :
239 : 8 : int move_to_front = -1;
240 : 8 : int total_cost = 0;
241 : 8 : int base_offset_index = -1;
242 : :
243 : : /* Find the last store that has the same offset the load, in the case that
244 : : we're eliminating the load. We will try to use it as a base register
245 : : to avoid bit inserts (see second loop below). We want the last one, as
246 : : it will be wider and we don't want to overwrite the base register if
247 : : there are many of them. */
248 : 4 : if (load_elim)
249 : : {
250 : 8 : FOR_EACH_VEC_ELT_REVERSE (stores, i, it)
251 : : {
252 : 4 : const bool has_base_offset
253 : 4 : = known_eq (poly_uint64 (it->offset),
254 : : subreg_size_lowpart_offset (MEM_SIZE (it->store_mem),
255 : : load_size));
256 : 4 : if (has_base_offset)
257 : : {
258 : 4 : base_offset_index = i;
259 : 4 : break;
260 : : }
261 : : }
262 : : }
263 : :
264 : : /* Check if we can emit bit insert instructions for all forwarded stores. */
265 : 18 : FOR_EACH_VEC_ELT (stores, i, it)
266 : : {
267 : 10 : it->mov_reg = gen_reg_rtx (GET_MODE (it->store_mem));
268 : 10 : rtx_insn *insns = NULL;
269 : :
270 : : /* Check if this is a store with base offset, if we're eliminating the
271 : : load, and use it as the base register to avoid a bit insert if
272 : : possible. Load elimination is implied by base_offset_index != -1. */
273 : 10 : if (i == (unsigned) base_offset_index)
274 : : {
275 : 4 : start_sequence ();
276 : :
277 : 8 : rtx base_reg = lowpart_subreg (GET_MODE (dest), it->mov_reg,
278 : 4 : GET_MODE (it->mov_reg));
279 : :
280 : 4 : if (base_reg)
281 : : {
282 : 4 : rtx_insn *move0 = emit_move_insn (dest, base_reg);
283 : 4 : if (recog_memoized (move0) >= 0)
284 : : {
285 : 4 : insns = get_insns ();
286 : 4 : move_to_front = (int) i;
287 : : }
288 : : }
289 : :
290 : 4 : end_sequence ();
291 : : }
292 : :
293 : 4 : if (!insns)
294 : 6 : insns = generate_bit_insert_sequence (&(*it), dest);
295 : :
296 : 6 : if (!insns)
297 : : {
298 : 0 : if (dump_file)
299 : : {
300 : 0 : fprintf (dump_file, "Failed due to: ");
301 : 0 : print_rtl_single (dump_file, it->store_insn);
302 : : }
303 : 0 : return false;
304 : : }
305 : :
306 : 10 : total_cost += seq_cost (insns, true);
307 : 10 : it->bits_insert_insns = insns;
308 : :
309 : 10 : rtx store_set = single_set (it->store_insn);
310 : :
311 : : /* Create a register move at the store's original position to save the
312 : : stored value. */
313 : 10 : start_sequence ();
314 : 10 : rtx_insn *insn1
315 : 10 : = emit_insn (gen_rtx_SET (it->mov_reg, SET_SRC (store_set)));
316 : 10 : end_sequence ();
317 : :
318 : 10 : if (recog_memoized (insn1) < 0)
319 : : {
320 : 0 : if (dump_file)
321 : : {
322 : 0 : fprintf (dump_file, "Failed due to unrecognizable insn: ");
323 : 0 : print_rtl_single (dump_file, insn1);
324 : : }
325 : 0 : return false;
326 : : }
327 : :
328 : 10 : it->save_store_value_insn = insn1;
329 : :
330 : : /* Create a new store after the load with the saved original value.
331 : : This avoids the forwarding stall. */
332 : 10 : start_sequence ();
333 : 10 : rtx_insn *insn2
334 : 10 : = emit_insn (gen_rtx_SET (SET_DEST (store_set), it->mov_reg));
335 : 10 : end_sequence ();
336 : :
337 : 10 : if (recog_memoized (insn2) < 0)
338 : : {
339 : 0 : if (dump_file)
340 : : {
341 : 0 : fprintf (dump_file, "Failed due to unrecognizable insn: ");
342 : 0 : print_rtl_single (dump_file, insn2);
343 : : }
344 : 0 : return false;
345 : : }
346 : :
347 : 10 : it->store_saved_value_insn = insn2;
348 : : }
349 : :
350 : 8 : if (load_elim)
351 : 4 : total_cost -= insn_cost (load_insn, true);
352 : :
353 : : /* Let the target decide if transforming this store forwarding instance is
354 : : profitable. */
355 : 8 : if (!targetm.avoid_store_forwarding_p (stores, load_mem, total_cost,
356 : : load_elim))
357 : : {
358 : 1 : if (dump_file)
359 : 0 : fprintf (dump_file, "Not transformed due to target decision.\n");
360 : :
361 : 1 : return false;
362 : : }
363 : :
364 : : /* If we have a move instead of bit insert, it needs to be emitted first in
365 : : the resulting sequence. */
366 : 7 : if (move_to_front != -1)
367 : : {
368 : 4 : store_fwd_info copy = stores[move_to_front];
369 : 4 : stores.safe_push (copy);
370 : 4 : stores.ordered_remove (move_to_front);
371 : : }
372 : :
373 : 7 : if (load_elim)
374 : : {
375 : 4 : machine_mode outer_mode = GET_MODE (SET_DEST (load));
376 : 4 : rtx load_move;
377 : 4 : rtx load_value = dest;
378 : 4 : if (outer_mode != load_mem_mode)
379 : : {
380 : 0 : load_value = simplify_gen_unary (GET_CODE (SET_SRC (load)),
381 : : outer_mode, dest, load_mem_mode);
382 : : }
383 : 4 : load_move = gen_rtx_SET (SET_DEST (load), load_value);
384 : :
385 : 4 : start_sequence ();
386 : 4 : rtx_insn *insn = emit_insn (load_move);
387 : 4 : rtx_insn *seq = end_sequence ();
388 : :
389 : 4 : if (recog_memoized (insn) < 0)
390 : : return false;
391 : :
392 : 4 : emit_insn_after (seq, load_insn);
393 : : }
394 : :
395 : 7 : if (dump_file)
396 : : {
397 : 0 : fprintf (dump_file, "Store forwarding avoided with bit inserts:\n");
398 : :
399 : 0 : FOR_EACH_VEC_ELT (stores, i, it)
400 : : {
401 : 0 : if (stores.length () > 1)
402 : : {
403 : 0 : fprintf (dump_file, "For: ");
404 : 0 : print_rtl_single (dump_file, it->store_insn);
405 : : }
406 : :
407 : 0 : fprintf (dump_file, "With sequence:\n");
408 : :
409 : 0 : for (rtx_insn *insn = it->bits_insert_insns; insn;
410 : 0 : insn = NEXT_INSN (insn))
411 : : {
412 : 0 : fprintf (dump_file, " ");
413 : 0 : print_rtl_single (dump_file, insn);
414 : : }
415 : : }
416 : :
417 : 0 : if (redundant_stores.length () > 0)
418 : : {
419 : 0 : fprintf (dump_file, "\nRedundant stores that have been removed:\n");
420 : 0 : FOR_EACH_VEC_ELT (redundant_stores, i, it)
421 : : {
422 : 0 : fprintf (dump_file, " ");
423 : 0 : print_rtl_single (dump_file, it->store_insn);
424 : : }
425 : : }
426 : : }
427 : :
428 : 7 : stats_sf_avoided++;
429 : :
430 : : /* Done, emit all the generated instructions and delete the stores.
431 : : Note that STORES are in reverse program order. */
432 : :
433 : 16 : FOR_EACH_VEC_ELT (stores, i, it)
434 : : {
435 : 9 : emit_insn_after (it->bits_insert_insns, load_insn);
436 : 9 : emit_insn_after (it->store_saved_value_insn, load_insn);
437 : : }
438 : :
439 : 16 : FOR_EACH_VEC_ELT (stores, i, it)
440 : : {
441 : 9 : emit_insn_before (it->save_store_value_insn, it->store_insn);
442 : 9 : delete_insn (it->store_insn);
443 : : }
444 : :
445 : : /* Delete redundant stores. */
446 : 7 : FOR_EACH_VEC_ELT (redundant_stores, i, it)
447 : 0 : delete_insn (it->store_insn);
448 : :
449 : 7 : df_insn_rescan (load_insn);
450 : :
451 : 7 : if (load_elim)
452 : 4 : delete_insn (load_insn);
453 : :
454 : : return true;
455 : 8 : }
456 : :
457 : : /* Try to modify BB so that expensive store forwarding cases are avoided. */
458 : :
459 : : void
460 : 64 : store_forwarding_analyzer::avoid_store_forwarding (basic_block bb)
461 : : {
462 : 64 : if (!optimize_bb_for_speed_p (bb))
463 : 25 : return;
464 : :
465 : 52 : auto_vec<store_fwd_info, 8> store_exprs;
466 : 52 : auto_vec<rtx> store_exprs_del;
467 : 52 : rtx_insn *insn;
468 : 52 : unsigned int insn_cnt = 0;
469 : :
470 : : /* We are iterating over the basic block's instructions detecting store
471 : : instructions. Upon reaching a load instruction, we check if any of the
472 : : previously detected stores could result in store forwarding. In that
473 : : case, we try to reorder the load and store instructions.
474 : : We skip this transformation when we encounter complex memory operations,
475 : : instructions that might throw an exception, instruction dependencies,
476 : : etc. This is done by clearing the vector of detected stores, while
477 : : keeping the removed stores in another vector. By doing so, we can check
478 : : if any of the removed stores operated on the load's address range, when
479 : : reaching a subsequent store that operates on the same address range,
480 : : as this would lead to incorrect values on the register that keeps the
481 : : loaded value. */
482 : 556 : FOR_BB_INSNS (bb, insn)
483 : : {
484 : 517 : if (!NONDEBUG_INSN_P (insn))
485 : 147 : continue;
486 : :
487 : 410 : vec_rtx_properties properties;
488 : 410 : properties.add_insn (insn, false);
489 : :
490 : 410 : rtx set = single_set (insn);
491 : :
492 : 410 : if (!set || insn_could_throw_p (insn))
493 : : {
494 : : unsigned int i;
495 : : store_fwd_info *it;
496 : 71 : FOR_EACH_VEC_ELT (store_exprs, i, it)
497 : 31 : store_exprs_del.safe_push (it->store_mem);
498 : 40 : store_exprs.truncate (0);
499 : 40 : continue;
500 : 40 : }
501 : :
502 : : /* The inner mem RTX if INSN is a load, NULL_RTX otherwise. */
503 : 370 : rtx load_mem = SET_SRC (set);
504 : :
505 : 370 : if (GET_CODE (load_mem) == ZERO_EXTEND
506 : 370 : || GET_CODE (load_mem) == SIGN_EXTEND)
507 : 12 : load_mem = XEXP (load_mem, 0);
508 : :
509 : 370 : if (!MEM_P (load_mem))
510 : 317 : load_mem = NULL_RTX;
511 : :
512 : : /* The mem RTX if INSN is a store, NULL_RTX otherwise. */
513 : 370 : rtx store_mem = MEM_P (SET_DEST (set)) ? SET_DEST (set) : NULL_RTX;
514 : :
515 : : /* We cannot analyze memory RTXs that have unknown size. */
516 : 194 : if ((store_mem && (!MEM_SIZE_KNOWN_P (store_mem)
517 : : || !MEM_SIZE (store_mem).is_constant ()))
518 : 423 : || (load_mem && (!MEM_SIZE_KNOWN_P (load_mem)
519 : : || !MEM_SIZE (load_mem).is_constant ())))
520 : : {
521 : : unsigned int i;
522 : : store_fwd_info *it;
523 : 0 : FOR_EACH_VEC_ELT (store_exprs, i, it)
524 : 0 : store_exprs_del.safe_push (it->store_mem);
525 : 0 : store_exprs.truncate (0);
526 : 0 : continue;
527 : 0 : }
528 : :
529 : 370 : bool is_simple = !properties.has_asm
530 : 370 : && !properties.has_side_effects ();
531 : 370 : bool is_simple_store = is_simple
532 : 370 : && store_mem
533 : 370 : && !contains_mem_rtx_p (SET_SRC (set));
534 : 370 : bool is_simple_load = is_simple
535 : 370 : && load_mem
536 : 370 : && !contains_mem_rtx_p (SET_DEST (set));
537 : :
538 : 370 : int removed_count = 0;
539 : :
540 : 370 : if (is_simple_store)
541 : : {
542 : : /* Record store forwarding candidate. */
543 : 162 : store_fwd_info info;
544 : 162 : info.store_insn = insn;
545 : 162 : info.store_mem = store_mem;
546 : 162 : info.insn_cnt = insn_cnt;
547 : 162 : info.remove = false;
548 : 162 : info.forwarded = false;
549 : 162 : store_exprs.safe_push (info);
550 : : }
551 : :
552 : 370 : bool reads_mem = false;
553 : 370 : bool writes_mem = false;
554 : 1291 : for (auto ref : properties.refs ())
555 : 921 : if (ref.is_mem ())
556 : : {
557 : 225 : reads_mem |= ref.is_read ();
558 : 225 : writes_mem |= ref.is_write ();
559 : : }
560 : 696 : else if (ref.is_write ())
561 : : {
562 : : /* Drop store forwarding candidates when the address register is
563 : : overwritten. */
564 : 215 : bool remove_rest = false;
565 : 215 : unsigned int i;
566 : 215 : store_fwd_info *it;
567 : 1683 : FOR_EACH_VEC_ELT_REVERSE (store_exprs, i, it)
568 : : {
569 : 332 : if (remove_rest
570 : 664 : || reg_overlap_mentioned_p (regno_reg_rtx[ref.regno],
571 : 332 : it->store_mem))
572 : : {
573 : 0 : it->remove = true;
574 : 0 : removed_count++;
575 : 0 : remove_rest = true;
576 : 0 : store_exprs_del.safe_push (it->store_mem);
577 : : }
578 : : }
579 : : }
580 : :
581 : 370 : if (is_simple_load)
582 : : {
583 : : /* Process load for possible store forwarding cases.
584 : : Possible newly created/moved stores, resulted from a successful
585 : : forwarding, will be processed in subsequent iterations. */
586 : 49 : auto_vec<store_fwd_info> forwardings;
587 : 49 : bool partial_forwarding = false;
588 : 49 : bool remove_rest = false;
589 : :
590 : 49 : bool vector_load = VECTOR_MODE_P (GET_MODE (load_mem));
591 : :
592 : 49 : unsigned int i;
593 : 49 : store_fwd_info *it;
594 : 171 : FOR_EACH_VEC_ELT_REVERSE (store_exprs, i, it)
595 : : {
596 : 73 : rtx store_mem = it->store_mem;
597 : 73 : HOST_WIDE_INT off_val;
598 : :
599 : 73 : bool vector_store = VECTOR_MODE_P (GET_MODE (store_mem));
600 : :
601 : 73 : if (remove_rest)
602 : : {
603 : 9 : it->remove = true;
604 : 9 : removed_count++;
605 : : }
606 : 64 : else if (vector_load ^ vector_store)
607 : : {
608 : : /* Vector stores followed by a non-vector load or the
609 : : opposite, cause store_bit_field to generate non-canonical
610 : : expressions, like (subreg:V4SI (reg:DI ...) 0)).
611 : : Cases like that should be handled using vec_duplicate,
612 : : so we reject the transformation in those cases. */
613 : 1 : it->remove = true;
614 : 1 : removed_count++;
615 : 1 : remove_rest = true;
616 : 1 : forwardings.truncate (0);
617 : : }
618 : 63 : else if (is_store_forwarding (store_mem, load_mem, &off_val))
619 : : {
620 : : unsigned int j;
621 : : rtx *del_it;
622 : : bool same_range_as_removed = false;
623 : :
624 : : /* Check if another store in the load's address range has
625 : : been deleted due to a constraint violation. In this case
626 : : we can't forward any other stores that operate in this
627 : : range, as it would lead to partial update of the register
628 : : that holds the loaded value. */
629 : 17 : FOR_EACH_VEC_ELT (store_exprs_del, j, del_it)
630 : : {
631 : 5 : rtx del_store_mem = *del_it;
632 : 5 : same_range_as_removed
633 : 5 : = is_store_forwarding (del_store_mem, load_mem, NULL);
634 : 5 : if (same_range_as_removed)
635 : : break;
636 : : }
637 : :
638 : : /* Check if moving this store after the load is legal. */
639 : 13 : bool write_dep = false;
640 : 13 : if (!same_range_as_removed)
641 : : {
642 : 12 : unsigned int j = store_exprs.length () - 1;
643 : 21 : for (; j != i; j--)
644 : : {
645 : 9 : if (!store_exprs[j].forwarded
646 : 16 : && output_dependence (store_mem,
647 : 7 : store_exprs[j].store_mem))
648 : : {
649 : : write_dep = true;
650 : : break;
651 : : }
652 : : }
653 : : }
654 : :
655 : 12 : if (!same_range_as_removed && !write_dep)
656 : : {
657 : 12 : it->forwarded = true;
658 : 12 : it->offset = off_val;
659 : 12 : forwardings.safe_push (*it);
660 : : }
661 : : else
662 : : partial_forwarding = true;
663 : :
664 : 13 : it->remove = true;
665 : 13 : removed_count++;
666 : : }
667 : 50 : else if (true_dependence (store_mem, GET_MODE (store_mem),
668 : : load_mem))
669 : : {
670 : : /* We cannot keep a store forwarding candidate if it possibly
671 : : interferes with this load. */
672 : 2 : it->remove = true;
673 : 2 : removed_count++;
674 : 2 : remove_rest = true;
675 : 2 : forwardings.truncate (0);
676 : : }
677 : : }
678 : :
679 : 67 : if (!forwardings.is_empty () && !partial_forwarding)
680 : 8 : process_store_forwarding (forwardings, insn, load_mem);
681 : 49 : }
682 : :
683 : : /* Abort in case that we encounter a memory read/write that is not a
684 : : simple store/load, as we can't make safe assumptions about the
685 : : side-effects of this. */
686 : 370 : if ((writes_mem && !is_simple_store)
687 : 367 : || (reads_mem && !is_simple_load))
688 : 13 : return;
689 : :
690 : 357 : if (removed_count)
691 : : {
692 : 12 : unsigned int i, j;
693 : 12 : store_fwd_info *it;
694 : 47 : VEC_ORDERED_REMOVE_IF (store_exprs, i, j, it, it->remove);
695 : : }
696 : :
697 : : /* Don't consider store forwarding if the RTL instruction distance is
698 : : more than PARAM_STORE_FORWARDING_MAX_DISTANCE and the cost checks
699 : : are not disabled. */
700 : 357 : const bool unlimited_cost = (param_store_forwarding_max_distance == 0);
701 : 226 : if (!unlimited_cost && !store_exprs.is_empty ()
702 : 357 : && (store_exprs[0].insn_cnt
703 : 226 : + param_store_forwarding_max_distance <= insn_cnt))
704 : 64 : store_exprs.ordered_remove (0);
705 : :
706 : 357 : insn_cnt++;
707 : 410 : }
708 : 52 : }
709 : :
710 : : /* Update pass statistics. */
711 : :
712 : : void
713 : 22 : store_forwarding_analyzer::update_stats (function *fn)
714 : : {
715 : 22 : statistics_counter_event (fn, "Cases of store forwarding detected: ",
716 : 22 : stats_sf_detected);
717 : 22 : statistics_counter_event (fn, "Cases of store forwarding avoided: ",
718 : 22 : stats_sf_detected);
719 : 22 : }
720 : :
721 : : unsigned int
722 : 22 : pass_rtl_avoid_store_forwarding::execute (function *fn)
723 : : {
724 : 22 : df_set_flags (DF_DEFER_INSN_RESCAN);
725 : :
726 : 22 : init_alias_analysis ();
727 : :
728 : 22 : store_forwarding_analyzer analyzer;
729 : :
730 : 22 : basic_block bb;
731 : 86 : FOR_EACH_BB_FN (bb, fn)
732 : 64 : analyzer.avoid_store_forwarding (bb);
733 : :
734 : 22 : end_alias_analysis ();
735 : :
736 : 22 : analyzer.update_stats (fn);
737 : :
738 : 22 : return 0;
739 : : }
740 : :
741 : : } // anon namespace.
742 : :
743 : : rtl_opt_pass *
744 : 285689 : make_pass_rtl_avoid_store_forwarding (gcc::context *ctxt)
745 : : {
746 : 285689 : return new pass_rtl_avoid_store_forwarding (ctxt);
747 : : }
|