static int try_apply_stack_adjustment (rtx insn, struct csa_memlist *memlist, HOST_WIDE_INT new_adjust, HOST_WIDE_INT delta) { struct csa_memlist *ml; rtx set; set = single_set_for_csa (insn); validate_change (insn, &XEXP (SET_SRC (set), 1), GEN_INT (new_adjust), 1); for (ml = memlist; ml ; ml = ml->next) validate_change (ml->insn, ml->mem, replace_equiv_address_nv (*ml->mem, plus_constant (stack_pointer_rtx, ml->sp_offset - delta)), 1); if (apply_change_group ()) { /* Succeeded. Update our knowledge of the memory references. */ for (ml = memlist; ml ; ml = ml->next) ml->sp_offset -= delta; return 1; } else return 0; }
static bool replace_oldest_value_reg (rtx *loc, enum reg_class cl, rtx insn, struct value_data *vd) { rtx new_rtx = find_oldest_value_reg (cl, *loc, vd); if (new_rtx) { if (DEBUG_INSN_P (insn)) { struct queued_debug_insn_change *change; if (dump_file) fprintf (dump_file, "debug_insn %u: queued replacing reg %u with %u\n", INSN_UID (insn), REGNO (*loc), REGNO (new_rtx)); change = (struct queued_debug_insn_change *) pool_alloc (debug_insn_changes_pool); change->next = vd->e[REGNO (new_rtx)].debug_insn_changes; change->insn = insn; change->loc = loc; change->new_rtx = new_rtx; vd->e[REGNO (new_rtx)].debug_insn_changes = change; ++vd->n_debug_insn_changes; return true; } if (dump_file) fprintf (dump_file, "insn %u: replaced reg %u with %u\n", INSN_UID (insn), REGNO (*loc), REGNO (new_rtx)); validate_change (insn, loc, new_rtx, 1); return true; } return false; }
static bool can_eliminate_compare (rtx compare, rtx eh_note, struct comparison *cmp) { /* Take care that it's in the same EH region. */ if (cfun->can_throw_non_call_exceptions && !rtx_equal_p (eh_note, cmp->eh_note)) return false; /* Make sure the compare is redundant with the previous. */ if (!rtx_equal_p (XEXP (compare, 0), cmp->in_a) || !rtx_equal_p (XEXP (compare, 1), cmp->in_b)) return false; /* New mode must be compatible with the previous compare mode. */ enum machine_mode new_mode = targetm.cc_modes_compatible (GET_MODE (compare), cmp->orig_mode); if (new_mode == VOIDmode) return false; if (cmp->orig_mode != new_mode) { /* Generate new comparison for substitution. */ rtx flags = gen_rtx_REG (new_mode, targetm.flags_regnum); rtx x = gen_rtx_COMPARE (new_mode, cmp->in_a, cmp->in_b); x = gen_rtx_SET (flags, x); if (!validate_change (cmp->insn, &PATTERN (cmp->insn), x, false)) return false; cmp->orig_mode = new_mode; } return true; }
static int try_apply_stack_adjustment (rtx insn, struct csa_reflist *reflist, HOST_WIDE_INT new_adjust, HOST_WIDE_INT delta) { struct csa_reflist *ml; rtx set; set = single_set_for_csa (insn); if (MEM_P (SET_DEST (set))) validate_change (insn, &SET_DEST (set), replace_equiv_address (SET_DEST (set), stack_pointer_rtx), 1); else validate_change (insn, &XEXP (SET_SRC (set), 1), GEN_INT (new_adjust), 1); for (ml = reflist; ml ; ml = ml->next) { rtx new_addr = plus_constant (Pmode, stack_pointer_rtx, ml->sp_offset - delta); rtx new_val; if (MEM_P (*ml->ref)) new_val = replace_equiv_address_nv (*ml->ref, new_addr); else if (GET_MODE (*ml->ref) == GET_MODE (stack_pointer_rtx)) new_val = new_addr; else new_val = lowpart_subreg (GET_MODE (*ml->ref), new_addr, GET_MODE (new_addr)); validate_change (ml->insn, ml->ref, new_val, 1); } if (apply_change_group ()) { /* Succeeded. Update our knowledge of the stack references. */ for (ml = reflist; ml ; ml = ml->next) ml->sp_offset -= delta; return 1; } else return 0; }
static bool update_reg_equal_equiv_notes (rtx insn, enum machine_mode new_mode, enum machine_mode old_mode, enum rtx_code code) { rtx *loc = ®_NOTES (insn); while (*loc) { enum reg_note kind = REG_NOTE_KIND (*loc); if (kind == REG_EQUAL || kind == REG_EQUIV) { rtx orig_src = XEXP (*loc, 0); /* Update equivalency constants. Recall that RTL constants are sign-extended. */ if (GET_CODE (orig_src) == CONST_INT && HOST_BITS_PER_WIDE_INT >= GET_MODE_BITSIZE (new_mode)) { if (INTVAL (orig_src) >= 0 || code == SIGN_EXTEND) /* Nothing needed. */; else { /* Zero-extend the negative constant by masking out the bits outside the source mode. */ rtx new_const_int = gen_int_mode (INTVAL (orig_src) & GET_MODE_MASK (old_mode), new_mode); if (!validate_change (insn, &XEXP (*loc, 0), new_const_int, true)) return false; } loc = &XEXP (*loc, 1); } /* Drop all other notes, they assume a wrong mode. */ else if (!validate_change (insn, loc, XEXP (*loc, 1), true)) return false; } else loc = &XEXP (*loc, 1); } return true; }
static void apply_debug_insn_changes (struct value_data *vd, unsigned int regno) { struct queued_debug_insn_change *change; rtx last_insn = vd->e[regno].debug_insn_changes->insn; for (change = vd->e[regno].debug_insn_changes; change; change = change->next) { if (last_insn != change->insn) { apply_change_group (); last_insn = change->insn; } validate_change (change->insn, change->loc, change->new_rtx, 1); } apply_change_group (); }
static bool transform_ifelse (ext_cand *cand, rtx def_insn) { rtx set_insn = PATTERN (def_insn); rtx srcreg, dstreg, srcreg2; rtx map_srcreg, map_dstreg, map_srcreg2; rtx ifexpr; rtx cond; rtx new_set; gcc_assert (GET_CODE (set_insn) == SET); cond = XEXP (SET_SRC (set_insn), 0); dstreg = SET_DEST (set_insn); srcreg = XEXP (SET_SRC (set_insn), 1); srcreg2 = XEXP (SET_SRC (set_insn), 2); /* If the conditional move already has the right or wider mode, there is nothing to do. */ if (GET_MODE_SIZE (GET_MODE (dstreg)) >= GET_MODE_SIZE (cand->mode)) return true; map_srcreg = gen_rtx_REG (cand->mode, REGNO (srcreg)); map_srcreg2 = gen_rtx_REG (cand->mode, REGNO (srcreg2)); map_dstreg = gen_rtx_REG (cand->mode, REGNO (dstreg)); ifexpr = gen_rtx_IF_THEN_ELSE (cand->mode, cond, map_srcreg, map_srcreg2); new_set = gen_rtx_SET (VOIDmode, map_dstreg, ifexpr); if (validate_change (def_insn, &PATTERN (def_insn), new_set, true) && update_reg_equal_equiv_notes (def_insn, cand->mode, GET_MODE (dstreg), cand->code)) { if (dump_file) { fprintf (dump_file, "Mode of conditional move instruction extended:\n"); print_rtl_single (dump_file, def_insn); } return true; } return false; }
static bool combine_set_extension (ext_cand *cand, rtx curr_insn, rtx *orig_set) { rtx orig_src = SET_SRC (*orig_set); enum machine_mode orig_mode = GET_MODE (SET_DEST (*orig_set)); rtx new_reg = gen_rtx_REG (cand->mode, REGNO (SET_DEST (*orig_set))); rtx new_set; /* Merge constants by directly moving the constant into the register under some conditions. Recall that RTL constants are sign-extended. */ if (GET_CODE (orig_src) == CONST_INT && HOST_BITS_PER_WIDE_INT >= GET_MODE_BITSIZE (cand->mode)) { if (INTVAL (orig_src) >= 0 || cand->code == SIGN_EXTEND) new_set = gen_rtx_SET (VOIDmode, new_reg, orig_src); else { /* Zero-extend the negative constant by masking out the bits outside the source mode. */ rtx new_const_int = GEN_INT (INTVAL (orig_src) & GET_MODE_MASK (orig_mode)); new_set = gen_rtx_SET (VOIDmode, new_reg, new_const_int); } } else if (GET_MODE (orig_src) == VOIDmode) { /* This is mostly due to a call insn that should not be optimized. */ return false; } else if (GET_CODE (orig_src) == cand->code) { /* Here is a sequence of two extensions. Try to merge them. */ rtx temp_extension = gen_rtx_fmt_e (cand->code, cand->mode, XEXP (orig_src, 0)); rtx simplified_temp_extension = simplify_rtx (temp_extension); if (simplified_temp_extension) temp_extension = simplified_temp_extension; new_set = gen_rtx_SET (VOIDmode, new_reg, temp_extension); } else if (GET_CODE (orig_src) == IF_THEN_ELSE) { /* Only IF_THEN_ELSE of phi-type copies are combined. Otherwise, in general, IF_THEN_ELSE should not be combined. */ return false; } else { /* This is the normal case. */ rtx temp_extension = gen_rtx_fmt_e (cand->code, cand->mode, orig_src); rtx simplified_temp_extension = simplify_rtx (temp_extension); if (simplified_temp_extension) temp_extension = simplified_temp_extension; new_set = gen_rtx_SET (VOIDmode, new_reg, temp_extension); } /* This change is a part of a group of changes. Hence, validate_change will not try to commit the change. */ if (validate_change (curr_insn, orig_set, new_set, true) && update_reg_equal_equiv_notes (curr_insn, cand->mode, orig_mode, cand->code)) { if (dump_file) { fprintf (dump_file, "Tentatively merged extension with definition:\n"); print_rtl_single (dump_file, curr_insn); } return true; } return false; }
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 int reload_cse_simplify_operands (rtx insn, rtx testreg) { int i, j; /* For each operand, all registers that are equivalent to it. */ HARD_REG_SET equiv_regs[MAX_RECOG_OPERANDS]; const char *constraints[MAX_RECOG_OPERANDS]; /* Vector recording how bad an alternative is. */ int *alternative_reject; /* Vector recording how many registers can be introduced by choosing this alternative. */ int *alternative_nregs; /* Array of vectors recording, for each operand and each alternative, which hard register to substitute, or -1 if the operand should be left as it is. */ int *op_alt_regno[MAX_RECOG_OPERANDS]; /* Array of alternatives, sorted in order of decreasing desirability. */ int *alternative_order; extract_insn (insn); if (recog_data.n_alternatives == 0 || recog_data.n_operands == 0) return 0; /* Figure out which alternative currently matches. */ if (! constrain_operands (1)) fatal_insn_not_found (insn); alternative_reject = XALLOCAVEC (int, recog_data.n_alternatives); alternative_nregs = XALLOCAVEC (int, recog_data.n_alternatives); alternative_order = XALLOCAVEC (int, recog_data.n_alternatives); memset (alternative_reject, 0, recog_data.n_alternatives * sizeof (int)); memset (alternative_nregs, 0, recog_data.n_alternatives * sizeof (int)); /* For each operand, find out which regs are equivalent. */ for (i = 0; i < recog_data.n_operands; i++) { cselib_val *v; struct elt_loc_list *l; rtx op; enum machine_mode mode; CLEAR_HARD_REG_SET (equiv_regs[i]); /* cselib blows up on CODE_LABELs. Trying to fix that doesn't seem right, so avoid the problem here. Likewise if we have a constant and the insn pattern doesn't tell us the mode we need. */ if (LABEL_P (recog_data.operand[i]) || (CONSTANT_P (recog_data.operand[i]) && recog_data.operand_mode[i] == VOIDmode)) continue; op = recog_data.operand[i]; mode = GET_MODE (op); #ifdef LOAD_EXTEND_OP if (MEM_P (op) && GET_MODE_BITSIZE (mode) < BITS_PER_WORD && LOAD_EXTEND_OP (mode) != UNKNOWN) { rtx set = single_set (insn); /* We might have multiple sets, some of which do implicit extension. Punt on this for now. */ if (! set) continue; /* If the destination is also a MEM or a STRICT_LOW_PART, no extension applies. Also, if there is an explicit extension, we don't have to worry about an implicit one. */ else if (MEM_P (SET_DEST (set)) || GET_CODE (SET_DEST (set)) == STRICT_LOW_PART || GET_CODE (SET_SRC (set)) == ZERO_EXTEND || GET_CODE (SET_SRC (set)) == SIGN_EXTEND) ; /* Continue ordinary processing. */ #ifdef CANNOT_CHANGE_MODE_CLASS /* If the register cannot change mode to word_mode, it follows that it cannot have been used in word_mode. */ else if (REG_P (SET_DEST (set)) && CANNOT_CHANGE_MODE_CLASS (GET_MODE (SET_DEST (set)), word_mode, REGNO_REG_CLASS (REGNO (SET_DEST (set))))) ; /* Continue ordinary processing. */ #endif /* If this is a straight load, make the extension explicit. */ else if (REG_P (SET_DEST (set)) && recog_data.n_operands == 2 && SET_SRC (set) == op && SET_DEST (set) == recog_data.operand[1-i]) { validate_change (insn, recog_data.operand_loc[i], gen_rtx_fmt_e (LOAD_EXTEND_OP (mode), word_mode, op), 1); validate_change (insn, recog_data.operand_loc[1-i], gen_rtx_REG (word_mode, REGNO (SET_DEST (set))), 1); if (! apply_change_group ()) return 0; return reload_cse_simplify_operands (insn, testreg); } else /* ??? There might be arithmetic operations with memory that are safe to optimize, but is it worth the trouble? */ continue; } #endif /* LOAD_EXTEND_OP */ v = cselib_lookup (op, recog_data.operand_mode[i], 0); if (! v) continue; for (l = v->locs; l; l = l->next) if (REG_P (l->loc)) SET_HARD_REG_BIT (equiv_regs[i], REGNO (l->loc)); } for (i = 0; i < recog_data.n_operands; i++) { enum machine_mode mode; int regno; const char *p; op_alt_regno[i] = XALLOCAVEC (int, recog_data.n_alternatives); for (j = 0; j < recog_data.n_alternatives; j++) op_alt_regno[i][j] = -1; p = constraints[i] = recog_data.constraints[i]; mode = recog_data.operand_mode[i]; /* Add the reject values for each alternative given by the constraints for this operand. */ j = 0; while (*p != '\0') { char c = *p++; if (c == ',') j++; else if (c == '?') alternative_reject[j] += 3; else if (c == '!') alternative_reject[j] += 300; } /* We won't change operands which are already registers. We also don't want to modify output operands. */ regno = true_regnum (recog_data.operand[i]); if (regno >= 0 || constraints[i][0] == '=' || constraints[i][0] == '+') continue; for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) { int rclass = (int) NO_REGS; if (! TEST_HARD_REG_BIT (equiv_regs[i], regno)) continue; SET_REGNO (testreg, regno); PUT_MODE (testreg, mode); /* We found a register equal to this operand. Now look for all alternatives that can accept this register and have not been assigned a register they can use yet. */ j = 0; p = constraints[i]; for (;;) { char c = *p; switch (c) { case '=': case '+': case '?': case '#': case '&': case '!': case '*': case '%': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '<': case '>': case 'V': case 'o': case 'E': case 'F': case 'G': case 'H': case 's': case 'i': case 'n': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'p': case 'X': case TARGET_MEM_CONSTRAINT: /* These don't say anything we care about. */ break; case 'g': case 'r': rclass = reg_class_subunion[(int) rclass][(int) GENERAL_REGS]; break; default: rclass = (reg_class_subunion [(int) rclass] [(int) REG_CLASS_FROM_CONSTRAINT ((unsigned char) c, p)]); break; case ',': case '\0': /* See if REGNO fits this alternative, and set it up as the replacement register if we don't have one for this alternative yet and the operand being replaced is not a cheap CONST_INT. */ if (op_alt_regno[i][j] == -1 && reg_fits_class_p (testreg, rclass, 0, mode) && (GET_CODE (recog_data.operand[i]) != CONST_INT || (rtx_cost (recog_data.operand[i], SET, optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn))) > rtx_cost (testreg, SET, optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn)))))) { alternative_nregs[j]++; op_alt_regno[i][j] = regno; } j++; rclass = (int) NO_REGS; break; } p += CONSTRAINT_LEN (c, p); if (c == '\0') break; } } } /* Record all alternatives which are better or equal to the currently matching one in the alternative_order array. */ for (i = j = 0; i < recog_data.n_alternatives; i++) if (alternative_reject[i] <= alternative_reject[which_alternative]) alternative_order[j++] = i; recog_data.n_alternatives = j; /* Sort it. Given a small number of alternatives, a dumb algorithm won't hurt too much. */ for (i = 0; i < recog_data.n_alternatives - 1; i++) { int best = i; int best_reject = alternative_reject[alternative_order[i]]; int best_nregs = alternative_nregs[alternative_order[i]]; int tmp; for (j = i + 1; j < recog_data.n_alternatives; j++) { int this_reject = alternative_reject[alternative_order[j]]; int this_nregs = alternative_nregs[alternative_order[j]]; if (this_reject < best_reject || (this_reject == best_reject && this_nregs > best_nregs)) { best = j; best_reject = this_reject; best_nregs = this_nregs; } } tmp = alternative_order[best]; alternative_order[best] = alternative_order[i]; alternative_order[i] = tmp; } /* Substitute the operands as determined by op_alt_regno for the best alternative. */ j = alternative_order[0]; for (i = 0; i < recog_data.n_operands; i++) { enum machine_mode mode = recog_data.operand_mode[i]; if (op_alt_regno[i][j] == -1) continue; validate_change (insn, recog_data.operand_loc[i], gen_rtx_REG (mode, op_alt_regno[i][j]), 1); } for (i = recog_data.n_dups - 1; i >= 0; i--) { int op = recog_data.dup_num[i]; enum machine_mode mode = recog_data.operand_mode[op]; if (op_alt_regno[op][j] == -1) continue; validate_change (insn, recog_data.dup_loc[i], gen_rtx_REG (mode, op_alt_regno[op][j]), 1); } return apply_change_group (); }
static bool combine_set_extension (ext_cand *cand, rtx_insn *curr_insn, rtx *orig_set) { rtx orig_src = SET_SRC (*orig_set); machine_mode orig_mode = GET_MODE (SET_DEST (*orig_set)); rtx new_set; rtx cand_pat = PATTERN (cand->insn); /* If the extension's source/destination registers are not the same then we need to change the original load to reference the destination of the extension. Then we need to emit a copy from that destination to the original destination of the load. */ rtx new_reg; bool copy_needed = (REGNO (SET_DEST (cand_pat)) != REGNO (XEXP (SET_SRC (cand_pat), 0))); if (copy_needed) new_reg = gen_rtx_REG (cand->mode, REGNO (SET_DEST (cand_pat))); else new_reg = gen_rtx_REG (cand->mode, REGNO (SET_DEST (*orig_set))); #if 0 /* Rethinking test. Temporarily disabled. */ /* We're going to be widening the result of DEF_INSN, ensure that doing so doesn't change the number of hard registers needed for the result. */ if (HARD_REGNO_NREGS (REGNO (new_reg), cand->mode) != HARD_REGNO_NREGS (REGNO (SET_DEST (*orig_set)), GET_MODE (SET_DEST (*orig_set)))) return false; #endif /* Merge constants by directly moving the constant into the register under some conditions. Recall that RTL constants are sign-extended. */ if (GET_CODE (orig_src) == CONST_INT && HOST_BITS_PER_WIDE_INT >= GET_MODE_BITSIZE (cand->mode)) { if (INTVAL (orig_src) >= 0 || cand->code == SIGN_EXTEND) new_set = gen_rtx_SET (new_reg, orig_src); else { /* Zero-extend the negative constant by masking out the bits outside the source mode. */ rtx new_const_int = gen_int_mode (INTVAL (orig_src) & GET_MODE_MASK (orig_mode), GET_MODE (new_reg)); new_set = gen_rtx_SET (new_reg, new_const_int); } } else if (GET_MODE (orig_src) == VOIDmode) { /* This is mostly due to a call insn that should not be optimized. */ return false; } else if (GET_CODE (orig_src) == cand->code) { /* Here is a sequence of two extensions. Try to merge them. */ rtx temp_extension = gen_rtx_fmt_e (cand->code, cand->mode, XEXP (orig_src, 0)); rtx simplified_temp_extension = simplify_rtx (temp_extension); if (simplified_temp_extension) temp_extension = simplified_temp_extension; new_set = gen_rtx_SET (new_reg, temp_extension); } else if (GET_CODE (orig_src) == IF_THEN_ELSE) { /* Only IF_THEN_ELSE of phi-type copies are combined. Otherwise, in general, IF_THEN_ELSE should not be combined. */ return false; } else { /* This is the normal case. */ rtx temp_extension = gen_rtx_fmt_e (cand->code, cand->mode, orig_src); rtx simplified_temp_extension = simplify_rtx (temp_extension); if (simplified_temp_extension) temp_extension = simplified_temp_extension; new_set = gen_rtx_SET (new_reg, temp_extension); } /* This change is a part of a group of changes. Hence, validate_change will not try to commit the change. */ if (validate_change (curr_insn, orig_set, new_set, true) && update_reg_equal_equiv_notes (curr_insn, cand->mode, orig_mode, cand->code)) { if (dump_file) { fprintf (dump_file, "Tentatively merged extension with definition %s:\n", (copy_needed) ? "(copy needed)" : ""); print_rtl_single (dump_file, curr_insn); } return true; } return false; }
static void combine_stack_adjustments_for_block (basic_block bb) { HOST_WIDE_INT last_sp_adjust = 0; rtx last_sp_set = NULL_RTX; struct csa_memlist *memlist = NULL; rtx insn, next, set; struct record_stack_memrefs_data data; bool end_of_block = false; for (insn = BB_HEAD (bb); !end_of_block ; insn = next) { end_of_block = insn == BB_END (bb); next = NEXT_INSN (insn); if (! INSN_P (insn)) continue; set = single_set_for_csa (insn); if (set) { rtx dest = SET_DEST (set); rtx src = SET_SRC (set); /* Find constant additions to the stack pointer. */ if (dest == stack_pointer_rtx && GET_CODE (src) == PLUS && XEXP (src, 0) == stack_pointer_rtx && GET_CODE (XEXP (src, 1)) == CONST_INT) { HOST_WIDE_INT this_adjust = INTVAL (XEXP (src, 1)); /* If we've not seen an adjustment previously, record it now and continue. */ if (! last_sp_set) { last_sp_set = insn; last_sp_adjust = this_adjust; continue; } /* If not all recorded memrefs can be adjusted, or the adjustment is now too large for a constant addition, we cannot merge the two stack adjustments. Also we need to be careful to not move stack pointer such that we create stack accesses outside the allocated area. We can combine an allocation into the first insn, or a deallocation into the second insn. We can not combine an allocation followed by a deallocation. The only somewhat frequent occurrence of the later is when a function allocates a stack frame but does not use it. For this case, we would need to analyze rtl stream to be sure that allocated area is really unused. This means not only checking the memory references, but also all registers or global memory references possibly containing a stack frame address. Perhaps the best way to address this problem is to teach gcc not to allocate stack for objects never used. */ /* Combine an allocation into the first instruction. */ if (STACK_GROWS_DOWNWARD ? this_adjust <= 0 : this_adjust >= 0) { if (try_apply_stack_adjustment (last_sp_set, memlist, last_sp_adjust + this_adjust, this_adjust)) { /* It worked! */ delete_insn (insn); last_sp_adjust += this_adjust; continue; } } /* Otherwise we have a deallocation. Do not combine with a previous allocation. Combine into the second insn. */ else if (STACK_GROWS_DOWNWARD ? last_sp_adjust >= 0 : last_sp_adjust <= 0) { if (try_apply_stack_adjustment (insn, memlist, last_sp_adjust + this_adjust, -last_sp_adjust)) { /* It worked! */ delete_insn (last_sp_set); last_sp_set = insn; last_sp_adjust += this_adjust; free_csa_memlist (memlist); memlist = NULL; continue; } } /* Combination failed. Restart processing from here. If deallocation+allocation conspired to cancel, we can delete the old deallocation insn. */ if (last_sp_set && last_sp_adjust == 0) delete_insn (insn); free_csa_memlist (memlist); memlist = NULL; last_sp_set = insn; last_sp_adjust = this_adjust; continue; } /* Find a predecrement of exactly the previous adjustment and turn it into a direct store. Obviously we can't do this if there were any intervening uses of the stack pointer. */ if (memlist == NULL && MEM_P (dest) && ((GET_CODE (XEXP (dest, 0)) == PRE_DEC && (last_sp_adjust == (HOST_WIDE_INT) GET_MODE_SIZE (GET_MODE (dest)))) || (GET_CODE (XEXP (dest, 0)) == PRE_MODIFY && GET_CODE (XEXP (XEXP (dest, 0), 1)) == PLUS && XEXP (XEXP (XEXP (dest, 0), 1), 0) == stack_pointer_rtx && (GET_CODE (XEXP (XEXP (XEXP (dest, 0), 1), 1)) == CONST_INT) && (INTVAL (XEXP (XEXP (XEXP (dest, 0), 1), 1)) == -last_sp_adjust))) && XEXP (XEXP (dest, 0), 0) == stack_pointer_rtx && ! reg_mentioned_p (stack_pointer_rtx, src) && memory_address_p (GET_MODE (dest), stack_pointer_rtx) && validate_change (insn, &SET_DEST (set), replace_equiv_address (dest, stack_pointer_rtx), 0)) { delete_insn (last_sp_set); free_csa_memlist (memlist); memlist = NULL; last_sp_set = NULL_RTX; last_sp_adjust = 0; continue; } } data.insn = insn; data.memlist = memlist; if (!CALL_P (insn) && last_sp_set && !for_each_rtx (&PATTERN (insn), record_stack_memrefs, &data)) { memlist = data.memlist; continue; } memlist = data.memlist; /* Otherwise, we were not able to process the instruction. Do not continue collecting data across such a one. */ if (last_sp_set && (CALL_P (insn) || reg_mentioned_p (stack_pointer_rtx, PATTERN (insn)))) { if (last_sp_set && last_sp_adjust == 0) delete_insn (last_sp_set); free_csa_memlist (memlist); memlist = NULL; last_sp_set = NULL_RTX; last_sp_adjust = 0; } } if (last_sp_set && last_sp_adjust == 0) delete_insn (last_sp_set); if (memlist) free_csa_memlist (memlist); }
void eliminate_regs_in_insn (rtx_insn *insn, bool replace_p, bool first_p, HOST_WIDE_INT update_sp_offset) { int icode = recog_memoized (insn); rtx old_set = single_set (insn); bool validate_p; int i; rtx substed_operand[MAX_RECOG_OPERANDS]; rtx orig_operand[MAX_RECOG_OPERANDS]; struct lra_elim_table *ep; rtx plus_src, plus_cst_src; lra_insn_recog_data_t id; struct lra_static_insn_data *static_id; if (icode < 0 && asm_noperands (PATTERN (insn)) < 0 && ! DEBUG_INSN_P (insn)) { lra_assert (GET_CODE (PATTERN (insn)) == USE || GET_CODE (PATTERN (insn)) == CLOBBER || GET_CODE (PATTERN (insn)) == ASM_INPUT); return; } /* Check for setting an eliminable register. */ if (old_set != 0 && REG_P (SET_DEST (old_set)) && (ep = get_elimination (SET_DEST (old_set))) != NULL) { for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) if (ep->from_rtx == SET_DEST (old_set) && ep->can_eliminate) { bool delete_p = replace_p; #ifdef HARD_FRAME_POINTER_REGNUM if (ep->from == FRAME_POINTER_REGNUM && ep->to == HARD_FRAME_POINTER_REGNUM) /* If this is setting the frame pointer register to the hardware frame pointer register and this is an elimination that will be done (tested above), this insn is really adjusting the frame pointer downward to compensate for the adjustment done before a nonlocal goto. */ { rtx src = SET_SRC (old_set); rtx off = remove_reg_equal_offset_note (insn, ep->to_rtx); /* We should never process such insn with non-zero UPDATE_SP_OFFSET. */ lra_assert (update_sp_offset == 0); if (off != NULL_RTX || src == ep->to_rtx || (GET_CODE (src) == PLUS && XEXP (src, 0) == ep->to_rtx && CONST_INT_P (XEXP (src, 1)))) { HOST_WIDE_INT offset; if (replace_p) { SET_DEST (old_set) = ep->to_rtx; lra_update_insn_recog_data (insn); return; } offset = (off != NULL_RTX ? INTVAL (off) : src == ep->to_rtx ? 0 : INTVAL (XEXP (src, 1))); offset -= (ep->offset - ep->previous_offset); src = plus_constant (Pmode, ep->to_rtx, offset); /* First see if this insn remains valid when we make the change. If not, keep the INSN_CODE the same and let the constraint pass fit it up. */ validate_change (insn, &SET_SRC (old_set), src, 1); validate_change (insn, &SET_DEST (old_set), ep->from_rtx, 1); if (! apply_change_group ()) { SET_SRC (old_set) = src; SET_DEST (old_set) = ep->from_rtx; } lra_update_insn_recog_data (insn); /* Add offset note for future updates. */ add_reg_note (insn, REG_EQUAL, src); return; } } #endif /* This insn isn't serving a useful purpose. We delete it when REPLACE is set. */ if (delete_p) lra_delete_dead_insn (insn); return; } } /* We allow one special case which happens to work on all machines we currently support: a single set with the source or a REG_EQUAL note being a PLUS of an eliminable register and a constant. */ plus_src = plus_cst_src = 0; if (old_set && REG_P (SET_DEST (old_set))) { if (GET_CODE (SET_SRC (old_set)) == PLUS) plus_src = SET_SRC (old_set); /* First see if the source is of the form (plus (...) CST). */ if (plus_src && CONST_INT_P (XEXP (plus_src, 1))) plus_cst_src = plus_src; /* Check that the first operand of the PLUS is a hard reg or the lowpart subreg of one. */ if (plus_cst_src) { rtx reg = XEXP (plus_cst_src, 0); if (GET_CODE (reg) == SUBREG && subreg_lowpart_p (reg)) reg = SUBREG_REG (reg); if (!REG_P (reg) || REGNO (reg) >= FIRST_PSEUDO_REGISTER) plus_cst_src = 0; } } if (plus_cst_src) { rtx reg = XEXP (plus_cst_src, 0); HOST_WIDE_INT offset = INTVAL (XEXP (plus_cst_src, 1)); if (GET_CODE (reg) == SUBREG) reg = SUBREG_REG (reg); if (REG_P (reg) && (ep = get_elimination (reg)) != NULL) { rtx to_rtx = replace_p ? ep->to_rtx : ep->from_rtx; if (! replace_p) { if (update_sp_offset == 0) offset += (ep->offset - ep->previous_offset); if (ep->to_rtx == stack_pointer_rtx) { if (first_p) offset -= lra_get_insn_recog_data (insn)->sp_offset; else offset += update_sp_offset; } offset = trunc_int_for_mode (offset, GET_MODE (plus_cst_src)); } if (GET_CODE (XEXP (plus_cst_src, 0)) == SUBREG) to_rtx = gen_lowpart (GET_MODE (XEXP (plus_cst_src, 0)), to_rtx); /* If we have a nonzero offset, and the source is already a simple REG, the following transformation would increase the cost of the insn by replacing a simple REG with (plus (reg sp) CST). So try only when we already had a PLUS before. */ if (offset == 0 || plus_src) { rtx new_src = plus_constant (GET_MODE (to_rtx), to_rtx, offset); old_set = single_set (insn); /* First see if this insn remains valid when we make the change. If not, try to replace the whole pattern with a simple set (this may help if the original insn was a PARALLEL that was only recognized as single_set due to REG_UNUSED notes). If this isn't valid either, keep the INSN_CODE the same and let the constraint pass fix it up. */ if (! validate_change (insn, &SET_SRC (old_set), new_src, 0)) { rtx new_pat = gen_rtx_SET (SET_DEST (old_set), new_src); if (! validate_change (insn, &PATTERN (insn), new_pat, 0)) SET_SRC (old_set) = new_src; } lra_update_insn_recog_data (insn); /* This can't have an effect on elimination offsets, so skip right to the end. */ return; } } } /* Eliminate all eliminable registers occurring in operands that can be handled by the constraint pass. */ id = lra_get_insn_recog_data (insn); static_id = id->insn_static_data; validate_p = false; for (i = 0; i < static_id->n_operands; i++) { orig_operand[i] = *id->operand_loc[i]; substed_operand[i] = *id->operand_loc[i]; /* For an asm statement, every operand is eliminable. */ if (icode < 0 || insn_data[icode].operand[i].eliminable) { /* Check for setting a hard register that we know about. */ if (static_id->operand[i].type != OP_IN && REG_P (orig_operand[i])) { /* If we are assigning to a hard register that can be eliminated, it must be as part of a PARALLEL, since the code above handles single SETs. This reg can not be longer eliminated -- it is forced by mark_not_eliminable. */ for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) lra_assert (ep->from_rtx != orig_operand[i] || ! ep->can_eliminate); } /* Companion to the above plus substitution, we can allow invariants as the source of a plain move. */ substed_operand[i] = lra_eliminate_regs_1 (insn, *id->operand_loc[i], VOIDmode, replace_p, ! replace_p && ! first_p, update_sp_offset, first_p); if (substed_operand[i] != orig_operand[i]) validate_p = true; } } if (! validate_p) return; /* Substitute the operands; the new values are in the substed_operand array. */ for (i = 0; i < static_id->n_operands; i++) *id->operand_loc[i] = substed_operand[i]; for (i = 0; i < static_id->n_dups; i++) *id->dup_loc[i] = substed_operand[(int) static_id->dup_num[i]]; /* If we had a move insn but now we don't, re-recognize it. This will cause spurious re-recognition if the old move had a PARALLEL since the new one still will, but we can't call single_set without having put new body into the insn and the re-recognition won't hurt in this rare case. */ id = lra_update_insn_recog_data (insn); static_id = id->insn_static_data; }
/* Check if *XP is equivalent to Y. Until an an unreconcilable difference is found, use in-group changes with validate_change on *XP to make register assignments agree. It is the (not necessarily direct) callers responsibility to verify / confirm / cancel these changes, as appropriate. RVALUE indicates if the processed piece of rtl is used as a destination, in which case we can't have different registers being an input. Returns nonzero if the two blocks have been identified as equivalent, zero otherwise. RVALUE == 0: destination RVALUE == 1: source RVALUE == -1: source, ignore SET_DEST of SET / clobber. */ bool rtx_equiv_p (rtx *xp, rtx y, int rvalue, struct equiv_info *info) { rtx x = *xp; enum rtx_code code; int length; const char *format; int i; if (!y || !x) return x == y; code = GET_CODE (y); if (code != REG && x == y) return true; if (GET_CODE (x) != code || GET_MODE (x) != GET_MODE (y)) return false; /* ??? could extend to allow CONST_INT inputs. */ switch (code) { case REG: { unsigned x_regno = REGNO (x); unsigned y_regno = REGNO (y); int x_common_live, y_common_live; if (reload_completed && (x_regno >= FIRST_PSEUDO_REGISTER || y_regno >= FIRST_PSEUDO_REGISTER)) { /* We should only see this in REG_NOTEs. */ gcc_assert (!info->live_update); /* Returning false will cause us to remove the notes. */ return false; } #ifdef STACK_REGS /* After reg-stack, can only accept literal matches of stack regs. */ if (info->mode & CLEANUP_POST_REGSTACK && (IN_RANGE (x_regno, FIRST_STACK_REG, LAST_STACK_REG) || IN_RANGE (y_regno, FIRST_STACK_REG, LAST_STACK_REG))) return x_regno == y_regno; #endif /* If the register is a locally live one in one block, the corresponding one must be locally live in the other, too, and match of identical regnos doesn't apply. */ if (REGNO_REG_SET_P (info->x_local_live, x_regno)) { if (!REGNO_REG_SET_P (info->y_local_live, y_regno)) return false; } else if (REGNO_REG_SET_P (info->y_local_live, y_regno)) return false; else if (x_regno == y_regno) { if (!rvalue && info->cur.input_valid && (reg_overlap_mentioned_p (x, info->x_input) || reg_overlap_mentioned_p (x, info->y_input))) return false; /* Update liveness information. */ if (info->live_update && assign_reg_reg_set (info->common_live, x, rvalue)) info->cur.version++; return true; } x_common_live = REGNO_REG_SET_P (info->common_live, x_regno); y_common_live = REGNO_REG_SET_P (info->common_live, y_regno); if (x_common_live != y_common_live) return false; else if (x_common_live) { if (! rvalue || info->input_cost < 0 || no_new_pseudos) return false; /* If info->live_update is not set, we are processing notes. We then allow a match with x_input / y_input found in a previous pass. */ if (info->live_update && !info->cur.input_valid) { info->cur.input_valid = true; info->x_input = x; info->y_input = y; info->cur.input_count += optimize_size ? 2 : 1; if (info->input_reg && GET_MODE (info->input_reg) != GET_MODE (info->x_input)) info->input_reg = NULL_RTX; if (!info->input_reg) info->input_reg = gen_reg_rtx (GET_MODE (info->x_input)); } else if ((info->live_update ? ! info->cur.input_valid : ! info->x_input) || ! rtx_equal_p (x, info->x_input) || ! rtx_equal_p (y, info->y_input)) return false; validate_change (info->cur.x_start, xp, info->input_reg, 1); } else { int x_nregs = (x_regno >= FIRST_PSEUDO_REGISTER ? 1 : hard_regno_nregs[x_regno][GET_MODE (x)]); int y_nregs = (y_regno >= FIRST_PSEUDO_REGISTER ? 1 : hard_regno_nregs[y_regno][GET_MODE (y)]); int size = GET_MODE_SIZE (GET_MODE (x)); enum machine_mode x_mode = GET_MODE (x); unsigned x_regno_i, y_regno_i; int x_nregs_i, y_nregs_i, size_i; int local_count = info->cur.local_count; /* This might be a register local to each block. See if we have it already registered. */ for (i = local_count - 1; i >= 0; i--) { x_regno_i = REGNO (info->x_local[i]); x_nregs_i = (x_regno_i >= FIRST_PSEUDO_REGISTER ? 1 : hard_regno_nregs[x_regno_i][GET_MODE (x)]); y_regno_i = REGNO (info->y_local[i]); y_nregs_i = (y_regno_i >= FIRST_PSEUDO_REGISTER ? 1 : hard_regno_nregs[y_regno_i][GET_MODE (y)]); size_i = GET_MODE_SIZE (GET_MODE (info->x_local[i])); /* If we have a new pair of registers that is wider than an old pair and enclosing it with matching offsets, remove the old pair. If we find a matching, wider, old pair, use the old one. If the width is the same, use the old one if the modes match, but the new if they don't. We don't want to get too fancy with subreg_regno_offset here, so we just test two straightforward cases each. */ if (info->live_update && (x_mode != GET_MODE (info->x_local[i]) ? size >= size_i : size > size_i)) { /* If the new pair is fully enclosing a matching existing pair, remove the old one. N.B. because we are removing one entry here, the check below if we have space for a new entry will succeed. */ if ((x_regno <= x_regno_i && x_regno + x_nregs >= x_regno_i + x_nregs_i && x_nregs == y_nregs && x_nregs_i == y_nregs_i && x_regno - x_regno_i == y_regno - y_regno_i) || (x_regno == x_regno_i && y_regno == y_regno_i && x_nregs >= x_nregs_i && y_nregs >= y_nregs_i)) { info->cur.local_count = --local_count; info->x_local[i] = info->x_local[local_count]; info->y_local[i] = info->y_local[local_count]; continue; } } else { /* If the new pair is fully enclosed within a matching existing pair, succeed. */ if (x_regno >= x_regno_i && x_regno + x_nregs <= x_regno_i + x_nregs_i && x_nregs == y_nregs && x_nregs_i == y_nregs_i && x_regno - x_regno_i == y_regno - y_regno_i) break; if (x_regno == x_regno_i && y_regno == y_regno_i && x_nregs <= x_nregs_i && y_nregs <= y_nregs_i) break; } /* Any other overlap causes a match failure. */ if (x_regno + x_nregs > x_regno_i && x_regno_i + x_nregs_i > x_regno) return false; if (y_regno + y_nregs > y_regno_i && y_regno_i + y_nregs_i > y_regno) return false; } if (i < 0) { /* Not found. Create a new entry if possible. */ if (!info->live_update || info->cur.local_count >= STRUCT_EQUIV_MAX_LOCAL) return false; info->x_local[info->cur.local_count] = x; info->y_local[info->cur.local_count] = y; info->cur.local_count++; info->cur.version++; } note_local_live (info, x, y, rvalue); } return true; } case SET: gcc_assert (rvalue < 0); /* Ignore the destinations role as a destination. Still, we have to consider input registers embedded in the addresses of a MEM. N.B., we process the rvalue aspect of STRICT_LOW_PART / ZERO_EXTEND / SIGN_EXTEND along with their lvalue aspect. */ if(!set_dest_addr_equiv_p (SET_DEST (x), SET_DEST (y), info)) return false; /* Process source. */ return rtx_equiv_p (&SET_SRC (x), SET_SRC (y), 1, info); case PRE_MODIFY: /* Process destination. */ if (!rtx_equiv_p (&XEXP (x, 0), XEXP (y, 0), 0, info)) return false; /* Process source. */ return rtx_equiv_p (&XEXP (x, 1), XEXP (y, 1), 1, info); case POST_MODIFY: { rtx x_dest0, x_dest1; /* Process destination. */ x_dest0 = XEXP (x, 0); gcc_assert (REG_P (x_dest0)); if (!rtx_equiv_p (&XEXP (x, 0), XEXP (y, 0), 0, info)) return false; x_dest1 = XEXP (x, 0); /* validate_change might have changed the destination. Put it back so that we can do a proper match for its role a an input. */ XEXP (x, 0) = x_dest0; if (!rtx_equiv_p (&XEXP (x, 0), XEXP (y, 0), 1, info)) return false; gcc_assert (x_dest1 == XEXP (x, 0)); /* Process source. */ return rtx_equiv_p (&XEXP (x, 1), XEXP (y, 1), 1, info); } case CLOBBER: gcc_assert (rvalue < 0); return true; /* Some special forms are also rvalues when they appear in lvalue positions. However, we must ont try to match a register after we have already altered it with validate_change, consider the rvalue aspect while we process the lvalue. */ case STRICT_LOW_PART: case ZERO_EXTEND: case SIGN_EXTEND: { rtx x_inner, y_inner; enum rtx_code code; int change; if (rvalue) break; x_inner = XEXP (x, 0); y_inner = XEXP (y, 0); if (GET_MODE (x_inner) != GET_MODE (y_inner)) return false; code = GET_CODE (x_inner); if (code != GET_CODE (y_inner)) return false; /* The address of a MEM is an input that will be processed during rvalue == -1 processing. */ if (code == SUBREG) { if (SUBREG_BYTE (x_inner) != SUBREG_BYTE (y_inner)) return false; x = x_inner; x_inner = SUBREG_REG (x_inner); y_inner = SUBREG_REG (y_inner); if (GET_MODE (x_inner) != GET_MODE (y_inner)) return false; code = GET_CODE (x_inner); if (code != GET_CODE (y_inner)) return false; } if (code == MEM) return true; gcc_assert (code == REG); if (! rtx_equiv_p (&XEXP (x, 0), y_inner, rvalue, info)) return false; if (REGNO (x_inner) == REGNO (y_inner)) { change = assign_reg_reg_set (info->common_live, x_inner, 1); info->cur.version++; } else change = note_local_live (info, x_inner, y_inner, 1); gcc_assert (change); return true; } /* The AUTO_INC / POST_MODIFY / PRE_MODIFY sets are modelled to take place during input processing, however, that is benign, since they are paired with reads. */ case MEM: return !rvalue || rtx_equiv_p (&XEXP (x, 0), XEXP (y, 0), rvalue, info); case POST_INC: case POST_DEC: case PRE_INC: case PRE_DEC: return (rtx_equiv_p (&XEXP (x, 0), XEXP (y, 0), 0, info) && rtx_equiv_p (&XEXP (x, 0), XEXP (y, 0), 1, info)); case PARALLEL: /* If this is a top-level PATTERN PARALLEL, we expect the caller to have handled the SET_DESTs. A complex or vector PARALLEL can be identified by having a mode. */ gcc_assert (rvalue < 0 || GET_MODE (x) != VOIDmode); break; case LABEL_REF: /* Check special tablejump match case. */ if (XEXP (y, 0) == info->y_label) return (XEXP (x, 0) == info->x_label); /* We can't assume nonlocal labels have their following insns yet. */ if (LABEL_REF_NONLOCAL_P (x) || LABEL_REF_NONLOCAL_P (y)) return XEXP (x, 0) == XEXP (y, 0); /* Two label-refs are equivalent if they point at labels in the same position in the instruction stream. */ return (next_real_insn (XEXP (x, 0)) == next_real_insn (XEXP (y, 0))); case SYMBOL_REF: return XSTR (x, 0) == XSTR (y, 0); /* Some rtl is guaranteed to be shared, or unique; If we didn't match EQ equality above, they aren't the same. */ case CONST_INT: case CODE_LABEL: return false; default: break; } /* For commutative operations, the RTX match if the operands match in any order. */ if (targetm.commutative_p (x, UNKNOWN)) return ((rtx_equiv_p (&XEXP (x, 0), XEXP (y, 0), rvalue, info) && rtx_equiv_p (&XEXP (x, 1), XEXP (y, 1), rvalue, info)) || (rtx_equiv_p (&XEXP (x, 0), XEXP (y, 1), rvalue, info) && rtx_equiv_p (&XEXP (x, 1), XEXP (y, 0), rvalue, info))); /* Process subexpressions - this is similar to rtx_equal_p. */ length = GET_RTX_LENGTH (code); format = GET_RTX_FORMAT (code); for (i = 0; i < length; ++i) { switch (format[i]) { case 'w': if (XWINT (x, i) != XWINT (y, i)) return false; break; case 'n': case 'i': if (XINT (x, i) != XINT (y, i)) return false; break; case 'V': case 'E': if (XVECLEN (x, i) != XVECLEN (y, i)) return false; if (XVEC (x, i) != 0) { int j; for (j = 0; j < XVECLEN (x, i); ++j) { if (! rtx_equiv_p (&XVECEXP (x, i, j), XVECEXP (y, i, j), rvalue, info)) return false; } } break; case 'e': if (! rtx_equiv_p (&XEXP (x, i), XEXP (y, i), rvalue, info)) return false; break; case 'S': case 's': if ((XSTR (x, i) || XSTR (y, i)) && (! XSTR (x, i) || ! XSTR (y, i) || strcmp (XSTR (x, i), XSTR (y, i)))) return false; break; case 'u': /* These are just backpointers, so they don't matter. */ break; case '0': case 't': break; /* It is believed that rtx's at this level will never contain anything but integers and other rtx's, except for within LABEL_REFs and SYMBOL_REFs. */ default: gcc_unreachable (); } } return true; }
void find_comparison_dom_walker::before_dom_children (basic_block bb) { struct comparison *last_cmp; rtx insn, next, last_clobber; bool last_cmp_valid; bool need_purge = false; bitmap killed; killed = BITMAP_ALLOC (NULL); /* The last comparison that was made. Will be reset to NULL once the flags are clobbered. */ last_cmp = NULL; /* True iff the last comparison has not been clobbered, nor have its inputs. Used to eliminate duplicate compares. */ last_cmp_valid = false; /* The last insn that clobbered the flags, if that insn is of a form that may be valid for eliminating a following compare. To be reset to NULL once the flags are set otherwise. */ last_clobber = NULL; /* Propagate the last live comparison throughout the extended basic block. */ if (single_pred_p (bb)) { last_cmp = (struct comparison *) single_pred (bb)->aux; if (last_cmp) last_cmp_valid = last_cmp->inputs_valid; } for (insn = BB_HEAD (bb); insn; insn = next) { rtx src; next = (insn == BB_END (bb) ? NULL_RTX : NEXT_INSN (insn)); if (!NONDEBUG_INSN_P (insn)) continue; /* Compute the set of registers modified by this instruction. */ bitmap_clear (killed); df_simulate_find_defs (insn, killed); src = conforming_compare (insn); if (src) { enum machine_mode src_mode = GET_MODE (src); rtx eh_note = NULL; if (flag_non_call_exceptions) eh_note = find_reg_note (insn, REG_EH_REGION, NULL); if (!last_cmp_valid) goto dont_delete; /* Take care that it's in the same EH region. */ if (flag_non_call_exceptions && !rtx_equal_p (eh_note, last_cmp->eh_note)) goto dont_delete; /* Make sure the compare is redundant with the previous. */ if (!rtx_equal_p (last_cmp->in_a, XEXP (src, 0)) || !rtx_equal_p (last_cmp->in_b, XEXP (src, 1))) goto dont_delete; /* New mode must be compatible with the previous compare mode. */ { enum machine_mode new_mode = targetm.cc_modes_compatible (last_cmp->orig_mode, src_mode); if (new_mode == VOIDmode) goto dont_delete; if (new_mode != last_cmp->orig_mode) { rtx x, flags = gen_rtx_REG (src_mode, targetm.flags_regnum); /* Generate new comparison for substitution. */ x = gen_rtx_COMPARE (new_mode, XEXP (src, 0), XEXP (src, 1)); x = gen_rtx_SET (VOIDmode, flags, x); if (!validate_change (last_cmp->insn, &PATTERN (last_cmp->insn), x, false)) goto dont_delete; last_cmp->orig_mode = new_mode; } } /* All tests and substitutions succeeded! */ if (eh_note) need_purge = true; delete_insn (insn); continue; dont_delete: last_cmp = XCNEW (struct comparison); last_cmp->insn = insn; last_cmp->prev_clobber = last_clobber; last_cmp->in_a = XEXP (src, 0); last_cmp->in_b = XEXP (src, 1); last_cmp->eh_note = eh_note; last_cmp->orig_mode = src_mode; all_compares.safe_push (last_cmp); /* It's unusual, but be prepared for comparison patterns that also clobber an input, or perhaps a scratch. */ last_clobber = NULL; last_cmp_valid = true; } /* Notice if this instruction kills the flags register. */ else if (bitmap_bit_p (killed, targetm.flags_regnum)) { /* See if this insn could be the "clobber" that eliminates a future comparison. */ last_clobber = (arithmetic_flags_clobber_p (insn) ? insn : NULL); /* In either case, the previous compare is no longer valid. */ last_cmp = NULL; last_cmp_valid = false; continue; } /* Notice if this instruction uses the flags register. */ else if (last_cmp) find_flags_uses_in_insn (last_cmp, insn); /* Notice if any of the inputs to the comparison have changed. */ if (last_cmp_valid && (bitmap_bit_p (killed, REGNO (last_cmp->in_a)) || (REG_P (last_cmp->in_b) && bitmap_bit_p (killed, REGNO (last_cmp->in_b))))) last_cmp_valid = false; }
static bool copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd) { bool anything_changed = false; rtx insn; for (insn = BB_HEAD (bb); ; insn = NEXT_INSN (insn)) { int n_ops, i, alt, predicated; bool is_asm, any_replacements; rtx set; bool replaced[MAX_RECOG_OPERANDS]; bool changed = false; struct kill_set_value_data ksvd; if (!NONDEBUG_INSN_P (insn)) { if (DEBUG_INSN_P (insn)) { rtx loc = INSN_VAR_LOCATION_LOC (insn); if (!VAR_LOC_UNKNOWN_P (loc)) replace_oldest_value_addr (&INSN_VAR_LOCATION_LOC (insn), ALL_REGS, GET_MODE (loc), ADDR_SPACE_GENERIC, insn, vd); } if (insn == BB_END (bb)) break; else continue; } set = single_set (insn); extract_insn (insn); if (! constrain_operands (1)) fatal_insn_not_found (insn); preprocess_constraints (); alt = which_alternative; n_ops = recog_data.n_operands; is_asm = asm_noperands (PATTERN (insn)) >= 0; /* Simplify the code below by rewriting things to reflect matching constraints. Also promote OP_OUT to OP_INOUT in predicated instructions. */ predicated = GET_CODE (PATTERN (insn)) == COND_EXEC; for (i = 0; i < n_ops; ++i) { int matches = recog_op_alt[i][alt].matches; if (matches >= 0) recog_op_alt[i][alt].cl = recog_op_alt[matches][alt].cl; if (matches >= 0 || recog_op_alt[i][alt].matched >= 0 || (predicated && recog_data.operand_type[i] == OP_OUT)) recog_data.operand_type[i] = OP_INOUT; } /* Apply changes to earlier DEBUG_INSNs if possible. */ if (vd->n_debug_insn_changes) note_uses (&PATTERN (insn), cprop_find_used_regs, vd); /* For each earlyclobber operand, zap the value data. */ for (i = 0; i < n_ops; i++) if (recog_op_alt[i][alt].earlyclobber) kill_value (recog_data.operand[i], vd); /* Within asms, a clobber cannot overlap inputs or outputs. I wouldn't think this were true for regular insns, but scan_rtx treats them like that... */ note_stores (PATTERN (insn), kill_clobbered_value, vd); /* Kill all auto-incremented values. */ /* ??? REG_INC is useless, since stack pushes aren't done that way. */ for_each_rtx (&PATTERN (insn), kill_autoinc_value, vd); /* Kill all early-clobbered operands. */ for (i = 0; i < n_ops; i++) if (recog_op_alt[i][alt].earlyclobber) kill_value (recog_data.operand[i], vd); /* Special-case plain move instructions, since we may well be able to do the move from a different register class. */ if (set && REG_P (SET_SRC (set))) { rtx src = SET_SRC (set); unsigned int regno = REGNO (src); enum machine_mode mode = GET_MODE (src); unsigned int i; rtx new_rtx; /* If we are accessing SRC in some mode other that what we set it in, make sure that the replacement is valid. */ if (mode != vd->e[regno].mode) { if (hard_regno_nregs[regno][mode] > hard_regno_nregs[regno][vd->e[regno].mode]) goto no_move_special_case; /* And likewise, if we are narrowing on big endian the transformation is also invalid. */ if (hard_regno_nregs[regno][mode] < hard_regno_nregs[regno][vd->e[regno].mode] && (GET_MODE_SIZE (vd->e[regno].mode) > UNITS_PER_WORD ? WORDS_BIG_ENDIAN : BYTES_BIG_ENDIAN)) goto no_move_special_case; } /* If the destination is also a register, try to find a source register in the same class. */ if (REG_P (SET_DEST (set))) { new_rtx = find_oldest_value_reg (REGNO_REG_CLASS (regno), src, vd); if (new_rtx && validate_change (insn, &SET_SRC (set), new_rtx, 0)) { if (dump_file) fprintf (dump_file, "insn %u: replaced reg %u with %u\n", INSN_UID (insn), regno, REGNO (new_rtx)); changed = true; goto did_replacement; } /* We need to re-extract as validate_change clobbers recog_data. */ extract_insn (insn); if (! constrain_operands (1)) fatal_insn_not_found (insn); preprocess_constraints (); } /* Otherwise, try all valid registers and see if its valid. */ for (i = vd->e[regno].oldest_regno; i != regno; i = vd->e[i].next_regno) { new_rtx = maybe_mode_change (vd->e[i].mode, vd->e[regno].mode, mode, i, regno); if (new_rtx != NULL_RTX) { if (validate_change (insn, &SET_SRC (set), new_rtx, 0)) { ORIGINAL_REGNO (new_rtx) = ORIGINAL_REGNO (src); REG_ATTRS (new_rtx) = REG_ATTRS (src); REG_POINTER (new_rtx) = REG_POINTER (src); if (dump_file) fprintf (dump_file, "insn %u: replaced reg %u with %u\n", INSN_UID (insn), regno, REGNO (new_rtx)); changed = true; goto did_replacement; } /* We need to re-extract as validate_change clobbers recog_data. */ extract_insn (insn); if (! constrain_operands (1)) fatal_insn_not_found (insn); preprocess_constraints (); } } } no_move_special_case: any_replacements = false; /* For each input operand, replace a hard register with the eldest live copy that's in an appropriate register class. */ for (i = 0; i < n_ops; i++) { replaced[i] = false; /* Don't scan match_operand here, since we've no reg class information to pass down. Any operands that we could substitute in will be represented elsewhere. */ if (recog_data.constraints[i][0] == '\0') continue; /* Don't replace in asms intentionally referencing hard regs. */ if (is_asm && REG_P (recog_data.operand[i]) && (REGNO (recog_data.operand[i]) == ORIGINAL_REGNO (recog_data.operand[i]))) continue; if (recog_data.operand_type[i] == OP_IN) { if (recog_op_alt[i][alt].is_address) replaced[i] = replace_oldest_value_addr (recog_data.operand_loc[i], recog_op_alt[i][alt].cl, VOIDmode, ADDR_SPACE_GENERIC, insn, vd); else if (REG_P (recog_data.operand[i])) replaced[i] = replace_oldest_value_reg (recog_data.operand_loc[i], recog_op_alt[i][alt].cl, insn, vd); else if (MEM_P (recog_data.operand[i])) replaced[i] = replace_oldest_value_mem (recog_data.operand[i], insn, vd); } else if (MEM_P (recog_data.operand[i])) replaced[i] = replace_oldest_value_mem (recog_data.operand[i], insn, vd); /* If we performed any replacement, update match_dups. */ if (replaced[i]) { int j; rtx new_rtx; new_rtx = *recog_data.operand_loc[i]; recog_data.operand[i] = new_rtx; for (j = 0; j < recog_data.n_dups; j++) if (recog_data.dup_num[j] == i) validate_unshare_change (insn, recog_data.dup_loc[j], new_rtx, 1); any_replacements = true; } } if (any_replacements) { if (! apply_change_group ()) { for (i = 0; i < n_ops; i++) if (replaced[i]) { rtx old = *recog_data.operand_loc[i]; recog_data.operand[i] = old; } if (dump_file) fprintf (dump_file, "insn %u: reg replacements not verified\n", INSN_UID (insn)); } else changed = true; } did_replacement: if (changed) { anything_changed = true; /* If something changed, perhaps further changes to earlier DEBUG_INSNs can be applied. */ if (vd->n_debug_insn_changes) note_uses (&PATTERN (insn), cprop_find_used_regs, vd); } ksvd.vd = vd; ksvd.ignore_set_reg = NULL_RTX; /* Clobber call-clobbered registers. */ if (CALL_P (insn)) { unsigned int set_regno = INVALID_REGNUM; unsigned int set_nregs = 0; unsigned int regno; rtx exp; hard_reg_set_iterator hrsi; for (exp = CALL_INSN_FUNCTION_USAGE (insn); exp; exp = XEXP (exp, 1)) { rtx x = XEXP (exp, 0); if (GET_CODE (x) == SET) { rtx dest = SET_DEST (x); kill_value (dest, vd); set_value_regno (REGNO (dest), GET_MODE (dest), vd); copy_value (dest, SET_SRC (x), vd); ksvd.ignore_set_reg = dest; set_regno = REGNO (dest); set_nregs = hard_regno_nregs[set_regno][GET_MODE (dest)]; break; } } EXECUTE_IF_SET_IN_HARD_REG_SET (regs_invalidated_by_call, 0, regno, hrsi) if (regno < set_regno || regno >= set_regno + set_nregs) kill_value_regno (regno, 1, vd); /* If SET was seen in CALL_INSN_FUNCTION_USAGE, and SET_SRC of the SET isn't in regs_invalidated_by_call hard reg set, but instead among CLOBBERs on the CALL_INSN, we could wrongly assume the value in it is still live. */ if (ksvd.ignore_set_reg) note_stores (PATTERN (insn), kill_clobbered_value, vd); } /* Notice stores. */ note_stores (PATTERN (insn), kill_set_value, &ksvd); /* Notice copies. */ if (set && REG_P (SET_DEST (set)) && REG_P (SET_SRC (set))) copy_value (SET_DEST (set), SET_SRC (set), vd); if (insn == BB_END (bb)) break; } return anything_changed; }
static int reload_cse_simplify_set (rtx set, rtx insn) { int did_change = 0; int dreg; rtx src; enum reg_class dclass; int old_cost; cselib_val *val; struct elt_loc_list *l; #ifdef LOAD_EXTEND_OP enum rtx_code extend_op = UNKNOWN; #endif bool speed = optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn)); dreg = true_regnum (SET_DEST (set)); if (dreg < 0) return 0; src = SET_SRC (set); if (side_effects_p (src) || true_regnum (src) >= 0) return 0; dclass = REGNO_REG_CLASS (dreg); #ifdef LOAD_EXTEND_OP /* When replacing a memory with a register, we need to honor assumptions that combine made wrt the contents of sign bits. We'll do this by generating an extend instruction instead of a reg->reg copy. Thus the destination must be a register that we can widen. */ if (MEM_P (src) && GET_MODE_BITSIZE (GET_MODE (src)) < BITS_PER_WORD && (extend_op = LOAD_EXTEND_OP (GET_MODE (src))) != UNKNOWN && !REG_P (SET_DEST (set))) return 0; #endif val = cselib_lookup (src, GET_MODE (SET_DEST (set)), 0); if (! val) return 0; /* If memory loads are cheaper than register copies, don't change them. */ if (MEM_P (src)) old_cost = MEMORY_MOVE_COST (GET_MODE (src), dclass, 1); else if (REG_P (src)) old_cost = REGISTER_MOVE_COST (GET_MODE (src), REGNO_REG_CLASS (REGNO (src)), dclass); else old_cost = rtx_cost (src, SET, speed); for (l = val->locs; l; l = l->next) { rtx this_rtx = l->loc; int this_cost; if (CONSTANT_P (this_rtx) && ! references_value_p (this_rtx, 0)) { #ifdef LOAD_EXTEND_OP if (extend_op != UNKNOWN) { HOST_WIDE_INT this_val; /* ??? I'm lazy and don't wish to handle CONST_DOUBLE. Other constants, such as SYMBOL_REF, cannot be extended. */ if (GET_CODE (this_rtx) != CONST_INT) continue; this_val = INTVAL (this_rtx); switch (extend_op) { case ZERO_EXTEND: this_val &= GET_MODE_MASK (GET_MODE (src)); break; case SIGN_EXTEND: /* ??? In theory we're already extended. */ if (this_val == trunc_int_for_mode (this_val, GET_MODE (src))) break; default: gcc_unreachable (); } this_rtx = GEN_INT (this_val); } #endif this_cost = rtx_cost (this_rtx, SET, speed); } else if (REG_P (this_rtx)) { #ifdef LOAD_EXTEND_OP if (extend_op != UNKNOWN) { this_rtx = gen_rtx_fmt_e (extend_op, word_mode, this_rtx); this_cost = rtx_cost (this_rtx, SET, speed); } else #endif this_cost = REGISTER_MOVE_COST (GET_MODE (this_rtx), REGNO_REG_CLASS (REGNO (this_rtx)), dclass); } else continue; /* If equal costs, prefer registers over anything else. That tends to lead to smaller instructions on some machines. */ if (this_cost < old_cost || (this_cost == old_cost && REG_P (this_rtx) && !REG_P (SET_SRC (set)))) { #ifdef LOAD_EXTEND_OP if (GET_MODE_BITSIZE (GET_MODE (SET_DEST (set))) < BITS_PER_WORD && extend_op != UNKNOWN #ifdef CANNOT_CHANGE_MODE_CLASS && !CANNOT_CHANGE_MODE_CLASS (GET_MODE (SET_DEST (set)), word_mode, REGNO_REG_CLASS (REGNO (SET_DEST (set)))) #endif ) { rtx wide_dest = gen_rtx_REG (word_mode, REGNO (SET_DEST (set))); ORIGINAL_REGNO (wide_dest) = ORIGINAL_REGNO (SET_DEST (set)); validate_change (insn, &SET_DEST (set), wide_dest, 1); } #endif validate_unshare_change (insn, &SET_SRC (set), this_rtx, 1); old_cost = this_cost, did_change = 1; } } return did_change; }