/* Interprocedural reference lists. Copyright (C) 2010-2014 Free Software Foundation, Inc. Contributed by Jan Hubicka This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #include "config.h" #include "system.h" #include "coretypes.h" #include "tree.h" #include "ggc.h" #include "target.h" #include "cgraph.h" #include "ipa-utils.h" static const char *ipa_ref_use_name[] = {"read","write","addr","alias"}; /* Return ipa reference from REFERING_NODE or REFERING_VARPOOL_NODE to REFERED_NODE or REFERED_VARPOOL_NODE. USE_TYPE specify type of the use and STMT the statement (if it exists). */ struct ipa_ref * ipa_record_reference (symtab_node *referring_node, symtab_node *referred_node, enum ipa_ref_use use_type, gimple stmt) { struct ipa_ref *ref, *ref2; struct ipa_ref_list *list, *list2; ipa_ref_t *old_references; gcc_checking_assert (!stmt || is_a (referring_node)); gcc_checking_assert (use_type != IPA_REF_ALIAS || !stmt); list = &referring_node->ref_list; old_references = vec_safe_address (list->references); vec_safe_grow (list->references, vec_safe_length (list->references) + 1); ref = &list->references->last (); list2 = &referred_node->ref_list; list2->referring.safe_push (ref); ref->referred_index = list2->referring.length () - 1; ref->referring = referring_node; ref->referred = referred_node; ref->stmt = stmt; ref->lto_stmt_uid = 0; ref->use = use_type; ref->speculative = 0; /* If vector was moved in memory, update pointers. */ if (old_references != list->references->address ()) { int i; for (i = 0; ipa_ref_list_reference_iterate (list, i, ref2); i++) ipa_ref_referred_ref_list (ref2)->referring[ref2->referred_index] = ref2; } return ref; } /* If VAL is a reference to a function or a variable, add a reference from REFERRING_NODE to the corresponding symbol table node. USE_TYPE specify type of the use and STMT the statement (if it exists). Return the new reference or NULL if none was created. */ struct ipa_ref * ipa_maybe_record_reference (symtab_node *referring_node, tree val, enum ipa_ref_use use_type, gimple stmt) { STRIP_NOPS (val); if (TREE_CODE (val) != ADDR_EXPR) return NULL; val = get_base_var (val); if (val && (TREE_CODE (val) == FUNCTION_DECL || TREE_CODE (val) == VAR_DECL)) { symtab_node *referred = symtab_get_node (val); gcc_checking_assert (referred); return ipa_record_reference (referring_node, referred, use_type, stmt); } return NULL; } /* Remove reference REF. */ void ipa_remove_reference (struct ipa_ref *ref) { struct ipa_ref_list *list = ipa_ref_referred_ref_list (ref); struct ipa_ref_list *list2 = ipa_ref_referring_ref_list (ref); vec *old_references = list2->references; struct ipa_ref *last; gcc_assert (list->referring[ref->referred_index] == ref); last = list->referring.last (); if (ref != last) { list->referring[ref->referred_index] = list->referring.last (); list->referring[ref->referred_index]->referred_index = ref->referred_index; } list->referring.pop (); last = &list2->references->last (); if (ref != last) { *ref = *last; ipa_ref_referred_ref_list (ref)->referring[ref->referred_index] = ref; } list2->references->pop (); gcc_assert (list2->references == old_references); } /* Remove all references in ref list LIST. */ void ipa_remove_all_references (struct ipa_ref_list *list) { while (vec_safe_length (list->references)) ipa_remove_reference (&list->references->last ()); vec_free (list->references); } /* Remove all references in ref list LIST. */ void ipa_remove_all_referring (struct ipa_ref_list *list) { while (list->referring.length ()) ipa_remove_reference (list->referring.last ()); list->referring.release (); } /* Dump references in LIST to FILE. */ void ipa_dump_references (FILE * file, struct ipa_ref_list *list) { struct ipa_ref *ref; int i; for (i = 0; ipa_ref_list_reference_iterate (list, i, ref); i++) { fprintf (file, "%s/%i (%s)", ref->referred->asm_name (), ref->referred->order, ipa_ref_use_name [ref->use]); if (ref->speculative) fprintf (file, " (speculative)"); } fprintf (file, "\n"); } /* Dump referring in LIST to FILE. */ void ipa_dump_referring (FILE * file, struct ipa_ref_list *list) { struct ipa_ref *ref; int i; for (i = 0; ipa_ref_list_referring_iterate (list, i, ref); i++) { fprintf (file, "%s/%i (%s)", ref->referring->asm_name (), ref->referring->order, ipa_ref_use_name [ref->use]); if (ref->speculative) fprintf (file, " (speculative)"); } fprintf (file, "\n"); } /* Clone reference REF to DEST_NODE and set its stmt to STMT. */ struct ipa_ref * ipa_clone_ref (struct ipa_ref *ref, symtab_node *dest_node, gimple stmt) { bool speculative = ref->speculative; unsigned int stmt_uid = ref->lto_stmt_uid; struct ipa_ref *ref2; ref2 = ipa_record_reference (dest_node, ref->referred, ref->use, stmt); ref2->speculative = speculative; ref2->lto_stmt_uid = stmt_uid; return ref2; } /* Clone all references from SRC to DEST_NODE or DEST_VARPOOL_NODE. */ void ipa_clone_references (symtab_node *dest_node, struct ipa_ref_list *src) { struct ipa_ref *ref, *ref2; int i; for (i = 0; ipa_ref_list_reference_iterate (src, i, ref); i++) { bool speculative = ref->speculative; unsigned int stmt_uid = ref->lto_stmt_uid; ref2 = ipa_record_reference (dest_node, ref->referred, ref->use, ref->stmt); ref2->speculative = speculative; ref2->lto_stmt_uid = stmt_uid; } } /* Clone all referring from SRC to DEST_NODE or DEST_VARPOOL_NODE. */ void ipa_clone_referring (symtab_node *dest_node, struct ipa_ref_list *src) { struct ipa_ref *ref, *ref2; int i; for (i = 0; ipa_ref_list_referring_iterate (src, i, ref); i++) { bool speculative = ref->speculative; unsigned int stmt_uid = ref->lto_stmt_uid; ref2 = ipa_record_reference (ref->referring, dest_node, ref->use, ref->stmt); ref2->speculative = speculative; ref2->lto_stmt_uid = stmt_uid; } } /* Return true when execution of REF can lead to return from function. */ bool ipa_ref_cannot_lead_to_return (struct ipa_ref *ref) { return cgraph_node_cannot_return (ipa_ref_referring_node (ref)); } /* Return true if list contains an alias. */ bool ipa_ref_has_aliases_p (struct ipa_ref_list *ref_list) { struct ipa_ref *ref; int i; for (i = 0; ipa_ref_list_referring_iterate (ref_list, i, ref); i++) if (ref->use == IPA_REF_ALIAS) return true; return false; } /* Find the structure describing a reference in REFERRING_NODE to REFERRED_NODE and associated with statement STMT. */ struct ipa_ref * ipa_find_reference (symtab_node *referring_node, symtab_node *referred_node, gimple stmt, unsigned int lto_stmt_uid) { struct ipa_ref *r = NULL; int i; for (i = 0; ipa_ref_list_reference_iterate (&referring_node->ref_list, i, r); i++) if (r->referred == referred_node && !r->speculative && ((stmt && r->stmt == stmt) || (lto_stmt_uid && r->lto_stmt_uid == lto_stmt_uid) || (!stmt && !lto_stmt_uid && !r->stmt && !r->lto_stmt_uid))) return r; return NULL; } /* Remove all references from REFERRING_NODE that are associated with statement STMT. */ void ipa_remove_stmt_references (symtab_node *referring_node, gimple stmt) { struct ipa_ref *r = NULL; int i = 0; while (ipa_ref_list_reference_iterate (&referring_node->ref_list, i, r)) if (r->stmt == stmt) ipa_remove_reference (r); else i++; } /* Remove all stmt references in non-speculative references. Those are not maintained during inlining & clonning. The exception are speculative references that are updated along with callgraph edges associated with them. */ void ipa_clear_stmts_in_references (symtab_node *referring_node) { struct ipa_ref *r = NULL; int i; for (i = 0; ipa_ref_list_reference_iterate (&referring_node->ref_list, i, r); i++) if (!r->speculative) { r->stmt = NULL; r->lto_stmt_uid = 0; } }