/* Graph coloring register allocator Copyright (C) 2001, 2002 Free Software Foundation, Inc. Contributed by Michael Matz and Daniel Berlin . 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 2, 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 COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include "system.h" #include "coretypes.h" #include "tm.h" #include "rtl.h" #include "insn-config.h" #include "recog.h" #include "function.h" #include "hard-reg-set.h" #include "basic-block.h" #include "df.h" #include "output.h" #include "ra.h" #include "tm_p.h" /* This file contains various dumping and debug functions for the graph coloring register allocator. */ static void ra_print_rtx_1op PARAMS ((FILE *, rtx)); static void ra_print_rtx_2op PARAMS ((FILE *, rtx)); static void ra_print_rtx_3op PARAMS ((FILE *, rtx)); static void ra_print_rtx_object PARAMS ((FILE *, rtx)); /* The hardregs as names, for debugging. */ static const char *const reg_class_names[] = REG_CLASS_NAMES; /* Print a message to the dump file, if debug_new_regalloc and LEVEL have any bits in common. */ void ra_debug_msg VPARAMS ((unsigned int level, const char *format, ...)) { VA_OPEN (ap, format); VA_FIXEDARG (ap, unsigned int, level); VA_FIXEDARG (ap, const char *, format); if ((debug_new_regalloc & level) != 0 && rtl_dump_file != NULL) vfprintf (rtl_dump_file, format, ap); VA_CLOSE (ap); } /* The following ra_print_xxx() functions print RTL expressions in concise infix form. If the mode can be seen from context it's left out. Most operators are represented by their graphical characters, e.g. LE as "<=". Unknown constructs are currently printed with print_inline_rtx(), which disrupts the nice layout. Currently only the inline asm things are written this way. */ /* Print rtx X, which is a one operand rtx (op:mode (Y)), as "op(Y)" to FILE. */ static void ra_print_rtx_1op (file, x) FILE *file; rtx x; { enum rtx_code code = GET_CODE (x); rtx op0 = XEXP (x, 0); switch (code) { case NEG: case NOT: fputs ((code == NEG) ? "-(" : "~(", file); ra_print_rtx (file, op0, 0); fputs (")", file); break; case HIGH: fputs ("hi(", file); ra_print_rtx (file, op0, 0); fputs (")", file); break; default: fprintf (file, "%s", GET_RTX_NAME (code)); if (GET_MODE (x) != VOIDmode) fprintf (file, ":%s(", GET_MODE_NAME (GET_MODE (x))); else fputs ("(", file); ra_print_rtx (file, op0, 0); fputs (")", file); break; } } /* Print rtx X, which is a two operand rtx (op:mode (Y) (Z)) as "(Y op Z)", if the operand is know, or as "op(Y, Z)", if not, to FILE. */ static void ra_print_rtx_2op (file, x) FILE *file; rtx x; { int infix = 1; const char *opname = "shitop"; enum rtx_code code = GET_CODE (x); rtx op0 = XEXP (x, 0); rtx op1 = XEXP (x, 1); switch (code) { /* class '2' */ case COMPARE: opname = "?"; break; case MINUS: opname = "-"; break; case DIV: opname = "/"; break; case UDIV: opname = "u/"; break; case MOD: opname = "%"; break; case UMOD: opname = "u%"; break; case ASHIFT: opname = "<<"; break; case ASHIFTRT: opname = "a>>"; break; case LSHIFTRT: opname = "l>>"; break; /* class 'c' */ case PLUS: opname = "+"; break; case MULT: opname = "*"; break; case AND: opname = "&"; break; case IOR: opname = "|"; break; case XOR: opname = "^"; break; /* class '<' */ case NE: opname = "!="; break; case EQ: opname = "=="; break; case GE: opname = "s>="; break; case GT: opname = "s>"; break; case LE: opname = "s<="; break; case LT: opname = "s<"; break; case GEU: opname = "u>="; break; case GTU: opname = "u>"; break; case LEU: opname = "u<="; break; case LTU: opname = "u<"; break; default: infix = 0; opname = GET_RTX_NAME (code); break; } if (infix) { fputs ("(", file); ra_print_rtx (file, op0, 0); fprintf (file, " %s ", opname); ra_print_rtx (file, op1, 0); fputs (")", file); } else { fprintf (file, "%s(", opname); ra_print_rtx (file, op0, 0); fputs (", ", file); ra_print_rtx (file, op1, 0); fputs (")", file); } } /* Print rtx X, which a three operand rtx to FILE. I.e. X is either an IF_THEN_ELSE, or a bitmap operation. */ static void ra_print_rtx_3op (file, x) FILE *file; rtx x; { enum rtx_code code = GET_CODE (x); rtx op0 = XEXP (x, 0); rtx op1 = XEXP (x, 1); rtx op2 = XEXP (x, 2); if (code == IF_THEN_ELSE) { ra_print_rtx (file, op0, 0); fputs (" ? ", file); ra_print_rtx (file, op1, 0); fputs (" : ", file); ra_print_rtx (file, op2, 0); } else { /* Bitmap-operation */ fprintf (file, "%s:%s(", GET_RTX_NAME (code), GET_MODE_NAME (GET_MODE (x))); ra_print_rtx (file, op0, 0); fputs (", ", file); ra_print_rtx (file, op1, 0); fputs (", ", file); ra_print_rtx (file, op2, 0); fputs (")", file); } } /* Print rtx X, which represents an object (class 'o' or some constructs of class 'x' (e.g. subreg)), to FILE. (reg XX) rtl is represented as "pXX", of XX was a pseudo, as "name" it name is the nonnull hardreg name, or as "hXX", if XX is a hardreg, whose name is NULL, or empty. */ static void ra_print_rtx_object (file, x) FILE *file; rtx x; { enum rtx_code code = GET_CODE (x); enum machine_mode mode = GET_MODE (x); switch (code) { case CONST_INT: fprintf (file, HOST_WIDE_INT_PRINT_DEC, XWINT (x, 0)); break; case CONST_DOUBLE: { int i, num = 0; const char *fmt = GET_RTX_FORMAT (code); fputs ("dbl(", file); for (i = 0; i < GET_RTX_LENGTH (code); i++) { if (num) fputs (", ", file); if (fmt[i] == 'e' && XEXP (x, i)) /* The MEM or other stuff */ { ra_print_rtx (file, XEXP (x, i), 0); num++; } else if (fmt[i] == 'w') { fprintf (file, HOST_WIDE_INT_PRINT_HEX, XWINT (x, i)); num++; } } break; } case CONST_STRING: fprintf (file, "\"%s\"", XSTR (x, 0)); break; case CONST: fputs ("const(", file); ra_print_rtx (file, XEXP (x, 0), 0); fputs (")", file); break; case PC: fputs ("pc", file); break; case REG: { int regno = REGNO (x); if (regno < FIRST_PSEUDO_REGISTER) { int i, nregs = HARD_REGNO_NREGS (regno, mode); if (nregs > 1) fputs ("[", file); for (i = 0; i < nregs; i++) { if (i) fputs (", ", file); if (reg_names[regno+i] && *reg_names[regno + i]) fprintf (file, "%s", reg_names[regno + i]); else fprintf (file, "h%d", regno + i); } if (nregs > 1) fputs ("]", file); } else fprintf (file, "p%d", regno); break; } case SUBREG: { rtx sub = SUBREG_REG (x); int ofs = SUBREG_BYTE (x); if (GET_CODE (sub) == REG && REGNO (sub) < FIRST_PSEUDO_REGISTER) { int regno = REGNO (sub); int i, nregs = HARD_REGNO_NREGS (regno, mode); regno += subreg_regno_offset (regno, GET_MODE (sub), ofs, mode); if (nregs > 1) fputs ("[", file); for (i = 0; i < nregs; i++) { if (i) fputs (", ", file); if (reg_names[regno+i]) fprintf (file, "%s", reg_names[regno + i]); else fprintf (file, "h%d", regno + i); } if (nregs > 1) fputs ("]", file); } else { ra_print_rtx (file, sub, 0); fprintf (file, ":[%s+%d]", GET_MODE_NAME (mode), ofs); } break; } case SCRATCH: fputs ("scratch", file); break; case CONCAT: ra_print_rtx_2op (file, x); break; case HIGH: ra_print_rtx_1op (file, x); break; case LO_SUM: fputs ("(", file); ra_print_rtx (file, XEXP (x, 0), 0); fputs (" + lo(", file); ra_print_rtx (file, XEXP (x, 1), 0); fputs ("))", file); break; case MEM: fputs ("[", file); ra_print_rtx (file, XEXP (x, 0), 0); fprintf (file, "]:%s", GET_MODE_NAME (GET_MODE (x))); /* XXX print alias set too ?? */ break; case LABEL_REF: { rtx sub = XEXP (x, 0); if (GET_CODE (sub) == NOTE && NOTE_LINE_NUMBER (sub) == NOTE_INSN_DELETED_LABEL) fprintf (file, "(deleted uid=%d)", INSN_UID (sub)); else if (GET_CODE (sub) == CODE_LABEL) fprintf (file, "L%d", CODE_LABEL_NUMBER (sub)); else fprintf (file, "(nonlabel uid=%d)", INSN_UID (sub)); } break; case SYMBOL_REF: fprintf (file, "sym(\"%s\")", XSTR (x, 0)); break; case CC0: fputs ("cc0", file); break; default: print_inline_rtx (file, x, 0); break; } } /* Print a general rtx X to FILE in nice infix form. If WITH_PN is set, and X is one of the toplevel constructs (insns, notes, labels or barriers), then print also the UIDs of the preceding and following insn. */ void ra_print_rtx (file, x, with_pn) FILE *file; rtx x; int with_pn; { enum rtx_code code; char class; int unhandled = 0; if (!x) return; code = GET_CODE (x); class = GET_RTX_CLASS (code); /* First handle the insn like constructs. */ if (INSN_P (x) || code == NOTE || code == CODE_LABEL || code == BARRIER) { if (INSN_P (x)) fputs (" ", file); /* Non-insns are prefixed by a ';'. */ if (code == BARRIER) fputs ("; ", file); else if (code == NOTE) /* But notes are indented very far right. */ fprintf (file, "\t\t\t\t\t; "); else if (code == CODE_LABEL) /* And labels have their Lxx name first, before the actual UID. */ { fprintf (file, "L%d:\t; ", CODE_LABEL_NUMBER (x)); if (LABEL_NAME (x)) fprintf (file, "(%s) ", LABEL_NAME (x)); switch (LABEL_KIND (x)) { case LABEL_NORMAL: break; case LABEL_STATIC_ENTRY: fputs (" (entry)", file); break; case LABEL_GLOBAL_ENTRY: fputs (" (global entry)", file); break; case LABEL_WEAK_ENTRY: fputs (" (weak entry)", file); break; default: abort(); } fprintf (file, " [%d uses] uid=(", LABEL_NUSES (x)); } fprintf (file, "%d", INSN_UID (x)); if (with_pn) fprintf (file, " %d %d", PREV_INSN (x) ? INSN_UID (PREV_INSN (x)) : 0, NEXT_INSN (x) ? INSN_UID (NEXT_INSN (x)) : 0); if (code == BARRIER) fputs (" -------- barrier ---------", file); else if (code == CODE_LABEL) fputs (")", file); else if (code == NOTE) { int ln = NOTE_LINE_NUMBER (x); if (ln >= (int) NOTE_INSN_BIAS && ln < (int) NOTE_INSN_MAX) fprintf (file, " %s", GET_NOTE_INSN_NAME (ln)); else { fprintf (file, " line %d", ln); if (NOTE_SOURCE_FILE (x)) fprintf (file, ":%s", NOTE_SOURCE_FILE (x)); } } else { fprintf (file, "\t"); ra_print_rtx (file, PATTERN (x), 0); } return; } switch (code) { /* Top-level stuff. */ case PARALLEL: { int j; for (j = 0; j < XVECLEN (x, 0); j++) { if (j) fputs ("\t;; ", file); ra_print_rtx (file, XVECEXP (x, 0, j), 0); } break; } case UNSPEC: case UNSPEC_VOLATILE: { int j; fprintf (file, "unspec%s(%d", (code == UNSPEC) ? "" : "_vol", XINT (x, 1)); for (j = 0; j < XVECLEN (x, 0); j++) { fputs (", ", file); ra_print_rtx (file, XVECEXP (x, 0, j), 0); } fputs (")", file); break; } case SET: if (GET_CODE (SET_DEST (x)) == PC) { if (GET_CODE (SET_SRC (x)) == IF_THEN_ELSE && GET_CODE (XEXP (SET_SRC(x), 2)) == PC) { fputs ("if ", file); ra_print_rtx (file, XEXP (SET_SRC (x), 0), 0); fputs (" jump ", file); ra_print_rtx (file, XEXP (SET_SRC (x), 1), 0); } else { fputs ("jump ", file); ra_print_rtx (file, SET_SRC (x), 0); } } else { ra_print_rtx (file, SET_DEST (x), 0); fputs (" <= ", file); ra_print_rtx (file, SET_SRC (x), 0); } break; case USE: fputs ("use <= ", file); ra_print_rtx (file, XEXP (x, 0), 0); break; case CLOBBER: ra_print_rtx (file, XEXP (x, 0), 0); fputs (" <= clobber", file); break; case CALL: fputs ("call ", file); ra_print_rtx (file, XEXP (x, 0), 0); /* Address */ fputs (" numargs=", file); ra_print_rtx (file, XEXP (x, 1), 0); /* Num arguments */ break; case RETURN: fputs ("return", file); break; case TRAP_IF: fputs ("if (", file); ra_print_rtx (file, XEXP (x, 0), 0); fputs (") trap ", file); ra_print_rtx (file, XEXP (x, 1), 0); break; case RESX: fprintf (file, "resx from region %d", XINT (x, 0)); break; /* Different things of class 'x' */ case SUBREG: ra_print_rtx_object (file, x); break; case STRICT_LOW_PART: fputs ("low(", file); ra_print_rtx (file, XEXP (x, 0), 0); fputs (")", file); break; default: unhandled = 1; break; } if (!unhandled) return; if (class == '1') ra_print_rtx_1op (file, x); else if (class == '2' || class == 'c' || class == '<') ra_print_rtx_2op (file, x); else if (class == '3' || class == 'b') ra_print_rtx_3op (file, x); else if (class == 'o') ra_print_rtx_object (file, x); else print_inline_rtx (file, x, 0); } /* This only calls ra_print_rtx(), but emits a final newline. */ void ra_print_rtx_top (file, x, with_pn) FILE *file; rtx x; int with_pn; { ra_print_rtx (file, x, with_pn); fprintf (file, "\n"); } /* Callable from gdb. This prints rtx X onto stderr. */ void ra_debug_rtx (x) rtx x; { ra_print_rtx_top (stderr, x, 1); } /* This prints the content of basic block with index BBI. The first and last insn are emitted with UIDs of prev and next insns. */ void ra_debug_bbi (bbi) int bbi; { basic_block bb = BASIC_BLOCK (bbi); rtx insn; for (insn = bb->head; insn; insn = NEXT_INSN (insn)) { ra_print_rtx_top (stderr, insn, (insn == bb->head || insn == bb->end)); fprintf (stderr, "\n"); if (insn == bb->end) break; } } /* Beginning from INSN, emit NUM insns (if NUM is non-negative) or emit a window of NUM insns around INSN, to stderr. */ void ra_debug_insns (insn, num) rtx insn; int num; { int i, count = (num == 0 ? 1 : num < 0 ? -num : num); if (num < 0) for (i = count / 2; i > 0 && PREV_INSN (insn); i--) insn = PREV_INSN (insn); for (i = count; i > 0 && insn; insn = NEXT_INSN (insn), i--) { if (GET_CODE (insn) == CODE_LABEL) fprintf (stderr, "\n"); ra_print_rtx_top (stderr, insn, (i == count || i == 1)); } } /* Beginning with INSN, emit the whole insn chain into FILE. This also outputs comments when basic blocks start or end and omits some notes, if flag_ra_dump_notes is zero. */ void ra_print_rtl_with_bb (file, insn) FILE *file; rtx insn; { basic_block last_bb, bb; unsigned int num = 0; if (!insn) fputs ("nil", file); last_bb = NULL; for (; insn; insn = NEXT_INSN (insn)) { if (GET_CODE (insn) == BARRIER) bb = NULL; else bb = BLOCK_FOR_INSN (insn); if (bb != last_bb) { if (last_bb) fprintf (file, ";; End of basic block %d\n", last_bb->index); if (bb) fprintf (file, ";; Begin of basic block %d\n", bb->index); last_bb = bb; } if (GET_CODE (insn) == CODE_LABEL) fputc ('\n', file); if (GET_CODE (insn) == NOTE) { /* Ignore basic block and maybe other notes not referencing deleted things. */ if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_BASIC_BLOCK && (flag_ra_dump_notes || NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED || NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED_LABEL)) { ra_print_rtx_top (file, insn, (num == 0 || !NEXT_INSN (insn))); num++; } } else { ra_print_rtx_top (file, insn, (num == 0 || !NEXT_INSN (insn))); num++; } } } /* Count how many insns were seen how often, while building the interference graph, and prints the findings. */ void dump_number_seen () { #define N 17 int num[N]; int i; for (i = 0; i < N; i++) num[i] = 0; for (i = 0; i < get_max_uid (); i++) if (number_seen[i] < N - 1) num[number_seen[i]]++; else num[N - 1]++; for (i = 0; i < N - 1; i++) if (num[i]) ra_debug_msg (DUMP_PROCESS, "%d insns seen %d times\n", num[i], i); if (num[N - 1]) ra_debug_msg (DUMP_PROCESS, "%d insns seen %d and more times\n", num[i], N - 1); ra_debug_msg (DUMP_PROCESS, "from overall %d insns\n", get_max_uid ()); #undef N } /* Dump the interference graph, the move list and the webs. */ void dump_igraph (df) struct df *df ATTRIBUTE_UNUSED; { struct move_list *ml; unsigned int def1, def2; int num = 0; int num2; unsigned int i; if (!rtl_dump_file || (debug_new_regalloc & (DUMP_IGRAPH | DUMP_WEBS)) == 0) return; ra_debug_msg (DUMP_IGRAPH, "conflicts:\n "); for (def1 = 0; def1 < num_webs; def1++) { int num1 = num; num2 = 0; for (def2 = 0; def2 < num_webs; def2++) if (def1 != def2 && TEST_BIT (igraph, igraph_index (def1, def2))) { if (num1 == num) { if (SUBWEB_P (ID2WEB (def1))) ra_debug_msg (DUMP_IGRAPH, "%d (SUBREG %d, %d) with ", def1, ID2WEB (def1)->regno, SUBREG_BYTE (ID2WEB (def1)->orig_x)); else ra_debug_msg (DUMP_IGRAPH, "%d (REG %d) with ", def1, ID2WEB (def1)->regno); } if ((num2 % 9) == 8) ra_debug_msg (DUMP_IGRAPH, "\n "); num++; num2++; if (SUBWEB_P (ID2WEB (def2))) ra_debug_msg (DUMP_IGRAPH, "%d(%d,%d) ", def2, ID2WEB (def2)->regno, SUBREG_BYTE (ID2WEB (def2)->orig_x)); else ra_debug_msg (DUMP_IGRAPH, "%d(%d) ", def2, ID2WEB (def2)->regno); } if (num1 != num) ra_debug_msg (DUMP_IGRAPH, "\n "); } ra_debug_msg (DUMP_IGRAPH, "\n"); for (ml = wl_moves; ml; ml = ml->next) if (ml->move) { ra_debug_msg (DUMP_IGRAPH, "move: insn %d: Web %d <-- Web %d\n", INSN_UID (ml->move->insn), ml->move->target_web->id, ml->move->source_web->id); } ra_debug_msg (DUMP_WEBS, "\nWebs:\n"); for (i = 0; i < num_webs; i++) { struct web *web = ID2WEB (i); ra_debug_msg (DUMP_WEBS, " %4d : regno %3d", i, web->regno); if (SUBWEB_P (web)) { ra_debug_msg (DUMP_WEBS, " sub %d", SUBREG_BYTE (web->orig_x)); ra_debug_msg (DUMP_WEBS, " par %d", find_web_for_subweb (web)->id); } ra_debug_msg (DUMP_WEBS, " +%d (span %d, cost " HOST_WIDE_INT_PRINT_DEC ") (%s)", web->add_hardregs, web->span_deaths, web->spill_cost, reg_class_names[web->regclass]); if (web->spill_temp == 1) ra_debug_msg (DUMP_WEBS, " (spilltemp)"); else if (web->spill_temp == 2) ra_debug_msg (DUMP_WEBS, " (spilltem2)"); else if (web->spill_temp == 3) ra_debug_msg (DUMP_WEBS, " (short)"); if (web->type == PRECOLORED) ra_debug_msg (DUMP_WEBS, " (precolored, color=%d)", web->color); else if (find_web_for_subweb (web)->num_uses == 0) ra_debug_msg (DUMP_WEBS, " dead"); if (web->crosses_call) ra_debug_msg (DUMP_WEBS, " xcall"); if (web->regno >= max_normal_pseudo) ra_debug_msg (DUMP_WEBS, " stack"); ra_debug_msg (DUMP_WEBS, "\n"); } } /* Dump the interference graph and webs in a format easily parsable by programs. Used to emit real world interference graph to my custom graph colorizer. */ void dump_igraph_machine () { unsigned int i; if (!rtl_dump_file || (debug_new_regalloc & DUMP_IGRAPH_M) == 0) return; ra_debug_msg (DUMP_IGRAPH_M, "g %d %d\n", num_webs - num_subwebs, FIRST_PSEUDO_REGISTER); for (i = 0; i < num_webs - num_subwebs; i++) { struct web *web = ID2WEB (i); struct conflict_link *cl; int flags = 0; int numc = 0; int col = 0; flags = web->spill_temp & 0xF; flags |= ((web->type == PRECOLORED) ? 1 : 0) << 4; flags |= (web->add_hardregs & 0xF) << 5; for (cl = web->conflict_list; cl; cl = cl->next) if (cl->t->id < web->id) numc++; ra_debug_msg (DUMP_IGRAPH_M, "n %d %d %d %d %d %d %d\n", web->id, web->color, flags, (unsigned int)web->spill_cost, web->num_defs, web->num_uses, numc); if (web->type != PRECOLORED) { ra_debug_msg (DUMP_IGRAPH_M, "s %d", web->id); while (1) { unsigned int u = 0; int n; for (n = 0; n < 32 && col < FIRST_PSEUDO_REGISTER; n++, col++) if (TEST_HARD_REG_BIT (web->usable_regs, col)) u |= 1 << n; ra_debug_msg (DUMP_IGRAPH_M, " %u", u); if (col >= FIRST_PSEUDO_REGISTER) break; } ra_debug_msg (DUMP_IGRAPH_M, "\n"); } if (numc) { ra_debug_msg (DUMP_IGRAPH_M, "c %d", web->id); for (cl = web->conflict_list; cl; cl = cl->next) { if (cl->t->id < web->id) ra_debug_msg (DUMP_IGRAPH_M, " %d", cl->t->id); } ra_debug_msg (DUMP_IGRAPH_M, "\n"); } } ra_debug_msg (DUMP_IGRAPH_M, "e\n"); } /* This runs after colorization and changing the insn stream. It temporarily replaces all pseudo registers with their colors, and emits information, if the resulting insns are strictly valid. */ void dump_constraints () { rtx insn; int i; if (!rtl_dump_file || (debug_new_regalloc & DUMP_CONSTRAINTS) == 0) return; for (i = FIRST_PSEUDO_REGISTER; i < ra_max_regno; i++) if (regno_reg_rtx[i] && GET_CODE (regno_reg_rtx[i]) == REG) REGNO (regno_reg_rtx[i]) = ra_reg_renumber[i] >= 0 ? ra_reg_renumber[i] : i; for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) if (INSN_P (insn)) { int code; int uid = INSN_UID (insn); int o; /* Don't simply force rerecognition, as combine might left us with some unrecognizable ones, which later leads to aborts in regclass, if we now destroy the remembered INSN_CODE(). */ /*INSN_CODE (insn) = -1;*/ code = recog_memoized (insn); if (code < 0) { ra_debug_msg (DUMP_CONSTRAINTS, "%d: asm insn or not recognizable.\n", uid); continue; } ra_debug_msg (DUMP_CONSTRAINTS, "%d: code %d {%s}, %d operands, constraints: ", uid, code, insn_data[code].name, recog_data.n_operands); extract_insn (insn); /*preprocess_constraints ();*/ for (o = 0; o < recog_data.n_operands; o++) { ra_debug_msg (DUMP_CONSTRAINTS, "%d:%s ", o, recog_data.constraints[o]); } if (constrain_operands (1)) ra_debug_msg (DUMP_CONSTRAINTS, "matches strictly alternative %d", which_alternative); else ra_debug_msg (DUMP_CONSTRAINTS, "doesn't match strictly"); ra_debug_msg (DUMP_CONSTRAINTS, "\n"); } for (i = FIRST_PSEUDO_REGISTER; i < ra_max_regno; i++) if (regno_reg_rtx[i] && GET_CODE (regno_reg_rtx[i]) == REG) REGNO (regno_reg_rtx[i]) = i; } /* This counts and emits the cumulated cost of all spilled webs, preceded by a custom message MSG, with debug level LEVEL. */ void dump_graph_cost (level, msg) unsigned int level; const char *msg; { unsigned int i; unsigned HOST_WIDE_INT cost; if (!rtl_dump_file || (debug_new_regalloc & level) == 0) return; cost = 0; for (i = 0; i < num_webs; i++) { struct web *web = id2web[i]; if (alias (web)->type == SPILLED) cost += web->orig_spill_cost; } ra_debug_msg (level, " spill cost of graph (%s) = " HOST_WIDE_INT_PRINT_UNSIGNED "\n", msg ? msg : "", cost); } /* Dump the color assignment per web, the coalesced and spilled webs. */ void dump_ra (df) struct df *df ATTRIBUTE_UNUSED; { struct web *web; struct dlist *d; if (!rtl_dump_file || (debug_new_regalloc & DUMP_RESULTS) == 0) return; ra_debug_msg (DUMP_RESULTS, "\nColored:\n"); for (d = WEBS(COLORED); d; d = d->next) { web = DLIST_WEB (d); ra_debug_msg (DUMP_RESULTS, " %4d : color %d\n", web->id, web->color); } ra_debug_msg (DUMP_RESULTS, "\nCoalesced:\n"); for (d = WEBS(COALESCED); d; d = d->next) { web = DLIST_WEB (d); ra_debug_msg (DUMP_RESULTS, " %4d : to web %d, color %d\n", web->id, alias (web)->id, web->color); } ra_debug_msg (DUMP_RESULTS, "\nSpilled:\n"); for (d = WEBS(SPILLED); d; d = d->next) { web = DLIST_WEB (d); ra_debug_msg (DUMP_RESULTS, " %4d\n", web->id); } ra_debug_msg (DUMP_RESULTS, "\n"); dump_cost (DUMP_RESULTS); } /* Calculate and dump the cumulated costs of certain types of insns (loads, stores and copies). */ void dump_static_insn_cost (file, message, prefix) FILE *file; const char *message; const char *prefix; { struct cost { unsigned HOST_WIDE_INT cost; unsigned int count; }; basic_block bb; struct cost load, store, regcopy, selfcopy, overall; memset (&load, 0, sizeof(load)); memset (&store, 0, sizeof(store)); memset (®copy, 0, sizeof(regcopy)); memset (&selfcopy, 0, sizeof(selfcopy)); memset (&overall, 0, sizeof(overall)); if (!file) return; FOR_EACH_BB (bb) { unsigned HOST_WIDE_INT block_cost = bb->frequency; rtx insn, set; for (insn = bb->head; insn; insn = NEXT_INSN (insn)) { /* Yes, yes. We don't calculate the costs precisely. Only for "simple enough" insns. Those containing single sets only. */ if (INSN_P (insn) && ((set = single_set (insn)) != NULL)) { rtx src = SET_SRC (set); rtx dest = SET_DEST (set); struct cost *pcost = NULL; overall.cost += block_cost; overall.count++; if (rtx_equal_p (src, dest)) pcost = &selfcopy; else if (GET_CODE (src) == GET_CODE (dest) && ((GET_CODE (src) == REG) || (GET_CODE (src) == SUBREG && GET_CODE (SUBREG_REG (src)) == REG && GET_CODE (SUBREG_REG (dest)) == REG))) pcost = ®copy; else { if (GET_CODE (src) == SUBREG) src = SUBREG_REG (src); if (GET_CODE (dest) == SUBREG) dest = SUBREG_REG (dest); if (GET_CODE (src) == MEM && GET_CODE (dest) != MEM && memref_is_stack_slot (src)) pcost = &load; else if (GET_CODE (src) != MEM && GET_CODE (dest) == MEM && memref_is_stack_slot (dest)) pcost = &store; } if (pcost) { pcost->cost += block_cost; pcost->count++; } } if (insn == bb->end) break; } } if (!prefix) prefix = ""; fprintf (file, "static insn cost %s\n", message ? message : ""); fprintf (file, " %soverall:\tnum=%6d\tcost=" HOST_WIDE_INT_PRINT_DEC_SPACE "\n", prefix, overall.count, 8, overall.cost); fprintf (file, " %sloads:\tnum=%6d\tcost=" HOST_WIDE_INT_PRINT_DEC_SPACE "\n", prefix, load.count, 8, load.cost); fprintf (file, " %sstores:\tnum=%6d\tcost=" HOST_WIDE_INT_PRINT_DEC_SPACE "\n", prefix, store.count, 8, store.cost); fprintf (file, " %sregcopy:\tnum=%6d\tcost=" HOST_WIDE_INT_PRINT_DEC_SPACE "\n", prefix, regcopy.count, 8, regcopy.cost); fprintf (file, " %sselfcpy:\tnum=%6d\tcost=" HOST_WIDE_INT_PRINT_DEC_SPACE "\n", prefix, selfcopy.count, 8, selfcopy.cost); } /* Returns nonzero, if WEB1 and WEB2 have some possible hardregs in common. */ int web_conflicts_p (web1, web2) struct web *web1; struct web *web2; { if (web1->type == PRECOLORED && web2->type == PRECOLORED) return 0; if (web1->type == PRECOLORED) return TEST_HARD_REG_BIT (web2->usable_regs, web1->regno); if (web2->type == PRECOLORED) return TEST_HARD_REG_BIT (web1->usable_regs, web2->regno); return hard_regs_intersect_p (&web1->usable_regs, &web2->usable_regs); } /* Dump all uids of insns in which WEB is mentioned. */ void dump_web_insns (web) struct web *web; { unsigned int i; ra_debug_msg (DUMP_EVER, "Web: %i(%i)+%i class: %s freedom: %i degree %i\n", web->id, web->regno, web->add_hardregs, reg_class_names[web->regclass], web->num_freedom, web->num_conflicts); ra_debug_msg (DUMP_EVER, " def insns:"); for (i = 0; i < web->num_defs; ++i) { ra_debug_msg (DUMP_EVER, " %d ", INSN_UID (web->defs[i]->insn)); } ra_debug_msg (DUMP_EVER, "\n use insns:"); for (i = 0; i < web->num_uses; ++i) { ra_debug_msg (DUMP_EVER, " %d ", INSN_UID (web->uses[i]->insn)); } ra_debug_msg (DUMP_EVER, "\n"); } /* Dump conflicts for web WEB. */ void dump_web_conflicts (web) struct web *web; { int num = 0; unsigned int def2; ra_debug_msg (DUMP_EVER, "Web: %i(%i)+%i class: %s freedom: %i degree %i\n", web->id, web->regno, web->add_hardregs, reg_class_names[web->regclass], web->num_freedom, web->num_conflicts); for (def2 = 0; def2 < num_webs; def2++) if (TEST_BIT (igraph, igraph_index (web->id, def2)) && web->id != def2) { if ((num % 9) == 5) ra_debug_msg (DUMP_EVER, "\n "); num++; ra_debug_msg (DUMP_EVER, " %d(%d)", def2, id2web[def2]->regno); if (id2web[def2]->add_hardregs) ra_debug_msg (DUMP_EVER, "+%d", id2web[def2]->add_hardregs); if (web_conflicts_p (web, id2web[def2])) ra_debug_msg (DUMP_EVER, "/x"); if (id2web[def2]->type == SELECT) ra_debug_msg (DUMP_EVER, "/s"); if (id2web[def2]->type == COALESCED) ra_debug_msg (DUMP_EVER,"/c/%d", alias (id2web[def2])->id); } ra_debug_msg (DUMP_EVER, "\n"); { struct conflict_link *wl; num = 0; ra_debug_msg (DUMP_EVER, "By conflicts: "); for (wl = web->conflict_list; wl; wl = wl->next) { struct web* w = wl->t; if ((num % 9) == 8) ra_debug_msg (DUMP_EVER, "\n "); num++; ra_debug_msg (DUMP_EVER, "%d(%d)%s ", w->id, w->regno, web_conflicts_p (web, w) ? "+" : ""); } ra_debug_msg (DUMP_EVER, "\n"); } } /* Output HARD_REG_SET to stderr. */ void debug_hard_reg_set (set) HARD_REG_SET set; { int i; for (i = 0; i < FIRST_PSEUDO_REGISTER; ++i) { if (TEST_HARD_REG_BIT (set, i)) { fprintf (stderr, "%s ", reg_names[i]); } } fprintf (stderr, "\n"); } /* vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4: */