+/* Return ture if A1 and A2 represent equivalent function attribute lists.
+ Based on comp_type_attributes. */
+
+bool
+sem_item::compare_attributes (const_tree a1, const_tree a2)
+{
+ const_tree a;
+ if (a1 == a2)
+ return true;
+ for (a = a1; a != NULL_TREE; a = TREE_CHAIN (a))
+ {
+ const struct attribute_spec *as;
+ const_tree attr;
+
+ as = lookup_attribute_spec (get_attribute_name (a));
+ /* TODO: We can introduce as->affects_decl_identity
+ and as->affects_decl_reference_identity if attribute mismatch
+ gets a common reason to give up on merging. It may not be worth
+ the effort.
+ For example returns_nonnull affects only references, while
+ optimize attribute can be ignored because it is already lowered
+ into flags representation and compared separately. */
+ if (!as)
+ continue;
+
+ attr = lookup_attribute (as->name, CONST_CAST_TREE (a2));
+ if (!attr || !attribute_value_equal (a, attr))
+ break;
+ }
+ if (!a)
+ {
+ for (a = a2; a != NULL_TREE; a = TREE_CHAIN (a))
+ {
+ const struct attribute_spec *as;
+
+ as = lookup_attribute_spec (get_attribute_name (a));
+ if (!as)
+ continue;
+
+ if (!lookup_attribute (as->name, CONST_CAST_TREE (a1)))
+ break;
+ /* We don't need to compare trees again, as we did this
+ already in first loop. */
+ }
+ if (!a)
+ return true;
+ }
+ /* TODO: As in comp_type_attributes we may want to introduce target hook. */
+ return false;
+}
+
+/* Compare properties of symbols N1 and N2 that does not affect semantics of
+ symbol itself but affects semantics of its references from USED_BY (which
+ may be NULL if it is unknown). If comparsion is false, symbols
+ can still be merged but any symbols referring them can't.
+
+ If ADDRESS is true, do extra checking needed for IPA_REF_ADDR.
+
+ TODO: We can also split attributes to those that determine codegen of
+ a function body/variable constructor itself and those that are used when
+ referring to it. */
+
+bool
+sem_item::compare_referenced_symbol_properties (symtab_node *used_by,
+ symtab_node *n1,
+ symtab_node *n2,
+ bool address)
+{
+ if (is_a <cgraph_node *> (n1))
+ {
+ /* Inline properties matters: we do now want to merge uses of inline
+ function to uses of normal function because inline hint would be lost.
+ We however can merge inline function to noinline because the alias
+ will keep its DECL_DECLARED_INLINE flag.
+
+ Also ignore inline flag when optimizing for size or when function
+ is known to not be inlinable.
+
+ TODO: the optimize_size checks can also be assumed to be true if
+ unit has no !optimize_size functions. */
+
+ if ((!used_by || address || !is_a <cgraph_node *> (used_by)
+ || !opt_for_fn (used_by->decl, optimize_size))
+ && !opt_for_fn (n1->decl, optimize_size)
+ && n1->get_availability () > AVAIL_INTERPOSABLE
+ && (!DECL_UNINLINABLE (n1->decl) || !DECL_UNINLINABLE (n2->decl)))
+ {
+ if (DECL_DISREGARD_INLINE_LIMITS (n1->decl)
+ != DECL_DISREGARD_INLINE_LIMITS (n2->decl))
+ return return_false_with_msg
+ ("DECL_DISREGARD_INLINE_LIMITS are different");
+
+ if (DECL_DECLARED_INLINE_P (n1->decl)
+ != DECL_DECLARED_INLINE_P (n2->decl))
+ return return_false_with_msg ("inline attributes are different");
+ }
+
+ if (DECL_IS_OPERATOR_NEW (n1->decl)
+ != DECL_IS_OPERATOR_NEW (n2->decl))
+ return return_false_with_msg ("operator new flags are different");
+ }
+
+ /* Merging two definitions with a reference to equivalent vtables, but
+ belonging to a different type may result in ipa-polymorphic-call analysis
+ giving a wrong answer about the dynamic type of instance. */
+ if (is_a <varpool_node *> (n1))
+ {
+ if ((DECL_VIRTUAL_P (n1->decl) || DECL_VIRTUAL_P (n2->decl))
+ && (DECL_VIRTUAL_P (n1->decl) != DECL_VIRTUAL_P (n2->decl)
+ || !types_must_be_same_for_odr (DECL_CONTEXT (n1->decl),
+ DECL_CONTEXT (n2->decl)))
+ && (!used_by || !is_a <cgraph_node *> (used_by) || address
+ || opt_for_fn (used_by->decl, flag_devirtualize)))
+ return return_false_with_msg
+ ("references to virtual tables can not be merged");
+
+ if (address && DECL_ALIGN (n1->decl) != DECL_ALIGN (n2->decl))
+ return return_false_with_msg ("alignment mismatch");
+
+ /* For functions we compare attributes in equals_wpa, because we do
+ not know what attributes may cause codegen differences, but for
+ variables just compare attributes for references - the codegen
+ for constructors is affected only by those attributes that we lower
+ to explicit representation (such as DECL_ALIGN or DECL_SECTION). */
+ if (!compare_attributes (DECL_ATTRIBUTES (n1->decl),
+ DECL_ATTRIBUTES (n2->decl)))
+ return return_false_with_msg ("different var decl attributes");
+ if (comp_type_attributes (TREE_TYPE (n1->decl),
+ TREE_TYPE (n2->decl)) != 1)
+ return return_false_with_msg ("different var type attributes");
+ }
+
+ /* When matching virtual tables, be sure to also match information
+ relevant for polymorphic call analysis. */
+ if (used_by && is_a <varpool_node *> (used_by)
+ && DECL_VIRTUAL_P (used_by->decl))
+ {
+ if (DECL_VIRTUAL_P (n1->decl) != DECL_VIRTUAL_P (n2->decl))
+ return return_false_with_msg ("virtual flag mismatch");
+ if (DECL_VIRTUAL_P (n1->decl) && is_a <cgraph_node *> (n1)
+ && (DECL_FINAL_P (n1->decl) != DECL_FINAL_P (n2->decl)))
+ return return_false_with_msg ("final flag mismatch");
+ }
+ return true;
+}
+
+/* Hash properties that are compared by compare_referenced_symbol_properties. */
+
+void
+sem_item::hash_referenced_symbol_properties (symtab_node *ref,
+ inchash::hash &hstate,
+ bool address)
+{
+ if (is_a <cgraph_node *> (ref))
+ {
+ if ((type != FUNC || address || !opt_for_fn (decl, optimize_size))
+ && !opt_for_fn (ref->decl, optimize_size)
+ && !DECL_UNINLINABLE (ref->decl))
+ {
+ hstate.add_flag (DECL_DISREGARD_INLINE_LIMITS (ref->decl));
+ hstate.add_flag (DECL_DECLARED_INLINE_P (ref->decl));
+ }
+ hstate.add_flag (DECL_IS_OPERATOR_NEW (ref->decl));
+ }
+ else if (is_a <varpool_node *> (ref))
+ {
+ hstate.add_flag (DECL_VIRTUAL_P (ref->decl));
+ if (address)
+ hstate.add_int (DECL_ALIGN (ref->decl));
+ }
+}
+
+