]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/config/sh/sh_optimize_sett_clrt.cc
Update copyright years.
[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.
8d9254fc 2 Copyright (C) 2013-2020 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
8fcc61f8
RS
20#define IN_TARGET_CODE 1
21
ac973375 22#include "config.h"
218e2a54
OE
23#define INCLUDE_ALGORITHM
24#define INCLUDE_VECTOR
ac973375
OE
25#include "system.h"
26#include "coretypes.h"
c7131fb2 27#include "backend.h"
e11c4407 28#include "target.h"
c7131fb2
AM
29#include "rtl.h"
30#include "df.h"
60393bbc 31#include "cfgrtl.h"
ac973375 32#include "tree-pass.h"
ac973375 33
ac973375
OE
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);
579f4e64
OE
82 virtual bool gate (function*);
83 virtual unsigned int execute (function* fun);
ac973375
OE
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.
b32d5189 91 // Might be NULL if e.g. an unknown value is recorded for an
aaa57c69 92 // empty basic block.
b32d5189 93 rtx_insn *insn;
ac973375
OE
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.
8be6500b 114 bool find_last_ccreg_values (rtx_insn *start_insn, basic_block bb,
ac973375 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.
68a1a6c0 121 bool sh_cbranch_ccreg_value (rtx_insn *cbranch_insn,
ac973375
OE
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
ac973375
OE
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
149sh_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
158sh_optimize_sett_clrt::~sh_optimize_sett_clrt (void)
159{
160}
161
162bool
579f4e64 163sh_optimize_sett_clrt::gate (function*)
ac973375
OE
164{
165 return optimize > 0;
166}
167
168unsigned int
579f4e64 169sh_optimize_sett_clrt::execute (function* fun)
ac973375
OE
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
aaa57c69
OE
200 // Something for recording visited basic blocks to avoid infinite recursion.
201 std::vector<basic_block> visited_bbs;
202 visited_bbs.reserve (32);
203
ac973375
OE
204 // Look for insns that set the ccreg to a constant value and see if it can
205 // be optimized.
206 basic_block bb;
579f4e64 207 FOR_EACH_BB_REVERSE_FN (bb, fun)
b32d5189 208 for (rtx_insn *next_i, *i = NEXT_INSN (BB_HEAD (bb));
ac973375
OE
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 ();
aaa57c69 226 visited_bbs.clear ();
8be6500b
OE
227 bool ok = find_last_ccreg_values (PREV_INSN (i), bb, ccreg_values,
228 visited_bbs);
ac973375
OE
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.
8be6500b 236 if (ok && all_ccreg_values_equal (ccreg_values)
ac973375
OE
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
252void
253sh_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
264rtx
265sh_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
277bool
278sh_optimize_sett_clrt
68a1a6c0 279::sh_cbranch_ccreg_value (rtx_insn *cbranch_insn, basic_block cbranch_insn_bb,
ac973375
OE
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
8be6500b 310bool
ac973375 311sh_optimize_sett_clrt
b32d5189 312::find_last_ccreg_values (rtx_insn *start_insn, basic_block bb,
ac973375 313 std::vector<ccreg_value>& values_out,
aaa57c69 314 std::vector<basic_block>& prev_visited_bb) const
ac973375 315{
aaa57c69
OE
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");
ac973375 324
b32d5189 325 for (rtx_insn *i = start_insn; i != NULL && i != PREV_INSN (BB_HEAD (bb));
ac973375
OE
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);
8be6500b 349 return true;
ac973375
OE
350 }
351
aaa57c69 352 if (any_condjump_p (i) && onlyjump_p (i) && !prev_visited_bb.empty ())
ac973375
OE
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;
aaa57c69
OE
364 v.value = GEN_INT (sh_cbranch_ccreg_value (i, bb,
365 prev_visited_bb.back ()));
ac973375
OE
366
367 log_msg (" branches to [bb %d] with ccreg value ",
aaa57c69 368 prev_visited_bb.back ()->index);
ac973375
OE
369 log_rtx (v.value);
370 log_msg ("\n");
371
372 values_out.push_back (v);
8be6500b 373 return true;
ac973375
OE
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
aaa57c69
OE
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);
ac973375 390
aaa57c69
OE
391 for (edge_iterator ei = ei_start (bb->preds); !ei_end_p (ei);
392 ei_next (&ei))
393 {
8be6500b
OE
394 if (ei_edge (ei)->flags & EDGE_COMPLEX)
395 log_return (false, "aborting due to complex edge\n");
396
aaa57c69
OE
397 basic_block pred_bb = ei_edge (ei)->src;
398 pred_bb_count += 1;
8be6500b
OE
399 if (!find_last_ccreg_values (BB_END (pred_bb), pred_bb, values_out,
400 prev_visited_bb))
401 return false;
aaa57c69
OE
402 }
403
404 prev_visited_bb.pop_back ();
405 }
406 else
407 log_msg ("loop detected for [bb %d]\n", bb->index);
ac973375
OE
408
409 log_msg ("[bb %d] pred_bb_count = %u\n", bb->index, pred_bb_count);
410
ac973375
OE
411 if (pred_bb_count == 0)
412 {
aaa57c69
OE
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.
ac973375
OE
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 }
8be6500b
OE
424
425 return true;
ac973375
OE
426}
427
428bool
429sh_optimize_sett_clrt
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
450void
451sh_optimize_sett_clrt
452::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.
473opt_pass*
474make_pass_sh_optimize_sett_clrt (gcc::context* ctx, const char* name)
475{
476 return new sh_optimize_sett_clrt (ctx, name);
477}