aboutsummaryrefslogtreecommitdiff
path: root/gcc/pair-fusion.h
blob: 45e4edceecbb66a6163a98774b492395df0a4714 (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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
// Pass to fuse adjacent loads/stores into paired memory accesses.
//
// This file contains the definition of the virtual base class which is
// overriden by targets that make use of the pass.
//
// Copyright (C) 2023-2024 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 {
  class def_info;
  class insn_info;
  class insn_range_info;
  class bb_info;
}

// Information about a potential base candidate, used in try_fuse_pair.
// There may be zero, one, or two viable RTL bases for a given pair.
struct base_cand
{
  // DEF is the def of the base register to be used by the pair.
  rtl_ssa::def_info *def;

  // FROM_INSN is -1 if the base candidate is already shared by both
  // candidate insns.  Otherwise it holds the index of the insn from
  // which the base originated.
  //
  // In the case that the base is shared, either DEF is already used
  // by both candidate accesses, or both accesses see different versions
  // of the same regno, in which case DEF is the def consumed by the
  // first candidate access.
  int from_insn;

  // To form a pair, we do so by moving the first access down and the second
  // access up.  To determine where to form the pair, and whether or not
  // it is safe to form the pair, we track instructions which cannot be
  // re-ordered past due to either dataflow or alias hazards.
  //
  // Since we allow changing the base used by an access, the choice of
  // base can change which instructions act as re-ordering hazards for
  // this pair (due to different dataflow).  We store the initial
  // dataflow hazards for this choice of base candidate in HAZARDS.
  //
  // These hazards act as re-ordering barriers to each candidate insn
  // respectively, in program order.
  //
  // Later on, when we take alias analysis into account, we narrow
  // HAZARDS accordingly.
  rtl_ssa::insn_info *hazards[2];

  base_cand (rtl_ssa::def_info *def, int insn)
    : def (def), from_insn (insn), hazards {nullptr, nullptr} {}

  base_cand (rtl_ssa::def_info *def) : base_cand (def, -1) {}

  // Test if this base candidate is viable according to HAZARDS.
  bool viable () const;
};

struct alias_walker;

// When querying should_handle_writeback, this enum is used to
// qualify which opportunities we are asking about.
enum class writeback_type {
  // Only those writeback opportunities that arise from existing
  // auto-increment accesses.
  EXISTING,

  // All writeback opportunities, including those that involve folding
  // base register updates into a non-writeback pair.
  ALL
};

// This class can be overriden by targets to give a pass that fuses
// adjacent loads and stores into load/store pair instructions.
//
// The target can override the various virtual functions to customize
// the behaviour of the pass as appropriate for the target.
struct pair_fusion {
  pair_fusion ();

  // Given:
  // - an rtx REG_OP, the non-memory operand in a load/store insn,
  // - a machine_mode MEM_MODE, the mode of the MEM in that insn, and
  // - a boolean LOAD_P (true iff the insn is a load), then:
  // return true if the access should be considered an FP/SIMD access.
  // Such accesses are segregated from GPR accesses, since we only want
  // to form pairs for accesses that use the same register file.
  virtual bool fpsimd_op_p (rtx, machine_mode, bool)
  {
    return false;
  }

  // Return true if we should consider forming pairs from memory
  // accesses with operand mode MODE at this stage in compilation.
  virtual bool pair_operand_mode_ok_p (machine_mode mode) = 0;

  // Return true iff REG_OP is a suitable register operand for a paired
  // memory access, where LOAD_P is true if we're asking about loads and
  // false for stores.  MODE gives the mode of the operand.
  virtual bool pair_reg_operand_ok_p (bool load_p, rtx reg_op,
				      machine_mode mode) = 0;

  // Return alias check limit.
  // This is needed to avoid unbounded quadratic behaviour when
  // performing alias analysis.
  virtual int pair_mem_alias_check_limit () = 0;

  // Return true if we should try to handle writeback opportunities.
  // WHICH determines the kinds of writeback opportunities the caller
  // is asking about.
  virtual bool should_handle_writeback (writeback_type which) = 0;

  // Given BASE_MEM, the mem from the lower candidate access for a pair,
  // and LOAD_P (true if the access is a load), check if we should proceed
  // to form the pair given the target's code generation policy on
  // paired accesses.
  virtual bool pair_mem_ok_with_policy (rtx base_mem, bool load_p) = 0;

  // Generate the pattern for a paired access.  PATS gives the patterns
  // for the individual memory accesses (which by this point must share a
  // common base register).  If WRITEBACK is non-NULL, then this rtx
  // describes the update to the base register that should be performed by
  // the resulting insn.  LOAD_P is true iff the accesses are loads.
  virtual rtx gen_pair (rtx *pats, rtx writeback, bool load_p) = 0;

  // Return true if INSN is a paired memory access.  If so, set LOAD_P to
  // true iff INSN is a load pair.
  virtual bool pair_mem_insn_p (rtx_insn *insn, bool &load_p) = 0;

  // Return true if we should track loads.
  virtual bool track_loads_p ()
  {
    return true;
  }

  // Return true if we should track stores.
  virtual bool track_stores_p ()
  {
    return true;
  }

  // Return true if OFFSET is in range for a paired memory access.
  virtual bool pair_mem_in_range_p (HOST_WIDE_INT offset) = 0;

  // Given a load/store pair insn in PATTERN, unpack the insn, storing
  // the register operands in REGS, and returning the mem.  LOAD_P is
  // true for loads and false for stores.
  virtual rtx destructure_pair (rtx regs[2], rtx pattern, bool load_p) = 0;

  // Given a pair mem in MEM, register operands in REGS, and an rtx
  // representing the effect of writeback on the base register in WB_EFFECT,
  // return an insn representing a writeback variant of this pair.
  // LOAD_P is true iff the pair is a load.
  // This is used when promoting existing non-writeback pairs to writeback
  // variants.
  virtual rtx gen_promote_writeback_pair (rtx wb_effect, rtx mem,
					  rtx regs[2], bool load_p) = 0;

  void process_block (rtl_ssa::bb_info *bb);
  rtl_ssa::insn_info *find_trailing_add (rtl_ssa::insn_info *insns[2],
					 const rtl_ssa::insn_range_info
					 &pair_range,
					 int initial_writeback,
					 rtx *writeback_effect,
					 rtl_ssa::def_info **add_def,
					 rtl_ssa::def_info *base_def,
					 poly_int64 initial_offset,
					 unsigned access_size);
  int get_viable_bases (rtl_ssa::insn_info *insns[2],
			vec<base_cand> &base_cands,
			rtx cand_mems[2],
			unsigned access_size,
			bool reversed);
  void do_alias_analysis (rtl_ssa::insn_info *alias_hazards[4],
			  alias_walker *walkers[4],
			  bool load_p);
  void try_promote_writeback (rtl_ssa::insn_info *insn, bool load_p);
  void run ();
  ~pair_fusion ();
};