1 /* An SH specific RTL pass that tries to optimize clrt and sett insns.
2 Copyright (C) 2013-2017 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"
28 #include "tree-pass.h"
34 This pass tries to eliminate unnecessary sett or clrt instructions in cases
35 where the ccreg value is already known to be the same as the constant set
36 would set it to. This is done as follows:
38 Check every BB's insn and see if it's a sett or clrt.
39 Once a sett or clrt insn is hit, walk insns and predecessor basic blocks
40 backwards from that insn and determine all possible ccreg values from all
42 Insns that set the ccreg value in some way (simple set, clobber etc) are
43 recorded. Conditional branches where one edge leads to the sett / clrt insn
44 are also recorded, since for each edge in the conditional branch the ccreg
45 value is known constant.
46 After collecting all possible ccreg values at the sett / clrt insn, check that
47 all the values are the same. If that value is the same as the sett / clrt
48 insn would set the ccreg to, the sett / clrt insn can be eliminated.
52 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
56 do { if (dump_file != NULL) fprintf (dump_file, __VA_ARGS__); } while (0)
59 do { if (dump_file != NULL) print_rtl_single (dump_file, \
60 (const_rtx)i); } while (0)
63 do { if (dump_file != NULL) print_rtl (dump_file, (const_rtx)r); } while (0)
65 #define log_return(retval, ...)\
66 do { if (dump_file != NULL) fprintf (dump_file, __VA_ARGS__); \
67 return retval; } while (0)
69 #define log_return_void(...)\
70 do { if (dump_file != NULL) fprintf (dump_file, __VA_ARGS__); \
73 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
76 class sh_optimize_sett_clrt
: public rtl_opt_pass
79 sh_optimize_sett_clrt (gcc::context
* ctx
, const char* name
);
80 virtual ~sh_optimize_sett_clrt (void);
81 virtual bool gate (function
*);
82 virtual unsigned int execute (function
* fun
);
85 static const pass_data default_pass_data
;
89 // The insn at which the ccreg value was determined.
90 // Might be NULL if e.g. an unknown value is recorded for an
94 // The basic block where the insn was discovered.
97 // The value of ccreg. If NULL_RTX, the exact value is not known, but
98 // the ccreg is changed in some way (e.g. clobbered).
102 // Update the mode of the captured m_ccreg with the specified mode.
103 void update_ccreg_mode (machine_mode m
);
105 // Given an insn pattern, check if it sets the ccreg to a constant value
106 // of either zero or STORE_FLAG_VALUE. If so, return the value rtx,
107 // NULL_RTX otherwise.
108 rtx
const_setcc_value (rtx pat
) const;
110 // Given a start insn and its basic block, recursively determine all
111 // possible ccreg values in all basic block paths that can lead to the
113 bool find_last_ccreg_values (rtx_insn
*start_insn
, basic_block bb
,
114 std::vector
<ccreg_value
>& values_out
,
115 std::vector
<basic_block
>& prev_visited_bb
) const;
117 // Given a cbranch insn, its basic block and another basic block, determine
118 // the value to which the ccreg will be set after jumping/falling through to
119 // the specified target basic block.
120 bool sh_cbranch_ccreg_value (rtx_insn
*cbranch_insn
,
121 basic_block cbranch_insn_bb
,
122 basic_block branch_target_bb
) const;
124 // Check whether all of the ccreg values are the same.
125 static bool all_ccreg_values_equal (const std::vector
<ccreg_value
>& values
);
127 // Remove REG_DEAD and REG_UNUSED notes from insns of the specified
128 // ccreg_value entries.
129 void remove_ccreg_dead_unused_notes (std::vector
<ccreg_value
>& values
) const;
131 // rtx of the ccreg that is obtained from the target.
135 const pass_data
sh_optimize_sett_clrt::default_pass_data
=
138 "", // name (overwritten by the constructor)
139 OPTGROUP_NONE
, // optinfo_flags
140 TV_OPTIMIZE
, // tv_id
141 0, // properties_required
142 0, // properties_provided
143 0, // properties_destroyed
144 0, // todo_flags_start
145 0 // todo_flags_finish
148 sh_optimize_sett_clrt::sh_optimize_sett_clrt (gcc::context
* ctx
,
150 : rtl_opt_pass (default_pass_data
, ctx
),
153 // Overwrite default name in pass_data base class.
157 sh_optimize_sett_clrt::~sh_optimize_sett_clrt (void)
162 sh_optimize_sett_clrt::gate (function
*)
168 sh_optimize_sett_clrt::execute (function
* fun
)
170 unsigned int ccr0
= INVALID_REGNUM
;
171 unsigned int ccr1
= INVALID_REGNUM
;
173 if (targetm
.fixed_condition_code_regs (&ccr0
, &ccr1
)
174 && ccr0
!= INVALID_REGNUM
)
176 // Initially create a reg rtx with VOIDmode.
177 // When the constant setcc is discovered, the mode is changed
178 // to the mode that is actually used by the target.
179 m_ccreg
= gen_rtx_REG (VOIDmode
, ccr0
);
182 if (m_ccreg
== NULL_RTX
)
183 log_return (0, "no ccreg.\n\n");
185 if (STORE_FLAG_VALUE
!= 1)
186 log_return (0, "unsupported STORE_FLAG_VALUE %d", STORE_FLAG_VALUE
);
190 log_msg (" STORE_FLAG_VALUE = %d\n", STORE_FLAG_VALUE
);
192 if (!df_regs_ever_live_p (ccr0
))
193 log_return (0, "ccreg never live\n\n");
195 // Output vector for find_known_ccreg_values.
196 std::vector
<ccreg_value
> ccreg_values
;
197 ccreg_values
.reserve (32);
199 // Something for recording visited basic blocks to avoid infinite recursion.
200 std::vector
<basic_block
> visited_bbs
;
201 visited_bbs
.reserve (32);
203 // Look for insns that set the ccreg to a constant value and see if it can
206 FOR_EACH_BB_REVERSE_FN (bb
, fun
)
207 for (rtx_insn
*next_i
, *i
= NEXT_INSN (BB_HEAD (bb
));
208 i
!= NULL_RTX
&& i
!= BB_END (bb
); i
= next_i
)
210 next_i
= NEXT_INSN (i
);
212 if (!INSN_P (i
) || !NONDEBUG_INSN_P (i
))
215 rtx setcc_val
= const_setcc_value (PATTERN (i
));
216 if (setcc_val
!= NULL_RTX
)
218 update_ccreg_mode (GET_MODE (XEXP (PATTERN (i
), 0)));
220 log_msg ("\n\nfound const setcc insn in [bb %d]: \n", bb
->index
);
224 ccreg_values
.clear ();
225 visited_bbs
.clear ();
226 bool ok
= find_last_ccreg_values (PREV_INSN (i
), bb
, ccreg_values
,
229 log_msg ("number of ccreg values collected: %u\n",
230 (unsigned int)ccreg_values
.size ());
232 // If all the collected values are equal and are equal to the
233 // constant value of the setcc insn, the setcc insn can be
235 if (ok
&& all_ccreg_values_equal (ccreg_values
)
236 && rtx_equal_p (ccreg_values
.front ().value
, setcc_val
))
238 log_msg ("all values are ");
243 remove_ccreg_dead_unused_notes (ccreg_values
);
248 log_return (0, "\n\n");
252 sh_optimize_sett_clrt::update_ccreg_mode (machine_mode m
)
254 if (GET_MODE (m_ccreg
) == m
)
257 PUT_MODE (m_ccreg
, m
);
258 log_msg ("updated ccreg mode: ");
264 sh_optimize_sett_clrt::const_setcc_value (rtx pat
) const
266 if (GET_CODE (pat
) == SET
267 && REG_P (XEXP (pat
, 0)) && REGNO (XEXP (pat
, 0)) == REGNO (m_ccreg
)
268 && CONST_INT_P (XEXP (pat
, 1))
269 && (INTVAL (XEXP (pat
, 1)) == 0
270 || INTVAL (XEXP (pat
, 1)) == STORE_FLAG_VALUE
))
271 return XEXP (pat
, 1);
277 sh_optimize_sett_clrt
278 ::sh_cbranch_ccreg_value (rtx_insn
*cbranch_insn
, basic_block cbranch_insn_bb
,
279 basic_block branch_target_bb
) const
281 rtx pc_set_rtx
= pc_set (cbranch_insn
);
282 gcc_assert (pc_set_rtx
!= NULL_RTX
);
283 gcc_assert (branch_target_bb
!= NULL
);
285 rtx cond
= XEXP (XEXP (pc_set_rtx
, 1), 0);
288 if (GET_CODE (cond
) == NE
289 && REG_P (XEXP (cond
, 0)) && REGNO (XEXP (cond
, 0)) == REGNO (m_ccreg
)
290 && XEXP (cond
, 1) == const0_rtx
)
293 else if (GET_CODE (cond
) == EQ
294 && REG_P (XEXP (cond
, 0)) && REGNO (XEXP (cond
, 0)) == REGNO (m_ccreg
)
295 && XEXP (cond
, 1) == const0_rtx
)
301 if (branch_target_bb
== BRANCH_EDGE (cbranch_insn_bb
)->dest
)
303 else if (branch_target_bb
== FALLTHRU_EDGE (cbranch_insn_bb
)->dest
)
310 sh_optimize_sett_clrt
311 ::find_last_ccreg_values (rtx_insn
*start_insn
, basic_block bb
,
312 std::vector
<ccreg_value
>& values_out
,
313 std::vector
<basic_block
>& prev_visited_bb
) const
315 // FIXME: For larger CFGs this will unnecessarily re-visit basic blocks.
316 // Once a basic block has been visited, the result should be stored in
317 // some container so that it can be looked up quickly eliminating the
319 log_msg ("looking for ccreg values in [bb %d] ", bb
->index
);
320 if (!prev_visited_bb
.empty ())
321 log_msg ("(prev visited [bb %d])", prev_visited_bb
.back ()->index
);
324 for (rtx_insn
*i
= start_insn
; i
!= NULL
&& i
!= PREV_INSN (BB_HEAD (bb
));
330 if (reg_set_p (m_ccreg
, i
))
332 const_rtx set_rtx
= set_of (m_ccreg
, i
);
337 v
.value
= set_rtx
!= NULL_RTX
&& GET_CODE (set_rtx
) == SET
341 log_msg ("found setcc in [bb %d] in insn:\n", bb
->index
);
343 log_msg ("\nccreg value: ");
347 values_out
.push_back (v
);
351 if (any_condjump_p (i
) && onlyjump_p (i
) && !prev_visited_bb
.empty ())
353 // For a conditional branch the ccreg value will be a known constant
354 // of either 0 or STORE_FLAG_VALUE after branching/falling through
355 // to one of the two successor BBs. Record the value for the BB
356 // where we came from.
357 log_msg ("found cbranch in [bb %d]:\n", bb
->index
);
363 v
.value
= GEN_INT (sh_cbranch_ccreg_value (i
, bb
,
364 prev_visited_bb
.back ()));
366 log_msg (" branches to [bb %d] with ccreg value ",
367 prev_visited_bb
.back ()->index
);
371 values_out
.push_back (v
);
376 // If here, we've walked up all the insns of the current basic block
377 // and none of them seems to modify the ccreg.
378 // In this case, check the predecessor basic blocks.
379 unsigned int pred_bb_count
= 0;
381 // If the current basic block is not in the stack of previously visited
382 // basic blocks yet, we can recursively check the predecessor basic blocks.
383 // Otherwise we have a loop in the CFG and recursing again will result in
385 if (std::find (prev_visited_bb
.rbegin (), prev_visited_bb
.rend (), bb
)
386 == prev_visited_bb
.rend ())
388 prev_visited_bb
.push_back (bb
);
390 for (edge_iterator ei
= ei_start (bb
->preds
); !ei_end_p (ei
);
393 if (ei_edge (ei
)->flags
& EDGE_COMPLEX
)
394 log_return (false, "aborting due to complex edge\n");
396 basic_block pred_bb
= ei_edge (ei
)->src
;
398 if (!find_last_ccreg_values (BB_END (pred_bb
), pred_bb
, values_out
,
403 prev_visited_bb
.pop_back ();
406 log_msg ("loop detected for [bb %d]\n", bb
->index
);
408 log_msg ("[bb %d] pred_bb_count = %u\n", bb
->index
, pred_bb_count
);
410 if (pred_bb_count
== 0)
412 // If we haven't checked a single predecessor basic block, the current
413 // basic block is probably a leaf block and we don't know the ccreg value.
414 log_msg ("unknown ccreg value for [bb %d]\n", bb
->index
);
417 v
.insn
= BB_END (bb
);
421 values_out
.push_back (v
);
428 sh_optimize_sett_clrt
429 ::all_ccreg_values_equal (const std::vector
<ccreg_value
>& values
)
434 rtx last_value
= values
.front ().value
;
436 // If the ccreg is modified in the insn but the exact value is not known
437 // the value rtx might be null.
438 if (last_value
== NULL_RTX
)
441 for (std::vector
<ccreg_value
>::const_iterator i
= values
.begin ();
442 i
!= values
.end (); ++i
)
443 if (i
->value
== NULL_RTX
|| !rtx_equal_p (last_value
, i
->value
))
450 sh_optimize_sett_clrt
451 ::remove_ccreg_dead_unused_notes (std::vector
<ccreg_value
>& values
) const
453 for (std::vector
<ccreg_value
>::iterator i
= values
.begin ();
454 i
!= values
.end (); ++i
)
456 if (i
->insn
== NULL_RTX
)
459 rtx n
= find_regno_note (i
->insn
, REG_DEAD
, REGNO (m_ccreg
));
461 remove_note (i
->insn
, n
);
463 n
= find_regno_note (i
->insn
, REG_UNUSED
, REGNO (m_ccreg
));
465 remove_note (i
->insn
, n
);
469 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
470 // This allows instantiating the pass somewhere else without having to pull
473 make_pass_sh_optimize_sett_clrt (gcc::context
* ctx
, const char* name
)
475 return new sh_optimize_sett_clrt (ctx
, name
);