/* Check if the given DEF is available in INSN. This would require full computation of available expressions; we check only restricted conditions: - if DEF is the sole definition of its register, go ahead; - in the same basic block, we check for no definitions killing the definition of DEF_INSN; - if USE's basic block has DEF's basic block as the sole predecessor, we check if the definition is killed after DEF_INSN or before TARGET_INSN insn, in their respective basic blocks. */ static bool use_killed_between (df_ref use, rtx_insn *def_insn, rtx_insn *target_insn) { basic_block def_bb = BLOCK_FOR_INSN (def_insn); basic_block target_bb = BLOCK_FOR_INSN (target_insn); int regno; df_ref def; /* We used to have a def reaching a use that is _before_ the def, with the def not dominating the use even though the use and def are in the same basic block, when a register may be used uninitialized in a loop. This should not happen anymore since we do not use reaching definitions, but still we test for such cases and assume that DEF is not available. */ if (def_bb == target_bb ? DF_INSN_LUID (def_insn) >= DF_INSN_LUID (target_insn) : !dominated_by_p (CDI_DOMINATORS, target_bb, def_bb)) return true; /* Check if the reg in USE has only one definition. We already know that this definition reaches use, or we wouldn't be here. However, this is invalid for hard registers because if they are live at the beginning of the function it does not mean that we have an uninitialized access. */ regno = DF_REF_REGNO (use); def = DF_REG_DEF_CHAIN (regno); if (def && DF_REF_NEXT_REG (def) == NULL && regno >= FIRST_PSEUDO_REGISTER) return false; /* Check locally if we are in the same basic block. */ if (def_bb == target_bb) return local_ref_killed_between_p (use, def_insn, target_insn); /* Finally, if DEF_BB is the sole predecessor of TARGET_BB. */ if (single_pred_p (target_bb) && single_pred (target_bb) == def_bb) { df_ref x; /* See if USE is killed between DEF_INSN and the last insn in the basic block containing DEF_INSN. */ x = df_bb_regno_last_def_find (def_bb, regno); if (x && DF_INSN_LUID (DF_REF_INSN (x)) >= DF_INSN_LUID (def_insn)) return true; /* See if USE is killed between TARGET_INSN and the first insn in the basic block containing TARGET_INSN. */ x = df_bb_regno_first_def_find (target_bb, regno); if (x && DF_INSN_LUID (DF_REF_INSN (x)) < DF_INSN_LUID (target_insn)) return true; return false; } /* Otherwise assume the worst case. */ return true; }
static struct df_link * get_defs (rtx_insn *insn, rtx reg, vec<rtx_insn *> *dest) { df_ref use; struct df_link *ref_chain, *ref_link; FOR_EACH_INSN_USE (use, insn) { if (GET_CODE (DF_REF_REG (use)) == SUBREG) return NULL; if (REGNO (DF_REF_REG (use)) == REGNO (reg)) break; } gcc_assert (use != NULL); ref_chain = DF_REF_CHAIN (use); for (ref_link = ref_chain; ref_link; ref_link = ref_link->next) { /* Problem getting some definition for this instruction. */ if (ref_link->ref == NULL) return NULL; if (DF_REF_INSN_INFO (ref_link->ref) == NULL) return NULL; } if (dest) for (ref_link = ref_chain; ref_link; ref_link = ref_link->next) dest->safe_push (DF_REF_INSN (ref_link->ref)); return ref_chain; }
static struct df_link * get_defs (rtx_insn *insn, rtx reg, vec<rtx_insn *> *dest) { df_ref use; struct df_link *ref_chain, *ref_link; FOR_EACH_INSN_USE (use, insn) { if (GET_CODE (DF_REF_REG (use)) == SUBREG) return NULL; if (REGNO (DF_REF_REG (use)) == REGNO (reg)) break; } gcc_assert (use != NULL); ref_chain = DF_REF_CHAIN (use); for (ref_link = ref_chain; ref_link; ref_link = ref_link->next) { /* Problem getting some definition for this instruction. */ if (ref_link->ref == NULL) return NULL; if (DF_REF_INSN_INFO (ref_link->ref) == NULL) return NULL; /* As global regs are assumed to be defined at each function call dataflow can report a call_insn as being a definition of REG. But we can't do anything with that in this pass so proceed only if the instruction really sets REG in a way that can be deduced from the RTL structure. */ if (global_regs[REGNO (reg)] && !set_of (reg, DF_REF_INSN (ref_link->ref))) return NULL; } if (dest) for (ref_link = ref_chain; ref_link; ref_link = ref_link->next) dest->safe_push (DF_REF_INSN (ref_link->ref)); return ref_chain; }
static void replace_ref (struct ref *ref, rtx reg) { rtx oldreg = DF_REF_REAL_REG (ref); rtx *loc = DF_REF_REAL_LOC (ref); if (oldreg == reg) return; if (dump_file) fprintf (dump_file, "Updating insn %i (%i->%i)\n", INSN_UID (DF_REF_INSN (ref)), REGNO (oldreg), REGNO (reg)); *loc = reg; }
static struct df_link * get_defs (rtx insn, rtx reg, vec<rtx> *dest) { df_ref reg_info, *uses; struct df_link *ref_chain, *ref_link; reg_info = NULL; for (uses = DF_INSN_USES (insn); *uses; uses++) { reg_info = *uses; if (GET_CODE (DF_REF_REG (reg_info)) == SUBREG) return NULL; if (REGNO (DF_REF_REG (reg_info)) == REGNO (reg)) break; } gcc_assert (reg_info != NULL && uses != NULL); ref_chain = DF_REF_CHAIN (reg_info); for (ref_link = ref_chain; ref_link; ref_link = ref_link->next) { /* Problem getting some definition for this instruction. */ if (ref_link->ref == NULL) return NULL; if (DF_REF_INSN_INFO (ref_link->ref) == NULL) return NULL; } if (dest) for (ref_link = ref_chain; ref_link; ref_link = ref_link->next) dest->safe_push (DF_REF_INSN (ref_link->ref)); return ref_chain; }
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; } } }
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; }
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;