aboutsummaryrefslogtreecommitdiff
path: root/gcc/rtl-ssa/change-utils.h
blob: f5f3d8abf894952bc4edfb45d96fc511b6a173be (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
133
134
135
136
137
// RTL SSA utility functions for changing instructions              -*- C++ -*-
// Copyright (C) 2020-2022 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;
}

// Return a closure of insn_is_changing, for use as a predicate.
// This could be done using local lambdas instead, but the predicate is
// used often enough that having a class should be more convenient and allow
// reuse of template instantiations.
//
// We don't use std::bind because it would involve an indirect function call,
// whereas this function is used in relatively performance-critical code.
inline insn_is_changing_closure
insn_is_changing (array_slice<insn_change *const> changes)
{
  return insn_is_changing_closure (changes);
}

// Restrict CHANGE.move_range so that the changed instruction can perform
// all its definitions and uses.  Assume that if:
//
// - CHANGE contains an access A1 of resource R;
// - an instruction I2 contains another access A2 to R; and
// - IGNORE (I2) is true
//
// 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.
//
// IGNORE should return true for 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 IgnorePredicate>
bool
restrict_movement_ignoring (insn_change &change, IgnorePredicate ignore)
{
  // Uses generally lead to failure quicker, so test those first.
  return (restrict_movement_for_uses_ignoring (change.move_range,
					       change.new_uses, ignore)
	  && restrict_movement_for_defs_ignoring (change.move_range,
						  change.new_defs, ignore)
	  && canonicalize_move_range (change.move_range, change.insn ()));
}

// Like restrict_movement_ignoring, but ignore only the instruction
// that is being changed.
inline bool
restrict_movement (insn_change &change)
{
  return restrict_movement_ignoring (change, insn_is (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.  When determining
// whether R is live, ignore accesses made by an instruction I if
// IGNORE (I) is true.  The caller then assumes the responsibility
// of ensuring that CHANGE and I are placed in a valid order.
//
// IGNORE should return true for CHANGE.insn ().
//
// Return true on success.  Leave CHANGE unmodified on failure.
template<typename IgnorePredicate>
inline bool
recog_ignoring (obstack_watermark &watermark, insn_change &change,
		IgnorePredicate 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 for recog_ignoring, but ignore only the instruction that is being
// changed.
inline bool
recog (obstack_watermark &watermark, insn_change &change)
{
  return recog_ignoring (watermark, change, insn_is (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);
}

}