1 /* An SH specific RTL pass that tries to combine comparisons and redundant
2 condition code register stores across multiple basic blocks.
3 Copyright (C) 2013-2021 Free Software Foundation, Inc.
4
5 This file is part of GCC.
6
7 GCC is free software; you can redistribute it and/or modify
8 it 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,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU 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 #define IN_TARGET_CODE 1
22
23 #include "config.h"
24 #define INCLUDE_ALGORITHM
25 #define INCLUDE_LIST
26 #define INCLUDE_VECTOR
27 #include "system.h"
28 #include "coretypes.h"
29 #include "backend.h"
30 #include "target.h"
31 #include "rtl.h"
32 #include "tree.h"
33 #include "memmodel.h"
34 #include "optabs.h"
35 #include "emit-rtl.h"
36 #include "recog.h"
37 #include "cfgrtl.h"
38 #include "tree-pass.h"
39 #include "expr.h"
40
41 /*
42 This pass tries to optimize for example this:
43 mov.l @(4,r4),r1
44 tst r1,r1
45 movt r1
46 tst r1,r1
47 bt/s .L5
48
49 into something simpler:
50 mov.l @(4,r4),r1
51 tst r1,r1
52 bf/s .L5
53
54 Such sequences can be identified by looking for conditional branches and
55 checking whether the ccreg is set before the conditional branch
56 by testing another register for != 0, which was set by a ccreg store.
57 This can be optimized by eliminating the redundant comparison and
58 inverting the branch condition. There can be multiple comparisons in
59 different basic blocks that all end up in the redunant test insn before the
60 conditional branch. Some example RTL ...
61
62 Example 1)
63 ----------
64
65 [bb 3]
66 (set (reg:SI 147 t) (eq:SI (reg:SI 173) (const_int 0)))
67 (set (reg:SI 167) (xor:SI (reg:SI 147 t) (const_int 1)))
68 -> bb 5
69
70 [bb 4]
71 (set (reg:SI 147 t) (eq:SI (reg:SI 177) (const_int 0)))
72 (set (reg:SI 167) (reg:SI 147 t))
73 -> bb 5
74
75 [bb 5]
76 (set (reg:SI 147 t) (eq:SI (reg:SI 167) (const_int 0)))
77 (set (pc) (if_then_else (ne (reg:SI 147 t) (const_int 0))
78 (label_ref:SI 50) (pc)))
79
80 In [bb 4] elimination of the comparison would require inversion of the branch
81 condition and compensation of other BBs.
82 Instead the comparison in [bb 3] can be replaced with the comparison in [bb 5]
83 by using a reg-reg move. In [bb 4] a logical not is used to compensate the
84 inverted condition.
85
86 [bb 3]
87 (set (reg:SI 167) (reg:SI 173))
88 -> bb 5
89
90 [BB 4]
91 (set (reg:SI 147 t) (eq:SI (reg:SI 177) (const_int 0)))
92 (set (reg:SI 167) (reg:SI 147 t))
93 -> bb 5
94
95 [bb 5]
96 (set (reg:SI 147 t) (eq:SI (reg:SI 167) (const_int 0)))
97 (set (pc) (if_then_else (ne (reg:SI 147 t) (const_int 0)))
98 (label_ref:SI 50) (pc)))
99
100
101 Example 2)
102 ----------
103
104 [bb 3]
105 (set (reg:SI 147 t) (gt:SI (reg:SI 173) (reg:SI 175)))
106 (set (reg:SI 167) (reg:SI 147 t))
107 -> bb 5
108
109 [bb 4]
110 (set (reg:SI 147 t) (gt:SI (reg:SI 177) (reg:SI 179)))
111 (set (reg:SI 167) (reg:SI 147 t))
112 -> bb 5
113
114 [bb 5]
115 (set (reg:SI 147 t) (eq:SI (reg:SI 167) (const_int 0)))
116 (set (pc) (if_then_else (ne (reg:SI 147 t) (const_int 0))
117 (label_ref:SI 51) (pc)))
118
119 The common comparison is factored out and the branch condition is inverted:
120
121 [bb 3]
122 (set (reg:SI 167) (reg:SI 173))
123 (set (reg:SI 200) (reg:SI 175))
124 -> bb 5
125
126 [bb 4]
127 (set (reg:SI 167) (reg:SI 177))
128 (set (reg:SI 200) (reg:SI 179))
129 -> bb 5
130
131 [bb 5]
132 (set (reg:SI 147 t) (gt:SI (reg:SI 167) (reg:SI 200)))
133 (set (pc) (if_then_else (eq (reg:SI 147 t) (const_int 0))
134 (label_ref:SI 51) (pc)))
135
136
137 Example 3)
138 ----------
139
140 [bb 3]
141 (set (reg:SI 147 t) (gt:SI (reg:SI 173) (reg:SI 175)))
142 (set (reg:SI 167) (reg:SI 147 t))
143 -> bb 5
144
145 [bb 4]
146 (set (reg:SI 147 t) (ge:SI (reg:SI 179) (reg:SI 177)))
147 (set (reg:SI 167) (reg:SI 147 t))
148 -> bb 5
149
150 [bb 5]
151 (set (reg:SI 147 t) (eq:SI (reg:SI 167) (const_int 0)))
152 (set (pc) (if_then_else (ne (reg:SI 147 t) (const_int 0))
153 (label_ref:SI 51) (pc)))
154
155 The T bit lifetime is extended and the branch condition is inverted:
156
157 [bb 3]
158 (set (reg:SI 147 t) (gt:SI (reg:SI 173) (reg:SI 175)))
159 -> bb 5
160
161 [bb 4]
162 (set (reg:SI 147 t) (ge:SI (reg:SI 179) (reg:SI 177)))
163 -> bb 5
164
165 [bb 5]
166 (set (pc) (if_then_else (eq (reg:SI 147 t) (const_int 0))
167 (label_ref:SI 51) (pc)))
168
169
170 Example 4)
171 ----------
172
173 [bb 3]
174 (set (reg:SI 147 t) (eq:SI (reg:SI 173) (const_int 5)))
175 (set (reg:SI 167) (reg:SI 147 t))
176 -> bb 5
177
178 [bb 4]
179 (set (reg:SI 147 t) (eq:SI (reg:SI 176) (const_int 5)))
180 (set (reg:SI 167) (xor:SI (reg:SI 147 t) (const_int 1)))
181 -> bb 5
182
183 [bb 5]
184 (set (reg:SI 147 t) (eq:SI (reg:SI 167) (const_int 0)))
185 (set (pc) (if_then_else (ne (reg:SI 147 t) (const_int 0))
186 (label_ref:SI 50) (pc)))
187
188 In this case the comparisons are the same and could be combined, but the
189 branch condition is different for [bb 3] and [bb 5]. Since the comparison
190 is not a zero comparison, we can't negate one of the operands. The best thing
191 we can do here is to eliminate the comparison before the cbranch and invert
192 the ccreg in one of the BBs. On SH2A this will utilize the 'nott' instruction.
193
194 [bb 3]
195 (set (reg:SI 147 t) (eq:SI (reg:SI 173) (const_int 5)))
196 -> bb 5
197
198 [bb 4]
199 (set (reg:SI 147 t) (eq:SI (reg:SI 176) (const_int 5)))
200 (set (reg:SI 147 t) (xor:SI (reg:SI 147 t) (const_int 1)))
201 -> bb 5
202
203 [bb 5]
204 (set (pc) (if_then_else (eq (reg:SI 147 t) (const_int 0)) // inverted
205 (label_ref:SI 50) (pc)))
206
207
208 In order to handle cases such as above the RTL pass does the following:
209
210 - Find the ccreg sets (comparisons) and ccreg stores
211 (inverting and non-inverting) in all related BBs.
212
213 - If the comparison types in the BBs are all the same, try to combine the
214 comparisons in the BBs and replace the zero comparison before the cbranch
215 with the common comparison.
216
217 - If the cstores are the same, move the comparison before the cbranch
218 and replace the comparisons in the BBs with reg-reg copies to get the
219 operands in place (create new pseudo regs).
220
221 - If the cstores differ and the comparison is a test against zero,
222 use reg-reg copies for the dominating cstores and logical not cstores
223 for the subordinate cstores.
224
225 - If the comparison types in the BBs are not the same, or the first approach
226 doesn't work out for some reason, try to eliminate the comparison before the
227 cbranch by extending the lifetime of the ccreg by leaving the individual
228 comparisons but eliminating the cstores.
229 If the cstores are all the same this is straight forward.
230 If they're not, try to reverse the ccreg for the subordinate cstore type
231 and eliminate the dominating one.
232 */
233
234 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
235 // Helper functions
236
237 #define log_msg(...)\
238 do { if (dump_file != NULL) fprintf (dump_file, __VA_ARGS__); } while (0)
239
240 #define log_insn(i)\
241 do { if (dump_file != NULL) print_rtl_single (dump_file, \
242 (const_rtx)i); } while (0)
243
244 #define log_rtx(r)\
245 do { if (dump_file != NULL) print_rtl (dump_file, (const_rtx)r); } while (0)
246
247 #define log_return(retval, ...)\
248 do { if (dump_file != NULL) fprintf (dump_file, __VA_ARGS__); \
249 return retval; } while (0)
250
251 #define log_return_void(...)\
252 do { if (dump_file != NULL) fprintf (dump_file, __VA_ARGS__); \
253 return; } while (0)
254
255 struct set_of_reg
256 {
257 // The insn where the search stopped or NULL.
258 rtx_insn *insn;
259
260 // The set rtx of the specified reg if found, NULL_RTX otherwise.
261 // Notice that the set rtx can also be in a parallel.
262 const_rtx set_rtx;
263
264 // The set source operand rtx if found, NULL_RTX otherwise.
265 rtx
set_srcset_of_reg266 set_src (void) const
267 {
268 return set_rtx == NULL_RTX ? NULL_RTX : XEXP (set_rtx, 1);
269 }
270
271 // The set destination operand rtx if found, NULL_RTX otherwise.
272 rtx
set_dstset_of_reg273 set_dst (void) const
274 {
275 return set_rtx == NULL_RTX ? NULL_RTX : XEXP (set_rtx, 0);
276 }
277
278 bool
emptyset_of_reg279 empty (void) const
280 {
281 return insn == NULL_RTX || set_rtx == NULL_RTX;
282 }
283 };
284
285 // Given a reg rtx and a start insn find the insn (in the same basic block)
286 // that sets the reg.
287 static set_of_reg
find_set_of_reg_bb(rtx reg,rtx_insn * insn)288 find_set_of_reg_bb (rtx reg, rtx_insn *insn)
289 {
290 set_of_reg result = { insn, NULL_RTX };
291
292 if (!REG_P (reg) || insn == NULL)
293 return result;
294
295 for (result.insn = insn; result.insn != NULL;
296 result.insn = prev_nonnote_nondebug_insn_bb (result.insn))
297 {
298 if (BARRIER_P (result.insn))
299 return result;
300 if (!NONJUMP_INSN_P (result.insn))
301 continue;
302 if (reg_set_p (reg, result.insn))
303 {
304 result.set_rtx = set_of (reg, result.insn);
305 if (result.set_rtx == NULL_RTX || GET_CODE (result.set_rtx) != SET)
306 result.set_rtx = NULL_RTX;
307 return result;
308 }
309 }
310
311 return result;
312 }
313
314 static bool
reg_dead_after_insn(const_rtx reg,const_rtx insn)315 reg_dead_after_insn (const_rtx reg, const_rtx insn)
316 {
317 return find_regno_note (insn, REG_DEAD, REGNO (reg)) != NULL_RTX;
318 }
319
320 static bool
reg_unused_after_insn(const_rtx reg,const_rtx insn)321 reg_unused_after_insn (const_rtx reg, const_rtx insn)
322 {
323 return find_regno_note (insn, REG_UNUSED, REGNO (reg)) != NULL_RTX;
324 }
325
326 // Check whether the two specified basic blocks are adjacent, i.e. there's no
327 // other basic block in between them.
328 static bool
is_adjacent_bb(basic_block a,basic_block b)329 is_adjacent_bb (basic_block a, basic_block b)
330 {
331 basic_block bb0[] = { a, b };
332 basic_block bb1[] = { b, a };
333
334 for (int i = 0; i < 2; ++i)
335 for (edge_iterator ei = ei_start (bb0[i]->succs);
336 !ei_end_p (ei); ei_next (&ei))
337 if (ei_edge (ei)->dest == bb1[i])
338 return true;
339
340 return false;
341 }
342
343 // Internal function of trace_reg_uses.
344 static void
trace_reg_uses_1(rtx reg,rtx_insn * start_insn,basic_block bb,int & count,std::vector<basic_block> & visited_bb,rtx abort_at_insn)345 trace_reg_uses_1 (rtx reg, rtx_insn *start_insn, basic_block bb, int& count,
346 std::vector<basic_block>& visited_bb, rtx abort_at_insn)
347 {
348 if (bb == NULL)
349 return;
350
351 if (std::find (visited_bb.begin (), visited_bb.end (), bb)
352 != visited_bb.end ())
353 log_return_void ("[bb %d] already visited\n", bb->index);
354
355 visited_bb.push_back (bb);
356
357 if (BB_END (bb) == NULL_RTX)
358 log_return_void ("[bb %d] BB_END is null\n", bb->index);
359
360 if (start_insn == NULL_RTX)
361 log_return_void ("[bb %d] start_insn is null\n", bb->index);
362
363 rtx end_insn = NEXT_INSN (BB_END (bb));
364 if (end_insn == NULL_RTX)
365 log_return_void ("[bb %d] end_insn is null\n", bb->index);
366
367 for (rtx_insn *i = NEXT_INSN (start_insn); i != end_insn; i = NEXT_INSN (i))
368 {
369 if (INSN_P (i))
370 {
371 if (NONDEBUG_INSN_P (i)
372 && (reg_overlap_mentioned_p (reg, PATTERN (i))
373 || (CALL_P (i) && find_reg_fusage (i, USE, reg))))
374 {
375 log_msg ("found use in [bb %d] at insn:\n", bb->index);
376 log_insn (i);
377 log_msg ("\n");
378 count += 1;
379 }
380
381 // Stop following this BB if the reg is set or dies along the way.
382 if (reg_set_p (reg, i) || reg_dead_after_insn (reg, i))
383 return;
384 }
385
386 if (abort_at_insn != NULL_RTX && abort_at_insn == i)
387 return;
388 }
389
390 for (edge_iterator ei = ei_start (bb->succs); !ei_end_p (ei); ei_next (&ei))
391 {
392 basic_block succ_bb = ei_edge (ei)->dest;
393 trace_reg_uses_1 (reg, BB_HEAD (succ_bb), succ_bb, count, visited_bb,
394 abort_at_insn);
395 }
396 }
397
398 // Trace uses of the specified reg in all basic blocks that are reachable from
399 // the specified insn. If 'abort_at_insn' is not null, abort the trace at
400 // that insn. If the insn 'abort_at_insn' uses the specified reg, it is also
401 // counted.
402 static int
trace_reg_uses(rtx reg,rtx_insn * start_insn,rtx abort_at_insn)403 trace_reg_uses (rtx reg, rtx_insn *start_insn, rtx abort_at_insn)
404 {
405 log_msg ("\ntrace_reg_uses\nreg = ");
406 log_rtx (reg);
407 log_msg ("\nstart_insn = ");
408 log_insn (start_insn);
409
410 int count = 0;
411 std::vector<basic_block> visited_bb;
412 visited_bb.reserve (32);
413
414 trace_reg_uses_1 (reg, start_insn, BLOCK_FOR_INSN (start_insn),
415 count, visited_bb, abort_at_insn);
416 return count;
417 }
418
419 static bool
is_conditional_insn(rtx_insn * i)420 is_conditional_insn (rtx_insn* i)
421 {
422 if (! (INSN_P (i) && NONDEBUG_INSN_P (i)))
423 return false;
424
425 rtx p = PATTERN (i);
426 return GET_CODE (p) == SET && GET_CODE (XEXP (p, 1)) == IF_THEN_ELSE;
427 }
428
429 // FIXME: Remove dependency on SH predicate function somehow.
430 extern int t_reg_operand (rtx, machine_mode);
431 extern int negt_reg_operand (rtx, machine_mode);
432
433 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
434 // RTL pass class
435
436 class sh_treg_combine : public rtl_opt_pass
437 {
438 public:
439 sh_treg_combine (gcc::context* ctx, bool split_insns, const char* name);
440 virtual ~sh_treg_combine (void);
441 virtual bool gate (function *);
442 virtual unsigned int execute (function *);
443
444 private:
445 // Type of ccreg store that is supported.
446 enum cstore_type_t
447 {
448 cstore_normal = 0,
449 cstore_inverted = 1,
450 cstore_unknown = -1
451 };
452
453 // Type of branch condition that is supported.
454 enum branch_condition_type_t
455 {
456 branch_if_true = 1,
457 branch_if_false = 0,
458 unknown_branch_condition = -1
459 };
460
461 // For each basic block there can be a trace entry which consists of an
462 // insn that sets the ccreg (usually a comparison) and a ccreg store.
463 struct bb_entry
464 {
465 basic_block bb;
466 set_of_reg setcc;
467 set_of_reg cstore;
468 cstore_type_t cstore_type;
469 std::vector<set_of_reg> cstore_reg_reg_copies;
470
bb_entrysh_treg_combine::bb_entry471 bb_entry (basic_block b)
472 : bb (b), setcc (), cstore (), cstore_type (cstore_unknown) { }
473
comparison_rtxsh_treg_combine::bb_entry474 rtx comparison_rtx (void) const { return setcc.set_src (); }
475 };
476
477 // A ccreg trace for a conditional branch.
478 struct cbranch_trace
479 {
480 rtx_insn *cbranch_insn;
481 rtx* condition_rtx_in_insn;
482 branch_condition_type_t cbranch_type;
483
484 // The comparison against zero right before the conditional branch.
485 set_of_reg setcc;
486
487 // All BBs that are related to the cbranch. The last BB in the list is
488 // the BB of the cbranch itself and might be empty.
489 std::list<bb_entry> bb_entries;
490
cbranch_tracesh_treg_combine::cbranch_trace491 cbranch_trace (rtx_insn *insn)
492 : cbranch_insn (insn),
493 condition_rtx_in_insn (NULL),
494 cbranch_type (unknown_branch_condition),
495 setcc ()
496 {
497 if (is_conditional_insn (cbranch_insn))
498 condition_rtx_in_insn = &XEXP (XEXP (PATTERN (cbranch_insn), 1), 0);
499 else if (rtx x = pc_set (cbranch_insn))
500 condition_rtx_in_insn = &XEXP (XEXP (x, 1), 0);
501 }
502
bbsh_treg_combine::cbranch_trace503 basic_block bb (void) const { return BLOCK_FOR_INSN (cbranch_insn); }
504
505 rtx
branch_condition_rtxsh_treg_combine::cbranch_trace506 branch_condition_rtx (void) const
507 {
508 return condition_rtx_in_insn != NULL ? *condition_rtx_in_insn : NULL;
509 }
510 rtx&
branch_condition_rtx_refsh_treg_combine::cbranch_trace511 branch_condition_rtx_ref (void) const
512 {
513 // Before anything gets to invoke this function, there are other checks
514 // in place to make sure that we have a known branch condition and thus
515 // the ref to the rtx in the insn.
516 gcc_assert (condition_rtx_in_insn != NULL);
517 return *condition_rtx_in_insn;
518 }
519
520 bool
can_invert_conditionsh_treg_combine::cbranch_trace521 can_invert_condition (void) const
522 {
523 // The branch condition can be inverted safely only if the condition
524 // reg is dead after the cbranch.
525 return reg_dead_after_insn (XEXP (branch_condition_rtx (), 0),
526 cbranch_insn);
527 }
528 };
529
530 static const pass_data default_pass_data;
531
532 // Tells whether modified or newly added insns are to be split at the end
533 // of the pass.
534 const bool m_split_insns;
535
536 // rtx of the ccreg that is obtained from the target.
537 rtx m_ccreg;
538
539 // Newly added or modified insns.
540 std::vector<rtx> m_touched_insns;
541
542 // Given an rtx determine whether it's a comparison with a constant zero.
543 static bool is_cmp_eq_zero (const_rtx i);
544
545 // Update the stored mode of the ccreg from the given branch condition rtx.
546 void update_ccreg_mode (const_rtx cond);
547
548 // Given an rtx, figure out the branch condition, assuming that it is
549 // in canonical form:
550 // (ne (reg) (const_int 0))
551 // (eq (reg) (const_int 0))
552 branch_condition_type_t branch_condition_type (const_rtx cond) const;
553
554 // Return true if the specified rtx is either a normal ccreg or
555 // a negated form of the ccreg.
556 bool is_normal_ccreg (const_rtx x) const;
557 bool is_inverted_ccreg (const_rtx x) const;
558
559 // Given a reg rtx and a start insn rtx, try to find the insn in the same
560 // basic block that sets the specified reg.
561 // Return how the search ended and the insn where it stopped or NULL_RTX.
562 enum record_return_t
563 {
564 set_found,
565 set_not_found,
566 other_set_found
567 };
568 record_return_t record_set_of_reg (rtx reg, rtx_insn *start_insn,
569 bb_entry& e);
570
571 // Tells whether the cbranch insn of the specified bb_entry can be removed
572 // safely without triggering any side effects.
573 bool can_remove_cstore (const bb_entry& e,
574 const cbranch_trace& trace) const;
575
576 // Tells whether the setcc insn of the specified bb_entry can be removed
577 // safely without triggering any side effects.
578 bool can_remove_comparison (const bb_entry& e,
579 const cbranch_trace& trace) const;
580
581 // Tells whether the two specified comparison rtx can be combined into a
582 // single comparison.
583 bool can_combine_comparisons (const_rtx x, const_rtx y) const;
584
585 // Tells whether the ccreg usage can be extended from the bb_entry on until
586 // the final cbranch of the trace.
587 bool can_extend_ccreg_usage (const bb_entry& e,
588 const cbranch_trace& trace) const;
589
590 // Create an insn rtx that performs a logical not (test != 0) on the src_reg
591 // and stores the result in dst_reg.
592 rtx make_not_reg_insn (rtx dst_reg, rtx src_reg) const;
593
594 // Create an insn rtx that inverts the ccreg.
595 rtx_insn *make_inv_ccreg_insn (void) const;
596
597 // Adds the specified insn to the set of modified or newly added insns that
598 // might need splitting at the end of the pass.
599 rtx touched_insn (rtx i);
600
601 // Try to invert the branch condition of the specified trace.
602 bool try_invert_branch_condition (cbranch_trace& trace);
603
604 // Try to optimize a cbranch trace by combining comparisons in BBs and
605 // eliminate the cstores.
606 bool try_combine_comparisons (cbranch_trace& trace,
607 int cstore_count, int inv_cstore_count,
608 cstore_type_t dominating_cstore);
609
610 // Try to optimize a cbranch trace by eliminating the cstores in BBs only.
611 bool try_eliminate_cstores (cbranch_trace& trace,
612 int cstore_count, int inv_cstore_count,
613 cstore_type_t dominating_cstore);
614
615 // Given a branch insn, try to optimize its branch condition.
616 // If any insns are modified or added they are added to 'm_touched_insns'.
617 void try_optimize_cbranch (rtx_insn *i);
618 };
619
620
621 const pass_data sh_treg_combine::default_pass_data =
622 {
623 RTL_PASS, // type
624 "", // name (overwritten by the constructor)
625 OPTGROUP_NONE, // optinfo_flags
626 TV_OPTIMIZE, // tv_id
627 0, // properties_required
628 0, // properties_provided
629 0, // properties_destroyed
630 0, // todo_flags_start
631 TODO_df_finish | TODO_df_verify // todo_flags_finish
632 };
633
sh_treg_combine(gcc::context * ctx,bool split_insns,const char * name)634 sh_treg_combine::sh_treg_combine (gcc::context* ctx, bool split_insns,
635 const char* name)
636 : rtl_opt_pass (default_pass_data, ctx),
637 m_split_insns (split_insns),
638 m_ccreg (NULL_RTX)
639 {
640 // Overwrite default name in pass_data base class.
641 this->name = name;
642 }
643
~sh_treg_combine(void)644 sh_treg_combine::~sh_treg_combine (void)
645 {
646 }
647
update_ccreg_mode(const_rtx cond)648 void sh_treg_combine::update_ccreg_mode (const_rtx cond)
649 {
650 if (REG_P (XEXP (cond, 0)) && REGNO (XEXP (cond, 0)) != REGNO (m_ccreg))
651 return;
652
653 machine_mode m = GET_MODE (XEXP (cond, 0));
654 if (m == GET_MODE (m_ccreg))
655 return;
656
657 PUT_MODE (m_ccreg, m);
658 log_msg ("updated ccreg mode: ");
659 log_rtx (m_ccreg);
660 log_msg ("\n");
661 }
662
663 bool
is_cmp_eq_zero(const_rtx i)664 sh_treg_combine::is_cmp_eq_zero (const_rtx i)
665 {
666 return i != NULL_RTX && GET_CODE (i) == EQ
667 && REG_P (XEXP (i, 0)) && XEXP (i, 1) == const0_rtx;
668 }
669
670 sh_treg_combine::branch_condition_type_t
branch_condition_type(const_rtx cond) const671 sh_treg_combine::branch_condition_type (const_rtx cond) const
672 {
673 if (cond == NULL_RTX)
674 return unknown_branch_condition;
675
676 if (GET_CODE (cond) == NE
677 && REG_P (XEXP (cond, 0)) && REGNO (XEXP (cond, 0)) == REGNO (m_ccreg)
678 && XEXP (cond, 1) == const0_rtx)
679 return branch_if_true;
680
681 else if (GET_CODE (cond) == EQ
682 && REG_P (XEXP (cond, 0)) && REGNO (XEXP (cond, 0)) == REGNO (m_ccreg)
683 && XEXP (cond, 1) == const0_rtx)
684 return branch_if_false;
685
686 else
687 return unknown_branch_condition;
688 }
689
690 bool
is_normal_ccreg(const_rtx x) const691 sh_treg_combine::is_normal_ccreg (const_rtx x) const
692 {
693 return t_reg_operand (const_cast<rtx> (x), VOIDmode);
694 }
695
696 bool
is_inverted_ccreg(const_rtx x) const697 sh_treg_combine::is_inverted_ccreg (const_rtx x) const
698 {
699 return negt_reg_operand (const_cast<rtx> (x), VOIDmode);
700 }
701
702 sh_treg_combine::record_return_t
record_set_of_reg(rtx reg,rtx_insn * start_insn,bb_entry & new_entry)703 sh_treg_combine::record_set_of_reg (rtx reg, rtx_insn *start_insn,
704 bb_entry& new_entry)
705 {
706 log_msg ("\n[bb %d]\n", new_entry.bb->index);
707
708 if (start_insn == NULL_RTX)
709 log_return (set_not_found, "set of reg not found. empty BB?\n");
710
711 new_entry.cstore_type = cstore_unknown;
712
713 for (rtx_insn *i = start_insn; i != NULL; )
714 {
715 new_entry.cstore = find_set_of_reg_bb (reg, i);
716
717 if (new_entry.cstore.set_src () == NULL_RTX)
718 log_return (set_not_found, "set of reg not found (cstore)\n");
719
720 log_insn (new_entry.cstore.insn);
721 log_msg ("\n");
722
723 if (is_normal_ccreg (new_entry.cstore.set_src ()))
724 {
725 log_msg ("normal condition store\n");
726 new_entry.cstore_type = cstore_normal;
727 }
728 else if (is_inverted_ccreg (new_entry.cstore.set_src ()))
729 {
730 log_msg ("inverted condition store\n");
731 new_entry.cstore_type = cstore_inverted;
732 }
733 else if (REG_P (new_entry.cstore.set_src ()))
734 {
735 // If it's a reg-reg copy follow the copied reg.
736 new_entry.cstore_reg_reg_copies.push_back (new_entry.cstore);
737 reg = new_entry.cstore.set_src ();
738 i = new_entry.cstore.insn;
739
740 log_msg ("reg-reg copy. tracing ");
741 log_rtx (reg);
742 log_msg ("\n");
743 continue;
744 }
745 else
746 log_return (other_set_found, "not a condition store\n");
747
748 gcc_assert (new_entry.cstore_type != cstore_unknown);
749
750 // Now see how the ccreg was set.
751 // For now it must be in the same BB.
752 log_msg ("tracing ccreg\n");
753 new_entry.setcc = find_set_of_reg_bb
754 (m_ccreg, prev_nonnote_nondebug_insn_bb (new_entry.cstore.insn));
755
756 // If cstore was found but setcc was not found continue anyway, as
757 // for some of the optimization types the setcc is irrelevant.
758 if (new_entry.setcc.set_src () == NULL_RTX)
759 log_return (set_found, "set of ccreg not found\n");
760
761 else if (GET_CODE (new_entry.setcc.set_rtx) == SET)
762 {
763 // Also allow insns that set the ccreg, but are not true comparison
764 // insns, as long as they are sets and not e.g. clobbers.
765 log_insn (new_entry.setcc.insn);
766 log_msg ("\n");
767 return set_found;
768 }
769 else
770 // If cstore was found but setcc was not found continue anyway, as
771 // for some of the optimization types the setcc is irrelevant.
772 log_return (set_found, "unknown set of ccreg\n");
773 }
774
775 log_return (set_not_found, "set of reg not found\n");
776 }
777
778 bool
can_remove_cstore(const bb_entry & e,const cbranch_trace & trace) const779 sh_treg_combine::can_remove_cstore (const bb_entry& e,
780 const cbranch_trace& trace) const
781 {
782 if (volatile_insn_p (PATTERN (e.cstore.insn)))
783 {
784 log_msg ("can't remove insn\n");
785 log_insn (e.cstore.insn);
786 log_return (false, "\nbecause it's volatile\n");
787 }
788
789 // On SH there are parallel patterns which store the ccreg multiple times.
790 // In this case it's not safe.
791 rtx cstore_pat = PATTERN (e.cstore.insn);
792 if (GET_CODE (cstore_pat) == PARALLEL)
793 for (int i = 0; i < XVECLEN (cstore_pat, 0); ++i)
794 {
795 rtx x = XVECEXP (cstore_pat, 0, i);
796
797 // It's the cstore set that we're referring to, ignore that one.
798 if (x != e.cstore.set_rtx
799 && GET_CODE (x) == SET && reg_referenced_p (m_ccreg, x))
800 {
801 log_msg ("can't remove insn\n");
802 log_insn (e.cstore.insn);
803 log_return (false, "\nbecause it's a multiple ccreg store\n");
804 }
805 }
806
807 // If the cstore sets the ccreg (e.g. negc) and the ccreg is used afterwards
808 // it's not safe.
809 if (modified_in_p (m_ccreg, e.cstore.insn)
810 && !(reg_dead_after_insn (m_ccreg, e.cstore.insn)
811 || reg_unused_after_insn (m_ccreg, e.cstore.insn)))
812 {
813 log_msg ("can't remove insn\n");
814 log_insn (e.cstore.insn);
815 log_return (false, "\nbecause it sets the ccreg\n");
816 }
817
818 // If the cstore destination reg is copied around check the reg-reg
819 // copies. At every reg-reg copy the copied reg must be dead and there
820 // must not be a usage of the copied regs between the reg-reg copies.
821 // Otherwise we assume that the result of the cstore is used in some
822 // other way.
823 rtx_insn *prev_insn = e.cstore.insn;
824 for (std::vector<set_of_reg>::const_reverse_iterator i =
825 e.cstore_reg_reg_copies.rbegin ();
826 i != e.cstore_reg_reg_copies.rend (); ++i)
827 {
828 if (!reg_dead_after_insn (i->set_src (), i->insn))
829 {
830 log_msg ("can't remove insn\n");
831 log_insn (i->insn);
832 log_return (false, "\nbecause source of reg-reg copy doesn't die\n");
833 }
834
835 if (reg_used_between_p (i->set_src (), prev_insn, i->insn))
836 {
837 log_msg ("can't remove insn\n");
838 log_insn (i->insn);
839 log_return (false, "\nbecause reg %d is otherwise used\n",
840 REGNO (i->set_src ()));
841 }
842
843 prev_insn = i->insn;
844 }
845
846 // The cstore_dst reg must die after the test before the cbranch, otherwise
847 // it's not safe to remove the cstore.
848 // If the cstore destination reg is copied around check the effective
849 // destination reg of the cstore. The reg-reg copies are recorded in
850 // reverse order, i.e. the most recent reg-reg copy in the insn list
851 // comes first.
852 rtx cstore_dst = e.cstore_reg_reg_copies.empty ()
853 ? e.cstore.set_dst ()
854 : e.cstore_reg_reg_copies.front ().set_dst ();
855
856 if (!reg_dead_after_insn (cstore_dst, trace.setcc.insn))
857 {
858 log_msg ("can't remove insn\n");
859 log_insn (e.cstore.insn);
860 log_return (false, "\nbecause its effective target reg %d doesn't die "
861 "after trace.setcc.insn\n", REGNO (cstore_dst));
862 }
863
864 // Also check that the cstore_dst reg is not used in other reachable code
865 // paths before it dies.
866 // Count the uses of the effective cstore_dst reg (i.e. the last known reg
867 // that holds the cstore value after reg-reg copies) in all BBs that can be
868 // reached from bb_entry's BB including the BB of the cstore insn.
869 // If we get more than 1 uses we assume that it's used somewhere else and is
870 // not safe to be removed.
871 int cstore_dst_use_count = trace_reg_uses (cstore_dst, e.cstore.insn,
872 trace.setcc.insn);
873 if (cstore_dst_use_count > 1)
874 {
875 log_msg ("can't remove insn\n");
876 log_insn (e.cstore.insn);
877 log_return (false, "\nbecause its effective target reg %d is used "
878 "in %d other places\n", REGNO (cstore_dst),
879 cstore_dst_use_count - 1);
880 }
881
882 return true;
883 }
884
885 bool
can_remove_comparison(const bb_entry & e,const cbranch_trace &) const886 sh_treg_combine::can_remove_comparison (const bb_entry& e,
887 const cbranch_trace&/* trace*/) const
888 {
889 // If the ccreg is used otherwise between the comparison and the cstore,
890 // it's not safe.
891 if (reg_used_between_p (m_ccreg, e.setcc.insn, e.cstore.insn))
892 {
893 log_msg ("can't remove insn\n");
894 log_insn (e.setcc.insn);
895 log_return (false, "\nbecause the ccreg is used otherwise\n");
896 }
897
898 if (!reg_dead_after_insn (m_ccreg, e.cstore.insn)
899 && !reg_unused_after_insn (m_ccreg, e.cstore.insn))
900 {
901 log_msg ("can't remove insn\n");
902 log_insn (e.cstore.insn);
903 log_return (false, "\nbecause ccreg is not dead or unused afterwards\n");
904 }
905
906 // On SH there are also multiple set patterns that can be used for
907 // comparisons, such as "shll". It's not safe to remove those.
908 if (multiple_sets (e.setcc.insn))
909 {
910 log_msg ("can't remove insn\n");
911 log_insn (e.cstore.insn);
912 log_return (false, "\nbecause it's a multiple set\n");
913 }
914
915 return true;
916 }
917
918 rtx
make_not_reg_insn(rtx dst_reg,rtx src_reg) const919 sh_treg_combine::make_not_reg_insn (rtx dst_reg, rtx src_reg) const
920 {
921 // On SH we can do only SImode and DImode comparisons.
922 if (! (GET_MODE (src_reg) == SImode || GET_MODE (src_reg) == DImode))
923 return NULL;
924
925 // On SH we can store the ccreg into an SImode or DImode reg only.
926 if (! (GET_MODE (dst_reg) == SImode || GET_MODE (dst_reg) == DImode))
927 return NULL;
928
929 start_sequence ();
930
931 emit_insn (gen_rtx_SET (m_ccreg, gen_rtx_fmt_ee (EQ, SImode,
932 src_reg, const0_rtx)));
933
934 if (GET_MODE (dst_reg) == SImode)
935 emit_move_insn (dst_reg, m_ccreg);
936 else if (GET_MODE (dst_reg) == DImode)
937 {
938 emit_move_insn (gen_lowpart (SImode, dst_reg), m_ccreg);
939 emit_move_insn (gen_highpart (SImode, dst_reg), const0_rtx);
940 }
941 else
942 gcc_unreachable ();
943
944 rtx i = get_insns ();
945 end_sequence ();
946
947 return i;
948 }
949
950 rtx_insn *
make_inv_ccreg_insn(void) const951 sh_treg_combine::make_inv_ccreg_insn (void) const
952 {
953 start_sequence ();
954 rtx_insn *i = emit_insn (gen_rtx_SET (m_ccreg,
955 gen_rtx_fmt_ee (XOR, GET_MODE (m_ccreg),
956 m_ccreg, const1_rtx)));
957 end_sequence ();
958 return i;
959 }
960
961 rtx
touched_insn(rtx i)962 sh_treg_combine::touched_insn (rtx i)
963 {
964 m_touched_insns.push_back (i);
965 return i;
966 }
967
968 bool
can_combine_comparisons(const_rtx x,const_rtx y) const969 sh_treg_combine::can_combine_comparisons (const_rtx x, const_rtx y) const
970 {
971 if (GET_CODE (x) != GET_CODE (y))
972 return false;
973
974 rtx x_op0 = XEXP (x, 0);
975 rtx x_op1 = XEXP (x, 1);
976
977 rtx y_op0 = XEXP (y, 0);
978 rtx y_op1 = XEXP (y, 1);
979
980 if (!REG_P (x_op0) || !REG_P (y_op0))
981 return false;
982
983 if (GET_MODE (x_op0) != GET_MODE (y_op0))
984 return false;
985
986 // rtx_equal_p also compares the reg numbers which we do not care about
987 // here, as long as both are regs and the modes are the same.
988 if (REG_P (x_op1))
989 return REG_P (y_op1) && GET_MODE (x_op1) == GET_MODE (y_op1);
990
991 return rtx_equal_p (x_op1, y_op1);
992 }
993
994 bool
can_extend_ccreg_usage(const bb_entry & e,const cbranch_trace & trace) const995 sh_treg_combine::can_extend_ccreg_usage (const bb_entry& e,
996 const cbranch_trace& trace) const
997 {
998 // Check if the ccreg is not modified by other insins in the BB path until
999 // the final cbranch of the trace.
1000 // Start checking after the cstore that follows the setcc, assuming that
1001 // the cstore will be removed.
1002
1003 // The assumption here is that the specified bb_entry's BB is a direct
1004 // predecessor of the trace.cbranch_insn's BB.
1005 if (e.bb != trace.bb () && !is_adjacent_bb (e.bb, trace.bb ()))
1006 log_return (false,
1007 "can't extend ccreg usage -- [bb %d] and [bb %d] are not adjacent\n",
1008 e.bb->index, trace.bb ()->index);
1009
1010 if (e.cstore.empty ())
1011 log_return (false, "can't extend ccreg usage -- no cstore\n");
1012
1013 // The entry's cstore is in the same BB as the final cbranch.
1014 if (e.bb == trace.bb ())
1015 {
1016 if (reg_set_between_p (m_ccreg, e.cstore.insn, trace.setcc.insn))
1017 log_return (false,
1018 "can't extend ccreg usage -- it's modified between e.cstore.insn "
1019 "and trace.setcc.insn");
1020 else
1021 return true;
1022 }
1023
1024 // The entry's cstore and the final cbranch are in different BBs.
1025 if (reg_set_between_p (m_ccreg, e.cstore.insn, NEXT_INSN (BB_END (e.bb))))
1026 log_return (false,
1027 "can't extend ccreg usage -- it's modified in [bb %d]", e.bb->index);
1028
1029 if (reg_set_between_p (m_ccreg, PREV_INSN (BB_HEAD (trace.bb ())),
1030 trace.setcc.insn))
1031 log_return (false,
1032 "can't extend ccreg usage -- it's modified in [bb %d]",
1033 trace.bb ()->index);
1034
1035 return true;
1036 }
1037
1038 bool
try_invert_branch_condition(cbranch_trace & trace)1039 sh_treg_combine::try_invert_branch_condition (cbranch_trace& trace)
1040 {
1041 log_msg ("inverting branch condition\n");
1042
1043 rtx& comp = trace.branch_condition_rtx_ref ();
1044
1045 rtx_code rev_cmp_code = reversed_comparison_code (comp, trace.cbranch_insn);
1046
1047 if (rev_cmp_code == UNKNOWN)
1048 log_return (false, "reversed_comparison_code = UNKNOWN\n");
1049
1050 validate_change (trace.cbranch_insn, &comp,
1051 gen_rtx_fmt_ee (rev_cmp_code,
1052 GET_MODE (comp), XEXP (comp, 0),
1053 XEXP (comp, 1)),
1054 1);
1055
1056 if (verify_changes (num_validated_changes ()))
1057 confirm_change_group ();
1058 else
1059 log_return (false, "verify_changed failed\n");
1060
1061 touched_insn (trace.cbranch_insn);
1062 return true;
1063 }
1064
1065 bool
try_combine_comparisons(cbranch_trace & trace,int cstore_count,int inv_cstore_count,cstore_type_t dominating_cstore)1066 sh_treg_combine::try_combine_comparisons (cbranch_trace& trace,
1067 int cstore_count,
1068 int inv_cstore_count,
1069 cstore_type_t dominating_cstore)
1070 {
1071 log_msg ("\ntry_combine_comparisons\n");
1072
1073 // This function will always try to create new pseudos.
1074 if (!can_create_pseudo_p ())
1075 log_return (false, "can't create pseudos\n");
1076
1077 // Check that all ccset insns are comparisons and all comparison types in
1078 // all BBs are the same and could be combined into one single comparison.
1079 rtx comp = NULL_RTX;
1080 rtx comp_insn = NULL_RTX;
1081
1082 for (std::list<bb_entry>::const_iterator i = trace.bb_entries.begin ();
1083 i != trace.bb_entries.end (); ++i)
1084 {
1085 int i_empty_count = i->setcc.empty () + i->cstore.empty ();
1086
1087 // A completly empty entry is OK (could be the BB of the cbranch).
1088 if (i_empty_count == 2)
1089 continue;
1090
1091 // Otherwise we need both, the setcc and the cstore.
1092 if (i_empty_count != 0)
1093 log_return (false, "bb entry is not a setcc cstore pair\n");
1094
1095 rtx other_comp = i->comparison_rtx ();
1096
1097 if (!COMPARISON_P (other_comp))
1098 {
1099 log_msg ("setcc is not a comparison:\n");
1100 log_rtx (other_comp);
1101 log_return (false, "\n");
1102 }
1103
1104 if (comp_insn == NULL_RTX)
1105 {
1106 comp = other_comp;
1107 comp_insn = i->setcc.insn;
1108 }
1109 else if (!can_combine_comparisons (comp, other_comp))
1110 return false;
1111
1112 // The goal here is to eliminate all cstores and comparisons in the BBs.
1113 // Thus check if every cstore can actually be removed safely.
1114 if (!can_remove_cstore (*i, trace) || !can_remove_comparison (*i, trace))
1115 return false;
1116 }
1117
1118 // FIXME: The first operand of the comparison must be a simple reg.
1119 // This effectively prohibits combining div0s comparisons such as
1120 // (lt:SI (xor:SI (reg:SI) (reg:SI)))
1121 if (!REG_P (XEXP (comp, 0)))
1122 {
1123 log_msg ("comparison operand 0\n");
1124 log_rtx (XEXP (comp, 0));
1125 log_return (false, "\nis not a reg\n");
1126 }
1127
1128 rtx comp_op0 = gen_reg_rtx (GET_MODE (XEXP (comp, 0)));
1129 rtx comp_op1 = REG_P (XEXP (comp, 1))
1130 ? gen_reg_rtx (GET_MODE (XEXP (comp, 1)))
1131 : XEXP (comp, 1);
1132
1133 // If there are both, inverting and non-inverting cstores, they can only
1134 // be eliminated if the comparison can be inverted. We assume that the
1135 // comparison insns that we find are already minimal and canonicalized.
1136 // There is one special case though, where an integer comparison
1137 // (eq (reg) (const_int 0))
1138 // can be inverted with a sequence
1139 // (set (t) (eq (reg) (const_int 0))
1140 // (set (reg) (t))
1141 // (eq (reg) (const_int 0))
1142 //
1143 // FIXME: On SH2A it might be better to use the nott insn in this case,
1144 // i.e. do the try_eliminate_cstores approach instead.
1145 if (inv_cstore_count != 0 && cstore_count != 0)
1146 {
1147 if (make_not_reg_insn (comp_op0, comp_op0) == NULL_RTX)
1148 log_return (false, "make_not_reg_insn failed.\n");
1149
1150 for (std::list<bb_entry>::const_iterator i = trace.bb_entries.begin ();
1151 i != trace.bb_entries.end (); ++i)
1152 {
1153 if (i->setcc.empty () || i->cstore.empty ())
1154 continue;
1155
1156 if (i->cstore_type != dominating_cstore
1157 && !is_cmp_eq_zero (i->comparison_rtx ()))
1158 {
1159 log_msg ("can't invert comparison in insn\n");
1160 log_insn (i->setcc.insn);
1161 log_return (false,
1162 "\nbecause it's not a (eq (reg) (const_int 0))\n");
1163 }
1164 }
1165 }
1166
1167 if (dominating_cstore == cstore_normal
1168 && !try_invert_branch_condition (trace))
1169 return false;
1170
1171 // Replace the test insn before the cbranch with the common comparison.
1172 // Instead of creating a new insn from scratch we copy the common comparison
1173 // pattern. This simplifies handling parallel comparison patterns, such as
1174 // FP comparisons on SH, which have an extra use on FPSCR.
1175 log_msg ("installing common comparison in [bb %d]\n", trace.bb ()->index);
1176
1177 rtx common_comp_pat = copy_rtx (PATTERN (comp_insn));
1178 rtx common_comp = const_cast<rtx> (set_of (m_ccreg, common_comp_pat));
1179
1180 gcc_assert (common_comp != NULL_RTX);
1181
1182 XEXP (XEXP (common_comp, 1), 0) = comp_op0;
1183 XEXP (XEXP (common_comp, 1), 1) = comp_op1;
1184
1185 log_rtx (common_comp_pat);
1186 log_msg ("\n");
1187
1188 rtx common_comp_insn = touched_insn (emit_insn_after (common_comp_pat,
1189 trace.setcc.insn));
1190
1191 if (REG_P (comp_op0))
1192 add_reg_note (common_comp_insn, REG_DEAD, copy_rtx (comp_op0));
1193 if (REG_P (comp_op1))
1194 add_reg_note (common_comp_insn, REG_DEAD, copy_rtx (comp_op1));
1195
1196 delete_insn (trace.setcc.insn);
1197
1198 // Replace comparison and cstore insns with reg-reg moves in all BBs.
1199 for (std::list<bb_entry>::const_iterator i = trace.bb_entries.begin ();
1200 i != trace.bb_entries.end (); ++i)
1201 {
1202 if (i->setcc.empty () || i->cstore.empty ())
1203 continue;
1204
1205 rtx i_comp_op0 = XEXP (i->comparison_rtx (), 0);
1206 rtx i_comp_op1 = XEXP (i->comparison_rtx (), 1);
1207
1208 if (i->cstore_type == dominating_cstore)
1209 {
1210 log_msg ("replacing comparison and cstore with reg move "
1211 "in [bb %d]\n", i->bb->index);
1212
1213 rtx new_i = touched_insn (
1214 emit_insn_after (gen_move_insn (comp_op0, i_comp_op0),
1215 i->setcc.insn));
1216
1217 if (REG_P (i_comp_op0)
1218 && reg_dead_after_insn (i_comp_op0, i->setcc.insn))
1219 add_reg_note (new_i, REG_DEAD, copy_rtx (i_comp_op0));
1220
1221 // If the second operand is a reg, have to emit a move insn.
1222 // Otherwise assume it's a const_int and just reference it.
1223 if (REG_P (comp_op1))
1224 {
1225 new_i = touched_insn (
1226 emit_insn_after (gen_move_insn (comp_op1, i_comp_op1),
1227 i->setcc.insn));
1228
1229 if (reg_dead_after_insn (i_comp_op1, i->setcc.insn))
1230 add_reg_note (new_i, REG_DEAD, copy_rtx (i_comp_op1));
1231 }
1232 }
1233 else
1234 {
1235 log_msg ("replacing comparison and cstore with inverting reg move "
1236 "in [bb %d]\n", i->bb->index);
1237
1238 rtx new_i = make_not_reg_insn (comp_op0, i_comp_op0);
1239 if (REG_P (i_comp_op0)
1240 && reg_dead_after_insn (i_comp_op0, i->setcc.insn))
1241 add_reg_note (new_i, REG_DEAD, copy_rtx (i_comp_op0));
1242
1243 touched_insn (emit_insn_after (new_i, i->setcc.insn));
1244 }
1245
1246 delete_insn (i->cstore.insn);
1247 delete_insn (i->setcc.insn);
1248 }
1249
1250 return true;
1251 }
1252
1253 bool
try_eliminate_cstores(cbranch_trace & trace,int cstore_count,int inv_cstore_count,cstore_type_t dominating_cstore)1254 sh_treg_combine::try_eliminate_cstores (cbranch_trace& trace,
1255 int cstore_count, int inv_cstore_count,
1256 cstore_type_t dominating_cstore)
1257 {
1258 log_msg ("\ntry_eliminate_cstores\n");
1259
1260 for (std::list<bb_entry>::const_iterator i = trace.bb_entries.begin ();
1261 i != trace.bb_entries.end (); ++i)
1262 {
1263 // A completly empty entry is OK (could be the BB of the cbranch).
1264 if (i->setcc.empty () && i->cstore.empty ())
1265 continue;
1266
1267 // We're going to eliminate cstores, but for that they have to be
1268 // there. We don't care about the setcc in this case.
1269 if (i->cstore.empty ())
1270 log_return (false, "bb entry cstore empty -- aborting\n");
1271
1272 // The goal here is to eliminate all cstores in the BBs and extend the
1273 // ccreg usage.
1274 if (!can_extend_ccreg_usage (*i, trace))
1275 return false;
1276
1277 // If the cstore can't be removed we can keep it around as long as
1278 // it doesn't modify the ccreg.
1279 if (!can_remove_cstore (*i, trace)
1280 && modified_in_p (m_ccreg, i->cstore.insn))
1281 log_return (false, "cstore sets ccreg -- aborting\n");
1282 }
1283
1284 // If there are both, inverting and non-inverting cstores, we'll have to
1285 // invert the ccreg as a replacement for one of them.
1286 if (cstore_count != 0 && inv_cstore_count != 0)
1287 {
1288 rtx_insn *i = make_inv_ccreg_insn ();
1289 if (recog_memoized (i) < 0)
1290 {
1291 log_msg ("failed to match ccreg inversion insn:\n");
1292 log_rtx (PATTERN (i));
1293 log_return (false, "\naborting\n");
1294 }
1295 }
1296
1297 if (dominating_cstore == cstore_normal
1298 && !try_invert_branch_condition (trace))
1299 return false;
1300
1301 // Eliminate cstores in all BBs.
1302 for (std::list<bb_entry>::const_iterator i = trace.bb_entries.begin ();
1303 i != trace.bb_entries.end (); ++i)
1304 {
1305 if (i->cstore.empty ())
1306 continue;
1307
1308 if (i->cstore_type == dominating_cstore)
1309 log_msg ("removing cstore in [bb %d]\n", i->bb->index);
1310 else
1311 {
1312 log_msg ("replacing cstore with ccreg inversion in [bb %d]\n",
1313 i->bb->index);
1314
1315 touched_insn (
1316 emit_insn_after (make_inv_ccreg_insn (), i->cstore.insn));
1317 }
1318
1319 if (can_remove_cstore (*i, trace))
1320 delete_insn (i->cstore.insn);
1321 }
1322
1323 log_msg ("removing test insn before cbranch\n");
1324 delete_insn (trace.setcc.insn);
1325 return true;
1326 }
1327
1328 void
try_optimize_cbranch(rtx_insn * insn)1329 sh_treg_combine::try_optimize_cbranch (rtx_insn *insn)
1330 {
1331 cbranch_trace trace (insn);
1332
1333 log_msg ("\n\n--------------------------------------\n");
1334 log_msg ("found cbranch insn in [bb %d]:\n", trace.bb ()->index);
1335 log_insn (insn);
1336
1337 trace.cbranch_type = branch_condition_type (trace.branch_condition_rtx ());
1338
1339 if (trace.cbranch_type == branch_if_true)
1340 log_msg ("condition: branch if true\n");
1341 else if (trace.cbranch_type == branch_if_false)
1342 log_msg ("condition: branch if false\n");
1343 else
1344 {
1345 log_msg ("unknown branch condition\n");
1346 log_rtx (trace.branch_condition_rtx ());
1347 log_return_void ("\n");
1348 }
1349
1350 update_ccreg_mode (trace.branch_condition_rtx ());
1351
1352 // Scan the insns backwards for an insn that sets the ccreg by testing a
1353 // reg against zero like
1354 // (set (reg ccreg) (eq (reg) (const_int 0)))
1355 // The testing insn could also be outside of the current basic block, but
1356 // for now we limit the search to the current basic block.
1357 trace.setcc = find_set_of_reg_bb
1358 (m_ccreg, prev_nonnote_nondebug_insn_bb (insn));
1359
1360 if (trace.setcc.set_src () == NULL_RTX)
1361 log_return_void ("could not find set of ccreg in current BB\n");
1362
1363 if (!is_cmp_eq_zero (trace.setcc.set_src ())
1364 && !is_inverted_ccreg (trace.setcc.set_src ()))
1365 {
1366 log_msg ("unsupported set of ccreg in current BB: ");
1367 log_rtx (trace.setcc.set_src ());
1368 log_return_void ("\n");
1369 }
1370
1371 rtx trace_reg = XEXP (trace.setcc.set_src (), 0);
1372
1373 log_msg ("set of ccreg:\n");
1374 log_insn (trace.setcc.insn);
1375
1376 // See if we can remove the trace.setcc insn safely.
1377 if (reg_used_between_p (m_ccreg, trace.setcc.insn, trace.cbranch_insn))
1378 log_return_void ("ccreg used between testing insn and branch insn\n");
1379
1380 if (volatile_insn_p (PATTERN (trace.setcc.insn)))
1381 {
1382 log_msg ("can't remove insn\n");
1383 log_insn (trace.setcc.insn);
1384 log_return_void ("\nbecause it's volatile\n");
1385 }
1386
1387 // If the ccreg is inverted before cbranch try inverting the branch
1388 // condition.
1389 if (is_inverted_ccreg (trace.setcc.set_src ()))
1390 {
1391 if (!trace.can_invert_condition ())
1392 log_return_void ("branch condition can't be inverted - aborting\n");
1393
1394 if (try_invert_branch_condition (trace))
1395 delete_insn (trace.setcc.insn);
1396
1397 return;
1398 }
1399
1400 // Now that we have an insn which tests some reg and sets the condition
1401 // reg before the conditional branch, try to figure out how that tested
1402 // reg was formed, i.e. find all the insns that set the tested reg in
1403 // some way.
1404 // The tested reg might be set in multiple basic blocks so we need to
1405 // check all basic blocks which can reach this current basic block.
1406 // If the set of reg is an inverting or non-inverting store of the condition
1407 // register, check how the ccreg value was obtained.
1408 log_msg ("\ntracing ");
1409 log_rtx (trace_reg);
1410 log_msg ("\n");
1411
1412
1413 // First check the basic block where the conditional branch is in.
1414 // If we find it here there's no point in checking other BBs.
1415 trace.bb_entries.push_front (bb_entry (trace.bb ()));
1416
1417 record_return_t res = record_set_of_reg
1418 (trace_reg, prev_nonnote_nondebug_insn_bb (trace.setcc.insn),
1419 trace.bb_entries.front ());
1420
1421 if (res == other_set_found)
1422 log_return_void ("other set found - aborting trace\n");
1423 else if (res == set_not_found)
1424 {
1425 // It seems the initial search in the BB of the conditional branch
1426 // didn't find anything. Now look in all predecessor BBs.
1427 for (edge_iterator ei = ei_start (trace.bb ()->preds);
1428 !ei_end_p (ei); ei_next (&ei))
1429 {
1430 edge e = ei_edge (ei);
1431 trace.bb_entries.push_front (bb_entry (e->src));
1432
1433 res = record_set_of_reg (trace_reg, BB_END (e->src),
1434 trace.bb_entries.front ());
1435 if (res != set_found)
1436 log_return_void ("set not found - aborting trace\n");
1437 }
1438 }
1439
1440 if (dump_file != NULL)
1441 {
1442 log_msg ("\ncbranch trace summary:\n");
1443 for (std::list<bb_entry>::const_iterator i = trace.bb_entries.begin ();
1444 i != trace.bb_entries.end (); ++i)
1445 {
1446 log_msg ("\n[bb %d]\n", i->bb->index);
1447 if (!i->setcc.empty ())
1448 {
1449 log_rtx (i->setcc.set_rtx);
1450 log_msg ("\n");
1451 }
1452 if (!i->cstore.empty ())
1453 {
1454 log_rtx (i->cstore.set_rtx);
1455 log_msg ("\n");
1456 }
1457
1458 for (std::vector<set_of_reg>::const_reverse_iterator j =
1459 i->cstore_reg_reg_copies.rbegin ();
1460 j != i->cstore_reg_reg_copies.rend (); ++j)
1461 {
1462 log_rtx (j->set_rtx);
1463 log_msg ("\n");
1464 }
1465 }
1466
1467 log_rtx (trace.setcc.set_rtx);
1468 log_msg ("\n");
1469 log_rtx (PATTERN (trace.cbranch_insn));
1470 log_msg ("\n");
1471 }
1472
1473 // Check that we don't have any empty BBs.
1474 // Only the BB with the cbranch may be empty.
1475 for (std::list<bb_entry>::const_iterator i = trace.bb_entries.begin ();
1476 i != trace.bb_entries.end (); ++i)
1477 if (i->setcc.empty () && i->cstore.empty () && i->bb != trace.bb ())
1478 log_return_void ("\n[bb %d] is empty - aborting.\n", i->bb->index);
1479
1480 // Determine the dominating cstore type
1481 // FIXME: Try to take the probabilities of the BBs into account somehow.
1482 int cstore_count = 0;
1483 int inv_cstore_count = 0;
1484
1485 for (std::list<bb_entry>::const_iterator i = trace.bb_entries.begin ();
1486 i != trace.bb_entries.end (); ++i)
1487 {
1488 if (i->cstore_type == cstore_normal)
1489 cstore_count += 1;
1490 else if (i->cstore_type == cstore_inverted)
1491 inv_cstore_count += 1;
1492 }
1493
1494 log_msg ("cstore count = %d inverted cstore count = %d\n",
1495 cstore_count, inv_cstore_count);
1496
1497 // This puts a priority on inverting cstores.
1498 cstore_type_t dominating_cstore = inv_cstore_count >= cstore_count
1499 ? cstore_inverted
1500 : cstore_normal;
1501
1502 if (dominating_cstore == cstore_inverted)
1503 log_msg ("will try to eliminate inverted cstore\n");
1504 else if (dominating_cstore == cstore_normal)
1505 {
1506 log_msg ("will try to eliminate normal cstore\n");
1507 if (!trace.can_invert_condition ())
1508 log_return_void ("branch condition can't be inverted - aborting\n");
1509 }
1510 else
1511 gcc_unreachable ();
1512
1513 if (try_combine_comparisons (trace, cstore_count, inv_cstore_count,
1514 dominating_cstore))
1515 return;
1516
1517 try_eliminate_cstores (trace, cstore_count, inv_cstore_count,
1518 dominating_cstore);
1519 }
1520
1521 bool
gate(function *)1522 sh_treg_combine::gate (function *)
1523 {
1524 return optimize > 0;
1525 }
1526
1527 unsigned int
execute(function * fun)1528 sh_treg_combine::execute (function *fun)
1529 {
1530 unsigned int ccr0 = INVALID_REGNUM;
1531 unsigned int ccr1 = INVALID_REGNUM;
1532
1533 if (targetm.fixed_condition_code_regs (&ccr0, &ccr1)
1534 && ccr0 != INVALID_REGNUM)
1535 {
1536 // Initially create a reg rtx with VOIDmode.
1537 // When the first conditional branch is discovered, the mode is changed
1538 // to the mode that is actually used by the target.
1539 m_ccreg = gen_rtx_REG (VOIDmode, ccr0);
1540 }
1541
1542 if (m_ccreg == NULL_RTX)
1543 log_return (0, "no ccreg.\n\n");
1544
1545 if (STORE_FLAG_VALUE != 1)
1546 log_return (0, "unsupported STORE_FLAG_VALUE %d", STORE_FLAG_VALUE);
1547
1548 log_msg ("ccreg: ");
1549 log_rtx (m_ccreg);
1550 log_msg (" STORE_FLAG_VALUE = %d\n", STORE_FLAG_VALUE);
1551
1552 // Look for basic blocks that end with a conditional branch or for
1553 // conditional insns and try to optimize them.
1554 basic_block bb;
1555 FOR_EACH_BB_FN (bb, fun)
1556 {
1557 rtx_insn* i = BB_END (bb);
1558 if (i == NULL || i == PREV_INSN (BB_HEAD (bb)))
1559 continue;
1560
1561 // A conditional branch is always the last insn of a basic block.
1562 if (any_condjump_p (i) && onlyjump_p (i))
1563 {
1564 try_optimize_cbranch (i);
1565 i = PREV_INSN (i);
1566 }
1567
1568 // Check all insns in block for conditional insns.
1569 for (; i != NULL && i != PREV_INSN (BB_HEAD (bb)); i = PREV_INSN (i))
1570 if (is_conditional_insn (i))
1571 try_optimize_cbranch (i);
1572 }
1573
1574 log_msg ("\n\n");
1575
1576 // If new insns are created and this pass is executed after all insns
1577 // have been split already, we must split the insns we've changed or added
1578 // ourselves here.
1579 // FIXME: Multi-word operations (which emit multiple insns) are not handled
1580 // properly here, since only one insn will end up in 'm_touched_insns'.
1581 // On SH this is not a problem though.
1582 if (m_split_insns)
1583 for (std::vector<rtx>::const_iterator i = m_touched_insns.begin ();
1584 i != m_touched_insns.end (); ++i)
1585 {
1586 log_msg ("trying to split insn:\n");
1587 log_insn (*i);
1588 log_msg ("\n");
1589 try_split (PATTERN (*i), safe_as_a <rtx_insn *> (*i), 0);
1590 }
1591
1592 m_touched_insns.clear ();
1593 log_return (0, "\n\n");
1594 }
1595
1596 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1597 // This allows instantiating the pass somewhere else without having to pull
1598 // in a header file.
1599 opt_pass*
make_pass_sh_treg_combine(gcc::context * ctx,bool split_insns,const char * name)1600 make_pass_sh_treg_combine (gcc::context* ctx, bool split_insns,
1601 const char* name)
1602 {
1603 return new sh_treg_combine (ctx, split_insns, name);
1604 }
1605