2014-01-17 Richard Biener <rguenther@suse.de>
[official-gcc.git] / gcc / config / sh / sh_optimize_sett_clrt.cc
blob313e5b5f4c20c15a49f2fe2b97112af752f5fd29
1 /* An SH specific RTL pass that tries to optimize clrt and sett insns.
2 Copyright (C) 2013-2014 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 #include "config.h"
21 #include "system.h"
22 #include "coretypes.h"
23 #include "machmode.h"
24 #include "basic-block.h"
25 #include "df.h"
26 #include "rtl.h"
27 #include "insn-config.h"
28 #include "tree-pass.h"
29 #include "target.h"
31 #include <vector>
32 #include <algorithm>
35 This pass tries to eliminate unnecessary sett or clrt instructions in cases
36 where the ccreg value is already known to be the same as the constant set
37 would set it to. This is done as follows:
39 Check every BB's insn and see if it's a sett or clrt.
40 Once a sett or clrt insn is hit, walk insns and predecessor basic blocks
41 backwards from that insn and determine all possible ccreg values from all
42 basic block paths.
43 Insns that set the ccreg value in some way (simple set, clobber etc) are
44 recorded. Conditional branches where one edge leads to the sett / clrt insn
45 are also recorded, since for each edge in the conditional branch the ccreg
46 value is known constant.
47 After collecting all possible ccreg values at the sett / clrt insn, check that
48 all the values are the same. If that value is the same as the sett / clrt
49 insn would set the ccreg to, the sett / clrt insn can be eliminated.
53 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
54 // Helper functions
56 #define log_msg(...)\
57 do { if (dump_file != NULL) fprintf (dump_file, __VA_ARGS__); } while (0)
59 #define log_insn(i)\
60 do { if (dump_file != NULL) print_rtl_single (dump_file, \
61 (const_rtx)i); } while (0)
63 #define log_rtx(r)\
64 do { if (dump_file != NULL) print_rtl (dump_file, (const_rtx)r); } while (0)
66 #define log_return(retval, ...)\
67 do { if (dump_file != NULL) fprintf (dump_file, __VA_ARGS__); \
68 return retval; } while (0)
70 #define log_return_void(...)\
71 do { if (dump_file != NULL) fprintf (dump_file, __VA_ARGS__); \
72 return; } while (0)
74 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
75 // RTL pass class
77 class sh_optimize_sett_clrt : public rtl_opt_pass
79 public:
80 sh_optimize_sett_clrt (gcc::context* ctx, const char* name);
81 virtual ~sh_optimize_sett_clrt (void);
82 virtual bool gate (void);
83 virtual unsigned int execute (void);
85 private:
86 static const pass_data default_pass_data;
88 struct ccreg_value
90 // The insn at which the ccreg value was determined.
91 // Might be NULL_RTX if e.g. an unknown value is recorded for an
92 // empty basic block.
93 rtx insn;
95 // The basic block where the insn was discovered.
96 basic_block bb;
98 // The value of ccreg. If NULL_RTX, the exact value is not known, but
99 // the ccreg is changed in some way (e.g. clobbered).
100 rtx value;
103 // Update the mode of the captured m_ccreg with the specified mode.
104 void update_ccreg_mode (machine_mode m);
106 // Given an insn pattern, check if it sets the ccreg to a constant value
107 // of either zero or STORE_FLAG_VALUE. If so, return the value rtx,
108 // NULL_RTX otherwise.
109 rtx const_setcc_value (rtx pat) const;
111 // Given a start insn and its basic block, recursively determine all
112 // possible ccreg values in all basic block paths that can lead to the
113 // start insn.
114 void find_last_ccreg_values (rtx start_insn, basic_block bb,
115 std::vector<ccreg_value>& values_out,
116 std::vector<basic_block>& prev_visited_bb) const;
118 // Given a cbranch insn, its basic block and another basic block, determine
119 // the value to which the ccreg will be set after jumping/falling through to
120 // the specified target basic block.
121 bool sh_cbranch_ccreg_value (rtx cbranch_insn,
122 basic_block cbranch_insn_bb,
123 basic_block branch_target_bb) const;
125 // Check whether all of the ccreg values are the same.
126 static bool all_ccreg_values_equal (const std::vector<ccreg_value>& values);
128 // Remove REG_DEAD and REG_UNUSED notes from insns of the specified
129 // ccreg_value entries.
130 void remove_ccreg_dead_unused_notes (std::vector<ccreg_value>& values) const;
132 // rtx of the ccreg that is obtained from the target.
133 rtx m_ccreg;
136 const pass_data sh_optimize_sett_clrt::default_pass_data =
138 RTL_PASS, // type
139 "", // name (overwritten by the constructor)
140 OPTGROUP_NONE, // optinfo_flags
141 true, // has_gate
142 true, // has_execute
143 TV_OPTIMIZE, // tv_id
144 0, // properties_required
145 0, // properties_provided
146 0, // properties_destroyed
147 0, // todo_flags_start
148 0 // todo_flags_finish
151 sh_optimize_sett_clrt::sh_optimize_sett_clrt (gcc::context* ctx,
152 const char* name)
153 : rtl_opt_pass (default_pass_data, ctx),
154 m_ccreg (NULL_RTX)
156 // Overwrite default name in pass_data base class.
157 this->name = name;
160 sh_optimize_sett_clrt::~sh_optimize_sett_clrt (void)
164 bool
165 sh_optimize_sett_clrt::gate (void)
167 return optimize > 0;
170 unsigned int
171 sh_optimize_sett_clrt::execute (void)
173 unsigned int ccr0 = INVALID_REGNUM;
174 unsigned int ccr1 = INVALID_REGNUM;
176 if (targetm.fixed_condition_code_regs (&ccr0, &ccr1)
177 && ccr0 != INVALID_REGNUM)
179 // Initially create a reg rtx with VOIDmode.
180 // When the constant setcc is discovered, the mode is changed
181 // to the mode that is actually used by the target.
182 m_ccreg = gen_rtx_REG (VOIDmode, ccr0);
185 if (m_ccreg == NULL_RTX)
186 log_return (0, "no ccreg.\n\n");
188 if (STORE_FLAG_VALUE != 1)
189 log_return (0, "unsupported STORE_FLAG_VALUE %d", STORE_FLAG_VALUE);
191 log_msg ("ccreg: ");
192 log_rtx (m_ccreg);
193 log_msg (" STORE_FLAG_VALUE = %d\n", STORE_FLAG_VALUE);
195 if (!df_regs_ever_live_p (ccr0))
196 log_return (0, "ccreg never live\n\n");
198 // Output vector for find_known_ccreg_values.
199 std::vector<ccreg_value> ccreg_values;
200 ccreg_values.reserve (32);
202 // Something for recording visited basic blocks to avoid infinite recursion.
203 std::vector<basic_block> visited_bbs;
204 visited_bbs.reserve (32);
206 // Look for insns that set the ccreg to a constant value and see if it can
207 // be optimized.
208 basic_block bb;
209 FOR_EACH_BB_REVERSE_FN (bb, cfun)
210 for (rtx next_i, i = NEXT_INSN (BB_HEAD (bb));
211 i != NULL_RTX && i != BB_END (bb); i = next_i)
213 next_i = NEXT_INSN (i);
215 if (!INSN_P (i) || !NONDEBUG_INSN_P (i))
216 continue;
218 rtx setcc_val = const_setcc_value (PATTERN (i));
219 if (setcc_val != NULL_RTX)
221 update_ccreg_mode (GET_MODE (XEXP (PATTERN (i), 0)));
223 log_msg ("\n\nfound const setcc insn in [bb %d]: \n", bb->index);
224 log_insn (i);
225 log_msg ("\n");
227 ccreg_values.clear ();
228 visited_bbs.clear ();
229 find_last_ccreg_values (PREV_INSN (i), bb, ccreg_values,
230 visited_bbs);
232 log_msg ("number of ccreg values collected: %u\n",
233 (unsigned int)ccreg_values.size ());
235 // If all the collected values are equal and are equal to the
236 // constant value of the setcc insn, the setcc insn can be
237 // removed.
238 if (all_ccreg_values_equal (ccreg_values)
239 && rtx_equal_p (ccreg_values.front ().value, setcc_val))
241 log_msg ("all values are ");
242 log_rtx (setcc_val);
243 log_msg ("\n");
245 delete_insn (i);
246 remove_ccreg_dead_unused_notes (ccreg_values);
251 log_return (0, "\n\n");
254 void
255 sh_optimize_sett_clrt::update_ccreg_mode (machine_mode m)
257 if (GET_MODE (m_ccreg) == m)
258 return;
260 PUT_MODE (m_ccreg, m);
261 log_msg ("updated ccreg mode: ");
262 log_rtx (m_ccreg);
263 log_msg ("\n\n");
267 sh_optimize_sett_clrt::const_setcc_value (rtx pat) const
269 if (GET_CODE (pat) == SET
270 && REG_P (XEXP (pat, 0)) && REGNO (XEXP (pat, 0)) == REGNO (m_ccreg)
271 && CONST_INT_P (XEXP (pat, 1))
272 && (INTVAL (XEXP (pat, 1)) == 0
273 || INTVAL (XEXP (pat, 1)) == STORE_FLAG_VALUE))
274 return XEXP (pat, 1);
275 else
276 return NULL_RTX;
279 bool
280 sh_optimize_sett_clrt
281 ::sh_cbranch_ccreg_value (rtx cbranch_insn, basic_block cbranch_insn_bb,
282 basic_block branch_target_bb) const
284 rtx pc_set_rtx = pc_set (cbranch_insn);
285 gcc_assert (pc_set_rtx != NULL_RTX);
286 gcc_assert (branch_target_bb != NULL);
288 rtx cond = XEXP (XEXP (pc_set_rtx, 1), 0);
289 bool branch_if;
291 if (GET_CODE (cond) == NE
292 && REG_P (XEXP (cond, 0)) && REGNO (XEXP (cond, 0)) == REGNO (m_ccreg)
293 && XEXP (cond, 1) == const0_rtx)
294 branch_if = true;
296 else if (GET_CODE (cond) == EQ
297 && REG_P (XEXP (cond, 0)) && REGNO (XEXP (cond, 0)) == REGNO (m_ccreg)
298 && XEXP (cond, 1) == const0_rtx)
299 branch_if = false;
301 else
302 gcc_unreachable ();
304 if (branch_target_bb == BRANCH_EDGE (cbranch_insn_bb)->dest)
305 return branch_if;
306 else if (branch_target_bb == FALLTHRU_EDGE (cbranch_insn_bb)->dest)
307 return !branch_if;
308 else
309 gcc_unreachable ();
312 void
313 sh_optimize_sett_clrt
314 ::find_last_ccreg_values (rtx start_insn, basic_block bb,
315 std::vector<ccreg_value>& values_out,
316 std::vector<basic_block>& prev_visited_bb) const
318 // FIXME: For larger CFGs this will unnecessarily re-visit basic blocks.
319 // Once a basic block has been visited, the result should be stored in
320 // some container so that it can be looked up quickly eliminating the
321 // re-visits.
322 log_msg ("looking for ccreg values in [bb %d] ", bb->index);
323 if (!prev_visited_bb.empty ())
324 log_msg ("(prev visited [bb %d])", prev_visited_bb.back ()->index);
325 log_msg ("\n");
327 for (rtx i = start_insn; i != NULL_RTX && i != PREV_INSN (BB_HEAD (bb));
328 i = PREV_INSN (i))
330 if (!INSN_P (i))
331 continue;
333 if (reg_set_p (m_ccreg, i))
335 const_rtx set_rtx = set_of (m_ccreg, i);
337 ccreg_value v;
338 v.insn = i;
339 v.bb = bb;
340 v.value = set_rtx != NULL_RTX && GET_CODE (set_rtx) == SET
341 ? XEXP (set_rtx, 1)
342 : NULL_RTX;
344 log_msg ("found setcc in [bb %d] in insn:\n", bb->index);
345 log_insn (i);
346 log_msg ("\nccreg value: ");
347 log_rtx (v.value);
348 log_msg ("\n");
350 values_out.push_back (v);
351 return;
354 if (any_condjump_p (i) && onlyjump_p (i) && !prev_visited_bb.empty ())
356 // For a conditional branch the ccreg value will be a known constant
357 // of either 0 or STORE_FLAG_VALUE after branching/falling through
358 // to one of the two successor BBs. Record the value for the BB
359 // where we came from.
360 log_msg ("found cbranch in [bb %d]:\n", bb->index);
361 log_insn (i);
363 ccreg_value v;
364 v.insn = i;
365 v.bb = bb;
366 v.value = GEN_INT (sh_cbranch_ccreg_value (i, bb,
367 prev_visited_bb.back ()));
369 log_msg (" branches to [bb %d] with ccreg value ",
370 prev_visited_bb.back ()->index);
371 log_rtx (v.value);
372 log_msg ("\n");
374 values_out.push_back (v);
375 return;
379 // If here, we've walked up all the insns of the current basic block
380 // and none of them seems to modify the ccreg.
381 // In this case, check the predecessor basic blocks.
382 unsigned int pred_bb_count = 0;
384 // If the current basic block is not in the stack of previously visited
385 // basic blocks yet, we can recursively check the predecessor basic blocks.
386 // Otherwise we have a loop in the CFG and recursing again will result in
387 // an infinite loop.
388 if (std::find (prev_visited_bb.rbegin (), prev_visited_bb.rend (), bb)
389 == prev_visited_bb.rend ())
391 prev_visited_bb.push_back (bb);
393 for (edge_iterator ei = ei_start (bb->preds); !ei_end_p (ei);
394 ei_next (&ei))
396 basic_block pred_bb = ei_edge (ei)->src;
397 pred_bb_count += 1;
398 find_last_ccreg_values (BB_END (pred_bb), pred_bb, values_out,
399 prev_visited_bb);
402 prev_visited_bb.pop_back ();
404 else
405 log_msg ("loop detected for [bb %d]\n", bb->index);
407 log_msg ("[bb %d] pred_bb_count = %u\n", bb->index, pred_bb_count);
409 if (pred_bb_count == 0)
411 // If we haven't checked a single predecessor basic block, the current
412 // basic block is probably a leaf block and we don't know the ccreg value.
413 log_msg ("unknown ccreg value for [bb %d]\n", bb->index);
415 ccreg_value v;
416 v.insn = BB_END (bb);
417 v.bb = bb;
418 v.value = NULL_RTX;
420 values_out.push_back (v);
424 bool
425 sh_optimize_sett_clrt
426 ::all_ccreg_values_equal (const std::vector<ccreg_value>& values)
428 if (values.empty ())
429 return false;
431 rtx last_value = values.front ().value;
433 // If the ccreg is modified in the insn but the exact value is not known
434 // the value rtx might be null.
435 if (last_value == NULL_RTX)
436 return false;
438 for (std::vector<ccreg_value>::const_iterator i = values.begin ();
439 i != values.end (); ++i)
440 if (i->value == NULL_RTX || !rtx_equal_p (last_value, i->value))
441 return false;
443 return true;
446 void
447 sh_optimize_sett_clrt
448 ::remove_ccreg_dead_unused_notes (std::vector<ccreg_value>& values) const
450 for (std::vector<ccreg_value>::iterator i = values.begin ();
451 i != values.end (); ++i)
453 if (i->insn == NULL_RTX)
454 continue;
456 rtx n = find_regno_note (i->insn, REG_DEAD, REGNO (m_ccreg));
457 if (n != NULL_RTX)
458 remove_note (i->insn, n);
460 n = find_regno_note (i->insn, REG_UNUSED, REGNO (m_ccreg));
461 if (n != NULL_RTX)
462 remove_note (i->insn, n);
466 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
467 // This allows instantiating the pass somewhere else without having to pull
468 // in a header file.
469 opt_pass*
470 make_pass_sh_optimize_sett_clrt (gcc::context* ctx, const char* name)
472 return new sh_optimize_sett_clrt (ctx, name);