int aarch_crypto_can_dual_issue (rtx_insn *producer_insn, rtx_insn *consumer_insn) { rtx producer_set, consumer_set; rtx producer_src, consumer_src; producer_set = single_set (producer_insn); consumer_set = single_set (consumer_insn); producer_src = producer_set ? SET_SRC (producer_set) : NULL; consumer_src = consumer_set ? SET_SRC (consumer_set) : NULL; if (producer_src && consumer_src && GET_CODE (producer_src) == UNSPEC && GET_CODE (consumer_src) == UNSPEC && ((XINT (producer_src, 1) == UNSPEC_AESE && XINT (consumer_src, 1) == UNSPEC_AESMC) || (XINT (producer_src, 1) == UNSPEC_AESD && XINT (consumer_src, 1) == UNSPEC_AESIMC))) { unsigned int regno = REGNO (SET_DEST (producer_set)); return REGNO (SET_DEST (consumer_set)) == regno && REGNO (XVECEXP (consumer_src, 0, 0)) == regno; } return 0; }
int aarch_crypto_can_dual_issue (rtx_insn *producer_insn, rtx_insn *consumer_insn) { rtx producer_set, consumer_set; rtx producer_src, consumer_src; producer_set = single_set (producer_insn); consumer_set = single_set (consumer_insn); producer_src = producer_set ? SET_SRC (producer_set) : NULL; consumer_src = consumer_set ? SET_SRC (consumer_set) : NULL; if (producer_src && consumer_src && GET_CODE (producer_src) == UNSPEC && GET_CODE (consumer_src) == UNSPEC && ((XINT (producer_src, 1) == UNSPEC_AESE && XINT (consumer_src, 1) == UNSPEC_AESMC) || (XINT (producer_src, 1) == UNSPEC_AESD && XINT (consumer_src, 1) == UNSPEC_AESIMC))) { unsigned int regno = REGNO (SET_DEST (producer_set)); /* Before reload the registers are virtual, so the destination of consumer_set doesn't need to match. */ return (REGNO (SET_DEST (consumer_set)) == regno || !reload_completed) && REGNO (XVECEXP (consumer_src, 0, 0)) == regno; } return 0; }
static void test_single_set () { /* A label is not a SET. */ ASSERT_EQ (NULL_RTX, single_set (gen_label_rtx ())); /* An unconditional jump insn is a single SET. */ rtx set_pc = gen_rtx_SET (pc_rtx, gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ())); rtx_insn *jump_insn = emit_jump_insn (set_pc); ASSERT_EQ (set_pc, single_set (jump_insn)); /* etc */ }
/* Return true if insn is an instruction that sets a target register. if CHECK_CONST is true, only return true if the source is constant. If such a set is found and REGNO is nonzero, assign the register number of the destination register to *REGNO. */ static int insn_sets_btr_p (const_rtx insn, int check_const, int *regno) { rtx set; if (NONJUMP_INSN_P (insn) && (set = single_set (insn))) { rtx dest = SET_DEST (set); rtx src = SET_SRC (set); if (GET_CODE (dest) == SUBREG) dest = XEXP (dest, 0); if (REG_P (dest) && TEST_HARD_REG_BIT (all_btrs, REGNO (dest))) { gcc_assert (!btr_referenced_p (src, NULL)); if (!check_const || CONSTANT_P (src)) { if (regno) *regno = REGNO (dest); return 1; } } } return 0; }
static rtx conforming_compare (rtx_insn *insn) { rtx set, src, dest; set = single_set (insn); if (set == NULL) return NULL; src = SET_SRC (set); if (GET_CODE (src) != COMPARE) return NULL; dest = SET_DEST (set); if (!REG_P (dest) || REGNO (dest) != targetm.flags_regnum) return NULL; if (!REG_P (XEXP (src, 0))) return NULL; if (CONSTANT_P (XEXP (src, 1)) || REG_P (XEXP (src, 1))) return src; if (GET_CODE (XEXP (src, 1)) == UNSPEC) { for (int i = 0; i < XVECLEN (XEXP (src, 1), 0); i++) if (!REG_P (XVECEXP (XEXP (src, 1), 0, i))) return NULL; return src; } return NULL; }
static rtx single_set_for_csa (rtx insn) { int i; rtx tmp = single_set (insn); if (tmp) return tmp; if (!NONJUMP_INSN_P (insn) || GET_CODE (PATTERN (insn)) != PARALLEL) return NULL_RTX; tmp = PATTERN (insn); if (GET_CODE (XVECEXP (tmp, 0, 0)) != SET) return NULL_RTX; for (i = 1; i < XVECLEN (tmp, 0); ++i) { rtx this_rtx = XVECEXP (tmp, 0, i); /* The special case is allowing a no-op set. */ if (GET_CODE (this_rtx) == SET && SET_SRC (this_rtx) == SET_DEST (this_rtx)) ; else if (GET_CODE (this_rtx) != CLOBBER && GET_CODE (this_rtx) != USE) return NULL_RTX; } return XVECEXP (tmp, 0, 0); }
/* Check if all uses in DEF_INSN can be used in TARGET_INSN. This would require full computation of available expressions; we check only restricted conditions, see use_killed_between. */ static bool all_uses_available_at (rtx_insn *def_insn, rtx_insn *target_insn) { df_ref use; struct df_insn_info *insn_info = DF_INSN_INFO_GET (def_insn); rtx def_set = single_set (def_insn); rtx_insn *next; gcc_assert (def_set); /* If target_insn comes right after def_insn, which is very common for addresses, we can use a quicker test. Ignore debug insns other than target insns for this. */ next = NEXT_INSN (def_insn); while (next && next != target_insn && DEBUG_INSN_P (next)) next = NEXT_INSN (next); if (next == target_insn && REG_P (SET_DEST (def_set))) { rtx def_reg = SET_DEST (def_set); /* If the insn uses the reg that it defines, the substitution is invalid. */ FOR_EACH_INSN_INFO_USE (use, insn_info) if (rtx_equal_p (DF_REF_REG (use), def_reg)) return false; FOR_EACH_INSN_INFO_EQ_USE (use, insn_info) if (rtx_equal_p (DF_REF_REG (use), def_reg)) return false; }
/* In case function does not return value, we get clobber of pseudo followed by set to hard return value. */ static rtx skip_unreturned_value (rtx orig_insn) { rtx insn = next_nonnote_insn (orig_insn); /* Skip possible clobber of pseudo return register. */ if (insn && GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == CLOBBER && REG_P (XEXP (PATTERN (insn), 0)) && (REGNO (XEXP (PATTERN (insn), 0)) >= FIRST_PSEUDO_REGISTER)) { rtx set_insn = next_nonnote_insn (insn); rtx set; if (!set_insn) return insn; set = single_set (set_insn); if (!set || SET_SRC (set) != XEXP (PATTERN (insn), 0) || SET_DEST (set) != current_function_return_rtx) return insn; return set_insn; } return orig_insn; }
static unsigned int postreload_load (void) { basic_block bb; init_alias_analysis (); FOR_EACH_BB (bb) { rtx insn; htab_load = htab_create (10, load_htab_hash, load_htab_eq, NULL); FOR_BB_INSNS (bb, insn) { rtx set; struct load **load; /* Set reg_kill, invalidate entries if there is an aliasing store or if the registers making up the address change. */ htab_traverse_noresize (htab_load, find_reg_kill_and_mem_invalidate, insn); set = single_set (insn); if (interesting_second_load (set, &load, insn)) { rtx move; move = gen_move_insn (SET_DEST (set), (*load)->reg); /* Make sure we can generate a move. */ extract_insn (move); if (! constrain_operands (1)) continue; move = emit_insn_before (move, (*load)->reg_kill); delete_insn (insn); if (dump_file) { fputs ("Replaced this load:\n ", dump_file); print_inline_rtx (dump_file, insn, 2); fputs ("\n with this move:\n ", dump_file); print_inline_rtx (dump_file, move, 2); fputs ("\n\n", dump_file); } } else if (interesting_load (set)) alloc_load (set); else if (CALL_P (insn)) htab_empty (htab_load); } htab_empty (htab_load); }
static rtx skip_pic_restore (rtx orig_insn) { rtx insn, set = NULL_RTX; insn = next_nonnote_insn (orig_insn); if (insn) set = single_set (insn); if (insn && set && SET_DEST (set) == pic_offset_table_rtx) return insn; return orig_insn; }
static void maybe_propagate_label_ref (rtx jump_insn, rtx prev_nonjump_insn) { rtx label_note, pc, pc_src; pc = pc_set (jump_insn); pc_src = pc != NULL ? SET_SRC (pc) : NULL; label_note = find_reg_note (prev_nonjump_insn, REG_LABEL_OPERAND, NULL); /* 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 (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, jump_insn, false, true); gcc_assert (JUMP_LABEL (jump_insn) == XEXP (label_note, 0)); } } }
static bool is_cond_copy_insn (rtx insn, rtx *reg1, rtx *reg2) { rtx expr = single_set (insn); if (expr != NULL_RTX && GET_CODE (expr) == SET && GET_CODE (SET_DEST (expr)) == REG && GET_CODE (SET_SRC (expr)) == IF_THEN_ELSE && GET_CODE (XEXP (SET_SRC (expr), 1)) == REG && GET_CODE (XEXP (SET_SRC (expr), 2)) == REG) { *reg1 = XEXP (SET_SRC (expr), 1); *reg2 = XEXP (SET_SRC (expr), 2); return true; } return false; }
void union_defs (df_ref use, struct web_entry *def_entry, unsigned int *used, struct web_entry *use_entry, bool (*fun) (struct web_entry *, struct web_entry *)) { struct df_insn_info *insn_info = DF_REF_INSN_INFO (use); struct df_link *link = DF_REF_CHAIN (use); rtx set; if (insn_info) { df_ref eq_use; set = single_set (insn_info->insn); FOR_EACH_INSN_INFO_EQ_USE (eq_use, insn_info) if (use != eq_use && DF_REF_REAL_REG (use) == DF_REF_REAL_REG (eq_use)) (*fun) (use_entry + DF_REF_ID (use), use_entry + DF_REF_ID (eq_use)); } else
static rtx skip_stack_adjustment (rtx orig_insn) { rtx insn, set = NULL_RTX; insn = next_nonnote_insn (orig_insn); if (insn) set = single_set (insn); if (insn && set && GET_CODE (SET_SRC (set)) == PLUS && XEXP (SET_SRC (set), 0) == stack_pointer_rtx && GET_CODE (XEXP (SET_SRC (set), 1)) == CONST_INT && SET_DEST (set) == stack_pointer_rtx) return insn; return orig_insn; }
static rtx conforming_compare (rtx_insn *insn) { rtx set, src, dest; set = single_set (insn); if (set == NULL) return NULL; src = SET_SRC (set); if (GET_CODE (src) != COMPARE) return NULL; dest = SET_DEST (set); if (!REG_P (dest) || REGNO (dest) != targetm.flags_regnum) return NULL; if (REG_P (XEXP (src, 0)) && (REG_P (XEXP (src, 1)) || CONSTANT_P (XEXP (src, 1)))) return src; return NULL; }
/* Find the group that the target register definition DEF belongs to in the list starting with *ALL_BTR_DEF_GROUPS. If no such group exists, create one. Add def to the group. */ static void find_btr_def_group (btr_def_group *all_btr_def_groups, btr_def def) { if (insn_sets_btr_p (def->insn, 1, NULL)) { btr_def_group this_group; rtx def_src = SET_SRC (single_set (def->insn)); /* ?? This linear search is an efficiency concern, particularly as the search will almost always fail to find a match. */ for (this_group = *all_btr_def_groups; this_group != NULL; this_group = this_group->next) if (rtx_equal_p (def_src, this_group->src)) break; if (!this_group) { this_group = XOBNEW (&migrate_btrl_obstack, struct btr_def_group_s); this_group->src = def_src; this_group->members = NULL; this_group->next = *all_btr_def_groups; *all_btr_def_groups = this_group; }
/* 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; }
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); } } }
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 rtx skip_copy_to_return_value (rtx orig_insn) { rtx insn, set = NULL_RTX; rtx hardret, softret; /* If there is no return value, we have nothing to do. */ if (! identify_call_return_value (PATTERN (orig_insn), &hardret, &softret)) return orig_insn; insn = next_nonnote_insn (orig_insn); if (! insn) return orig_insn; set = single_set (insn); if (! set) return orig_insn; if (return_value_pseudo) { if (SET_DEST (set) == return_value_pseudo && SET_SRC (set) == softret) return insn; return orig_insn; } /* The destination must be the same as the called function's return value to ensure that any return value is put in the same place by the current function and the function we're calling. Further, the source must be the same as the pseudo into which the called function's return value was copied. Otherwise we're returning some other value. */ #ifndef OUTGOING_REGNO #define OUTGOING_REGNO(N) (N) #endif if (SET_DEST (set) == current_function_return_rtx && REG_P (SET_DEST (set)) && OUTGOING_REGNO (REGNO (SET_DEST (set))) == REGNO (hardret) && SET_SRC (set) == softret) return insn; /* Recognize the situation when the called function's return value is copied in two steps: first into an intermediate pseudo, then the into the calling functions return value register. */ if (REG_P (SET_DEST (set)) && SET_SRC (set) == softret) { rtx x = SET_DEST (set); insn = next_nonnote_insn (insn); if (! insn) return orig_insn; set = single_set (insn); if (! set) return orig_insn; if (SET_DEST (set) == current_function_return_rtx && REG_P (SET_DEST (set)) && OUTGOING_REGNO (REGNO (SET_DEST (set))) == REGNO (hardret) && SET_SRC (set) == x) return insn; } /* It did not look like a copy of the return value, so return the same insn we were passed. */ return orig_insn; }
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 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; }
/* 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; int coalesced_moves; int max_regno = max_reg_num (); bitmap_head involved_insns_bitmap; 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 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) && coalescable_pseudo_p (dregno) && ! 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; } 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 int identify_call_return_value (rtx cp, rtx *p_hard_return, rtx *p_soft_return) { rtx insn, set, hard, soft; insn = XEXP (cp, 0); /* Search backward through the "normal" call sequence to the CALL insn. */ while (NEXT_INSN (insn)) insn = NEXT_INSN (insn); while (GET_CODE (insn) != CALL_INSN) insn = PREV_INSN (insn); /* Assume the pattern is (set (dest) (call ...)), or that the first member of a parallel is. This is the hard return register used by the function. */ if (GET_CODE (PATTERN (insn)) == SET && GET_CODE (SET_SRC (PATTERN (insn))) == CALL) hard = SET_DEST (PATTERN (insn)); else if (GET_CODE (PATTERN (insn)) == PARALLEL && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET && GET_CODE (SET_SRC (XVECEXP (PATTERN (insn), 0, 0))) == CALL) hard = SET_DEST (XVECEXP (PATTERN (insn), 0, 0)); else return 0; /* If we didn't get a single hard register (e.g. a parallel), give up. */ if (GET_CODE (hard) != REG) return 0; /* Stack adjustment done after call may appear here. */ insn = skip_stack_adjustment (insn); if (! insn) return 0; /* Restore of GP register may appear here. */ insn = skip_pic_restore (insn); if (! insn) return 0; /* If there's nothing after, there's no soft return value. */ insn = NEXT_INSN (insn); if (! insn) return 0; /* We're looking for a source of the hard return register. */ set = single_set (insn); if (! set || SET_SRC (set) != hard) return 0; soft = SET_DEST (set); insn = NEXT_INSN (insn); /* Allow this first destination to be copied to a second register, as might happen if the first register wasn't the particular pseudo we'd been expecting. */ if (insn && (set = single_set (insn)) != NULL_RTX && SET_SRC (set) == soft) { soft = SET_DEST (set); insn = NEXT_INSN (insn); } /* Don't fool with anything but pseudo registers. */ if (GET_CODE (soft) != REG || REGNO (soft) < FIRST_PSEUDO_REGISTER) return 0; /* This value must not be modified before the end of the sequence. */ if (reg_set_between_p (soft, insn, NULL_RTX)) return 0; *p_hard_return = hard; *p_soft_return = soft; return 1; }
void optimize_sibling_and_tail_recursive_calls (void) { rtx insn, insns; basic_block alternate_exit = EXIT_BLOCK_PTR; bool no_sibcalls_this_function = false; bool successful_replacement = false; bool replaced_call_placeholder = false; edge e; insns = get_insns (); cleanup_cfg (CLEANUP_PRE_SIBCALL | CLEANUP_PRE_LOOP); /* If there are no basic blocks, then there is nothing to do. */ if (n_basic_blocks == 0) return; /* If we are using sjlj exceptions, we may need to add a call to _Unwind_SjLj_Unregister at exit of the function. Which means that we cannot do any sibcall transformations. */ if (USING_SJLJ_EXCEPTIONS && current_function_has_exception_handlers ()) no_sibcalls_this_function = true; return_value_pseudo = NULL_RTX; /* Find the exit block. It is possible that we have blocks which can reach the exit block directly. However, most of the time a block will jump (or fall into) N_BASIC_BLOCKS - 1, which in turn falls into the exit block. */ for (e = EXIT_BLOCK_PTR->pred; e && alternate_exit == EXIT_BLOCK_PTR; e = e->pred_next) { rtx insn; if (e->dest != EXIT_BLOCK_PTR || e->succ_next != NULL) continue; /* Walk forwards through the last normal block and see if it does nothing except fall into the exit block. */ for (insn = BB_HEAD (EXIT_BLOCK_PTR->prev_bb); insn; insn = NEXT_INSN (insn)) { rtx set; /* This should only happen once, at the start of this block. */ if (GET_CODE (insn) == CODE_LABEL) continue; if (GET_CODE (insn) == NOTE) continue; if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == USE) continue; /* Exit block also may contain copy from pseudo containing return value to hard register. */ if (GET_CODE (insn) == INSN && (set = single_set (insn)) && SET_DEST (set) == current_function_return_rtx && REG_P (SET_SRC (set)) && !return_value_pseudo) { return_value_pseudo = SET_SRC (set); continue; } break; } /* If INSN is zero, then the search walked all the way through the block without hitting anything interesting. This block is a valid alternate exit block. */ if (insn == NULL) alternate_exit = e->src; else return_value_pseudo = NULL; } /* If the function uses ADDRESSOF, we can't (easily) determine at this point if the value will end up on the stack. */ no_sibcalls_this_function |= sequence_uses_addressof (insns); /* Walk the insn chain and find any CALL_PLACEHOLDER insns. We need to select one of the insn sequences attached to each CALL_PLACEHOLDER. The different sequences represent different ways to implement the call, ie, tail recursion, sibling call or normal call. Since we do not create nested CALL_PLACEHOLDERs, the scan continues with the insn that was after a replaced CALL_PLACEHOLDER; we don't rescan the replacement insns. */ for (insn = insns; insn; insn = NEXT_INSN (insn)) { if (GET_CODE (insn) == CALL_INSN && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER) { int sibcall = (XEXP (PATTERN (insn), 1) != NULL_RTX); int tailrecursion = (XEXP (PATTERN (insn), 2) != NULL_RTX); basic_block call_block = BLOCK_FOR_INSN (insn); /* alloca (until we have stack slot life analysis) inhibits sibling call optimizations, but not tail recursion. Similarly if we use varargs or stdarg since they implicitly may take the address of an argument. */ if (current_function_calls_alloca || current_function_stdarg) sibcall = 0; /* See if there are any reasons we can't perform either sibling or tail call optimizations. We must be careful with stack slots which are live at potential optimization sites. */ if (no_sibcalls_this_function /* ??? Overly conservative. */ || frame_offset /* Any function that calls setjmp might have longjmp called from any called function. ??? We really should represent this properly in the CFG so that this needn't be special cased. */ || current_function_calls_setjmp /* Can't if more than one successor or single successor is not exit block. These two tests prevent tail call optimization in the presence of active exception handlers. */ || call_block->succ == NULL || call_block->succ->succ_next != NULL || (call_block->succ->dest != EXIT_BLOCK_PTR && call_block->succ->dest != alternate_exit) /* If this call doesn't end the block, there are operations at the end of the block which we must execute after returning. */ || ! call_ends_block_p (insn, BB_END (call_block))) sibcall = 0, tailrecursion = 0; /* Select a set of insns to implement the call and emit them. Tail recursion is the most efficient, so select it over a tail/sibling call. */ if (sibcall || tailrecursion) successful_replacement = true; replaced_call_placeholder = true; replace_call_placeholder (insn, tailrecursion != 0 ? sibcall_use_tail_recursion : sibcall != 0 ? sibcall_use_sibcall : sibcall_use_normal); } } if (successful_replacement) { rtx insn; tree arg; /* A sibling call sequence invalidates any REG_EQUIV notes made for this function's incoming arguments. At the start of RTL generation we know the only REG_EQUIV notes in the rtl chain are those for incoming arguments, so we can safely flush any REG_EQUIV note. This is (slight) overkill. We could keep track of the highest argument we clobber and be more selective in removing notes, but it does not seem to be worth the effort. */ purge_reg_equiv_notes (); /* A sibling call sequence also may invalidate RTX_UNCHANGING_P flag of some incoming arguments MEM RTLs, because it can write into those slots. We clear all those bits now. This is (slight) overkill, we could keep track of which arguments we actually write into. */ for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) { if (INSN_P (insn)) purge_mem_unchanging_flag (PATTERN (insn)); } /* Similarly, invalidate RTX_UNCHANGING_P for any incoming arguments passed in registers. */ for (arg = DECL_ARGUMENTS (current_function_decl); arg; arg = TREE_CHAIN (arg)) { if (REG_P (DECL_RTL (arg))) RTX_UNCHANGING_P (DECL_RTL (arg)) = false; } } /* There may have been NOTE_INSN_BLOCK_{BEGIN,END} notes in the CALL_PLACEHOLDER alternatives that we didn't emit. Rebuild the lexical block tree to correspond to the notes that still exist. */ if (replaced_call_placeholder) reorder_blocks (); /* This information will be invalid after inline expansion. Kill it now. */ free_basic_block_vars (0); free_EXPR_LIST_list (&tail_recursion_label_list); }
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 unsigned int rest_of_handle_simplify_got (void) { df_ref ref; rtx use = NULL_RTX; int i, n_symbol, n_access = 0; struct got_access_info* got_accesses; htab_t var_table = htab_create (VAR_TABLE_SIZE, htab_hash_pointer, htab_eq_pointer, NULL); rtx pic_reg = targetm.got_access.get_pic_reg (); gcc_assert (pic_reg); ref = DF_REG_USE_CHAIN (REGNO (pic_reg)); got_accesses = XNEWVEC(struct got_access_info, DF_REG_USE_COUNT (REGNO (pic_reg))); /* Check if all uses of pic_reg are loading global address through the default method. */ while (ref) { rtx_insn* insn = DF_REF_INSN (ref); /* Check for the special USE insn, it is not a real usage of pic_reg. */ if (GET_CODE (PATTERN (insn)) == USE) use = insn; else { /* If an insn both set and use pic_reg, it is in the process of constructing the value of pic_reg. We should also ignore it. */ rtx set = single_set (insn); if (!(set && SET_DEST (set) == pic_reg)) { rtx offset_reg; rtx offset_insn; rtx symbol = targetm.got_access.loaded_global_var (insn, &offset_reg, &offset_insn); if (symbol) { rtx* slot = (rtx*) htab_find_slot (var_table, symbol, INSERT); if (*slot == HTAB_EMPTY_ENTRY) *slot = symbol; gcc_assert (set); got_accesses[n_access].symbol = symbol; got_accesses[n_access].offset_reg = offset_reg; got_accesses[n_access].address_reg = SET_DEST (set); got_accesses[n_access].load_insn = insn; got_accesses[n_access].offset_insn = offset_insn; n_access++; } else { /* This insn doesn't load a global address, but it has other unexpected usage of pic_reg, give up. */ free (got_accesses); htab_delete (var_table); return 0; } } } ref = DF_REF_NEXT_REG(ref); } /* Check if we can simplify it. */ n_symbol = htab_elements (var_table); gcc_assert (n_symbol <= n_access); if (!targetm.got_access.can_simplify_got_access (n_symbol, n_access)) { free (got_accesses); htab_delete (var_table); return 0; } /* Rewrite the global address loading insns. */ for (i=0; i<n_access; i++) targetm.got_access.load_global_address (got_accesses[i].symbol, got_accesses[i].offset_reg, got_accesses[i].address_reg, got_accesses[i].load_insn, got_accesses[i].offset_insn); /* Since there is no usage of pic_reg now, we can remove it. */ if (use) remove_insn (use); targetm.got_access.clear_pic_reg (); free (got_accesses); htab_delete (var_table); return 0; }
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; }
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; } } }
/* 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; }