static basic_block create_pre_exit (int n_entities, int *entity_map, const int *num_modes) { edge eg; edge_iterator ei; basic_block pre_exit; /* The only non-call predecessor at this stage is a block with a fallthrough edge; there can be at most one, but there could be none at all, e.g. when exit is called. */ pre_exit = 0; FOR_EACH_EDGE (eg, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds) if (eg->flags & EDGE_FALLTHRU) { basic_block src_bb = eg->src; rtx_insn *last_insn; rtx ret_reg; gcc_assert (!pre_exit); /* If this function returns a value at the end, we have to insert the final mode switch before the return value copy to its hard register. */ if (EDGE_COUNT (EXIT_BLOCK_PTR_FOR_FN (cfun)->preds) == 1 && NONJUMP_INSN_P ((last_insn = BB_END (src_bb))) && GET_CODE (PATTERN (last_insn)) == USE && GET_CODE ((ret_reg = XEXP (PATTERN (last_insn), 0))) == REG) { int ret_start = REGNO (ret_reg); int nregs = hard_regno_nregs[ret_start][GET_MODE (ret_reg)]; int ret_end = ret_start + nregs; bool short_block = false; bool multi_reg_return = false; bool forced_late_switch = false; rtx_insn *before_return_copy; do { rtx_insn *return_copy = PREV_INSN (last_insn); rtx return_copy_pat, copy_reg; int copy_start, copy_num; int j; if (NONDEBUG_INSN_P (return_copy)) { /* When using SJLJ exceptions, the call to the unregister function is inserted between the clobber of the return value and the copy. We do not want to split the block before this or any other call; if we have not found the copy yet, the copy must have been deleted. */ if (CALL_P (return_copy)) { short_block = true; break; } return_copy_pat = PATTERN (return_copy); switch (GET_CODE (return_copy_pat)) { case USE: /* Skip USEs of multiple return registers. __builtin_apply pattern is also handled here. */ if (GET_CODE (XEXP (return_copy_pat, 0)) == REG && (targetm.calls.function_value_regno_p (REGNO (XEXP (return_copy_pat, 0))))) { multi_reg_return = true; last_insn = return_copy; continue; } break; case ASM_OPERANDS: /* Skip barrier insns. */ if (!MEM_VOLATILE_P (return_copy_pat)) break; /* Fall through. */ case ASM_INPUT: case UNSPEC_VOLATILE: last_insn = return_copy; continue; default: break; } /* If the return register is not (in its entirety) likely spilled, the return copy might be partially or completely optimized away. */ return_copy_pat = single_set (return_copy); if (!return_copy_pat) { return_copy_pat = PATTERN (return_copy); if (GET_CODE (return_copy_pat) != CLOBBER) break; else if (!optimize) { /* This might be (clobber (reg [<result>])) when not optimizing. Then check if the previous insn is the clobber for the return register. */ copy_reg = SET_DEST (return_copy_pat); if (GET_CODE (copy_reg) == REG && !HARD_REGISTER_NUM_P (REGNO (copy_reg))) { if (INSN_P (PREV_INSN (return_copy))) { return_copy = PREV_INSN (return_copy); return_copy_pat = PATTERN (return_copy); if (GET_CODE (return_copy_pat) != CLOBBER) break; } } } } copy_reg = SET_DEST (return_copy_pat); if (GET_CODE (copy_reg) == REG) copy_start = REGNO (copy_reg); else if (GET_CODE (copy_reg) == SUBREG && GET_CODE (SUBREG_REG (copy_reg)) == REG) copy_start = REGNO (SUBREG_REG (copy_reg)); else { /* When control reaches end of non-void function, there are no return copy insns at all. This avoids an ice on that invalid function. */ if (ret_start + nregs == ret_end) short_block = true; break; } if (!targetm.calls.function_value_regno_p (copy_start)) copy_num = 0; else copy_num = hard_regno_nregs[copy_start][GET_MODE (copy_reg)]; /* If the return register is not likely spilled, - as is the case for floating point on SH4 - then it might be set by an arithmetic operation that needs a different mode than the exit block. */ for (j = n_entities - 1; j >= 0; j--) { int e = entity_map[j]; int mode = targetm.mode_switching.needed (e, return_copy); if (mode != num_modes[e] && mode != targetm.mode_switching.exit (e)) break; } if (j >= 0) { /* __builtin_return emits a sequence of loads to all return registers. One of them might require another mode than MODE_EXIT, even if it is unrelated to the return value, so we want to put the final mode switch after it. */ if (multi_reg_return && targetm.calls.function_value_regno_p (copy_start)) forced_late_switch = true; /* For the SH4, floating point loads depend on fpscr, thus we might need to put the final mode switch after the return value copy. That is still OK, because a floating point return value does not conflict with address reloads. */ if (copy_start >= ret_start && copy_start + copy_num <= ret_end && OBJECT_P (SET_SRC (return_copy_pat))) forced_late_switch = true; break; } if (copy_num == 0) { last_insn = return_copy; continue; } if (copy_start >= ret_start && copy_start + copy_num <= ret_end) nregs -= copy_num; else if (!multi_reg_return || !targetm.calls.function_value_regno_p (copy_start)) break; last_insn = return_copy; } /* ??? Exception handling can lead to the return value copy being already separated from the return value use, as in unwind-dw2.c . Similarly, conditionally returning without a value, and conditionally using builtin_return can lead to an isolated use. */ if (return_copy == BB_HEAD (src_bb)) { short_block = true; break; } last_insn = return_copy; } while (nregs); /* If we didn't see a full return value copy, verify that there is a plausible reason for this. If some, but not all of the return register is likely spilled, we can expect that there is a copy for the likely spilled part. */ gcc_assert (!nregs || forced_late_switch || short_block || !(targetm.class_likely_spilled_p (REGNO_REG_CLASS (ret_start))) || (nregs != hard_regno_nregs[ret_start][GET_MODE (ret_reg)]) /* For multi-hard-register floating point values, sometimes the likely-spilled part is ordinarily copied first, then the other part is set with an arithmetic operation. This doesn't actually cause reload failures, so let it pass. */ || (GET_MODE_CLASS (GET_MODE (ret_reg)) != MODE_INT && nregs != 1)); if (!NOTE_INSN_BASIC_BLOCK_P (last_insn)) { before_return_copy = emit_note_before (NOTE_INSN_DELETED, last_insn); /* Instructions preceding LAST_INSN in the same block might require a different mode than MODE_EXIT, so if we might have such instructions, keep them in a separate block from pre_exit. */ src_bb = split_block (src_bb, PREV_INSN (before_return_copy))->dest; } else before_return_copy = last_insn; pre_exit = split_block (src_bb, before_return_copy)->src; } else { pre_exit = split_edge (eg); } } return pre_exit; }
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 void adjust_frame_related_expr (rtx last_sp_set, rtx insn, HOST_WIDE_INT this_adjust) { rtx note = find_reg_note (last_sp_set, REG_FRAME_RELATED_EXPR, NULL_RTX); rtx new_expr = NULL_RTX; if (note == NULL_RTX && RTX_FRAME_RELATED_P (insn)) return; if (note && GET_CODE (XEXP (note, 0)) == SEQUENCE && XVECLEN (XEXP (note, 0), 0) >= 2) { rtx expr = XEXP (note, 0); rtx last = XVECEXP (expr, 0, XVECLEN (expr, 0) - 1); int i; if (GET_CODE (last) == SET && RTX_FRAME_RELATED_P (last) == RTX_FRAME_RELATED_P (insn) && SET_DEST (last) == stack_pointer_rtx && GET_CODE (SET_SRC (last)) == PLUS && XEXP (SET_SRC (last), 0) == stack_pointer_rtx && GET_CODE (XEXP (SET_SRC (last), 1)) == CONST_INT) { XEXP (SET_SRC (last), 1) = GEN_INT (INTVAL (XEXP (SET_SRC (last), 1)) + this_adjust); return; } new_expr = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (XVECLEN (expr, 0) + 1)); for (i = 0; i < XVECLEN (expr, 0); i++) XVECEXP (new_expr, 0, i) = XVECEXP (expr, 0, i); } else { new_expr = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (2)); if (note) XVECEXP (new_expr, 0, 0) = XEXP (note, 0); else { rtx expr = copy_rtx (single_set_for_csa (last_sp_set)); XEXP (SET_SRC (expr), 1) = GEN_INT (INTVAL (XEXP (SET_SRC (expr), 1)) - this_adjust); RTX_FRAME_RELATED_P (expr) = 1; XVECEXP (new_expr, 0, 0) = expr; } } XVECEXP (new_expr, 0, XVECLEN (new_expr, 0) - 1) = copy_rtx (single_set_for_csa (insn)); RTX_FRAME_RELATED_P (XVECEXP (new_expr, 0, XVECLEN (new_expr, 0) - 1)) = RTX_FRAME_RELATED_P (insn); if (note) XEXP (note, 0) = new_expr; else REG_NOTES (last_sp_set) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, new_expr, REG_NOTES (last_sp_set)); }
static void union_defs (struct df *df, struct ref *use, struct web_entry *def_entry, struct web_entry *use_entry) { rtx insn = DF_REF_INSN (use); struct df_link *link = DF_REF_CHAIN (use); struct df_link *use_link = DF_INSN_USES (df, insn); struct df_link *def_link = DF_INSN_DEFS (df, insn); rtx set = single_set (insn); /* Some instructions may use match_dup for their operands. In case the operands are dead, we will assign them different pseudos, creating invalid instructions, so union all uses of the same operand for each insn. */ while (use_link) { if (use != use_link->ref && DF_REF_REAL_REG (use) == DF_REF_REAL_REG (use_link->ref)) unionfind_union (use_entry + DF_REF_ID (use), use_entry + DF_REF_ID (use_link->ref)); use_link = use_link->next; } /* Recognize trivial noop moves and attempt to keep them as noop. While most of noop moves should be removed, we still keep some of them at libcall boundaries and such. */ if (set && SET_SRC (set) == DF_REF_REG (use) && SET_SRC (set) == SET_DEST (set)) { while (def_link) { if (DF_REF_REAL_REG (use) == DF_REF_REAL_REG (def_link->ref)) unionfind_union (use_entry + DF_REF_ID (use), def_entry + DF_REF_ID (def_link->ref)); def_link = def_link->next; } } while (link) { unionfind_union (use_entry + DF_REF_ID (use), def_entry + DF_REF_ID (link->ref)); link = link->next; } /* A READ_WRITE use requires the corresponding def to be in the same register. Find it and union. */ if (use->flags & DF_REF_READ_WRITE) { struct df_link *link = DF_INSN_DEFS (df, DF_REF_INSN (use)); while (link) { if (DF_REF_REAL_REG (link->ref) == DF_REF_REAL_REG (use)) unionfind_union (use_entry + DF_REF_ID (use), def_entry + DF_REF_ID (link->ref)); link = link->next; } } }
/* Function to check whether the OP is a valid stack push/pop operation. For a valid stack operation, it must satisfy following conditions: 1. Consecutive registers push/pop operations. 2. Valid $fp/$gp/$lp push/pop operations. 3. The last element must be stack adjustment rtx. See the prologue/epilogue implementation for details. */ bool nds32_valid_stack_push_pop_p (rtx op, bool push_p) { int index; int total_count; int rest_count; int first_regno; int save_fp, save_gp, save_lp; rtx elt; rtx elt_reg; rtx elt_mem; rtx elt_plus; /* Get the counts of elements in the parallel rtx. */ total_count = XVECLEN (op, 0); /* Perform some quick check for that every element should be 'set'. */ for (index = 0; index < total_count; index++) { elt = XVECEXP (op, 0, index); if (GET_CODE (elt) != SET) return false; } /* For push operation, the parallel rtx looks like: (parallel [(set (mem (plus (reg:SI SP_REGNUM) (const_int -32))) (reg:SI Rb)) (set (mem (plus (reg:SI SP_REGNUM) (const_int -28))) (reg:SI Rb+1)) ... (set (mem (plus (reg:SI SP_REGNUM) (const_int -16))) (reg:SI Re)) (set (mem (plus (reg:SI SP_REGNUM) (const_int -12))) (reg:SI FP_REGNUM)) (set (mem (plus (reg:SI SP_REGNUM) (const_int -8))) (reg:SI GP_REGNUM)) (set (mem (plus (reg:SI SP_REGNUM) (const_int -4))) (reg:SI LP_REGNUM)) (set (reg:SI SP_REGNUM) (plus (reg:SI SP_REGNUM) (const_int -32)))]) For pop operation, the parallel rtx looks like: (parallel [(set (reg:SI Rb) (mem (reg:SI SP_REGNUM))) (set (reg:SI Rb+1) (mem (plus (reg:SI SP_REGNUM) (const_int 4)))) ... (set (reg:SI Re) (mem (plus (reg:SI SP_REGNUM) (const_int 16)))) (set (reg:SI FP_REGNUM) (mem (plus (reg:SI SP_REGNUM) (const_int 20)))) (set (reg:SI GP_REGNUM) (mem (plus (reg:SI SP_REGNUM) (const_int 24)))) (set (reg:SI LP_REGNUM) (mem (plus (reg:SI SP_REGNUM) (const_int 28)))) (set (reg:SI SP_REGNUM) (plus (reg:SI SP_REGNUM) (const_int 32)))]) */ /* 1. Consecutive registers push/pop operations. We need to calculate how many registers should be consecutive. The $sp adjustment rtx, $fp push rtx, $gp push rtx, and $lp push rtx are excluded. */ /* Detect whether we have $fp, $gp, or $lp in the parallel rtx. */ save_fp = reg_mentioned_p (gen_rtx_REG (SImode, FP_REGNUM), op); save_gp = reg_mentioned_p (gen_rtx_REG (SImode, GP_REGNUM), op); save_lp = reg_mentioned_p (gen_rtx_REG (SImode, LP_REGNUM), op); /* Exclude last $sp adjustment rtx. */ rest_count = total_count - 1; /* Exclude $fp, $gp, and $lp if they are in the parallel rtx. */ if (save_fp) rest_count--; if (save_gp) rest_count--; if (save_lp) rest_count--; if (rest_count > 0) { elt = XVECEXP (op, 0, 0); /* Pick up register element. */ elt_reg = push_p ? SET_SRC (elt) : SET_DEST (elt); first_regno = REGNO (elt_reg); /* The 'push' operation is a kind of store operation. The 'pop' operation is a kind of load operation. Pass corresponding false/true as second argument (bool load_p). The par_index is supposed to start with index 0. */ if (!nds32_consecutive_registers_load_store_p (op, !push_p ? true : false, 0, first_regno, rest_count)) return false; } /* 2. Valid $fp/$gp/$lp push/pop operations. Remember to set start index for checking them. */ /* The rest_count is the start index for checking $fp/$gp/$lp. */ index = rest_count; /* If index < 0, this parallel rtx is definitely not a valid stack push/pop operation. */ if (index < 0) return false; /* Check $fp/$gp/$lp one by one. We use 'push_p' to pick up reg rtx and mem rtx. */ if (save_fp) { elt = XVECEXP (op, 0, index); elt_mem = push_p ? SET_DEST (elt) : SET_SRC (elt); elt_reg = push_p ? SET_SRC (elt) : SET_DEST (elt); index++; if (GET_CODE (elt_mem) != MEM || GET_CODE (elt_reg) != REG || REGNO (elt_reg) != FP_REGNUM) return false; } if (save_gp) { elt = XVECEXP (op, 0, index); elt_mem = push_p ? SET_DEST (elt) : SET_SRC (elt); elt_reg = push_p ? SET_SRC (elt) : SET_DEST (elt); index++; if (GET_CODE (elt_mem) != MEM || GET_CODE (elt_reg) != REG || REGNO (elt_reg) != GP_REGNUM) return false; } if (save_lp) { elt = XVECEXP (op, 0, index); elt_mem = push_p ? SET_DEST (elt) : SET_SRC (elt); elt_reg = push_p ? SET_SRC (elt) : SET_DEST (elt); index++; if (GET_CODE (elt_mem) != MEM || GET_CODE (elt_reg) != REG || REGNO (elt_reg) != LP_REGNUM) return false; } /* 3. The last element must be stack adjustment rtx. Its form of rtx should be: (set (reg:SI SP_REGNUM) (plus (reg:SI SP_REGNUM) (const_int X))) The X could be positive or negative value. */ /* Pick up the last element. */ elt = XVECEXP (op, 0, total_count - 1); /* Extract its destination and source rtx. */ elt_reg = SET_DEST (elt); elt_plus = SET_SRC (elt); /* Check this is (set (stack_reg) (plus stack_reg const)) pattern. */ if (GET_CODE (elt_reg) != REG || GET_CODE (elt_plus) != PLUS || REGNO (elt_reg) != SP_REGNUM) return false; /* Pass all test, this is a valid rtx. */ return true; }
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))); /* 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 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; rtx link; bool replaced[MAX_RECOG_OPERANDS]; bool changed = false; 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); /* If we have dead sets in the insn, then we need to note these as we would clobbers. */ for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) { if (REG_NOTE_KIND (link) == REG_UNUSED) { kill_value (XEXP (link, 0), vd); /* Furthermore, if the insn looked like a single-set, but the dead store kills the source value of that set, then we can no-longer use the plain move special case below. */ if (set && reg_overlap_mentioned_p (XEXP (link, 0), SET_SRC (set))) set = NULL; } } /* 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); } /* Clobber call-clobbered registers. */ if (CALL_P (insn)) for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i)) kill_value_regno (i, 1, vd); /* Notice stores. */ note_stores (PATTERN (insn), kill_set_value, vd); /* 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; }
/* Do transforms 3) and 4) on INSN if applicable. */ static bool mod_subtract_transform (rtx insn) { rtx set, set_src, set_dest, op1, op2, value, histogram; enum rtx_code code; enum machine_mode mode; gcov_type wrong_values, counts[2], count, all; edge e; int i, prob1, prob2; set = single_set (insn); if (!set) return false; set_src = SET_SRC (set); set_dest = SET_DEST (set); code = GET_CODE (set_src); mode = GET_MODE (set_dest); if (code != UMOD) return false; op1 = XEXP (set_src, 0); op2 = XEXP (set_src, 1); for (histogram = REG_NOTES (insn); histogram; histogram = XEXP (histogram, 1)) if (REG_NOTE_KIND (histogram) == REG_VALUE_PROFILE && XEXP (XEXP (histogram, 0), 0) == GEN_INT (HIST_TYPE_INTERVAL)) break; if (!histogram) return false; histogram = XEXP (XEXP (histogram, 0), 1); value = XEXP (histogram, 0); histogram = XEXP (histogram, 1); all = 0; for (i = 0; i < 2; i++) { counts[i] = INTVAL (XEXP (histogram, 0)); all += counts[i]; histogram = XEXP (histogram, 1); } wrong_values = INTVAL (XEXP (histogram, 0)); histogram = XEXP (histogram, 1); wrong_values += INTVAL (XEXP (histogram, 0)); all += wrong_values; /* We require that we use just subtractions in at least 50% of all evaluations. */ count = 0; for (i = 0; i < 2; i++) { count += counts[i]; if (count * 2 >= all) break; } if (i == 2) return false; if (dump_file) fprintf (dump_file, "Mod subtract transformation on insn %d\n", INSN_UID (insn)); /* Compute probability of taking the optimal path(s). */ prob1 = (counts[0] * REG_BR_PROB_BASE + all / 2) / all; prob2 = (counts[1] * REG_BR_PROB_BASE + all / 2) / all; e = split_block (BLOCK_FOR_INSN (insn), PREV_INSN (insn)); delete_insn (insn); insert_insn_on_edge ( gen_mod_subtract (mode, code, set_dest, op1, op2, i, prob1, prob2), e); return true; }
static void mark_all_labels (rtx f) { rtx insn; rtx prev_nonjump_insn = NULL; for (insn = f; insn; insn = NEXT_INSN (insn)) if (INSN_P (insn)) { mark_jump_label (PATTERN (insn), insn, 0); /* If the previous non-jump insn sets something to a label, something that this jump insn uses, make that label the primary target of this insn if we don't yet have any. That previous insn must be a single_set and not refer to more than one label. The jump insn must not refer to other labels as jump targets and must be a plain (set (pc) ...), maybe in a parallel, and may refer to the item being set only directly or as one of the arms in an IF_THEN_ELSE. */ if (! INSN_DELETED_P (insn) && JUMP_P (insn) && JUMP_LABEL (insn) == NULL) { rtx label_note = NULL; rtx pc = pc_set (insn); rtx pc_src = pc != NULL ? SET_SRC (pc) : NULL; if (prev_nonjump_insn != NULL) label_note = find_reg_note (prev_nonjump_insn, REG_LABEL_OPERAND, NULL); if (label_note != NULL && pc_src != NULL) { rtx label_set = single_set (prev_nonjump_insn); rtx label_dest = label_set != NULL ? SET_DEST (label_set) : NULL; if (label_set != NULL /* The source must be the direct LABEL_REF, not a PLUS, UNSPEC, IF_THEN_ELSE etc. */ && GET_CODE (SET_SRC (label_set)) == LABEL_REF && (rtx_equal_p (label_dest, pc_src) || (GET_CODE (pc_src) == IF_THEN_ELSE && (rtx_equal_p (label_dest, XEXP (pc_src, 1)) || rtx_equal_p (label_dest, XEXP (pc_src, 2)))))) { /* The CODE_LABEL referred to in the note must be the CODE_LABEL in the LABEL_REF of the "set". We can conveniently use it for the marker function, which requires a LABEL_REF wrapping. */ gcc_assert (XEXP (label_note, 0) == XEXP (SET_SRC (label_set), 0)); mark_jump_label_1 (label_set, insn, false, true); gcc_assert (JUMP_LABEL (insn) == XEXP (SET_SRC (label_set), 0)); } } } else if (! INSN_DELETED_P (insn)) prev_nonjump_insn = insn; } else if (LABEL_P (insn)) prev_nonjump_insn = NULL; /* If we are in cfglayout mode, there may be non-insns between the basic blocks. If those non-insns represent tablejump data, they contain label references that we must record. */ if (current_ir_type () == IR_RTL_CFGLAYOUT) { basic_block bb; rtx insn; FOR_EACH_BB (bb) { for (insn = bb->il.rtl->header; insn; insn = NEXT_INSN (insn)) if (INSN_P (insn)) { gcc_assert (JUMP_TABLE_DATA_P (insn)); mark_jump_label (PATTERN (insn), insn, 0); } for (insn = bb->il.rtl->footer; insn; insn = NEXT_INSN (insn)) if (INSN_P (insn)) { gcc_assert (JUMP_TABLE_DATA_P (insn)); mark_jump_label (PATTERN (insn), insn, 0); } } }
/* Do transform 1) on INSN if applicable. */ static bool divmod_fixed_value_transform (rtx insn) { rtx set, set_src, set_dest, op1, op2, value, histogram; enum rtx_code code; enum machine_mode mode; gcov_type val, count, all; edge e; int prob; set = single_set (insn); if (!set) return false; set_src = SET_SRC (set); set_dest = SET_DEST (set); code = GET_CODE (set_src); mode = GET_MODE (set_dest); if (code != DIV && code != MOD && code != UDIV && code != UMOD) return false; op1 = XEXP (set_src, false); op2 = XEXP (set_src, 1); for (histogram = REG_NOTES (insn); histogram; histogram = XEXP (histogram, 1)) if (REG_NOTE_KIND (histogram) == REG_VALUE_PROFILE && XEXP (XEXP (histogram, 0), 0) == GEN_INT (HIST_TYPE_SINGLE_VALUE)) break; if (!histogram) return false; histogram = XEXP (XEXP (histogram, 0), 1); value = XEXP (histogram, 0); histogram = XEXP (histogram, 1); val = INTVAL (XEXP (histogram, 0)); histogram = XEXP (histogram, 1); count = INTVAL (XEXP (histogram, 0)); histogram = XEXP (histogram, 1); all = INTVAL (XEXP (histogram, 0)); /* We require that count be at least half of all; this means that for the transformation to fire the value must be constant at least 50% of time (and 75% gives the guarantee of usage). */ if (!rtx_equal_p (op2, value) || 2 * count < all) return false; if (dump_file) fprintf (dump_file, "Div/mod by constant transformation on insn %d\n", INSN_UID (insn)); /* Compute probability of taking the optimal path. */ prob = (count * REG_BR_PROB_BASE + all / 2) / all; e = split_block (BLOCK_FOR_INSN (insn), PREV_INSN (insn)); delete_insn (insn); insert_insn_on_edge ( gen_divmod_fixed_value (mode, code, set_dest, op1, op2, val, prob), e); return true; }
/* Do transform 2) on INSN if applicable. */ static bool mod_pow2_value_transform (rtx insn) { rtx set, set_src, set_dest, op1, op2, value, histogram; enum rtx_code code; enum machine_mode mode; gcov_type wrong_values, count; edge e; int i, all, prob; set = single_set (insn); if (!set) return false; set_src = SET_SRC (set); set_dest = SET_DEST (set); code = GET_CODE (set_src); mode = GET_MODE (set_dest); if (code != UMOD) return false; op1 = XEXP (set_src, 0); op2 = XEXP (set_src, 1); for (histogram = REG_NOTES (insn); histogram; histogram = XEXP (histogram, 1)) if (REG_NOTE_KIND (histogram) == REG_VALUE_PROFILE && XEXP (XEXP (histogram, 0), 0) == GEN_INT (HIST_TYPE_POW2)) break; if (!histogram) return false; histogram = XEXP (XEXP (histogram, 0), 1); value = XEXP (histogram, 0); histogram = XEXP (histogram, 1); wrong_values =INTVAL (XEXP (histogram, 0)); histogram = XEXP (histogram, 1); count = 0; for (i = 0; i < GET_MODE_BITSIZE (mode); i++) { count += INTVAL (XEXP (histogram, 0)); histogram = XEXP (histogram, 1); } if (!rtx_equal_p (op2, value)) return false; /* We require that we hit a power of two at least half of all evaluations. */ if (count < wrong_values) return false; if (dump_file) fprintf (dump_file, "Mod power of 2 transformation on insn %d\n", INSN_UID (insn)); /* Compute probability of taking the optimal path. */ all = count + wrong_values; prob = (count * REG_BR_PROB_BASE + all / 2) / all; e = split_block (BLOCK_FOR_INSN (insn), PREV_INSN (insn)); delete_insn (insn); insert_insn_on_edge ( gen_mod_pow2 (mode, code, set_dest, op1, op2, prob), e); return true; }
/* Find values inside INSN for that we want to measure histograms for division/modulo optimization and stores them to VALUES. */ static void insn_divmod_values_to_profile (rtx insn, histogram_values *values) { rtx set, set_src, op1, op2; enum machine_mode mode; histogram_value hist; if (!INSN_P (insn)) return; set = single_set (insn); if (!set) return; mode = GET_MODE (SET_DEST (set)); if (!INTEGRAL_MODE_P (mode)) return; set_src = SET_SRC (set); switch (GET_CODE (set_src)) { case DIV: case MOD: case UDIV: case UMOD: op1 = XEXP (set_src, 0); op2 = XEXP (set_src, 1); if (side_effects_p (op2)) return; /* Check for a special case where the divisor is power of 2. */ if ((GET_CODE (set_src) == UMOD) && !CONSTANT_P (op2)) { hist = ggc_alloc (sizeof (*hist)); hist->value = op2; hist->seq = NULL_RTX; hist->mode = mode; hist->insn = insn; hist->type = HIST_TYPE_POW2; hist->hdata.pow2.may_be_other = 1; VEC_safe_push (histogram_value, *values, hist); } /* Check whether the divisor is not in fact a constant. */ if (!CONSTANT_P (op2)) { hist = ggc_alloc (sizeof (*hist)); hist->value = op2; hist->mode = mode; hist->seq = NULL_RTX; hist->insn = insn; hist->type = HIST_TYPE_SINGLE_VALUE; VEC_safe_push (histogram_value, *values, hist); } /* For mod, check whether it is not often a noop (or replaceable by a few subtractions). */ if (GET_CODE (set_src) == UMOD && !side_effects_p (op1)) { rtx tmp; hist = ggc_alloc (sizeof (*hist)); start_sequence (); tmp = simplify_gen_binary (DIV, mode, copy_rtx (op1), copy_rtx (op2)); hist->value = force_operand (tmp, NULL_RTX); hist->seq = get_insns (); end_sequence (); hist->mode = mode; hist->insn = insn; hist->type = HIST_TYPE_INTERVAL; hist->hdata.intvl.int_start = 0; hist->hdata.intvl.steps = 2; hist->hdata.intvl.may_be_less = 1; hist->hdata.intvl.may_be_more = 1; VEC_safe_push (histogram_value, *values, hist); } return; default: return; } }
void vax_notice_update_cc (rtx exp, rtx insn ATTRIBUTE_UNUSED) { if (GET_CODE (exp) == SET) { if (GET_CODE (SET_SRC (exp)) == CALL) CC_STATUS_INIT; else if (GET_CODE (SET_DEST (exp)) != ZERO_EXTRACT && GET_CODE (SET_DEST (exp)) != PC) { cc_status.flags = 0; /* The integer operations below don't set carry or set it in an incompatible way. That's ok though as the Z bit is all we need when doing unsigned comparisons on the result of these insns (since they're always with 0). Set CC_NO_OVERFLOW to generate the correct unsigned branches. */ switch (GET_CODE (SET_SRC (exp))) { case NEG: if (GET_MODE_CLASS (GET_MODE (exp)) == MODE_FLOAT) break; case AND: case IOR: case XOR: case NOT: case MEM: case REG: cc_status.flags = CC_NO_OVERFLOW; break; default: break; } cc_status.value1 = SET_DEST (exp); cc_status.value2 = SET_SRC (exp); } } else if (GET_CODE (exp) == PARALLEL && GET_CODE (XVECEXP (exp, 0, 0)) == SET) { if (GET_CODE (SET_SRC (XVECEXP (exp, 0, 0))) == CALL) CC_STATUS_INIT; else if (GET_CODE (SET_DEST (XVECEXP (exp, 0, 0))) != PC) { cc_status.flags = 0; cc_status.value1 = SET_DEST (XVECEXP (exp, 0, 0)); cc_status.value2 = SET_SRC (XVECEXP (exp, 0, 0)); } else /* PARALLELs whose first element sets the PC are aob, sob insns. They do change the cc's. */ CC_STATUS_INIT; } else CC_STATUS_INIT; if (cc_status.value1 && REG_P (cc_status.value1) && cc_status.value2 && reg_overlap_mentioned_p (cc_status.value1, cc_status.value2)) cc_status.value2 = 0; if (cc_status.value1 && MEM_P (cc_status.value1) && cc_status.value2 && MEM_P (cc_status.value2)) cc_status.value2 = 0; /* Actual condition, one line up, should be that value2's address depends on value1, but that is too much of a pain. */ }
/* 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; }
static bool parse_add_or_inc (rtx insn, bool before_mem) { rtx pat = single_set (insn); if (!pat) return false; /* Result must be single reg. */ if (!REG_P (SET_DEST (pat))) return false; if ((GET_CODE (SET_SRC (pat)) != PLUS) && (GET_CODE (SET_SRC (pat)) != MINUS)) return false; if (!REG_P (XEXP (SET_SRC (pat), 0))) return false; inc_insn.insn = insn; inc_insn.pat = pat; inc_insn.reg_res = SET_DEST (pat); inc_insn.reg0 = XEXP (SET_SRC (pat), 0); if (rtx_equal_p (inc_insn.reg_res, inc_insn.reg0)) inc_insn.form = before_mem ? FORM_PRE_INC : FORM_POST_INC; else inc_insn.form = before_mem ? FORM_PRE_ADD : FORM_POST_ADD; if (CONST_INT_P (XEXP (SET_SRC (pat), 1))) { /* Process a = b + c where c is a const. */ inc_insn.reg1_is_const = true; if (GET_CODE (SET_SRC (pat)) == PLUS) { inc_insn.reg1 = XEXP (SET_SRC (pat), 1); inc_insn.reg1_val = INTVAL (inc_insn.reg1); } else { inc_insn.reg1_val = -INTVAL (XEXP (SET_SRC (pat), 1)); inc_insn.reg1 = GEN_INT (inc_insn.reg1_val); } return true; } else if ((HAVE_PRE_MODIFY_REG || HAVE_POST_MODIFY_REG) && (REG_P (XEXP (SET_SRC (pat), 1))) && GET_CODE (SET_SRC (pat)) == PLUS) { /* Process a = b + c where c is a reg. */ inc_insn.reg1 = XEXP (SET_SRC (pat), 1); inc_insn.reg1_is_const = false; if (inc_insn.form == FORM_PRE_INC || inc_insn.form == FORM_POST_INC) return true; else if (rtx_equal_p (inc_insn.reg_res, inc_insn.reg1)) { /* Reverse the two operands and turn *_ADD into *_INC since a = c + a. */ reverse_inc (); inc_insn.form = before_mem ? FORM_PRE_INC : FORM_POST_INC; return true; } else return true; } return false; }
static int is_predicable (struct queue_elem *elem) { rtvec vec = XVEC (elem->data, 4); const char *value; int i; if (! vec) return predicable_default; for (i = GET_NUM_ELEM (vec) - 1; i >= 0; --i) { rtx sub = RTVEC_ELT (vec, i); switch (GET_CODE (sub)) { case SET_ATTR: if (strcmp (XSTR (sub, 0), "predicable") == 0) { value = XSTR (sub, 1); goto found; } break; case SET_ATTR_ALTERNATIVE: if (strcmp (XSTR (sub, 0), "predicable") == 0) { message_with_line (elem->lineno, "multiple alternatives for `predicable'"); errors = 1; return 0; } break; case SET: if (GET_CODE (SET_DEST (sub)) != ATTR || strcmp (XSTR (SET_DEST (sub), 0), "predicable") != 0) break; sub = SET_SRC (sub); if (GET_CODE (sub) == CONST_STRING) { value = XSTR (sub, 0); goto found; } /* ??? It would be possible to handle this if we really tried. It's not easy though, and I'm not going to bother until it really proves necessary. */ message_with_line (elem->lineno, "non-constant value for `predicable'"); errors = 1; return 0; default: abort (); } } return predicable_default; found: /* Verify that predicability does not vary on the alternative. */ /* ??? It should be possible to handle this by simply eliminating the non-predicable alternatives from the insn. FRV would like to do this. Delay this until we've got the basics solid. */ if (strchr (value, ',') != NULL) { message_with_line (elem->lineno, "multiple alternatives for `predicable'"); errors = 1; return 0; } /* Find out which value we're looking at. */ if (strcmp (value, predicable_true) == 0) return 1; if (strcmp (value, predicable_false) == 0) return 0; message_with_line (elem->lineno, "unknown value `%s' for `predicable' attribute", value); errors = 1; return 0; }
static bool combine_set_extension (ext_cand *cand, rtx curr_insn, rtx *orig_set) { rtx orig_src = SET_SRC (*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. */ enum machine_mode src_mode = GET_MODE (SET_DEST (*orig_set)); rtx new_const_int = GEN_INT (INTVAL (orig_src) & GET_MODE_MASK (src_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)) { 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 find_call_stack_args (rtx_call_insn *call_insn, bool do_mark, bool fast, bitmap arg_stores) { rtx p; rtx_insn *insn, *prev_insn; bool ret; HOST_WIDE_INT min_sp_off, max_sp_off; bitmap sp_bytes; gcc_assert (CALL_P (call_insn)); if (!ACCUMULATE_OUTGOING_ARGS) return true; if (!do_mark) { gcc_assert (arg_stores); bitmap_clear (arg_stores); } min_sp_off = INTTYPE_MAXIMUM (HOST_WIDE_INT); max_sp_off = 0; /* First determine the minimum and maximum offset from sp for stored arguments. */ for (p = CALL_INSN_FUNCTION_USAGE (call_insn); p; p = XEXP (p, 1)) if (GET_CODE (XEXP (p, 0)) == USE && MEM_P (XEXP (XEXP (p, 0), 0))) { rtx mem = XEXP (XEXP (p, 0), 0), addr; HOST_WIDE_INT off = 0, size; if (!MEM_SIZE_KNOWN_P (mem)) return false; size = MEM_SIZE (mem); addr = XEXP (mem, 0); if (GET_CODE (addr) == PLUS && REG_P (XEXP (addr, 0)) && CONST_INT_P (XEXP (addr, 1))) { off = INTVAL (XEXP (addr, 1)); addr = XEXP (addr, 0); } if (addr != stack_pointer_rtx) { if (!REG_P (addr)) return false; /* If not fast, use chains to see if addr wasn't set to sp + offset. */ if (!fast) { df_ref use; struct df_link *defs; rtx set; FOR_EACH_INSN_USE (use, call_insn) if (rtx_equal_p (addr, DF_REF_REG (use))) break; if (use == NULL) return false; for (defs = DF_REF_CHAIN (use); defs; defs = defs->next) if (! DF_REF_IS_ARTIFICIAL (defs->ref)) break; if (defs == NULL) return false; set = single_set (DF_REF_INSN (defs->ref)); if (!set) return false; if (GET_CODE (SET_SRC (set)) != PLUS || XEXP (SET_SRC (set), 0) != stack_pointer_rtx || !CONST_INT_P (XEXP (SET_SRC (set), 1))) return false; off += INTVAL (XEXP (SET_SRC (set), 1)); } else 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_reflist *reflist = NULL; rtx insn, next, set; struct record_stack_refs_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 && CONST_INT_P (XEXP (src, 1))) { 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 refs 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, reflist, last_sp_adjust + this_adjust, this_adjust)) { if (RTX_FRAME_RELATED_P (last_sp_set)) adjust_frame_related_expr (last_sp_set, insn, 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, reflist, 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_reflist (reflist); reflist = 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 (last_sp_set); free_csa_reflist (reflist); reflist = NULL; last_sp_set = insn; last_sp_adjust = this_adjust; continue; } /* Find a store with pre-(dec|inc)rement or pre-modify of exactly the previous adjustment and turn it into a simple store. This is equivalent to anticipating the stack adjustment so this must be an allocation. */ if (MEM_P (dest) && ((STACK_GROWS_DOWNWARD ? (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_INC && last_sp_adjust == -(HOST_WIDE_INT) GET_MODE_SIZE (GET_MODE (dest)))) || ((STACK_GROWS_DOWNWARD ? last_sp_adjust >= 0 : last_sp_adjust <= 0) && 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) && try_apply_stack_adjustment (insn, reflist, 0, -last_sp_adjust)) { delete_insn (last_sp_set); free_csa_reflist (reflist); reflist = NULL; last_sp_set = NULL_RTX; last_sp_adjust = 0; continue; } } data.insn = insn; data.reflist = reflist; if (!CALL_P (insn) && last_sp_set && !for_each_rtx (&PATTERN (insn), record_stack_refs, &data)) { reflist = data.reflist; continue; } reflist = data.reflist; /* 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_reflist (reflist); reflist = NULL; last_sp_set = NULL_RTX; last_sp_adjust = 0; } } if (last_sp_set && last_sp_adjust == 0) delete_insn (last_sp_set); if (reflist) free_csa_reflist (reflist); }
/* The major function for aggressive pseudo coalescing of moves only if the both pseudos were spilled and not special reload pseudos. */ bool lra_coalesce (void) { basic_block bb; rtx mv, set, insn, next, *sorted_moves; int i, mv_num, sregno, dregno, restore_regno; unsigned int regno; int coalesced_moves; int max_regno = max_reg_num (); bitmap_head involved_insns_bitmap, split_origin_bitmap; bitmap_iterator bi; timevar_push (TV_LRA_COALESCE); if (lra_dump_file != NULL) fprintf (lra_dump_file, "\n********** Pseudos coalescing #%d: **********\n\n", ++lra_coalesce_iter); first_coalesced_pseudo = XNEWVEC (int, max_regno); next_coalesced_pseudo = XNEWVEC (int, max_regno); for (i = 0; i < max_regno; i++) first_coalesced_pseudo[i] = next_coalesced_pseudo[i] = i; sorted_moves = XNEWVEC (rtx, get_max_uid ()); mv_num = 0; /* Collect pseudos whose live ranges were split. */ bitmap_initialize (&split_origin_bitmap, ®_obstack); EXECUTE_IF_SET_IN_BITMAP (&lra_split_regs, 0, regno, bi) if ((restore_regno = lra_reg_info[regno].restore_regno) >= 0) bitmap_set_bit (&split_origin_bitmap, restore_regno); /* Collect moves. */ coalesced_moves = 0; FOR_EACH_BB (bb) { FOR_BB_INSNS_SAFE (bb, insn, next) if (INSN_P (insn) && (set = single_set (insn)) != NULL_RTX && REG_P (SET_DEST (set)) && REG_P (SET_SRC (set)) && (sregno = REGNO (SET_SRC (set))) >= FIRST_PSEUDO_REGISTER && (dregno = REGNO (SET_DEST (set))) >= FIRST_PSEUDO_REGISTER && mem_move_p (sregno, dregno) && coalescable_pseudo_p (sregno, &split_origin_bitmap) && coalescable_pseudo_p (dregno, &split_origin_bitmap) && ! side_effects_p (set) && !(lra_intersected_live_ranges_p (lra_reg_info[sregno].live_ranges, lra_reg_info[dregno].live_ranges))) sorted_moves[mv_num++] = insn; } bitmap_clear (&split_origin_bitmap); qsort (sorted_moves, mv_num, sizeof (rtx), move_freq_compare_func); /* Coalesced copies, most frequently executed first. */ bitmap_initialize (&coalesced_pseudos_bitmap, ®_obstack); bitmap_initialize (&involved_insns_bitmap, ®_obstack); for (i = 0; i < mv_num; i++) { mv = sorted_moves[i]; set = single_set (mv); lra_assert (set != NULL && REG_P (SET_SRC (set)) && REG_P (SET_DEST (set))); sregno = REGNO (SET_SRC (set)); dregno = REGNO (SET_DEST (set)); if (first_coalesced_pseudo[sregno] == first_coalesced_pseudo[dregno]) { coalesced_moves++; if (lra_dump_file != NULL) fprintf (lra_dump_file, " Coalescing move %i:r%d-r%d (freq=%d)\n", INSN_UID (mv), sregno, dregno, BLOCK_FOR_INSN (mv)->frequency); /* We updated involved_insns_bitmap when doing the merge. */ } else if (!(lra_intersected_live_ranges_p (lra_reg_info[first_coalesced_pseudo[sregno]].live_ranges, lra_reg_info[first_coalesced_pseudo[dregno]].live_ranges))) { coalesced_moves++; if (lra_dump_file != NULL) fprintf (lra_dump_file, " Coalescing move %i:r%d(%d)-r%d(%d) (freq=%d)\n", INSN_UID (mv), sregno, ORIGINAL_REGNO (SET_SRC (set)), dregno, ORIGINAL_REGNO (SET_DEST (set)), BLOCK_FOR_INSN (mv)->frequency); bitmap_ior_into (&involved_insns_bitmap, &lra_reg_info[sregno].insn_bitmap); bitmap_ior_into (&involved_insns_bitmap, &lra_reg_info[dregno].insn_bitmap); merge_pseudos (sregno, dregno); } } bitmap_initialize (&used_pseudos_bitmap, ®_obstack); FOR_EACH_BB (bb) { update_live_info (df_get_live_in (bb)); update_live_info (df_get_live_out (bb)); FOR_BB_INSNS_SAFE (bb, insn, next) if (INSN_P (insn) && bitmap_bit_p (&involved_insns_bitmap, INSN_UID (insn))) { if (! substitute (&insn)) continue; lra_update_insn_regno_info (insn); if ((set = single_set (insn)) != NULL_RTX && set_noop_p (set)) { /* Coalesced move. */ if (lra_dump_file != NULL) fprintf (lra_dump_file, " Removing move %i (freq=%d)\n", INSN_UID (insn), BLOCK_FOR_INSN (insn)->frequency); lra_set_insn_deleted (insn); } } } bitmap_clear (&used_pseudos_bitmap); bitmap_clear (&involved_insns_bitmap); bitmap_clear (&coalesced_pseudos_bitmap); if (lra_dump_file != NULL && coalesced_moves != 0) fprintf (lra_dump_file, "Coalesced Moves = %d\n", coalesced_moves); free (sorted_moves); free (next_coalesced_pseudo); free (first_coalesced_pseudo); timevar_pop (TV_LRA_COALESCE); return coalesced_moves != 0; }
static void walk_insn_part (rtx part, int recog_p, int non_pc_set_src) { int i, j; RTX_CODE code; const char *format_ptr; if (part == 0) return; code = GET_CODE (part); switch (code) { case CLOBBER: case CLOBBER_HIGH: clobbers_seen_this_insn++; break; case MATCH_OPERAND: if (XINT (part, 0) > max_recog_operands) max_recog_operands = XINT (part, 0); return; case MATCH_OP_DUP: case MATCH_PAR_DUP: ++dup_operands_seen_this_insn; /* FALLTHRU */ case MATCH_SCRATCH: case MATCH_PARALLEL: case MATCH_OPERATOR: if (XINT (part, 0) > max_recog_operands) max_recog_operands = XINT (part, 0); /* Now scan the rtl's in the vector inside the MATCH_OPERATOR or MATCH_PARALLEL. */ break; case LABEL_REF: if (GET_CODE (XEXP (part, 0)) == MATCH_OPERAND || GET_CODE (XEXP (part, 0)) == MATCH_DUP) break; return; case MATCH_DUP: ++dup_operands_seen_this_insn; if (XINT (part, 0) > max_recog_operands) max_recog_operands = XINT (part, 0); return; case CC0: if (recog_p) have_cc0_flag = 1; return; case LO_SUM: if (recog_p) have_lo_sum_flag = 1; return; case ROTATE: if (recog_p) have_rotate_flag = 1; return; case ROTATERT: if (recog_p) have_rotatert_flag = 1; return; case SET: walk_insn_part (SET_DEST (part), 0, recog_p); walk_insn_part (SET_SRC (part), recog_p, GET_CODE (SET_DEST (part)) != PC); return; case IF_THEN_ELSE: /* Only consider this machine as having a conditional move if the two arms of the IF_THEN_ELSE are both MATCH_OPERAND. Otherwise, we have some specific IF_THEN_ELSE construct (like the doz instruction on the RS/6000) that can't be used in the general context we want it for. */ if (recog_p && non_pc_set_src && GET_CODE (XEXP (part, 1)) == MATCH_OPERAND && GET_CODE (XEXP (part, 2)) == MATCH_OPERAND) have_cmove_flag = 1; break; case COND_EXEC: if (recog_p) have_cond_exec_flag = 1; break; case REG: case CONST_INT: case SYMBOL_REF: case PC: return; default: break; } format_ptr = GET_RTX_FORMAT (GET_CODE (part)); for (i = 0; i < GET_RTX_LENGTH (GET_CODE (part)); i++) switch (*format_ptr++) { case 'e': case 'u': walk_insn_part (XEXP (part, i), recog_p, non_pc_set_src); break; case 'E': if (XVEC (part, i) != NULL) for (j = 0; j < XVECLEN (part, i); j++) walk_insn_part (XVECEXP (part, i, j), recog_p, non_pc_set_src); break; } }
/* Scan rtx X for references to elimination source or target registers in contexts that would prevent the elimination from happening. Update the table of eliminables to reflect the changed state. MEM_MODE is the mode of an enclosing MEM rtx, or VOIDmode if not within a MEM. */ static void mark_not_eliminable (rtx x, machine_mode mem_mode) { enum rtx_code code = GET_CODE (x); struct lra_elim_table *ep; int i, j; const char *fmt; switch (code) { case PRE_INC: case POST_INC: case PRE_DEC: case POST_DEC: case POST_MODIFY: case PRE_MODIFY: if (XEXP (x, 0) == stack_pointer_rtx && ((code != PRE_MODIFY && code != POST_MODIFY) || (GET_CODE (XEXP (x, 1)) == PLUS && XEXP (x, 0) == XEXP (XEXP (x, 1), 0) && CONST_INT_P (XEXP (XEXP (x, 1), 1))))) { int size = GET_MODE_SIZE (mem_mode); #ifdef PUSH_ROUNDING /* If more bytes than MEM_MODE are pushed, account for them. */ size = PUSH_ROUNDING (size); #endif if (code == PRE_DEC || code == POST_DEC) curr_sp_change -= size; else if (code == PRE_INC || code == POST_INC) curr_sp_change += size; else if (code == PRE_MODIFY || code == POST_MODIFY) curr_sp_change += INTVAL (XEXP (XEXP (x, 1), 1)); } else if (REG_P (XEXP (x, 0)) && REGNO (XEXP (x, 0)) >= FIRST_PSEUDO_REGISTER) { /* If we modify the source of an elimination rule, disable it. Do the same if it is the destination and not the hard frame register. */ for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) if (ep->from_rtx == XEXP (x, 0) || (ep->to_rtx == XEXP (x, 0) && ep->to_rtx != hard_frame_pointer_rtx)) setup_can_eliminate (ep, false); } return; case USE: if (REG_P (XEXP (x, 0)) && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER) /* If using a hard register that is the source of an eliminate we still think can be performed, note it cannot be performed since we don't know how this hard register is used. */ for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) if (ep->from_rtx == XEXP (x, 0) && ep->to_rtx != hard_frame_pointer_rtx) setup_can_eliminate (ep, false); return; case CLOBBER: if (REG_P (XEXP (x, 0)) && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER) /* If clobbering a hard register that is the replacement register for an elimination we still think can be performed, note that it cannot be performed. Otherwise, we need not be concerned about it. */ for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) if (ep->to_rtx == XEXP (x, 0) && ep->to_rtx != hard_frame_pointer_rtx) setup_can_eliminate (ep, false); return; case SET: if (SET_DEST (x) == stack_pointer_rtx && GET_CODE (SET_SRC (x)) == PLUS && XEXP (SET_SRC (x), 0) == SET_DEST (x) && CONST_INT_P (XEXP (SET_SRC (x), 1))) { curr_sp_change += INTVAL (XEXP (SET_SRC (x), 1)); return; } if (! REG_P (SET_DEST (x)) || REGNO (SET_DEST (x)) >= FIRST_PSEUDO_REGISTER) mark_not_eliminable (SET_DEST (x), mem_mode); else { /* See if this is setting the replacement hard register for an elimination. If DEST is the hard frame pointer, we do nothing because we assume that all assignments to the frame pointer are for non-local gotos and are being done at a time when they are valid and do not disturb anything else. Some machines want to eliminate a fake argument pointer (or even a fake frame pointer) with either the real frame pointer or the stack pointer. Assignments to the hard frame pointer must not prevent this elimination. */ for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) if (ep->to_rtx == SET_DEST (x) && SET_DEST (x) != hard_frame_pointer_rtx) setup_can_eliminate (ep, false); } mark_not_eliminable (SET_SRC (x), mem_mode); return; case MEM: /* Our only special processing is to pass the mode of the MEM to our recursive call. */ mark_not_eliminable (XEXP (x, 0), GET_MODE (x)); return; default: break; } fmt = GET_RTX_FORMAT (code); for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++) { if (*fmt == 'e') mark_not_eliminable (XEXP (x, i), mem_mode); else if (*fmt == 'E') for (j = 0; j < XVECLEN (x, i); j++) mark_not_eliminable (XVECEXP (x, i, j), mem_mode); } }
/* Function to check whether the OP is a valid load/store operation. This is a helper function for the predicates: 'nds32_load_multiple_operation' and 'nds32_store_multiple_operation' in predicates.md file. The OP is supposed to be a parallel rtx. For each element within this parallel rtx: (set (reg) (mem addr)) is the form for load operation. (set (mem addr) (reg)) is the form for store operation. We have to extract reg and mem of every element and check if the information is valid for multiple load/store operation. */ bool nds32_valid_multiple_load_store_p (rtx op, bool load_p, bool bim_p) { int count; int first_elt_regno; int update_base_elt_idx; int offset; rtx elt; rtx update_base; /* Get the counts of elements in the parallel rtx. Last one is update base register if bim_p. and pick up the first element. */ if (bim_p) { count = XVECLEN (op, 0) - 1; elt = XVECEXP (op, 0, 1); } else { count = XVECLEN (op, 0); elt = XVECEXP (op, 0, 0); } /* Perform some quick check for the first element in the parallel rtx. */ if (GET_CODE (elt) != SET || count <= 1 || count > 25) return false; /* Pick up regno of first element for further detail checking. Note that the form is different between load and store operation. */ if (load_p) { if (GET_CODE (SET_DEST (elt)) != REG || GET_CODE (SET_SRC (elt)) != MEM) return false; first_elt_regno = REGNO (SET_DEST (elt)); } else { if (GET_CODE (SET_SRC (elt)) != REG || GET_CODE (SET_DEST (elt)) != MEM) return false; first_elt_regno = REGNO (SET_SRC (elt)); } /* Perform detail check for each element. Refer to nds32-multiple.md for more information about following checking. The starting element of parallel rtx is index 0. */ if (!nds32_consecutive_registers_load_store_p (op, load_p, bim_p ? 1 : 0, first_elt_regno, count)) return false; if (bim_p) { update_base_elt_idx = 0; update_base = XVECEXP (op, 0, update_base_elt_idx); if (!REG_P (SET_DEST (update_base))) return false; if (GET_CODE (SET_SRC (update_base)) != PLUS) return false; else { offset = count * UNITS_PER_WORD; elt = XEXP (SET_SRC (update_base), 1); if (GET_CODE (elt) != CONST_INT || (INTVAL (elt) != offset)) return false; } } /* Pass all test, this is a valid rtx. */ return true; }
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; }
void func_fma_steering::analyze_fma_fmul_insn (fma_forest *ref_forest, du_chain *chain, du_head_p head) { fma_forest *forest; fma_node *node = this->get_fma_node (chain->insn); /* This is a root node. */ if (!node) { fma_root_node *root_node; root_node = new fma_root_node (this, chain, this->m_next_forest_id++); forest = root_node->get_forest (); node = root_node; /* Until proved otherwise, assume this root is not part of an existing forest and thus add its forest to the list of forests. */ this->m_fma_forests.push_back (forest); } else forest = node->get_forest (); node->set_head (head); /* fma_node is part of a chain with several defs, one of them having already been processed. The root of that already processed def is the canonical one and the root of fma_node is added to its forest. No need to process the children nodes as they were already processed when the other def was processed. */ if (ref_forest) { ref_forest->merge_forest (forest); return; } for (chain = head->first; chain; chain = chain->next_use) { fma_node *child_fma; rtx fma_rtx, *accum_rtx_p; if (!is_fmul_fmac_insn (chain->insn, false)) continue; /* Get FMA rtx. */ fma_rtx = SET_SRC (PATTERN (chain->insn)); /* FMA is negated. */ if (GET_CODE (fma_rtx) == NEG) fma_rtx = XEXP (fma_rtx, 0); /* Get accumulator rtx. */ accum_rtx_p = &XEXP (fma_rtx, 2); /* Accumulator is negated. */ if (!REG_P (*accum_rtx_p)) accum_rtx_p = &XEXP (*accum_rtx_p, 0); /* This du_chain structure is not for the accumulator register. */ if (accum_rtx_p != chain->loc) continue; /* If object already created, this is a loop carried dependency. We don't include this object in the children as we want trees for rename_fma_trees to not be an infinite loop. */ if (this->get_fma_node (chain->insn)) continue; child_fma = new fma_node (node, chain); /* Memorize the mapping of this instruction to its fma_node object as it will be processed for the chain starting at its destination register later. */ /* Link to siblings. */ node->add_child (child_fma); } }
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; }