/* Provide a slim dump the instruction chain starting at FIRST to F, honoring the dump flags given in FLAGS. Currently, TDF_BLOCKS and TDF_DETAILS include more information on the basic blocks. */ void print_rtl_slim_with_bb (FILE *f, rtx first, int flags) { basic_block current_bb = NULL; rtx insn; for (insn = first; NULL != insn; insn = NEXT_INSN (insn)) { if ((flags & TDF_BLOCKS) && (INSN_P (insn) || GET_CODE (insn) == NOTE) && BLOCK_FOR_INSN (insn) && !current_bb) { current_bb = BLOCK_FOR_INSN (insn); dump_bb_info (current_bb, true, false, flags, ";; ", f); } dump_insn_slim (f, insn); if ((flags & TDF_BLOCKS) && current_bb && insn == BB_END (current_bb)) { dump_bb_info (current_bb, false, true, flags, ";; ", f); current_bb = NULL; } } }
/* Same as above, but stop at LAST or when COUNT == 0. If COUNT < 0 it will stop only at LAST or NULL rtx. */ void print_rtl_slim (FILE *f, rtx first, rtx last, int count, int flags) { basic_block current_bb = NULL; rtx insn, tail; tail = last ? NEXT_INSN (last) : NULL_RTX; for (insn = first; (insn != NULL) && (insn != tail) && (count != 0); insn = NEXT_INSN (insn)) { if ((flags & TDF_BLOCKS) && (INSN_P (insn) || GET_CODE (insn) == NOTE) && BLOCK_FOR_INSN (insn) && !current_bb) { current_bb = BLOCK_FOR_INSN (insn); dump_bb_info (current_bb, true, false, flags, ";; ", f); } dump_insn_slim (f, insn); if ((flags & TDF_BLOCKS) && current_bb && insn == BB_END (current_bb)) { dump_bb_info (current_bb, false, true, flags, ";; ", f); current_bb = NULL; } if (count > 0) count--; } }
static void dump_mem_insn (FILE *file) { dump_insn_slim (file, mem_insn.insn); if (mem_insn.reg1_is_const) fprintf (file, "found mem(%d) *(r[%d]+%d)\n", INSN_UID (mem_insn.insn), REGNO (mem_insn.reg0), (int) mem_insn.reg1_val); else fprintf (file, "found mem(%d) *(r[%d]+r[%d])\n", INSN_UID (mem_insn.insn), REGNO (mem_insn.reg0), REGNO (mem_insn.reg1)); }
DEBUG_FUNCTION void debug_rtl_slim (FILE *f, const_rtx first, const_rtx last, int count, int flags ATTRIBUTE_UNUSED) { const_rtx insn, tail; tail = last ? NEXT_INSN (last) : NULL_RTX; for (insn = first; (insn != NULL) && (insn != tail) && (count != 0); insn = NEXT_INSN (insn)) { dump_insn_slim (f, insn); if (count > 0) count--; } }
static void dump_inc_insn (FILE *file) { const char *f = ((inc_insn.form == FORM_PRE_ADD) || (inc_insn.form == FORM_PRE_INC)) ? "pre" : "post"; dump_insn_slim (file, inc_insn.insn); switch (inc_insn.form) { case FORM_PRE_ADD: case FORM_POST_ADD: if (inc_insn.reg1_is_const) fprintf (file, "found %s add(%d) r[%d]=r[%d]+%d\n", f, INSN_UID (inc_insn.insn), REGNO (inc_insn.reg_res), REGNO (inc_insn.reg0), (int) inc_insn.reg1_val); else fprintf (file, "found %s add(%d) r[%d]=r[%d]+r[%d]\n", f, INSN_UID (inc_insn.insn), REGNO (inc_insn.reg_res), REGNO (inc_insn.reg0), REGNO (inc_insn.reg1)); break; case FORM_PRE_INC: case FORM_POST_INC: if (inc_insn.reg1_is_const) fprintf (file, "found %s inc(%d) r[%d]+=%d\n", f, INSN_UID (inc_insn.insn), REGNO (inc_insn.reg_res), (int) inc_insn.reg1_val); else fprintf (file, "found %s inc(%d) r[%d]+=r[%d]\n", f, INSN_UID (inc_insn.insn), REGNO (inc_insn.reg_res), REGNO (inc_insn.reg1)); break; default: break; } }
static bool attempt_change (rtx new_addr, rtx inc_reg) { /* There are four cases: For the two cases that involve an add instruction, we are going to have to delete the add and insert a mov. We are going to assume that the mov is free. This is fairly early in the backend and there are a lot of opportunities for removing that move later. In particular, there is the case where the move may be dead, this is what dead code elimination passes are for. The two cases where we have an inc insn will be handled mov free. */ basic_block bb = BLOCK_FOR_INSN (mem_insn.insn); rtx mov_insn = NULL; int regno; rtx mem = *mem_insn.mem_loc; enum machine_mode mode = GET_MODE (mem); rtx new_mem; int old_cost = 0; int new_cost = 0; bool speed = optimize_bb_for_speed_p (bb); PUT_MODE (mem_tmp, mode); XEXP (mem_tmp, 0) = new_addr; old_cost = (set_src_cost (mem, speed) + set_rtx_cost (PATTERN (inc_insn.insn), speed)); new_cost = set_src_cost (mem_tmp, speed); /* The first item of business is to see if this is profitable. */ if (old_cost < new_cost) { if (dump_file) fprintf (dump_file, "cost failure old=%d new=%d\n", old_cost, new_cost); return false; } /* Jump through a lot of hoops to keep the attributes up to date. We do not want to call one of the change address variants that take an offset even though we know the offset in many cases. These assume you are changing where the address is pointing by the offset. */ new_mem = replace_equiv_address_nv (mem, new_addr); if (! validate_change (mem_insn.insn, mem_insn.mem_loc, new_mem, 0)) { if (dump_file) fprintf (dump_file, "validation failure\n"); return false; } /* From here to the end of the function we are committed to the change, i.e. nothing fails. Generate any necessary movs, move any regnotes, and fix up the reg_next_{use,inc_use,def}. */ switch (inc_insn.form) { case FORM_PRE_ADD: /* Replace the addition with a move. Do it at the location of the addition since the operand of the addition may change before the memory reference. */ mov_insn = insert_move_insn_before (inc_insn.insn, inc_insn.reg_res, inc_insn.reg0); move_dead_notes (mov_insn, inc_insn.insn, inc_insn.reg0); regno = REGNO (inc_insn.reg_res); reg_next_def[regno] = mov_insn; reg_next_use[regno] = NULL; regno = REGNO (inc_insn.reg0); reg_next_use[regno] = mov_insn; df_recompute_luids (bb); break; case FORM_POST_INC: regno = REGNO (inc_insn.reg_res); if (reg_next_use[regno] == reg_next_inc_use[regno]) reg_next_inc_use[regno] = NULL; /* Fallthru. */ case FORM_PRE_INC: regno = REGNO (inc_insn.reg_res); reg_next_def[regno] = mem_insn.insn; reg_next_use[regno] = NULL; break; case FORM_POST_ADD: mov_insn = insert_move_insn_before (mem_insn.insn, inc_insn.reg_res, inc_insn.reg0); move_dead_notes (mov_insn, inc_insn.insn, inc_insn.reg0); /* Do not move anything to the mov insn because the instruction pointer for the main iteration has not yet hit that. It is still pointing to the mem insn. */ regno = REGNO (inc_insn.reg_res); reg_next_def[regno] = mem_insn.insn; reg_next_use[regno] = NULL; regno = REGNO (inc_insn.reg0); reg_next_use[regno] = mem_insn.insn; if ((reg_next_use[regno] == reg_next_inc_use[regno]) || (reg_next_inc_use[regno] == inc_insn.insn)) reg_next_inc_use[regno] = NULL; df_recompute_luids (bb); break; case FORM_last: default: gcc_unreachable (); } if (!inc_insn.reg1_is_const) { regno = REGNO (inc_insn.reg1); reg_next_use[regno] = mem_insn.insn; if ((reg_next_use[regno] == reg_next_inc_use[regno]) || (reg_next_inc_use[regno] == inc_insn.insn)) reg_next_inc_use[regno] = NULL; } delete_insn (inc_insn.insn); if (dump_file && mov_insn) { fprintf (dump_file, "inserting mov "); dump_insn_slim (dump_file, mov_insn); } /* Record that this insn has an implicit side effect. */ add_reg_note (mem_insn.insn, REG_INC, inc_reg); if (dump_file) { fprintf (dump_file, "****success "); dump_insn_slim (dump_file, mem_insn.insn); } return true; }
static void merge_in_block (int max_reg, basic_block bb) { rtx insn; rtx curr; int success_in_block = 0; if (dump_file) fprintf (dump_file, "\n\nstarting bb %d\n", bb->index); FOR_BB_INSNS_REVERSE_SAFE (bb, insn, curr) { unsigned int uid = INSN_UID (insn); bool insn_is_add_or_inc = true; if (!NONDEBUG_INSN_P (insn)) continue; /* This continue is deliberate. We do not want the uses of the jump put into reg_next_use because it is not considered safe to combine a preincrement with a jump. */ if (JUMP_P (insn)) continue; if (dump_file) dump_insn_slim (dump_file, insn); /* Does this instruction increment or decrement a register? */ if (parse_add_or_inc (insn, true)) { int regno = REGNO (inc_insn.reg_res); /* Cannot handle case where there are three separate regs before a mem ref. Too many moves would be needed to be profitable. */ if ((inc_insn.form == FORM_PRE_INC) || inc_insn.reg1_is_const) { mem_insn.insn = get_next_ref (regno, bb, reg_next_use); if (mem_insn.insn) { bool ok = true; if (!inc_insn.reg1_is_const) { /* We are only here if we are going to try a HAVE_*_MODIFY_REG type transformation. c is a reg and we must sure that the path from the inc_insn to the mem_insn.insn is both def and use clear of c because the inc insn is going to move into the mem_insn.insn. */ int luid = DF_INSN_LUID (mem_insn.insn); rtx other_insn = get_next_ref (REGNO (inc_insn.reg1), bb, reg_next_use); if (other_insn && luid > DF_INSN_LUID (other_insn)) ok = false; other_insn = get_next_ref (REGNO (inc_insn.reg1), bb, reg_next_def); if (other_insn && luid > DF_INSN_LUID (other_insn)) ok = false; } if (dump_file) dump_inc_insn (dump_file); if (ok && find_address (&PATTERN (mem_insn.insn)) == -1) { if (dump_file) dump_mem_insn (dump_file); if (try_merge ()) { success_in_block++; insn_is_add_or_inc = false; } } } } } else { insn_is_add_or_inc = false; mem_insn.insn = insn; if (find_mem (&PATTERN (insn))) success_in_block++; } /* If the inc insn was merged with a mem, the inc insn is gone and there is noting to update. */ if (DF_INSN_UID_GET (uid)) { df_ref *def_rec; df_ref *use_rec; /* Need to update next use. */ for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++) { df_ref def = *def_rec; reg_next_use[DF_REF_REGNO (def)] = NULL; reg_next_inc_use[DF_REF_REGNO (def)] = NULL; reg_next_def[DF_REF_REGNO (def)] = insn; } for (use_rec = DF_INSN_UID_USES (uid); *use_rec; use_rec++) { df_ref use = *use_rec; reg_next_use[DF_REF_REGNO (use)] = insn; if (insn_is_add_or_inc) reg_next_inc_use[DF_REF_REGNO (use)] = insn; else reg_next_inc_use[DF_REF_REGNO (use)] = NULL; } } else if (dump_file) fprintf (dump_file, "skipping update of deleted insn %d\n", uid); }
DEBUG_FUNCTION void debug_insn_slim (const_rtx x) { dump_insn_slim (stderr, x); }
/* Emit a slim dump of X (an insn) to stderr. */ void debug_insn_slim (rtx x) { dump_insn_slim (stderr, x); }