111
|
1 /* An SH specific RTL pass that tries to combine comparisons and redundant
|
|
2 condition code register stores across multiple basic blocks.
|
131
|
3 Copyright (C) 2013-2018 Free Software Foundation, Inc.
|
111
|
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
|
131
|
21 #define IN_TARGET_CODE 1
|
|
22
|
111
|
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
|
|
266 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
|
|
273 set_dst (void) const
|
|
274 {
|
|
275 return set_rtx == NULL_RTX ? NULL_RTX : XEXP (set_rtx, 0);
|
|
276 }
|
|
277
|
|
278 bool
|
|
279 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
|
|
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;
|
131
|
296 result.insn = prev_nonnote_nondebug_insn_bb (result.insn))
|
111
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
471 bb_entry (basic_block b)
|
|
472 : bb (b), setcc (), cstore (), cstore_type (cstore_unknown) { }
|
|
473
|
|
474 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
|
|
491 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
|
|
503 basic_block bb (void) const { return BLOCK_FOR_INSN (cbranch_insn); }
|
|
504
|
|
505 rtx
|
|
506 branch_condition_rtx (void) const
|
|
507 {
|
|
508 return condition_rtx_in_insn != NULL ? *condition_rtx_in_insn : NULL;
|
|
509 }
|
|
510 rtx&
|
|
511 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
|
|
521 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
|
|
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
|
|
644 sh_treg_combine::~sh_treg_combine (void)
|
|
645 {
|
|
646 }
|
|
647
|
|
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
|
|
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
|
|
671 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
|
|
691 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
|
|
697 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
|
|
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");
|
131
|
753 new_entry.setcc = find_set_of_reg_bb
|
|
754 (m_ccreg, prev_nonnote_nondebug_insn_bb (new_entry.cstore.insn));
|
111
|
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
|
|
779 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
|
|
886 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
|
|
919 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 *
|
|
951 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
|
|
962 sh_treg_combine::touched_insn (rtx i)
|
|
963 {
|
|
964 m_touched_insns.push_back (i);
|
|
965 return i;
|
|
966 }
|
|
967
|
|
968 bool
|
|
969 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
|
|
995 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
|
|
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
|
|
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
|
|
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
|
|
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.
|
131
|
1357 trace.setcc = find_set_of_reg_bb
|
|
1358 (m_ccreg, prev_nonnote_nondebug_insn_bb (insn));
|
111
|
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
|
131
|
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 ());
|
111
|
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
|
|
1522 sh_treg_combine::gate (function *)
|
|
1523 {
|
|
1524 return optimize > 0;
|
|
1525 }
|
|
1526
|
|
1527 unsigned int
|
|
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*
|
|
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 }
|