aboutsummaryrefslogtreecommitdiff
path: root/gcc/rtl-ssa/change-utils.h
blob: 2eb8939b6a762f2b2454b4b1474c2cfda6925360 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// RTL SSA utility functions for changing instructions              -*- C++ -*-
// Copyright (C) 2020-2025 Free Software Foundation, Inc.
//
// This file is part of GCC.
//
// GCC is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3, or (at your option) any later
// version.
//
// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License
// along with GCC; see the file COPYING3.  If not see
// <http://www.gnu.org/licenses/>.

namespace rtl_ssa {

// Return true if INSN is one of the instructions being changed by CHANGES.
inline bool
insn_is_changing (array_slice<insn_change *const> changes,
		  const insn_info *insn)
{
  for (const insn_change *change : changes)
    if (change->insn () == insn)
      return true;
  return false;
}

// Restrict CHANGE.move_range so that the changed instruction can perform
// all its definitions and uses.
//
// IGNORE is an object that provides the same interface as ignore_nothing.
// Assume that if:
//
// - CHANGE contains an access A1 of resource R;
// - an instruction I2 contains another access A2 to R; and
// - IGNORE says that I2 should be ignored
//
// then either:
//
// - A2 will be removed; or
// - something will ensure that A1 and A2 maintain their current order,
//   without this having to be enforced by CHANGE's move range.
//
// Assume the same thing about a definition D of R, and about all uses of D,
// if IGNORE says that D should be ignored.
//
// IGNORE should ignore CHANGE.insn ().
//
// Return true on success, otherwise leave CHANGE.move_range in an invalid
// state.
//
// This function only works correctly for instructions that remain within
// the same extended basic block.
template<typename IgnorePredicates>
bool
restrict_movement (insn_change &change, IgnorePredicates ignore)
{
  // Uses generally lead to failure quicker, so test those first.
  return (restrict_movement_for_uses (change.move_range,
				      change.new_uses, ignore)
	  && restrict_movement_for_defs (change.move_range,
					 change.new_defs, ignore)
	  && canonicalize_move_range (change.move_range, change.insn ()));
}

// As above, but ignore only the instruction that is being changed.
inline bool
restrict_movement (insn_change &change)
{
  return restrict_movement (change, ignore_insn (change.insn ()));
}

using add_regno_clobber_fn = std::function<bool (insn_change &,
						 unsigned int)>;
bool recog_internal (insn_change &, add_regno_clobber_fn);

// Try to recognize the new instruction pattern for CHANGE, potentially
// tweaking the pattern or adding extra clobbers in order to make it match.
//
// When adding an extra clobber for register R, restrict CHANGE.move_range
// to a range of instructions for which R is not live.  Use IGNORE to guide
// this process, where IGNORE is an object that provides the same interface
// as ignore_nothing.  When determining whether R is live, ignore accesses
// made by an instruction I if IGNORE says that I should be ignored.
// The caller then assumes the responsibility of ensuring that CHANGE
// and I are placed in a valid order.  Similarly, ignore live ranges
// associated with a definition of R if IGNORE says that that definition
// should be ignored.
//
// IGNORE should ignore CHANGE.insn ().
//
// Return true on success.  Leave CHANGE unmodified on failure.
template<typename IgnorePredicates>
inline bool
recog (obstack_watermark &watermark, insn_change &change,
       IgnorePredicates ignore)
{
  auto add_regno_clobber = [&](insn_change &change, unsigned int regno)
    {
      return crtl->ssa->add_regno_clobber (watermark, change, regno, ignore);
    };
  return recog_internal (change, add_regno_clobber);
}

// As above, but ignore only the instruction that is being changed.
inline bool
recog (obstack_watermark &watermark, insn_change &change)
{
  return recog (watermark, change, ignore_insn (change.insn ()));
}

// Check whether insn costs indicate that the net effect of the changes
// in CHANGES is worthwhile.  Require a strict improvement if STRICT_P,
// otherwise allow the new instructions to be the same cost as the old
// instructions.
bool changes_are_worthwhile (array_slice<insn_change *const> changes,
			     bool strict_p = false);

// Like changes_are_worthwhile, but for a single change.
inline bool
change_is_worthwhile (insn_change &change, bool strict_p = false)
{
  insn_change *changes[] = { &change };
  return changes_are_worthwhile (changes, strict_p);
}

}