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 46
#include "toplev.h"

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


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

Razya Ladelsky committed
60
struct web_entry *
R. Kelley Cook committed
61
unionfind_root (struct web_entry *element)
Jan Hubicka committed
62 63 64 65 66 67 68 69 70 71 72 73 74 75
{
  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
76
/* Union sets.
Razya Ladelsky committed
77 78
   Return true if FIRST and SECOND points to the same web entry structure and
   nothing is done.  Otherwise, return false.  */
Jan Hubicka committed
79

Razya Ladelsky committed
80
bool
R. Kelley Cook committed
81
unionfind_union (struct web_entry *first, struct web_entry *second)
Jan Hubicka committed
82 83 84 85
{
  first = unionfind_root (first);
  second = unionfind_root (second);
  if (first == second)
Razya Ladelsky committed
86
    return true;
Jan Hubicka committed
87
  second->pred = first;
Razya Ladelsky committed
88
  return false;
Jan Hubicka committed
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
/* 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));
    }
}

131
/* For each use, all possible defs reaching it must come in the same
Razya Ladelsky committed
132
   register, union them.
133 134 135 136 137 138
   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
139

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

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

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

  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
176 177
    }

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

  if (set
      && SET_SRC (set) == DF_REF_REG (use)
      && SET_SRC (set) == SET_DEST (set))
    {
184 185 186 187 188 189 190 191
      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
192
    }
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211

  /* 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
212 213
  while (link)
    {
Razya Ladelsky committed
214 215
      (*fun) (use_entry + DF_REF_ID (use),
	      def_entry + DF_REF_ID (link->ref));
Jan Hubicka committed
216 217 218
      link = link->next;
    }

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

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

230 231 232 233 234 235 236 237
      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
238 239 240
    }
}

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

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

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

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

257 258 259 260 261 262 263
  /* 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
264 265 266 267 268 269 270
    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);
271 272
      if (dump_file)
	fprintf (dump_file, "Web oldreg=%i newreg=%i\n", REGNO (reg),
Jan Hubicka committed
273 274 275 276 277 278 279 280 281 282
		 REGNO (newreg));
    }

  root->reg = newreg;
  return newreg;
}

/* Replace the reference by REG.  */

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

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

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

/* Main entry point.  */

307
static unsigned int
R. Kelley Cook committed
308
web_main (void)
Jan Hubicka committed
309 310 311
{
  struct web_entry *def_entry;
  struct web_entry *use_entry;
312
  unsigned int max = max_reg_num ();
313
  unsigned int *used;
314 315 316 317 318 319 320 321 322 323 324 325 326 327
  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);
328
      if (NONDEBUG_INSN_P (insn))
329
	{
330
	  df_ref *use_rec;
331 332
	  for (use_rec = DF_INSN_UID_USES (uid); *use_rec; use_rec++)
	    {
333
	      df_ref use = *use_rec;
334 335 336 337 338
	      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++)
	    {
339
	      df_ref use = *use_rec;
340 341 342 343 344
	      if (DF_REF_REGNO (use) >= FIRST_PSEUDO_REGISTER)
		DF_REF_ID (use) = uses_num++;
	    }
	}
    }
Jan Hubicka committed
345

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

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

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

Jan Hubicka committed
406 407 408
  free (def_entry);
  free (use_entry);
  free (used);
409
  return 0;
410
}
411

412
struct rtl_opt_pass pass_web =
413
{
414 415
 {
  RTL_PASS,
416 417
  "web",                                /* name */
  gate_handle_web,                      /* gate */
418
  web_main,		                /* execute */
419 420 421 422 423 424 425 426
  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
427
  TODO_df_finish | TODO_verify_rtl_sharing |
428 429
  TODO_dump_func                        /* todo_flags_finish */
 }
430 431
};