web.c 12.2 KB
Newer Older
Jan Hubicka committed
1
/* Web construction code for GNU compiler.
2
   Contributed by Jan Hubicka.
3
   Copyright (C) 2001, 2002, 2004, 2006, 2007, 2008, 2010
4
   Free Software Foundation, Inc.
Jan Hubicka committed
5 6 7 8 9

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
10
Software Foundation; either version 3, or (at your option) any later
Jan Hubicka committed
11 12 13 14 15 16 17 18
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
19 20
along with GCC; see the file COPYING3.  If not see
<http://www.gnu.org/licenses/>.  */
Jan Hubicka committed
21

22
/* Simple optimization pass that splits independent uses of each pseudo,
23
   increasing effectiveness of other optimizations.  The optimization can
24
   serve as an example of use for the dataflow module.
Jan Hubicka committed
25

26 27 28
   We don't split registers with REG_USERVAR set unless -fmessy-debugging
   is specified, because debugging information about such split variables
   is almost unusable.
Jan Hubicka committed
29 30

   TODO
31 32 33 34 35
    - We may use profile information and ignore infrequent use for the
      purpose of web unifying, inserting the compensation code later to
      implement full induction variable expansion for loops (currently
      we expand only if the induction variable is dead afterward, which
      is often the case).  */
Jan Hubicka committed
36 37 38 39 40

#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
41
#include "diagnostic-core.h"
Jan Hubicka committed
42 43 44 45

#include "rtl.h"
#include "hard-reg-set.h"
#include "flags.h"
46
#include "obstack.h"
Jan Hubicka committed
47 48 49 50
#include "basic-block.h"
#include "output.h"
#include "df.h"
#include "function.h"
51 52
#include "insn-config.h"
#include "recog.h"
53 54
#include "timevar.h"
#include "tree-pass.h"
Jan Hubicka committed
55 56


57
/* Find the root of unionfind tree (the representative of set).  */
Jan Hubicka committed
58

Razya Ladelsky committed
59
struct web_entry *
R. Kelley Cook committed
60
unionfind_root (struct web_entry *element)
Jan Hubicka committed
61 62 63 64 65 66 67 68 69 70 71 72 73 74
{
  struct web_entry *element1 = element, *element2;

  while (element->pred)
    element = element->pred;
  while (element1->pred)
    {
      element2 = element1->pred;
      element1->pred = element;
      element1 = element2;
    }
  return element;
}

H.J. Lu committed
75
/* Union sets.
Razya Ladelsky committed
76 77
   Return true if FIRST and SECOND points to the same web entry structure and
   nothing is done.  Otherwise, return false.  */
Jan Hubicka committed
78

