Daily bump.
[official-gcc.git] / gcc / config / sh / sh_optimize_sett_clrt.cc
blob3349d6cfd565af7897b0ccf465bd2e46c87f2075
1 /* An SH specific RTL pass that tries to optimize clrt and sett insns.
2 Copyright (C) 2013-2018 Free Software Foundation, Inc.
4 This file is part of GCC.
6 GCC is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3, or (at your option)
9 any later version.
11 GCC is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GCC; see the file COPYING3. If not see
18 <http://www.gnu.org/licenses/>. */
20 #define IN_TARGET_CODE 1
22 #include "config.h"
23 #include "system.h"
24 #include "coretypes.h"
25 #include "backend.h"
26 #include "target.h"
27 #include "rtl.h"
28 #include "df.h"
29 #include "cfgrtl.h"
30 #include "tree-pass.h"
32 #include <vector>
33 #include <algorithm>
36 This pass tries to eliminate unnecessary sett or clrt instructions in cases
37 where the ccreg value is already known to be the same as the constant set
38 would set it to. This is done as follows:
40 Check every BB's insn and see if it's a sett or clrt.
41 Once a sett or clrt insn is hit, walk insns and predecessor basic blocks
42 backwards from that insn and determine all possible ccreg values from all
43 basic block paths.
44 Insns that set the ccreg value in some way (simple set, clobber etc) are
45 recorded. Conditional branches where one edge leads to the sett / clrt insn
46 are also recorded, since for each edge in the conditional branch the ccreg
47 value is known constant.
48 After collecting all possible ccreg values at the sett / clrt insn, check that
49 all the values are the same. If that value is the same as the sett / clrt
50 insn would set the ccreg to, the sett / clrt insn can be eliminated.
54 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
55 // Helper functions
57 #define log_msg(...)\
58 do { if (dump_file != NULL) fprintf (dump_file, __VA_ARGS__); } while (0)
60 #define log_insn(i)\
61 do { if (dump_file != NULL) print_rtl_single (dump_file, \
62 (const_rtx)i); } while (0)
64 #define log_rtx(r)\
65 do { if (dump_file != NULL) print_rtl (dump_file, (const_rtx)r); } while (0)
67 #define log_return(retval, ...)\
68 do { if (dump_file != NULL) fprintf (dump_file, __VA_ARGS__); \
69 return retval; } while (0)
71 #define log_return_void(...)\
72 do { if (dump_file != NULL) fprintf (dump_file, __VA_ARGS__); \
73 return; } while (0)
75 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
76 // RTL pass class
78 class sh_optimize_sett_clrt : public rtl_opt_pass
80 public:
81 sh_optimize_sett_clrt (gcc::context* ctx, const char* name);
82 virtual ~sh_optimize_sett_clrt (void);
83 virtual bool gate (function*);
84 virtual unsigned int execute (function* fun);
86 private:
87 static const pass_data default_pass_data;
89 struct ccreg_value
91 // The insn at which the ccreg value was determined.
92 // Might be NULL if e.g. an unknown value is recorded for an
93 // empty basic block.
94 rtx_insn *insn;
96 // The basic block where the insn was discovered.
97 basic_block bb;
99 // The value of ccreg. If NULL_RTX, the exact value is not known, but
100 // the ccreg is changed in some way (e.g. clobbered).
101 rtx value;
104 // Update the mode of the captured m_ccreg with the specified mode.
105 void update_ccreg_mode (machine_mode m);
107 // Given an insn pattern, check if it sets the ccreg to a constant value
108 // of either zero or STORE_FLAG_VALUE. If so, return the value rtx,
109 // NULL_RTX otherwise.
110 rtx const_setcc_value (rtx pat) const;
112 // Given a start insn and its basic block, recursively determine all
113 // possible ccreg values in all basic block paths that can lead to the
114 // start insn.
115 bool find_last_ccreg_values (rtx_insn *start_insn, basic_block bb,
116 std::vector<ccreg_value>& values_out,
117 std::vector<basic_block>& prev_visited_bb) const;
119 // Given a cbranch insn, its basic block and another basic block, determine
120 // the value to which the ccreg will be set after jumping/falling through to
121 // the specified target basic block.
122 bool sh_cbranch_ccreg_value (rtx_insn *cbranch_insn,
123 basic_block cbranch_insn_bb,
124 basic_block branch_target_bb) const;
126 // Check whether all of the ccreg values are the same.
127 static bool all_ccreg_values_equal (const std::vector<ccreg_value>& values);
129 // Remove REG_DEAD and REG_UNUSED notes from insns of the specified
130 // ccreg_value entries.
131 void remove_ccreg_dead_unused_notes (std::vector<ccreg_value>& values) const;
133 // rtx of the ccreg that is obtained from the target.
134 rtx m_ccreg;
137 const pass_data sh_optimize_sett_clrt::default_pass_data =
139 RTL_PASS, // type
140 "", // name (overwritten by the constructor)
141 OPTGROUP_NONE, // optinfo_flags
142 TV_OPTIMIZE, // tv_id
143 0, // properties_required
144 0, // properties_provided
145 0, // properties_destroyed
146 0, // todo_flags_start
147 0 // todo_flags_finish
150 sh_optimize_sett_clrt::sh_optimize_sett_clrt (gcc::context* ctx,
151 const char* name)
152 : rtl_opt_pass (default_pass_data, ctx),
153 m_ccreg (NULL_RTX)
155 // Overwrite default name in pass_data base class.
156 this->name = name;
159 sh_optimize_sett_clrt::~sh_optimize_sett_clrt (void)
163 bool
164 sh_optimize_sett_clrt::gate (function*)
166 return optimize > 0;
169 unsigned int
170 sh_optimize_sett_clrt::execute (function* fun)
172 unsigned int ccr0 = INVALID_REGNUM;
173 unsigned int ccr1 = INVALID_REGNUM;
175 if (targetm.fixed_condition_code_regs (&ccr0, &ccr1)
176 && ccr0 != INVALID_REGNUM)
178 // Initially create a reg rtx with VOIDmode.
179 // When the constant setcc is discovered, the mode is changed
180 // to the mode that is actually used by the target.
181 m_ccreg = gen_rtx_REG (VOIDmode, ccr0);
184 if (m_ccreg == NULL_RTX)
185 log_return (0, "no ccreg.\n\n");
187 if (STORE_FLAG_VALUE != 1)
188 log_return (0, "unsupported STORE_FLAG_VALUE %d", STORE_FLAG_VALUE);
190 log_msg ("ccreg: ");
191 log_rtx (m_ccreg);
192 log_msg (" STORE_FLAG_VALUE = %d\n", STORE_FLAG_VALUE);
194 if (!df_regs_ever_live_p (ccr0))
195 log_return (0, "ccreg never live\n\n");
197 // Output vector for find_known_ccreg_values.
198 std::vector<ccreg_value> ccreg_values;
199 ccreg_values.reserve (32);
201 // Something for recording visited basic blocks to avoid infinite recursion.
202 std::vector<basic_block> visited_bbs;
203 visited_bbs.reserve (32);
205 // Look for insns that set the ccreg to a constant value and see if it can
206 // be optimized.
207 basic_block bb;
208 FOR_EACH_BB_REVERSE_FN (bb, fun)
209 for (rtx_insn *next_i, *i = NEXT_INSN (BB_HEAD (bb));
210 i != NULL_RTX && i != BB_END (bb); i = next_i)
212 next_i = NEXT_INSN (i);
214 if (!INSN_P (i) || !NONDEBUG_INSN_P (i))
215 continue;
217 rtx setcc_val = const_setcc_value (PATTERN (i));
218 if (setcc_val != NULL_RTX)
220 update_ccreg_mode (GET_MODE (XEXP (PATTERN (i), 0)));
222 log_msg ("\n\nfound const setcc insn in [bb %d]: \n", bb->index);
223 log_insn (i);
224 log_msg ("\n");
226 ccreg_values.clear ();
227 visited_bbs.clear ();
228 bool ok = find_last_ccreg_values (PREV_INSN (i), bb, ccreg_values,
229 visited_bbs);
231 log_msg ("number of ccreg values collected: %u\n",
232 (unsigned int)ccreg_values.size ());
234 // If all the collected values are equal and are equal to the
235 // constant value of the setcc insn, the setcc insn can be
236 // removed.
237 if (ok && all_ccreg_values_equal (ccreg_values)
238 && rtx_equal_p (ccreg_values.front ().value, setcc_val))
240 log_msg ("all values are ");
241 log_rtx (setcc_val);
242 log_msg ("\n");
244 delete_insn (i);
245 remove_ccreg_dead_unused_notes (ccreg_values);
250 log_return (0, "\n\n");
253 void
254 sh_optimize_sett_clrt::update_ccreg_mode (machine_mode m)
256 if (GET_MODE (m_ccreg) == m)
257 return;
259 PUT_MODE (m_ccreg, m);
260 log_msg ("updated ccreg mode: ");
261 log_rtx (m_ccreg);
262 log_msg ("\n\n");
266 sh_optimize_sett_clrt::const_setcc_value (rtx pat) const
268 if (GET_CODE (pat) == SET
269 && REG_P (XEXP (pat, 0)) && REGNO (XEXP (pat, 0)) == REGNO (m_ccreg)
270 && CONST_INT_P (XEXP (pat, 1))
271 && (INTVAL (XEXP (pat, 1)) == 0
272 || INTVAL (XEXP (pat, 1)) == STORE_FLAG_VALUE))
273 return XEXP (pat, 1);
274 else
275 return NULL_RTX;
278 bool
279 sh_optimize_sett_clrt
280 ::sh_cbranch_ccreg_value (rtx_insn *cbranch_insn, basic_block cbranch_insn_bb,
281 basic_block branch_target_bb) const
283 rtx pc_set_rtx = pc_set (cbranch_insn);
284 gcc_assert (pc_set_rtx != NULL_RTX);
285 gcc_assert (branch_target_bb != NULL);
287 rtx cond = XEXP (XEXP (pc_set_rtx, 1), 0);
288 bool branch_if;
290 if (GET_CODE (cond) == NE
291 && REG_P (XEXP (cond, 0)) && REGNO (XEXP (cond, 0)) == REGNO (m_ccreg)
292 && XEXP (cond, 1) == const0_rtx)
293 branch_if = true;
295 else if (GET_CODE (cond) == EQ
296 && REG_P (XEXP (cond, 0)) && REGNO (XEXP (cond, 0)) == REGNO (m_ccreg)
297 && XEXP (cond, 1) == const0_rtx)
298 branch_if = false;
300 else
301 gcc_unreachable ();
303 if (branch_target_bb == BRANCH_EDGE (cbranch_insn_bb)->dest)
304 return branch_if;
305 else if (branch_target_bb == FALLTHRU_EDGE (cbranch_insn_bb)->dest)
306 return !branch_if;
307 else
308 gcc_unreachable ();
311 bool
312 sh_optimize_sett_clrt
313 ::find_last_ccreg_values (rtx_insn *start_insn, basic_block bb,
314 std::vector<ccreg_value>& values_out,
315 std::vector<basic_block>& prev_visited_bb) const
317 // FIXME: For larger CFGs this will unnecessarily re-visit basic blocks.
318 // Once a basic block has been visited, the result should be stored in
319 // some container so that it can be looked up quickly eliminating the
320 // re-visits.
321 log_msg ("looking for ccreg values in [bb %d] ", bb->index);
322 if (!prev_visited_bb.empty ())
323 log_msg ("(prev visited [bb %d])", prev_visited_bb.back ()->index);
324 log_msg ("\n");
326 for (rtx_insn *i = start_insn; i != NULL && i != PREV_INSN (BB_HEAD (bb));
327 i = PREV_INSN (i))
329 if (!INSN_P (i))
330 continue;
332 if (reg_set_p (m_ccreg, i))
334 const_rtx set_rtx = set_of (m_ccreg, i);
336 ccreg_value v;
337 v.insn = i;
338 v.bb = bb;
339 v.value = set_rtx != NULL_RTX && GET_CODE (set_rtx) == SET
340 ? XEXP (set_rtx, 1)
341 : NULL_RTX;
343 log_msg ("found setcc in [bb %d] in insn:\n", bb->index);
344 log_insn (i);
345 log_msg ("\nccreg value: ");
346 log_rtx (v.value);
347 log_msg ("\n");
349 values_out.push_back (v);
350 return true;
353 if (any_condjump_p (i) && onlyjump_p (i) && !prev_visited_bb.empty ())
355 // For a conditional branch the ccreg value will be a known constant
356 // of either 0 or STORE_FLAG_VALUE after branching/falling through
357 // to one of the two successor BBs. Record the value for the BB
358 // where we came from.
359 log_msg ("found cbranch in [bb %d]:\n", bb->index);
360 log_insn (i);
362 ccreg_value v;
363 v.insn = i;
364 v.bb = bb;
365 v.value = GEN_INT (sh_cbranch_ccreg_value (i, bb,
366 prev_visited_bb.back ()));
368 log_msg (" branches to [bb %d] with ccreg value ",
369 prev_visited_bb.back ()->index);
370 log_rtx (v.value);
371 log_msg ("\n");
373 values_out.push_back (v);
374 return true;
378 // If here, we've walked up all the insns of the current basic block
379 // and none of them seems to modify the ccreg.
380 // In this case, check the predecessor basic blocks.
381 unsigned int pred_bb_count = 0;
383 // If the current basic block is not in the stack of previously visited
384 // basic blocks yet, we can recursively check the predecessor basic blocks.
385 // Otherwise we have a loop in the CFG and recursing again will result in
386 // an infinite loop.
387 if (std::find (prev_visited_bb.rbegin (), prev_visited_bb.rend (), bb)
388 == prev_visited_bb.rend ())
390 prev_visited_bb.push_back (bb);
392 for (edge_iterator ei = ei_start (bb->preds); !ei_end_p (ei);
393 ei_next (&ei))
395 if (ei_edge (ei)->flags & EDGE_COMPLEX)
396 log_return (false, "aborting due to complex edge\n");
398 basic_block pred_bb = ei_edge (ei)->src;
399 pred_bb_count += 1;
400 if (!find_last_ccreg_values (BB_END (pred_bb), pred_bb, values_out,
401 prev_visited_bb))
402 return false;
405 prev_visited_bb.pop_back ();
407 else
408 log_msg ("loop detected for [bb %d]\n", bb->index);
410 log_msg ("[bb %d] pred_bb_count = %u\n", bb->index, pred_bb_count);
412 if (pred_bb_count == 0)
414 // If we haven't checked a single predecessor basic block, the current
415 // basic block is probably a leaf block and we don't know the ccreg value.
416 log_msg ("unknown ccreg value for [bb %d]\n", bb->index);
418 ccreg_value v;
419 v.insn = BB_END (bb);
420 v.bb = bb;
421 v.value = NULL_RTX;
423 values_out.push_back (v);
426 return true;
429 bool
430 sh_optimize_sett_clrt
431 ::all_ccreg_values_equal (const std::vector<ccreg_value>& values)
433 if (values.empty ())
434 return false;
436 rtx last_value = values.front ().value;
438 // If the ccreg is modified in the insn but the exact value is not known
439 // the value rtx might be null.
440 if (last_value == NULL_RTX)
441 return false;
443 for (std::vector<ccreg_value>::const_iterator i = values.begin ();
444 i != values.end (); ++i)
445 if (i->value == NULL_RTX || !rtx_equal_p (last_value, i->value))
446 return false;
448 return true;
451 void
452 sh_optimize_sett_clrt
453 ::remove_ccreg_dead_unused_notes (std::vector<ccreg_value>& values) const
455 for (std::vector<ccreg_value>::iterator i = values.begin ();
456 i != values.end (); ++i)
458 if (i->insn == NULL_RTX)
459 continue;
461 rtx n = find_regno_note (i->insn, REG_DEAD, REGNO (m_ccreg));
462 if (n != NULL_RTX)
463 remove_note (i->insn, n);
465 n = find_regno_note (i->insn, REG_UNUSED, REGNO (m_ccreg));
466 if (n != NULL_RTX)
467 remove_note (i->insn, n);
471 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
472 // This allows instantiating the pass somewhere else without having to pull
473 // in a header file.
474 opt_pass*
475 make_pass_sh_optimize_sett_clrt (gcc::context* ctx, const char* name)
477 return new sh_optimize_sett_clrt (ctx, name);