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)
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/>. */
22 #include "coretypes.h"
24 #include "basic-block.h"
27 #include "insn-config.h"
28 #include "tree-pass.h"
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
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 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
57 do { if (dump_file != NULL) fprintf (dump_file, __VA_ARGS__); } while (0)
60 do { if (dump_file != NULL) print_rtl_single (dump_file, \
61 (const_rtx)i); } while (0)
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__); \
74 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
77 class sh_optimize_sett_clrt
: public rtl_opt_pass
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);
86 static const pass_data default_pass_data
;
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
95 // The basic block where the insn was discovered.
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).
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
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.
136 const pass_data
sh_optimize_sett_clrt::default_pass_data
=
139 "", // name (overwritten by the constructor)
140 OPTGROUP_NONE
, // optinfo_flags
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
,
153 : rtl_opt_pass (default_pass_data
, ctx
),
156 // Overwrite default name in pass_data base class.
160 sh_optimize_sett_clrt::~sh_optimize_sett_clrt (void)
165 sh_optimize_sett_clrt::gate (void)
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
);
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
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
))
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
);
227 ccreg_values
.clear ();
228 visited_bbs
.clear ();
229 find_last_ccreg_values (PREV_INSN (i
), bb
, ccreg_values
,
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
238 if (all_ccreg_values_equal (ccreg_values
)
239 && rtx_equal_p (ccreg_values
.front ().value
, setcc_val
))
241 log_msg ("all values are ");
246 remove_ccreg_dead_unused_notes (ccreg_values
);
251 log_return (0, "\n\n");
255 sh_optimize_sett_clrt::update_ccreg_mode (machine_mode m
)
257 if (GET_MODE (m_ccreg
) == m
)
260 PUT_MODE (m_ccreg
, m
);
261 log_msg ("updated ccreg mode: ");
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);
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);
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
)
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
)
304 if (branch_target_bb
== BRANCH_EDGE (cbranch_insn_bb
)->dest
)
306 else if (branch_target_bb
== FALLTHRU_EDGE (cbranch_insn_bb
)->dest
)
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
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
);
327 for (rtx i
= start_insn
; i
!= NULL_RTX
&& i
!= PREV_INSN (BB_HEAD (bb
));
333 if (reg_set_p (m_ccreg
, i
))
335 const_rtx set_rtx
= set_of (m_ccreg
, i
);
340 v
.value
= set_rtx
!= NULL_RTX
&& GET_CODE (set_rtx
) == SET
344 log_msg ("found setcc in [bb %d] in insn:\n", bb
->index
);
346 log_msg ("\nccreg value: ");
350 values_out
.push_back (v
);
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
);
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
);
374 values_out
.push_back (v
);
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
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
);
396 basic_block pred_bb
= ei_edge (ei
)->src
;
398 find_last_ccreg_values (BB_END (pred_bb
), pred_bb
, values_out
,
402 prev_visited_bb
.pop_back ();
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
);
416 v
.insn
= BB_END (bb
);
420 values_out
.push_back (v
);
425 sh_optimize_sett_clrt
426 ::all_ccreg_values_equal (const std::vector
<ccreg_value
>& values
)
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
)
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
))
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
)
456 rtx n
= find_regno_note (i
->insn
, REG_DEAD
, REGNO (m_ccreg
));
458 remove_note (i
->insn
, n
);
460 n
= find_regno_note (i
->insn
, REG_UNUSED
, REGNO (m_ccreg
));
462 remove_note (i
->insn
, n
);
466 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
467 // This allows instantiating the pass somewhere else without having to pull
470 make_pass_sh_optimize_sett_clrt (gcc::context
* ctx
, const char* name
)
472 return new sh_optimize_sett_clrt (ctx
, name
);