Razya Ladelsky committed
79
bool
R. Kelley Cook committed
80
unionfind_union (struct web_entry *first, struct web_entry *second)
Jan Hubicka committed
81 82 83 84
{
  first = unionfind_root (first);
  second = unionfind_root (second);
  if (first == second)
Razya Ladelsky committed
85
    return true;
Jan Hubicka committed
86
  second->pred = first;
Razya Ladelsky committed
87
  return false;
Jan Hubicka committed
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
/* For INSN, union all defs and uses that are linked by match_dup.
   FUN is the function that does the union.  */

static void
union_match_dups (rtx insn, struct web_entry *def_entry,
		  struct web_entry *use_entry,
		  bool (*fun) (struct web_entry *, struct web_entry *))
{
  struct df_insn_info *insn_info = DF_INSN_INFO_GET (insn);
  df_ref *use_link = DF_INSN_INFO_USES (insn_info);
  df_ref *def_link = DF_INSN_INFO_DEFS (insn_info);
  int i;

  extract_insn (insn);

  for (i = 0; i < recog_data.n_dups; i++)
    {
      int op = recog_data.dup_num[i];
      enum op_type type = recog_data.operand_type[op];
      df_ref *ref, *dupref;
      struct web_entry *entry;

      for (dupref = use_link; *dupref; dupref++)
	if (DF_REF_LOC (*dupref) == recog_data.dup_loc[i])
	  break;

      if (*dupref == NULL
	  || DF_REF_REGNO (*dupref) < FIRST_PSEUDO_REGISTER)
	continue;

      ref = type == OP_IN ? use_link : def_link;
      entry = type == OP_IN ? use_entry : def_entry;
      for (; *ref; ref++)
	if (DF_REF_LOC (*ref) == recog_data.operand_loc[op])
	  break;

      (*fun) (use_entry + DF_REF_ID (*dupref), entry + DF_REF_ID (*ref));
    }
}

130
/* For each use, all possible defs reaching it must come in the same
Razya Ladelsky committed
131
   register, union them.
132 133 134 135 136 137
   FUN is the function that does the union.

   In USED, we keep the DF_REF_ID of the first uninitialized uses of a
   register, so that all uninitialized uses of the register can be
   combined into a single web.  We actually offset it by 2, because
   the values 0 and 1 are reserved for use by entry_register.  */
Jan Hubicka committed
138

Razya Ladelsky committed
139
void
140
union_defs (df_ref use, struct web_entry *def_entry,
141
	    unsigned int *used, struct web_entry *use_entry,
Razya Ladelsky committed
142
 	    bool (*fun) (struct web_entry *, struct web_entry *))
Jan Hubicka committed
143
{
144
  struct df_insn_info *insn_info = DF_REF_INSN_INFO (use);
Jan Hubicka committed
145
  struct df_link *link = DF_REF_CHAIN (use);
146 147
  df_ref *eq_use_link;
  df_ref *def_link;
Razya Ladelsky committed
148 149
  rtx set;

150
  if (insn_info)
Razya Ladelsky committed
151
    {
152 153 154
      rtx insn = insn_info->insn;
      eq_use_link = DF_INSN_INFO_EQ_USES (insn_info);
      def_link = DF_INSN_INFO_DEFS (insn_info);
Razya Ladelsky committed
155 156 157 158
      set = single_set (insn);
    }
  else
    {
159
      /* An artificial use.  It links up with nothing.  */
160
      eq_use_link = NULL;
Razya Ladelsky committed
161 162 163
      def_link = NULL;
      set = NULL;
    }
Jan Hubicka committed
164

165
  /* Union all occurrences of the same register in reg notes.  */
166 167 168 169 170 171 172 173 174

  if (eq_use_link)
    while (*eq_use_link)
      {
	if (use != *eq_use_link
	    && DF_REF_REAL_REG (use) == DF_REF_REAL_REG (*eq_use_link))
	  (*fun) (use_entry + DF_REF_ID (use),
		  use_entry + DF_REF_ID (*eq_use_link));
	eq_use_link++;
Jan Hubicka committed
175 176
    }

Steven Bosscher committed
177
  /* Recognize trivial noop moves and attempt to keep them as noop.  */
Jan Hubicka committed
178 179 180 181 182

  if (set
      && SET_SRC (set) == DF_REF_REG (use)
      && SET_SRC (set) == SET_DEST (set))
    {
183 184 185 186 187 188 189 190
      if (def_link)
	while (*def_link)
	  {
	    if (DF_REF_REAL_REG (use) == DF_REF_REAL_REG (*def_link))
	      (*fun) (use_entry + DF_REF_ID (use),
		      def_entry + DF_REF_ID (*def_link));
	    def_link++;
	  }
Jan Hubicka committed
191
    }
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210

  /* UD chains of uninitialized REGs are empty.  Keeping all uses of
     the same uninitialized REG in a single web is not necessary for
     correctness, since the uses are undefined, but it's wasteful to
     allocate one register or slot for each reference.  Furthermore,
     creating new pseudos for uninitialized references in debug insns
     (see PR 42631) causes -fcompare-debug failures.  We record the
     number of the first uninitialized reference we found, and merge
     with it any other uninitialized references to the same
     register.  */
  if (!link)
    {
      int regno = REGNO (DF_REF_REAL_REG (use));
      if (used[regno])
	(*fun) (use_entry + DF_REF_ID (use), use_entry + used[regno] - 2);
      else
	used[regno] = DF_REF_ID (use) + 2;
    }

Jan Hubicka committed
211 212
  while (link)
    {
Razya Ladelsky committed
213 214
      (*fun) (use_entry + DF_REF_ID (use),
	      def_entry + DF_REF_ID (link->ref));
Jan Hubicka committed
215 216 217
      link = link->next;
    }

218
  /* A READ_WRITE use requires the corresponding def to be in the same
Jan Hubicka committed
219
     register.  Find it and union.  */
220
  if (DF_REF_FLAGS (use) & DF_REF_READ_WRITE)
Jan Hubicka committed
221
    {
222
      df_ref *link;
Razya Ladelsky committed
223

224 225
      if (insn_info)
	link = DF_INSN_INFO_DEFS (insn_info);
Razya Ladelsky committed
226 227
      else
	link = NULL;
Jan Hubicka committed
228

229 230 231 232 233 234 235 236
      if (link)
	while (*link)
	  {
	    if (DF_REF_REAL_REG (*link) == DF_REF_REAL_REG (use))
	      (*fun) (use_entry + DF_REF_ID (use),
		      def_entry + DF_REF_ID (*link));
	    link++;
	  }
Jan Hubicka committed
237 238 239
    }
}

240
/* Find the corresponding register for the given entry.  */
Jan Hubicka committed
241 242

static rtx
243
entry_register (struct web_entry *entry, df_ref ref, unsigned int *used)
Jan Hubicka committed
244 245 246 247
{
  struct web_entry *root;
  rtx reg, newreg;

248
  /* Find the corresponding web and see if it has been visited.  */
Jan Hubicka committed
249 250 251 252
  root = unionfind_root (entry);
  if (root->reg)
    return root->reg;

253
  /* We are seeing this web for the first time, do the assignment.  */
Jan Hubicka committed
254 255
  reg = DF_REF_REAL_REG (ref);

256 257 258 259 260 261 262
  /* In case the original register is already assigned, generate new
     one.  Since we use USED to merge uninitialized refs into a single
     web, we might found an element to be nonzero without our having
     used it.  Test for 1, because union_defs saves it for our use,
     and there won't be any use for the other values when we get to
     this point.  */
  if (used[REGNO (reg)] != 1)
Jan Hubicka committed
263 264 265 266 267 268 269
    newreg = reg, used[REGNO (reg)] = 1;
  else
    {
      newreg = gen_reg_rtx (GET_MODE (reg));
      REG_USERVAR_P (newreg) = REG_USERVAR_P (reg);
      REG_POINTER (newreg) = REG_POINTER (reg);
      REG_ATTRS (newreg) = REG_ATTRS (reg);
270 271
      if (dump_file)
	fprintf (dump_file, "Web oldreg=%i newreg=%i\n", REGNO (reg),
Jan Hubicka committed
272 273 274 275 276 277 278 279 280 281
		 REGNO (newreg));
    }

  root->reg = newreg;
  return newreg;
}

/* Replace the reference by REG.  */

static void
282
replace_ref (df_ref ref, rtx reg)
Jan Hubicka committed
283 284 285
{
  rtx oldreg = DF_REF_REAL_REG (ref);
  rtx *loc = DF_REF_REAL_LOC (ref);
286
  unsigned int uid = DF_REF_INSN_UID (ref);
Jan Hubicka committed
287 288 289

  if (oldreg == reg)
    return;
290 291
  if (dump_file)
    fprintf (dump_file, "Updating insn %i (%i->%i)\n",
H.J. Lu committed
292
	     uid, REGNO (oldreg), REGNO (reg));
Jan Hubicka committed
293
  *loc = reg;
294 295 296 297 298 299 300 301
  df_insn_rescan (DF_REF_INSN (ref));
}


static bool
gate_handle_web (void)
{
  return (optimize > 0 && flag_web);
Jan Hubicka committed
302 303 304 305
}

/* Main entry point.  */

306
static unsigned int
R. Kelley Cook committed
307
web_main (void)
Jan Hubicka committed
308 309 310
{
  struct web_entry *def_entry;
  struct web_entry *use_entry;
311
  unsigned int max = max_reg_num ();
312
  unsigned int *used;
313 314 315 316 317 318 319 320 321 322 323 324 325 326
  basic_block bb;
  unsigned int uses_num = 0;
  rtx insn;

  df_set_flags (DF_NO_HARD_REGS + DF_EQ_NOTES);
  df_chain_add_problem (DF_UD_CHAIN);
  df_analyze ();
  df_set_flags (DF_DEFER_INSN_RESCAN);

  /* Assign ids to the uses.  */
  FOR_ALL_BB (bb)
    FOR_BB_INSNS (bb, insn)
    {
      unsigned int uid = INSN_UID (insn);
327
      if (NONDEBUG_INSN_P (insn))
328
	{
329
	  df_ref *use_rec;
330 331
	  for (use_rec = DF_INSN_UID_USES (uid); *use_rec; use_rec++)
	    {
332
	      df_ref use = *use_rec;
333 334 335 336 337
	      if (DF_REF_REGNO (use) >= FIRST_PSEUDO_REGISTER)
		DF_REF_ID (use) = uses_num++;
	    }
	  for (use_rec = DF_INSN_UID_EQ_USES (uid); *use_rec; use_rec++)
	    {
338
	      df_ref use = *use_rec;
339 340 341 342 343
	      if (DF_REF_REGNO (use) >= FIRST_PSEUDO_REGISTER)
		DF_REF_ID (use) = uses_num++;
	    }
	}
    }
Jan Hubicka committed
344

345 346
  /* Record the number of uses and defs at the beginning of the optimization.  */
  def_entry = XCNEWVEC (struct web_entry, DF_DEFS_TABLE_SIZE());
347
  used = XCNEWVEC (unsigned, max);
348
  use_entry = XCNEWVEC (struct web_entry, uses_num);
Jan Hubicka committed
349 350

  /* Produce the web.  */
351 352 353 354
  FOR_ALL_BB (bb)
    FOR_BB_INSNS (bb, insn)
    {
      unsigned int uid = INSN_UID (insn);
355
      if (NONDEBUG_INSN_P (insn))
356
	{
357
	  df_ref *use_rec;
358
	  union_match_dups (insn, def_entry, use_entry, unionfind_union);
359 360
	  for (use_rec = DF_INSN_UID_USES (uid); *use_rec; use_rec++)
	    {
361
	      df_ref use = *use_rec;
362
	      if (DF_REF_REGNO (use) >= FIRST_PSEUDO_REGISTER)
363
		union_defs (use, def_entry, used, use_entry, unionfind_union);
364 365 366
	    }
	  for (use_rec = DF_INSN_UID_EQ_USES (uid); *use_rec; use_rec++)
	    {
367
	      df_ref use = *use_rec;
368
	      if (DF_REF_REGNO (use) >= FIRST_PSEUDO_REGISTER)
369
		union_defs (use, def_entry, used, use_entry, unionfind_union);
370 371 372
	    }
	}
    }
Jan Hubicka committed
373 374 375

  /* Update the instruction stream, allocating new registers for split pseudos
     in progress.  */
376 377 378 379
  FOR_ALL_BB (bb)
    FOR_BB_INSNS (bb, insn)
    {
      unsigned int uid = INSN_UID (insn);
380
      if (NONDEBUG_INSN_P (insn))
381
	{
382 383
	  df_ref *use_rec;
	  df_ref *def_rec;
384 385
	  for (use_rec = DF_INSN_UID_USES (uid); *use_rec; use_rec++)
	    {
386
	      df_ref use = *use_rec;
387 388 389 390 391
	      if (DF_REF_REGNO (use) >= FIRST_PSEUDO_REGISTER)
		replace_ref (use, entry_register (use_entry + DF_REF_ID (use), use, used));
	    }
	  for (use_rec = DF_INSN_UID_EQ_USES (uid); *use_rec; use_rec++)
	    {
392
	      df_ref use = *use_rec;
393 394 395 396 397
	      if (DF_REF_REGNO (use) >= FIRST_PSEUDO_REGISTER)
		replace_ref (use, entry_register (use_entry + DF_REF_ID (use), use, used));
	    }
	  for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++)
	    {
398
	      df_ref def = *def_rec;
399 400 401 402 403 404
	      if (DF_REF_REGNO (def) >= FIRST_PSEUDO_REGISTER)
		replace_ref (def, entry_register (def_entry + DF_REF_ID (def), def, used));
	    }
	}
    }

Jan Hubicka committed
405 406 407
  free (def_entry);
  free (use_entry);
  free (used);
408
  return 0;
409
}
410

411
struct rtl_opt_pass pass_web =
412
{
413 414
 {
  RTL_PASS,
415 416
  "web",                                /* name */
  gate_handle_web,                      /* gate */
417
  web_main,		                /* execute */
418 419 420 421 422 423 424 425
  NULL,                                 /* sub */
  NULL,                                 /* next */
  0,                                    /* static_pass_number */
  TV_WEB,                               /* tv_id */
  0,                                    /* properties_required */
  0,                                    /* properties_provided */
  0,                                    /* properties_destroyed */
  0,                                    /* todo_flags_start */
H.J. Lu committed
426
  TODO_df_finish | TODO_verify_rtl_sharing |
427 428
  TODO_dump_func                        /* todo_flags_finish */
 }
429 430
};