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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
|
From 88d76baa38bb29d5cc732b3c0188b74ef9783713 Mon Sep 17 00:00:00 2001
From: Richard Sandiford <richard.sandiford@arm.com>
Date: Sat, 11 Nov 2023 17:28:59 +0000
Subject: [PATCH 043/157] [Backport][SME] mode-switching: Add a
target-configurable confluence operator
Reference: https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=493b0038d7d04986c7de977074d095e4eb7d9a27
The mode-switching pass assumed that all of an entity's modes
were mutually exclusive. However, the upcoming SME changes
have an entity with some overlapping modes, so that there is
sometimes a "superunion" mode that contains two given modes.
We can use this relationship to pass something more helpful than
"don't know" to the emit hook.
This patch adds a new hook that targets can use to specify
a mode confluence operator.
With mutually exclusive modes, it's possible to compute a block's
incoming and outgoing modes by looking at its availability sets.
With the confluence operator, we instead need to solve a full
dataflow problem.
However, when emitting a mode transition, the upcoming SME use of
mode-switching benefits from having as much information as possible
about the starting mode. Calculating this information is definitely
worth the compile time.
The dataflow problem is written to work before and after the LCM
problem has been solved. A later patch makes use of this.
While there (since git blame would ping me for the reindented code),
I used a lambda to avoid the cut-&-pasted loops.
gcc/
* target.def (mode_switching.confluence): New hook.
* doc/tm.texi (TARGET_MODE_CONFLUENCE): New @hook.
* doc/tm.texi.in: Regenerate.
* mode-switching.cc (confluence_info): New variable.
(mode_confluence, forward_confluence_n, forward_transfer): New
functions.
(optimize_mode_switching): Use them to calculate mode_in when
TARGET_MODE_CONFLUENCE is defined.
---
gcc/doc/tm.texi | 16 ++++
gcc/doc/tm.texi.in | 2 +
gcc/mode-switching.cc | 179 +++++++++++++++++++++++++++++++++++-------
gcc/target.def | 17 ++++
4 files changed, 186 insertions(+), 28 deletions(-)
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 7fce485b2..d7053ec9e 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -10306,6 +10306,22 @@ the number of modes if it does not know what mode @var{entity} has after
Not defining the hook is equivalent to returning @var{mode}.
@end deftypefn
+@deftypefn {Target Hook} int TARGET_MODE_CONFLUENCE (int @var{entity}, int @var{mode1}, int @var{mode2})
+By default, the mode-switching pass assumes that a given entity's modes
+are mutually exclusive. This means that the pass can only tell
+@code{TARGET_MODE_EMIT} about an entity's previous mode if all
+incoming paths of execution leave the entity in the same state.
+
+However, some entities might have overlapping, non-exclusive modes,
+so that it is sometimes possible to represent ``mode @var{mode1} or mode
+@var{mode2}'' with something more specific than ``mode not known''.
+If this is true for at least one entity, you should define this hook
+and make it return a mode that includes @var{mode1} and @var{mode2}
+as possibilities. (The mode can include other possibilities too.)
+The hook should return the number of modes if no suitable mode exists
+for the given arguments.
+@end deftypefn
+
@deftypefn {Target Hook} int TARGET_MODE_ENTRY (int @var{entity})
If this hook is defined, it is evaluated for every @var{entity} that
needs mode switching. It should return the mode that @var{entity} is
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index ad343504f..d420e62fd 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -6922,6 +6922,8 @@ mode or ``no mode'', depending on context.
@hook TARGET_MODE_AFTER
+@hook TARGET_MODE_CONFLUENCE
+
@hook TARGET_MODE_ENTRY
@hook TARGET_MODE_EXIT
diff --git a/gcc/mode-switching.cc b/gcc/mode-switching.cc
index 89a8494c6..065767902 100644
--- a/gcc/mode-switching.cc
+++ b/gcc/mode-switching.cc
@@ -484,6 +484,101 @@ create_pre_exit (int n_entities, int *entity_map, const int *num_modes)
return pre_exit;
}
+/* Return the confluence of modes MODE1 and MODE2 for entity ENTITY,
+ using NO_MODE to represent an unknown mode if nothing more precise
+ is available. */
+
+int
+mode_confluence (int entity, int mode1, int mode2, int no_mode)
+{
+ if (mode1 == mode2)
+ return mode1;
+
+ if (mode1 != no_mode
+ && mode2 != no_mode
+ && targetm.mode_switching.confluence)
+ return targetm.mode_switching.confluence (entity, mode1, mode2);
+
+ return no_mode;
+}
+
+/* Information for the dataflow problems below. */
+struct
+{
+ /* Information about each basic block, indexed by block id. */
+ struct bb_info *bb_info;
+
+ /* The entity that we're processing. */
+ int entity;
+
+ /* The number of modes defined for the entity, and thus the identifier
+ of the "don't know" mode. */
+ int no_mode;
+} confluence_info;
+
+/* Propagate information about any mode change on edge E to the
+ destination block's mode_in. Return true if something changed.
+
+ The mode_in and mode_out fields use no_mode + 1 to mean "not yet set". */
+
+static bool
+forward_confluence_n (edge e)
+{
+ /* The entry and exit blocks have no useful mode information. */
+ if (e->src->index == ENTRY_BLOCK || e->dest->index == EXIT_BLOCK)
+ return false;
+
+ /* We don't control mode changes across abnormal edges. */
+ if (e->flags & EDGE_ABNORMAL)
+ return false;
+
+ /* E->aux is nonzero if we have computed the LCM problem and scheduled
+ E to change the mode to E->aux - 1. Otherwise model the change
+ from the source to the destination. */
+ struct bb_info *bb_info = confluence_info.bb_info;
+ int no_mode = confluence_info.no_mode;
+ int src_mode = bb_info[e->src->index].mode_out;
+ if (e->aux)
+ src_mode = (int) (intptr_t) e->aux - 1;
+ if (src_mode == no_mode + 1)
+ return false;
+
+ int dest_mode = bb_info[e->dest->index].mode_in;
+ if (dest_mode == no_mode + 1)
+ {
+ bb_info[e->dest->index].mode_in = src_mode;
+ return true;
+ }
+
+ int entity = confluence_info.entity;
+ int new_mode = mode_confluence (entity, src_mode, dest_mode, no_mode);
+ if (dest_mode == new_mode)
+ return false;
+
+ bb_info[e->dest->index].mode_in = new_mode;
+ return true;
+}
+
+/* Update block BB_INDEX's mode_out based on its mode_in. Return true if
+ something changed. */
+
+static bool
+forward_transfer (int bb_index)
+{
+ /* The entry and exit blocks have no useful mode information. */
+ if (bb_index == ENTRY_BLOCK || bb_index == EXIT_BLOCK)
+ return false;
+
+ /* Only propagate through a block if the entity is transparent. */
+ struct bb_info *bb_info = confluence_info.bb_info;
+ if (bb_info[bb_index].computing != confluence_info.no_mode
+ || bb_info[bb_index].mode_out == bb_info[bb_index].mode_in)
+ return false;
+
+ bb_info[bb_index].mode_out = bb_info[bb_index].mode_in;
+ return true;
+}
+
/* Find all insns that need a particular mode setting, and insert the
necessary mode switches. Return true if we did work. */
@@ -567,6 +662,39 @@ optimize_mode_switching (void)
auto_sbitmap transp_all (last_basic_block_for_fn (cfun));
+ auto_bitmap blocks;
+
+ /* Forward-propagate mode information through blocks where the entity
+ is transparent, so that mode_in describes the mode on entry to each
+ block and mode_out describes the mode on exit from each block. */
+ auto forwprop_mode_info = [&](struct bb_info *info,
+ int entity, int no_mode)
+ {
+ /* Use no_mode + 1 to mean "not yet set". */
+ FOR_EACH_BB_FN (bb, cfun)
+ {
+ if (bb_has_abnormal_pred (bb))
+ info[bb->index].mode_in = info[bb->index].seginfo->mode;
+ else
+ info[bb->index].mode_in = no_mode + 1;
+ if (info[bb->index].computing != no_mode)
+ info[bb->index].mode_out = info[bb->index].computing;
+ else
+ info[bb->index].mode_out = no_mode + 1;
+ }
+
+ confluence_info.bb_info = info;
+ confluence_info.entity = entity;
+ confluence_info.no_mode = no_mode;
+
+ bitmap_set_range (blocks, 0, last_basic_block_for_fn (cfun));
+ df_simple_dataflow (DF_FORWARD, NULL, NULL, forward_confluence_n,
+ forward_transfer, blocks,
+ df_get_postorder (DF_FORWARD),
+ df_get_n_blocks (DF_FORWARD));
+
+ };
+
for (j = n_entities - 1; j >= 0; j--)
{
int e = entity_map[j];
@@ -720,6 +848,7 @@ optimize_mode_switching (void)
for (j = n_entities - 1; j >= 0; j--)
{
int no_mode = num_modes[entity_map[j]];
+ struct bb_info *info = bb_info[j];
/* Insert all mode sets that have been inserted by lcm. */
@@ -740,39 +869,33 @@ optimize_mode_switching (void)
}
}
+ /* mode_in and mode_out can be calculated directly from avin and
+ avout if all the modes are mutually exclusive. Use the target-
+ provided confluence function otherwise. */
+ if (targetm.mode_switching.confluence)
+ forwprop_mode_info (info, entity_map[j], no_mode);
+
FOR_EACH_BB_FN (bb, cfun)
{
- struct bb_info *info = bb_info[j];
- int last_mode = no_mode;
-
- /* intialize mode in availability for bb. */
- for (i = 0; i < no_mode; i++)
- if (mode_bit_p (avout[bb->index], j, i))
- {
- if (last_mode == no_mode)
- last_mode = i;
- if (last_mode != i)
+ auto modes_confluence = [&](sbitmap *av)
+ {
+ for (int i = 0; i < no_mode; ++i)
+ if (mode_bit_p (av[bb->index], j, i))
{
- last_mode = no_mode;
- break;
+ for (int i2 = i + 1; i2 < no_mode; ++i2)
+ if (mode_bit_p (av[bb->index], j, i2))
+ return no_mode;
+ return i;
}
- }
- info[bb->index].mode_out = last_mode;
+ return no_mode;
+ };
- /* intialize mode out availability for bb. */
- last_mode = no_mode;
- for (i = 0; i < no_mode; i++)
- if (mode_bit_p (avin[bb->index], j, i))
- {
- if (last_mode == no_mode)
- last_mode = i;
- if (last_mode != i)
- {
- last_mode = no_mode;
- break;
- }
- }
- info[bb->index].mode_in = last_mode;
+ /* intialize mode in/out availability for bb. */
+ if (!targetm.mode_switching.confluence)
+ {
+ info[bb->index].mode_out = modes_confluence (avout);
+ info[bb->index].mode_in = modes_confluence (avin);
+ }
for (i = 0; i < no_mode; i++)
if (mode_bit_p (del[bb->index], j, i))
diff --git a/gcc/target.def b/gcc/target.def
index 67c20bbb0..1e2091ed3 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -7025,6 +7025,23 @@ the number of modes if it does not know what mode @var{entity} has after\n\
Not defining the hook is equivalent to returning @var{mode}.",
int, (int entity, int mode, rtx_insn *insn, HARD_REG_SET regs_live), NULL)
+DEFHOOK
+(confluence,
+ "By default, the mode-switching pass assumes that a given entity's modes\n\
+are mutually exclusive. This means that the pass can only tell\n\
+@code{TARGET_MODE_EMIT} about an entity's previous mode if all\n\
+incoming paths of execution leave the entity in the same state.\n\
+\n\
+However, some entities might have overlapping, non-exclusive modes,\n\
+so that it is sometimes possible to represent ``mode @var{mode1} or mode\n\
+@var{mode2}'' with something more specific than ``mode not known''.\n\
+If this is true for at least one entity, you should define this hook\n\
+and make it return a mode that includes @var{mode1} and @var{mode2}\n\
+as possibilities. (The mode can include other possibilities too.)\n\
+The hook should return the number of modes if no suitable mode exists\n\
+for the given arguments.",
+ int, (int entity, int mode1, int mode2), NULL)
+
DEFHOOK
(entry,
"If this hook is defined, it is evaluated for every @var{entity} that\n\
--
2.33.0
|