1 /* An SH specific RTL pass that tries to optimize clrt and sett insns.
2    Copyright (C) 2013-2018 Free Software Foundation, Inc.
3 
4 This file is part of GCC.
5 
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.
10 
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.
15 
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/>.  */
19 
20 #define IN_TARGET_CODE 1
21 
22 #include "config.h"
23 #define INCLUDE_ALGORITHM
24 #define INCLUDE_VECTOR
25 #include "system.h"
26 #include "coretypes.h"
27 #include "backend.h"
28 #include "target.h"
29 #include "rtl.h"
30 #include "df.h"
31 #include "cfgrtl.h"
32 #include "tree-pass.h"
33 
34 /*
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:
38 
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.
50 */
51 
52 
53 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
54 // Helper functions
55 
56 #define log_msg(...)\
57   do { if (dump_file != NULL) fprintf (dump_file, __VA_ARGS__); } while (0)
58 
59 #define log_insn(i)\
60   do { if (dump_file != NULL) print_rtl_single (dump_file, \
61 						(const_rtx)i); } while (0)
62 
63 #define log_rtx(r)\
64   do { if (dump_file != NULL) print_rtl (dump_file, (const_rtx)r); } while (0)
65 
66 #define log_return(retval, ...)\
67   do { if (dump_file != NULL) fprintf (dump_file, __VA_ARGS__); \
68        return retval; } while (0)
69 
70 #define log_return_void(...)\
71   do { if (dump_file != NULL) fprintf (dump_file, __VA_ARGS__); \
72        return; } while (0)
73 
74 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
75 // RTL pass class
76 
77 class sh_optimize_sett_clrt : public rtl_opt_pass
78 {
79 public:
80   sh_optimize_sett_clrt (gcc::context* ctx, const char* name);
81   virtual ~sh_optimize_sett_clrt (void);
82   virtual bool gate (function*);
83   virtual unsigned int execute (function* fun);
84 
85 private:
86   static const pass_data default_pass_data;
87 
88   struct ccreg_value
89   {
90     // The insn at which the ccreg value was determined.
91     // Might be NULL if e.g. an unknown value is recorded for an
92     // empty basic block.
93     rtx_insn *insn;
94 
95     // The basic block where the insn was discovered.
96     basic_block bb;
97 
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;
101   };
102 
103   // Update the mode of the captured m_ccreg with the specified mode.
104   void update_ccreg_mode (machine_mode m);
105 
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;
110 
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   bool find_last_ccreg_values (rtx_insn *start_insn, basic_block bb,
115 			       std::vector<ccreg_value>& values_out,
116 			       std::vector<basic_block>& prev_visited_bb) const;
117 
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_insn *cbranch_insn,
122 			       basic_block cbranch_insn_bb,
123 			       basic_block branch_target_bb) const;
124 
125   // Check whether all of the ccreg values are the same.
126   static bool all_ccreg_values_equal (const std::vector<ccreg_value>& values);
127 
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;
131 
132   // rtx of the ccreg that is obtained from the target.
133   rtx m_ccreg;
134 };
135 
136 const pass_data sh_optimize_sett_clrt::default_pass_data =
137 {
138   RTL_PASS,		// type
139   "",			// name (overwritten by the constructor)
140   OPTGROUP_NONE,	// optinfo_flags
141   TV_OPTIMIZE,		// tv_id
142   0,			// properties_required
143   0,			// properties_provided
144   0,			// properties_destroyed
145   0,			// todo_flags_start
146   0			// todo_flags_finish
147 };
148 
sh_optimize_sett_clrt(gcc::context * ctx,const char * name)149 sh_optimize_sett_clrt::sh_optimize_sett_clrt (gcc::context* ctx,
150 					      const char* name)
151 : rtl_opt_pass (default_pass_data, ctx),
152   m_ccreg (NULL_RTX)
153 {
154   // Overwrite default name in pass_data base class.
155   this->name = name;
156 }
157 
~sh_optimize_sett_clrt(void)158 sh_optimize_sett_clrt::~sh_optimize_sett_clrt (void)
159 {
160 }
161 
162 bool
gate(function *)163 sh_optimize_sett_clrt::gate (function*)
164 {
165   return optimize > 0;
166 }
167 
168 unsigned int
execute(function * fun)169 sh_optimize_sett_clrt::execute (function* fun)
170 {
171   unsigned int ccr0 = INVALID_REGNUM;
172   unsigned int ccr1 = INVALID_REGNUM;
173 
174   if (targetm.fixed_condition_code_regs (&ccr0, &ccr1)
175       && ccr0 != INVALID_REGNUM)
176     {
177       // Initially create a reg rtx with VOIDmode.
178       // When the constant setcc is discovered, the mode is changed
179       // to the mode that is actually used by the target.
180       m_ccreg = gen_rtx_REG (VOIDmode, ccr0);
181     }
182 
183   if (m_ccreg == NULL_RTX)
184     log_return (0, "no ccreg.\n\n");
185 
186   if (STORE_FLAG_VALUE != 1)
187     log_return (0, "unsupported STORE_FLAG_VALUE %d", STORE_FLAG_VALUE);
188 
189   log_msg ("ccreg: ");
190   log_rtx (m_ccreg);
191   log_msg ("  STORE_FLAG_VALUE = %d\n", STORE_FLAG_VALUE);
192 
193   if (!df_regs_ever_live_p (ccr0))
194     log_return (0, "ccreg never live\n\n");
195 
196   // Output vector for find_known_ccreg_values.
197   std::vector<ccreg_value> ccreg_values;
198   ccreg_values.reserve (32);
199 
200   // Something for recording visited basic blocks to avoid infinite recursion.
201   std::vector<basic_block> visited_bbs;
202   visited_bbs.reserve (32);
203 
204   // Look for insns that set the ccreg to a constant value and see if it can
205   // be optimized.
206   basic_block bb;
207   FOR_EACH_BB_REVERSE_FN (bb, fun)
208     for (rtx_insn *next_i, *i = NEXT_INSN (BB_HEAD (bb));
209 	 i != NULL_RTX && i != BB_END (bb); i = next_i)
210       {
211 	next_i = NEXT_INSN (i);
212 
213 	if (!INSN_P (i) || !NONDEBUG_INSN_P (i))
214 	  continue;
215 
216 	rtx setcc_val = const_setcc_value (PATTERN (i));
217 	if (setcc_val != NULL_RTX)
218 	  {
219 	    update_ccreg_mode (GET_MODE (XEXP (PATTERN (i), 0)));
220 
221 	    log_msg ("\n\nfound const setcc insn in [bb %d]: \n", bb->index);
222 	    log_insn (i);
223 	    log_msg ("\n");
224 
225 	    ccreg_values.clear ();
226 	    visited_bbs.clear ();
227 	    bool ok = find_last_ccreg_values (PREV_INSN (i), bb, ccreg_values,
228 					      visited_bbs);
229 
230 	    log_msg ("number of ccreg values collected: %u\n",
231 		     (unsigned int)ccreg_values.size ());
232 
233 	    // If all the collected values are equal and are equal to the
234 	    // constant value of the setcc insn, the setcc insn can be
235 	    // removed.
236 	    if (ok && all_ccreg_values_equal (ccreg_values)
237 		&& rtx_equal_p (ccreg_values.front ().value, setcc_val))
238 	      {
239 		log_msg ("all values are ");
240 		log_rtx (setcc_val);
241 		log_msg ("\n");
242 
243 		delete_insn (i);
244 		remove_ccreg_dead_unused_notes (ccreg_values);
245 	      }
246 	  }
247       }
248 
249   log_return (0, "\n\n");
250 }
251 
252 void
update_ccreg_mode(machine_mode m)253 sh_optimize_sett_clrt::update_ccreg_mode (machine_mode m)
254 {
255   if (GET_MODE (m_ccreg) == m)
256     return;
257 
258   PUT_MODE (m_ccreg, m);
259   log_msg ("updated ccreg mode: ");
260   log_rtx (m_ccreg);
261   log_msg ("\n\n");
262 }
263 
264 rtx
const_setcc_value(rtx pat) const265 sh_optimize_sett_clrt::const_setcc_value (rtx pat) const
266 {
267   if (GET_CODE (pat) == SET
268       && REG_P (XEXP (pat, 0)) && REGNO (XEXP (pat, 0)) == REGNO (m_ccreg)
269       && CONST_INT_P (XEXP (pat, 1))
270       && (INTVAL (XEXP (pat, 1)) == 0
271 	  || INTVAL (XEXP (pat, 1)) == STORE_FLAG_VALUE))
272     return XEXP (pat, 1);
273   else
274     return NULL_RTX;
275 }
276 
277 bool
278 sh_optimize_sett_clrt
sh_cbranch_ccreg_value(rtx_insn * cbranch_insn,basic_block cbranch_insn_bb,basic_block branch_target_bb) const279 ::sh_cbranch_ccreg_value (rtx_insn *cbranch_insn, basic_block cbranch_insn_bb,
280 			  basic_block branch_target_bb) const
281 {
282   rtx pc_set_rtx = pc_set (cbranch_insn);
283   gcc_assert (pc_set_rtx != NULL_RTX);
284   gcc_assert (branch_target_bb != NULL);
285 
286   rtx cond = XEXP (XEXP (pc_set_rtx, 1), 0);
287   bool branch_if;
288 
289   if (GET_CODE (cond) == NE
290       && REG_P (XEXP (cond, 0)) && REGNO (XEXP (cond, 0)) == REGNO (m_ccreg)
291       && XEXP (cond, 1) == const0_rtx)
292     branch_if = true;
293 
294   else if (GET_CODE (cond) == EQ
295       && REG_P (XEXP (cond, 0)) && REGNO (XEXP (cond, 0)) == REGNO (m_ccreg)
296       && XEXP (cond, 1) == const0_rtx)
297     branch_if = false;
298 
299   else
300     gcc_unreachable ();
301 
302   if (branch_target_bb == BRANCH_EDGE (cbranch_insn_bb)->dest)
303     return branch_if;
304   else if (branch_target_bb == FALLTHRU_EDGE (cbranch_insn_bb)->dest)
305     return !branch_if;
306   else
307     gcc_unreachable ();
308 }
309 
310 bool
311 sh_optimize_sett_clrt
find_last_ccreg_values(rtx_insn * start_insn,basic_block bb,std::vector<ccreg_value> & values_out,std::vector<basic_block> & prev_visited_bb) const312 ::find_last_ccreg_values (rtx_insn *start_insn, basic_block bb,
313 			  std::vector<ccreg_value>& values_out,
314 			  std::vector<basic_block>& prev_visited_bb) const
315 {
316   // FIXME: For larger CFGs this will unnecessarily re-visit basic blocks.
317   // Once a basic block has been visited, the result should be stored in
318   // some container so that it can be looked up quickly eliminating the
319   // re-visits.
320   log_msg ("looking for ccreg values in [bb %d] ", bb->index);
321   if (!prev_visited_bb.empty ())
322     log_msg ("(prev visited [bb %d])", prev_visited_bb.back ()->index);
323   log_msg ("\n");
324 
325   for (rtx_insn *i = start_insn; i != NULL && i != PREV_INSN (BB_HEAD (bb));
326        i = PREV_INSN (i))
327     {
328       if (!INSN_P (i))
329 	continue;
330 
331       if (reg_set_p (m_ccreg, i))
332 	{
333 	  const_rtx set_rtx = set_of (m_ccreg, i);
334 
335 	  ccreg_value v;
336 	  v.insn = i;
337 	  v.bb = bb;
338 	  v.value = set_rtx != NULL_RTX && GET_CODE (set_rtx) == SET
339 		    ? XEXP (set_rtx, 1)
340 		    : NULL_RTX;
341 
342 	  log_msg ("found setcc in [bb %d] in insn:\n", bb->index);
343 	  log_insn (i);
344 	  log_msg ("\nccreg value: ");
345 	  log_rtx (v.value);
346 	  log_msg ("\n");
347 
348 	  values_out.push_back (v);
349 	  return true;
350 	}
351 
352       if (any_condjump_p (i) && onlyjump_p (i) && !prev_visited_bb.empty ())
353 	{
354 	  // For a conditional branch the ccreg value will be a known constant
355 	  // of either 0 or STORE_FLAG_VALUE after branching/falling through
356 	  // to one of the two successor BBs.  Record the value for the BB
357 	  // where we came from.
358 	  log_msg ("found cbranch in [bb %d]:\n", bb->index);
359 	  log_insn (i);
360 
361 	  ccreg_value v;
362 	  v.insn = i;
363 	  v.bb = bb;
364 	  v.value = GEN_INT (sh_cbranch_ccreg_value (i, bb,
365 						     prev_visited_bb.back ()));
366 
367 	  log_msg ("    branches to [bb %d] with ccreg value ",
368 		   prev_visited_bb.back ()->index);
369 	  log_rtx (v.value);
370 	  log_msg ("\n");
371 
372 	  values_out.push_back (v);
373 	  return true;
374 	}
375     }
376 
377   // If here, we've walked up all the insns of the current basic block
378   // and none of them seems to modify the ccreg.
379   // In this case, check the predecessor basic blocks.
380   unsigned int pred_bb_count = 0;
381 
382   // If the current basic block is not in the stack of previously visited
383   // basic blocks yet, we can recursively check the predecessor basic blocks.
384   // Otherwise we have a loop in the CFG and recursing again will result in
385   // an infinite loop.
386   if (std::find (prev_visited_bb.rbegin (), prev_visited_bb.rend (), bb)
387       == prev_visited_bb.rend ())
388     {
389       prev_visited_bb.push_back (bb);
390 
391       for (edge_iterator ei = ei_start (bb->preds); !ei_end_p (ei);
392 	   ei_next (&ei))
393 	{
394 	  if (ei_edge (ei)->flags & EDGE_COMPLEX)
395 	    log_return (false, "aborting due to complex edge\n");
396 
397 	  basic_block pred_bb = ei_edge (ei)->src;
398 	  pred_bb_count += 1;
399 	  if (!find_last_ccreg_values (BB_END (pred_bb), pred_bb, values_out,
400 				       prev_visited_bb))
401 	    return false;
402 	}
403 
404       prev_visited_bb.pop_back ();
405     }
406   else
407     log_msg ("loop detected for [bb %d]\n", bb->index);
408 
409   log_msg ("[bb %d] pred_bb_count = %u\n", bb->index, pred_bb_count);
410 
411   if (pred_bb_count == 0)
412   {
413     // If we haven't checked a single predecessor basic block, the current
414     // basic block is probably a leaf block and we don't know the ccreg value.
415     log_msg ("unknown ccreg value for [bb %d]\n", bb->index);
416 
417     ccreg_value v;
418     v.insn = BB_END (bb);
419     v.bb = bb;
420     v.value = NULL_RTX;
421 
422     values_out.push_back (v);
423   }
424 
425   return true;
426 }
427 
428 bool
429 sh_optimize_sett_clrt
all_ccreg_values_equal(const std::vector<ccreg_value> & values)430 ::all_ccreg_values_equal (const std::vector<ccreg_value>& values)
431 {
432   if (values.empty ())
433     return false;
434 
435   rtx last_value = values.front ().value;
436 
437   // If the ccreg is modified in the insn but the exact value is not known
438   // the value rtx might be null.
439   if (last_value == NULL_RTX)
440     return false;
441 
442   for (std::vector<ccreg_value>::const_iterator i = values.begin ();
443        i != values.end (); ++i)
444     if (i->value == NULL_RTX || !rtx_equal_p (last_value, i->value))
445       return false;
446 
447   return true;
448 }
449 
450 void
451 sh_optimize_sett_clrt
remove_ccreg_dead_unused_notes(std::vector<ccreg_value> & values) const452 ::remove_ccreg_dead_unused_notes (std::vector<ccreg_value>& values) const
453 {
454   for (std::vector<ccreg_value>::iterator i = values.begin ();
455        i != values.end (); ++i)
456     {
457       if (i->insn == NULL_RTX)
458 	continue;
459 
460       rtx n = find_regno_note (i->insn, REG_DEAD, REGNO (m_ccreg));
461       if (n != NULL_RTX)
462 	remove_note (i->insn, n);
463 
464       n = find_regno_note (i->insn, REG_UNUSED, REGNO (m_ccreg));
465       if (n != NULL_RTX)
466 	remove_note (i->insn, n);
467     }
468 }
469 
470 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
471 // This allows instantiating the pass somewhere else without having to pull
472 // in a header file.
473 opt_pass*
make_pass_sh_optimize_sett_clrt(gcc::context * ctx,const char * name)474 make_pass_sh_optimize_sett_clrt (gcc::context* ctx, const char* name)
475 {
476   return new sh_optimize_sett_clrt (ctx, name);
477 }
478