Line data Source code
1 : /* Web construction code for GNU compiler.
2 : Contributed by Jan Hubicka.
3 : Copyright (C) 2001-2026 Free Software Foundation, Inc.
4 :
5 : This file is part of GCC.
6 :
7 : GCC is free software; you can redistribute it and/or modify it under
8 : the terms of the GNU General Public License as published by the Free
9 : Software Foundation; either version 3, or (at your option) any later
10 : version.
11 :
12 : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 : WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 : for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with GCC; see the file COPYING3. If not see
19 : <http://www.gnu.org/licenses/>. */
20 :
21 : /* Simple optimization pass that splits independent uses of each pseudo,
22 : increasing effectiveness of other optimizations. The optimization can
23 : serve as an example of use for the dataflow module.
24 :
25 : TODO
26 : - We may use profile information and ignore infrequent use for the
27 : purpose of web unifying, inserting the compensation code later to
28 : implement full induction variable expansion for loops (currently
29 : we expand only if the induction variable is dead afterward, which
30 : is often the case). */
31 :
32 : #include "config.h"
33 : #include "system.h"
34 : #include "coretypes.h"
35 : #include "backend.h"
36 : #include "rtl.h"
37 : #include "df.h"
38 : #include "insn-config.h"
39 : #include "recog.h"
40 :
41 : #include "tree-pass.h"
42 :
43 :
44 : /* Find the root of unionfind tree (the representative of set). */
45 :
46 : web_entry_base *
47 15079810 : web_entry_base::unionfind_root ()
48 : {
49 15079810 : web_entry_base *element = this, *element1 = this, *element2;
50 :
51 26834380 : while (element->pred ())
52 : element = element->pred ();
53 26834380 : while (element1->pred ())
54 : {
55 11754570 : element2 = element1->pred ();
56 11754570 : element1->set_pred (element);
57 11754570 : element1 = element2;
58 : }
59 15079810 : return element;
60 : }
61 :
62 : /* Union sets.
63 : Return true if FIRST and SECOND points to the same web entry structure and
64 : nothing is done. Otherwise, return false. */
65 :
66 : bool
67 4832984 : unionfind_union (web_entry_base *first, web_entry_base *second)
68 : {
69 4832984 : first = first->unionfind_root ();
70 4832984 : second = second->unionfind_root ();
71 4832984 : if (first == second)
72 : return true;
73 3899469 : second->set_pred (first);
74 3899469 : return false;
75 : }
76 :
77 : struct web_entry : public web_entry_base
78 : {
79 : private:
80 : rtx reg_pvt;
81 :
82 : public:
83 5413842 : rtx reg () { return reg_pvt; }
84 1514373 : void set_reg (rtx r) { reg_pvt = r; }
85 : };
86 :
87 : /* For INSN, union all defs and uses that are linked by match_dup.
88 : FUN is the function that does the union. */
89 :
90 : static void
91 3556789 : union_match_dups (rtx_insn *insn, web_entry *def_entry, web_entry *use_entry,
92 : bool (*fun) (web_entry_base *, web_entry_base *))
93 : {
94 3556789 : struct df_insn_info *insn_info = DF_INSN_INFO_GET (insn);
95 3556789 : df_ref use_link = DF_INSN_INFO_USES (insn_info);
96 3556789 : df_ref def_link = DF_INSN_INFO_DEFS (insn_info);
97 3556789 : struct web_entry *dup_entry;
98 3556789 : int i;
99 :
100 3556789 : extract_insn (insn);
101 :
102 3602191 : for (i = 0; i < recog_data.n_dups; i++)
103 : {
104 45402 : int op = recog_data.dup_num[i];
105 45402 : enum op_type type = recog_data.operand_type[op];
106 45402 : df_ref ref, dupref;
107 45402 : struct web_entry *entry;
108 :
109 45402 : dup_entry = use_entry;
110 120990 : for (dupref = use_link; dupref; dupref = DF_REF_NEXT_LOC (dupref))
111 105137 : if (DF_REF_LOC (dupref) == recog_data.dup_loc[i])
112 : break;
113 :
114 45402 : if (dupref == NULL && type == OP_INOUT)
115 : {
116 11809 : dup_entry = def_entry;
117 11809 : for (dupref = def_link; dupref; dupref = DF_REF_NEXT_LOC (dupref))
118 7025 : if (DF_REF_LOC (dupref) == recog_data.dup_loc[i])
119 : break;
120 : }
121 : /* ??? *DUPREF can still be zero, because when an operand matches
122 : a memory, DF_REF_LOC (use_link[n]) points to the register part
123 : of the address, whereas recog_data.dup_loc[m] points to the
124 : entire memory ref, thus we fail to find the duplicate entry,
125 : even though it is there.
126 : Example: i686-pc-linux-gnu gcc.c-torture/compile/950607-1.c
127 : -O3 -fomit-frame-pointer -funroll-loops */
128 45402 : if (dupref == NULL
129 29558 : || DF_REF_REGNO (dupref) < FIRST_PSEUDO_REGISTER)
130 16455 : continue;
131 :
132 28947 : ref = type == OP_IN ? use_link : def_link;
133 28947 : entry = type == OP_IN ? use_entry : def_entry;
134 64758 : for (; ref; ref = DF_REF_NEXT_LOC (ref))
135 : {
136 64749 : rtx *l = DF_REF_LOC (ref);
137 64749 : if (l == recog_data.operand_loc[op])
138 : break;
139 35811 : if (l && DF_REF_REAL_LOC (ref) == recog_data.operand_loc[op])
140 : break;
141 : }
142 :
143 28947 : if (!ref && type == OP_INOUT)
144 : {
145 9 : entry = use_entry;
146 9 : for (ref = use_link; ref; ref = DF_REF_NEXT_LOC (ref))
147 : {
148 9 : rtx *l = DF_REF_LOC (ref);
149 9 : if (l == recog_data.operand_loc[op])
150 : break;
151 0 : if (l && DF_REF_REAL_LOC (ref) == recog_data.operand_loc[op])
152 : break;
153 : }
154 : }
155 :
156 28947 : gcc_assert (ref);
157 28947 : (*fun) (dup_entry + DF_REF_ID (dupref), entry + DF_REF_ID (ref));
158 : }
159 3556789 : }
160 :
161 : /* For each use, all possible defs reaching it must come in the same
162 : register, union them.
163 : FUN is the function that does the union.
164 :
165 : In USED, we keep the DF_REF_ID of the first uninitialized uses of a
166 : register, so that all uninitialized uses of the register can be
167 : combined into a single web. We actually offset it by 2, because
168 : the values 0 and 1 are reserved for use by entry_register. */
169 :
170 : void
171 3598288 : union_defs (df_ref use, web_entry *def_entry,
172 : unsigned int *used, web_entry *use_entry,
173 : bool (*fun) (web_entry_base *, web_entry_base *))
174 : {
175 3598288 : struct df_insn_info *insn_info = DF_REF_INSN_INFO (use);
176 3598288 : struct df_link *link = DF_REF_CHAIN (use);
177 3598288 : rtx set;
178 :
179 3598288 : if (insn_info)
180 : {
181 3598288 : df_ref eq_use;
182 :
183 3598288 : set = single_set (insn_info->insn);
184 3809401 : FOR_EACH_INSN_INFO_EQ_USE (eq_use, insn_info)
185 211113 : if (use != eq_use
186 164894 : && DF_REF_REAL_REG (use) == DF_REF_REAL_REG (eq_use))
187 35487 : (*fun) (use_entry + DF_REF_ID (use), use_entry + DF_REF_ID (eq_use));
188 : }
189 : else
190 : set = NULL;
191 :
192 : /* Union all occurrences of the same register in reg notes. */
193 :
194 : /* Recognize trivial noop moves and attempt to keep them as noop. */
195 :
196 3598288 : if (set
197 3578143 : && SET_SRC (set) == DF_REF_REG (use)
198 566887 : && SET_SRC (set) == SET_DEST (set))
199 : {
200 0 : df_ref def;
201 :
202 0 : FOR_EACH_INSN_INFO_DEF (def, insn_info)
203 0 : if (DF_REF_REAL_REG (use) == DF_REF_REAL_REG (def))
204 0 : (*fun) (use_entry + DF_REF_ID (use), def_entry + DF_REF_ID (def));
205 : }
206 :
207 : /* UD chains of uninitialized REGs are empty. Keeping all uses of
208 : the same uninitialized REG in a single web is not necessary for
209 : correctness, since the uses are undefined, but it's wasteful to
210 : allocate one register or slot for each reference. Furthermore,
211 : creating new pseudos for uninitialized references in debug insns
212 : (see PR 42631) causes -fcompare-debug failures. We record the
213 : number of the first uninitialized reference we found, and merge
214 : with it any other uninitialized references to the same
215 : register. */
216 3598288 : if (!link)
217 : {
218 14230 : int regno = REGNO (DF_REF_REAL_REG (use));
219 14230 : if (used[regno])
220 9143 : (*fun) (use_entry + DF_REF_ID (use), use_entry + used[regno] - 2);
221 : else
222 5087 : used[regno] = DF_REF_ID (use) + 2;
223 : }
224 :
225 8355393 : while (link)
226 : {
227 4757105 : (*fun) (use_entry + DF_REF_ID (use),
228 4757105 : def_entry + DF_REF_ID (link->ref));
229 4757105 : link = link->next;
230 : }
231 :
232 : /* A READ_WRITE use requires the corresponding def to be in the same
233 : register. Find it and union. */
234 3598288 : if (DF_REF_FLAGS (use) & DF_REF_READ_WRITE)
235 2302 : if (insn_info)
236 : {
237 2302 : df_ref def;
238 :
239 4604 : FOR_EACH_INSN_INFO_DEF (def, insn_info)
240 2302 : if (DF_REF_REAL_REG (use) == DF_REF_REAL_REG (def))
241 2302 : (*fun) (use_entry + DF_REF_ID (use), def_entry + DF_REF_ID (def));
242 : }
243 3598288 : }
244 :
245 : /* Find the corresponding register for the given entry. */
246 :
247 : static rtx
248 5413842 : entry_register (web_entry *entry, df_ref ref, unsigned int *used)
249 : {
250 5413842 : web_entry *root;
251 5413842 : rtx reg, newreg;
252 :
253 : /* Find the corresponding web and see if it has been visited. */
254 5413842 : root = (web_entry *)entry->unionfind_root ();
255 5413842 : if (root->reg ())
256 : return root->reg ();
257 :
258 : /* We are seeing this web for the first time, do the assignment. */
259 1514373 : reg = DF_REF_REAL_REG (ref);
260 :
261 : /* In case the original register is already assigned, generate new
262 : one. Since we use USED to merge uninitialized refs into a single
263 : web, we might found an element to be nonzero without our having
264 : used it. Test for 1, because union_defs saves it for our use,
265 : and there won't be any use for the other values when we get to
266 : this point. */
267 1514373 : if (used[REGNO (reg)] != 1)
268 1110335 : newreg = reg, used[REGNO (reg)] = 1;
269 : else
270 : {
271 404038 : newreg = gen_reg_rtx (GET_MODE (reg));
272 404038 : REG_USERVAR_P (newreg) = REG_USERVAR_P (reg);
273 404038 : REG_POINTER (newreg) = REG_POINTER (reg);
274 404038 : REG_ATTRS (newreg) = REG_ATTRS (reg);
275 404038 : if (dump_file)
276 146 : fprintf (dump_file, "Web oldreg=%i newreg=%i\n", REGNO (reg),
277 : REGNO (newreg));
278 : }
279 :
280 1514373 : root->set_reg (newreg);
281 1514373 : return newreg;
282 : }
283 :
284 : /* Replace the reference by REG. */
285 :
286 : static void
287 5413842 : replace_ref (df_ref ref, rtx reg)
288 : {
289 5413842 : rtx oldreg = DF_REF_REAL_REG (ref);
290 5413842 : rtx *loc = DF_REF_REAL_LOC (ref);
291 5413842 : unsigned int uid = DF_REF_INSN_UID (ref);
292 :
293 5413842 : if (oldreg == reg)
294 : return;
295 1086110 : if (dump_file)
296 428 : fprintf (dump_file, "Updating insn %i (%i->%i)\n",
297 : uid, REGNO (oldreg), REGNO (reg));
298 1086110 : *loc = reg;
299 1086110 : df_insn_rescan (DF_REF_INSN (ref));
300 : }
301 :
302 :
303 : namespace {
304 :
305 : const pass_data pass_data_web =
306 : {
307 : RTL_PASS, /* type */
308 : "web", /* name */
309 : OPTGROUP_NONE, /* optinfo_flags */
310 : TV_WEB, /* tv_id */
311 : 0, /* properties_required */
312 : 0, /* properties_provided */
313 : 0, /* properties_destroyed */
314 : 0, /* todo_flags_start */
315 : TODO_df_finish, /* todo_flags_finish */
316 : };
317 :
318 : class pass_web : public rtl_opt_pass
319 : {
320 : public:
321 285722 : pass_web (gcc::context *ctxt)
322 571444 : : rtl_opt_pass (pass_data_web, ctxt)
323 : {}
324 :
325 : /* opt_pass methods: */
326 1471370 : bool gate (function *) final override { return (optimize > 0 && flag_web); }
327 : unsigned int execute (function *) final override;
328 :
329 : }; // class pass_web
330 :
331 : unsigned int
332 22385 : pass_web::execute (function *fun)
333 : {
334 22385 : web_entry *def_entry;
335 22385 : web_entry *use_entry;
336 22385 : unsigned int max = max_reg_num ();
337 22385 : unsigned int *used;
338 22385 : basic_block bb;
339 22385 : unsigned int uses_num = 0;
340 22385 : rtx_insn *insn;
341 :
342 22385 : df_set_flags (DF_NO_HARD_REGS + DF_EQ_NOTES);
343 22385 : df_set_flags (DF_RD_PRUNE_DEAD_DEFS);
344 22385 : df_chain_add_problem (DF_UD_CHAIN);
345 22385 : df_analyze ();
346 22385 : df_set_flags (DF_DEFER_INSN_RESCAN);
347 :
348 : /* Assign ids to the uses. */
349 596186 : FOR_ALL_BB_FN (bb, fun)
350 5845307 : FOR_BB_INSNS (bb, insn)
351 : {
352 5271506 : if (NONDEBUG_INSN_P (insn))
353 : {
354 3556789 : struct df_insn_info *insn_info = DF_INSN_INFO_GET (insn);
355 3556789 : df_ref use;
356 8407035 : FOR_EACH_INSN_INFO_USE (use, insn_info)
357 4850246 : if (DF_REF_REGNO (use) >= FIRST_PSEUDO_REGISTER)
358 3552069 : DF_REF_ID (use) = uses_num++;
359 3669585 : FOR_EACH_INSN_INFO_EQ_USE (use, insn_info)
360 112796 : if (DF_REF_REGNO (use) >= FIRST_PSEUDO_REGISTER)
361 46219 : DF_REF_ID (use) = uses_num++;
362 : }
363 : }
364 :
365 : /* Record the number of uses and defs at the beginning of the optimization. */
366 22385 : def_entry = XCNEWVEC (web_entry, DF_DEFS_TABLE_SIZE ());
367 22385 : used = XCNEWVEC (unsigned, max);
368 22385 : use_entry = XCNEWVEC (web_entry, uses_num);
369 :
370 : /* Produce the web. */
371 596186 : FOR_ALL_BB_FN (bb, fun)
372 5845307 : FOR_BB_INSNS (bb, insn)
373 5271506 : if (NONDEBUG_INSN_P (insn))
374 : {
375 3556789 : struct df_insn_info *insn_info = DF_INSN_INFO_GET (insn);
376 3556789 : df_ref use;
377 3556789 : union_match_dups (insn, def_entry, use_entry, unionfind_union);
378 8407035 : FOR_EACH_INSN_INFO_USE (use, insn_info)
379 4850246 : if (DF_REF_REGNO (use) >= FIRST_PSEUDO_REGISTER)
380 3552069 : union_defs (use, def_entry, used, use_entry, unionfind_union);
381 3669585 : FOR_EACH_INSN_INFO_EQ_USE (use, insn_info)
382 112796 : if (DF_REF_REGNO (use) >= FIRST_PSEUDO_REGISTER)
383 46219 : union_defs (use, def_entry, used, use_entry, unionfind_union);
384 : }
385 :
386 : /* Update the instruction stream, allocating new registers for split pseudos
387 : in progress. */
388 596186 : FOR_ALL_BB_FN (bb, fun)
389 5845307 : FOR_BB_INSNS (bb, insn)
390 5271506 : if (NONDEBUG_INSN_P (insn)
391 : /* Ignore naked clobber. For example, reg 134 in the second insn
392 : of the following sequence will not be replaced.
393 :
394 : (insn (clobber (reg:SI 134)))
395 :
396 : (insn (set (reg:SI 0 r0) (reg:SI 134)))
397 :
398 : Thus the later passes can optimize them away. */
399 5271506 : && GET_CODE (PATTERN (insn)) != CLOBBER)
400 : {
401 3555929 : struct df_insn_info *insn_info = DF_INSN_INFO_GET (insn);
402 3555929 : df_ref def, use;
403 8406031 : FOR_EACH_INSN_INFO_USE (use, insn_info)
404 4850102 : if (DF_REF_REGNO (use) >= FIRST_PSEUDO_REGISTER)
405 3552069 : replace_ref (use, entry_register (use_entry + DF_REF_ID (use),
406 : use, used));
407 3668725 : FOR_EACH_INSN_INFO_EQ_USE (use, insn_info)
408 112796 : if (DF_REF_REGNO (use) >= FIRST_PSEUDO_REGISTER)
409 46219 : replace_ref (use, entry_register (use_entry + DF_REF_ID (use),
410 : use, used));
411 19035448 : FOR_EACH_INSN_INFO_DEF (def, insn_info)
412 15479519 : if (DF_REF_REGNO (def) >= FIRST_PSEUDO_REGISTER)
413 1815554 : replace_ref (def, entry_register (def_entry + DF_REF_ID (def),
414 : def, used));
415 : }
416 :
417 22385 : free (def_entry);
418 22385 : free (use_entry);
419 22385 : free (used);
420 22385 : return 0;
421 : }
422 :
423 : } // anon namespace
424 :
425 : rtl_opt_pass *
426 285722 : make_pass_web (gcc::context *ctxt)
427 : {
428 285722 : return new pass_web (ctxt);
429 : }
|