]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/config/sh/sh_optimize_sett_clrt.cc
Update copyright years in gcc/
[thirdparty/gcc.git] / gcc / config / sh / sh_optimize_sett_clrt.cc
CommitLineData
ac973375 1/* An SH specific RTL pass that tries to optimize clrt and sett insns.
23a5b65a 2 Copyright (C) 2013-2014 Free Software Foundation, Inc.
ac973375
OE
3
4This file is part of GCC.
5
6GCC is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 3, or (at your option)
9any later version.
10
11GCC is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GCC; see the file COPYING3. If not see
18<http://www.gnu.org/licenses/>. */
19
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"
30
31#include <vector>
aaa57c69 32#include <algorithm>
ac973375
OE
33
34/*
35This pass tries to eliminate unnecessary sett or clrt instructions in cases
36where the ccreg value is already known to be the same as the constant set
37would set it to. This is done as follows:
38
39Check every BB's insn and see if it's a sett or clrt.
40Once a sett or clrt insn is hit, walk insns and predecessor basic blocks
41backwards from that insn and determine all possible ccreg values from all
42basic block paths.
43Insns that set the ccreg value in some way (simple set, clobber etc) are
44recorded. Conditional branches where one edge leads to the sett / clrt insn
45are also recorded, since for each edge in the conditional branch the ccreg
46value is known constant.
47After collecting all possible ccreg values at the sett / clrt insn, check that
48all the values are the same. If that value is the same as the sett / clrt
49insn 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
77class sh_optimize_sett_clrt : public rtl_opt_pass
78{
79public:
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);
84
85private:
86 static const pass_data default_pass_data;
87
88 struct ccreg_value
89 {
90 // The insn at which the ccreg value was determined.
aaa57c69
OE
91 // Might be NULL_RTX if e.g. an unknown value is recorded for an
92 // empty basic block.
ac973375
OE
93 rtx insn;
94
95 // The basic block where the insn was discovered.
ac973375
OE
96 basic_block bb;
97
aaa57c69
OE
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).
ac973375
OE
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 void find_last_ccreg_values (rtx start_insn, basic_block bb,
115 std::vector<ccreg_value>& values_out,
aaa57c69 116 std::vector<basic_block>& prev_visited_bb) const;
ac973375
OE
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 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
136const 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 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
149};
150
151sh_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)
155{
156 // Overwrite default name in pass_data base class.
157 this->name = name;
158}
159
160sh_optimize_sett_clrt::~sh_optimize_sett_clrt (void)
161{
162}
163
164bool
165sh_optimize_sett_clrt::gate (void)
166{
167 return optimize > 0;
168}
169
170unsigned int
171sh_optimize_sett_clrt::execute (void)
172{
173 unsigned int ccr0 = INVALID_REGNUM;
174 unsigned int ccr1 = INVALID_REGNUM;
175
176 if (targetm.fixed_condition_code_regs (&ccr0, &ccr1)
177 && ccr0 != INVALID_REGNUM)
178 {
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);
183 }
184
185 if (m_ccreg == NULL_RTX)
186 log_return (0, "no ccreg.\n\n");
187
188 if (STORE_FLAG_VALUE != 1)
189 log_return (0, "unsupported STORE_FLAG_VALUE %d", STORE_FLAG_VALUE);
190
191 log_msg ("ccreg: ");
192 log_rtx (m_ccreg);
193 log_msg (" STORE_FLAG_VALUE = %d\n", STORE_FLAG_VALUE);
194
195 if (!df_regs_ever_live_p (ccr0))
196 log_return (0, "ccreg never live\n\n");
197
198 // Output vector for find_known_ccreg_values.
199 std::vector<ccreg_value> ccreg_values;
200 ccreg_values.reserve (32);
201
aaa57c69
OE
202 // Something for recording visited basic blocks to avoid infinite recursion.
203 std::vector<basic_block> visited_bbs;
204 visited_bbs.reserve (32);
205
ac973375
OE
206 // Look for insns that set the ccreg to a constant value and see if it can
207 // be optimized.
208 basic_block bb;
4f42035e 209 FOR_EACH_BB_REVERSE_FN (bb, cfun)
ac973375
OE
210 for (rtx next_i, i = NEXT_INSN (BB_HEAD (bb));
211 i != NULL_RTX && i != BB_END (bb); i = next_i)
212 {
213 next_i = NEXT_INSN (i);
214
215 if (!INSN_P (i) || !NONDEBUG_INSN_P (i))
216 continue;
217
218 rtx setcc_val = const_setcc_value (PATTERN (i));
219 if (setcc_val != NULL_RTX)
220 {
221 update_ccreg_mode (GET_MODE (XEXP (PATTERN (i), 0)));
222
223 log_msg ("\n\nfound const setcc insn in [bb %d]: \n", bb->index);
224 log_insn (i);
225 log_msg ("\n");
226
227 ccreg_values.clear ();
aaa57c69
OE
228 visited_bbs.clear ();
229 find_last_ccreg_values (PREV_INSN (i), bb, ccreg_values,
230 visited_bbs);
ac973375
OE
231
232 log_msg ("number of ccreg values collected: %u\n",
233 (unsigned int)ccreg_values.size ());
234
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))
240 {
241 log_msg ("all values are ");
242 log_rtx (setcc_val);
243 log_msg ("\n");
244
245 delete_insn (i);
246 remove_ccreg_dead_unused_notes (ccreg_values);
247 }
248 }
249 }
250
251 log_return (0, "\n\n");
252}
253
254void
255sh_optimize_sett_clrt::update_ccreg_mode (machine_mode m)
256{
257 if (GET_MODE (m_ccreg) == m)
258 return;
259
260 PUT_MODE (m_ccreg, m);
261 log_msg ("updated ccreg mode: ");
262 log_rtx (m_ccreg);
263 log_msg ("\n\n");
264}
265
266rtx
267sh_optimize_sett_clrt::const_setcc_value (rtx pat) const
268{
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;
277}
278
279bool
280sh_optimize_sett_clrt
281::sh_cbranch_ccreg_value (rtx cbranch_insn, basic_block cbranch_insn_bb,
282 basic_block branch_target_bb) const
283{
284 rtx pc_set_rtx = pc_set (cbranch_insn);
285 gcc_assert (pc_set_rtx != NULL_RTX);
286 gcc_assert (branch_target_bb != NULL);
287
288 rtx cond = XEXP (XEXP (pc_set_rtx, 1), 0);
289 bool branch_if;
290
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;
295
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;
300
301 else
302 gcc_unreachable ();
303
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 ();
310}
311
312void
313sh_optimize_sett_clrt
314::find_last_ccreg_values (rtx start_insn, basic_block bb,
315 std::vector<ccreg_value>& values_out,
aaa57c69 316 std::vector<basic_block>& prev_visited_bb) const
ac973375 317{
aaa57c69
OE
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");
ac973375
OE
326
327 for (rtx i = start_insn; i != NULL_RTX && i != PREV_INSN (BB_HEAD (bb));
328 i = PREV_INSN (i))
329 {
330 if (!INSN_P (i))
331 continue;
332
333 if (reg_set_p (m_ccreg, i))
334 {
335 const_rtx set_rtx = set_of (m_ccreg, i);
336
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;
343
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");
349
350 values_out.push_back (v);
351 return;
352 }
353
aaa57c69 354 if (any_condjump_p (i) && onlyjump_p (i) && !prev_visited_bb.empty ())
ac973375
OE
355 {
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);
362
363 ccreg_value v;
364 v.insn = i;
365 v.bb = bb;
aaa57c69
OE
366 v.value = GEN_INT (sh_cbranch_ccreg_value (i, bb,
367 prev_visited_bb.back ()));
ac973375
OE
368
369 log_msg (" branches to [bb %d] with ccreg value ",
aaa57c69 370 prev_visited_bb.back ()->index);
ac973375
OE
371 log_rtx (v.value);
372 log_msg ("\n");
373
374 values_out.push_back (v);
375 return;
376 }
377 }
378
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;
383
aaa57c69
OE
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 ())
390 {
391 prev_visited_bb.push_back (bb);
ac973375 392
aaa57c69
OE
393 for (edge_iterator ei = ei_start (bb->preds); !ei_end_p (ei);
394 ei_next (&ei))
395 {
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);
400 }
401
402 prev_visited_bb.pop_back ();
403 }
404 else
405 log_msg ("loop detected for [bb %d]\n", bb->index);
ac973375
OE
406
407 log_msg ("[bb %d] pred_bb_count = %u\n", bb->index, pred_bb_count);
408
ac973375
OE
409 if (pred_bb_count == 0)
410 {
aaa57c69
OE
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.
ac973375
OE
413 log_msg ("unknown ccreg value for [bb %d]\n", bb->index);
414
415 ccreg_value v;
416 v.insn = BB_END (bb);
417 v.bb = bb;
418 v.value = NULL_RTX;
419
420 values_out.push_back (v);
421 }
422}
423
424bool
425sh_optimize_sett_clrt
426::all_ccreg_values_equal (const std::vector<ccreg_value>& values)
427{
428 if (values.empty ())
429 return false;
430
431 rtx last_value = values.front ().value;
432
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;
437
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;
442
443 return true;
444}
445
446void
447sh_optimize_sett_clrt
448::remove_ccreg_dead_unused_notes (std::vector<ccreg_value>& values) const
449{
450 for (std::vector<ccreg_value>::iterator i = values.begin ();
451 i != values.end (); ++i)
452 {
453 if (i->insn == NULL_RTX)
454 continue;
455
456 rtx n = find_regno_note (i->insn, REG_DEAD, REGNO (m_ccreg));
457 if (n != NULL_RTX)
458 remove_note (i->insn, n);
459
460 n = find_regno_note (i->insn, REG_UNUSED, REGNO (m_ccreg));
461 if (n != NULL_RTX)
462 remove_note (i->insn, n);
463 }
464}
465
466// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
467// This allows instantiating the pass somewhere else without having to pull
468// in a header file.
469opt_pass*
470make_pass_sh_optimize_sett_clrt (gcc::context* ctx, const char* name)
471{
472 return new sh_optimize_sett_clrt (ctx, name);
473}