@@ -95,8 +95,8 @@ namespace ir
// Inject some std types in here.
using std::unordered_map;
-/// A convenience typedef fo r an ordered set of size_t.
-typedef unordered_set<size_t> pointer_set;
+/// A convenience typedef for an unordered set of pointer values
+typedef unordered_set<uintptr_t> pointer_set;
/// Functor to hash a canonical type by using its pointer value.
struct canonical_type_hash
@@ -133,17 +133,17 @@ typedef vector<type_base_sptr> type_base_sptrs_type;
/// that you can de-allocate the environment instance.
class environment
{
+public:
struct priv;
std::unique_ptr<priv> priv_;
-public:
-
/// A convenience typedef for a map of canonical types. The key is
/// the pretty representation string of a particular type and the
/// value is the vector of canonical types that have the same pretty
/// representation string.
typedef std::unordered_map<string, std::vector<type_base_sptr> >
canonical_types_map_type;
+
environment();
virtual ~environment();
@@ -151,6 +151,667 @@ struct translation_unit::priv
{return types_;}
}; // end translation_unit::priv
+// <type_base definitions>
+
+/// Definition of the private data of @ref type_base.
+struct type_base::priv
+{
+ size_t size_in_bits;
+ size_t alignment_in_bits;
+ type_base_wptr canonical_type;
+ // The data member below holds the canonical type that is managed by
+ // the smart pointer referenced by the canonical_type data member
+ // above. We are storing this underlying (naked) pointer here, so
+ // that users can access it *fast*. Otherwise, accessing
+ // canonical_type above implies creating a shared_ptr, and that has
+ // been measured to be slow for some performance hot spots.
+ type_base* naked_canonical_type;
+ // Computing the representation of a type again and again can be
+ // costly. So we cache the internal and non-internal type
+ // representation strings here.
+ interned_string internal_cached_repr_;
+ interned_string cached_repr_;
+ // The next two data members are used while comparing types during
+ // canonicalization. They are useful for the "canonical type
+ // propagation" (aka on-the-fly-canonicalization) optimization
+ // implementation.
+
+ // The set of canonical recursive types this type depends on.
+ unordered_set<uintptr_t> depends_on_recursive_type_;
+ bool canonical_type_propagated_;
+
+ priv()
+ : size_in_bits(),
+ alignment_in_bits(),
+ naked_canonical_type(),
+ canonical_type_propagated_(false)
+ {}
+
+ priv(size_t s,
+ size_t a,
+ type_base_sptr c = type_base_sptr())
+ : size_in_bits(s),
+ alignment_in_bits(a),
+ canonical_type(c),
+ naked_canonical_type(c.get()),
+ canonical_type_propagated_(false)
+ {}
+
+ /// Test if the current type depends on recursive type comparison.
+ ///
+ /// A recursive type T is a type T which has a sub-type that is T
+ /// (recursively) itself.
+ ///
+ /// So this function tests if the current type has a recursive
+ /// sub-type or is a recursive type itself.
+ ///
+ /// @return true if the current type depends on a recursive type.
+ bool
+ depends_on_recursive_type() const
+ {return !depends_on_recursive_type_.empty();}
+
+ /// Test if the current type depends on a given recursive type.
+ ///
+ /// A recursive type T is a type T which has a sub-type that is T
+ /// (recursively) itself.
+ ///
+ /// So this function tests if the current type depends on a given
+ /// recursive type.
+ ///
+ /// @param dependant the type we want to test if the current type
+ /// depends on.
+ ///
+ /// @return true if the current type depends on the recursive type
+ /// @dependant.
+ bool
+ depends_on_recursive_type(const type_base* dependant) const
+ {
+ return
+ (depends_on_recursive_type_.find(reinterpret_cast<uintptr_t>(dependant))
+ != depends_on_recursive_type_.end());
+ }
+
+ /// Set the flag that tells if the current type depends on a given
+ /// recursive type.
+ ///
+ /// A recursive type T is a type T which has asub-type that is T
+ /// (recursively) itself.
+ ///
+ /// So this function tests if the current type depends on a
+ /// recursive type.
+ ///
+ /// @param t the recursive type that current type depends on.
+ void
+ set_depends_on_recursive_type(const type_base * t)
+ {depends_on_recursive_type_.insert(reinterpret_cast<uintptr_t>(t));}
+
+ /// Unset the flag that tells if the current type depends on a given
+ /// recursive type.
+ ///
+ /// A recursive type T is a type T which has asub-type that is T
+ /// (recursively) itself.
+ ///
+ /// So this function flags the current type as not being dependant
+ /// on a given recursive type.
+ ///
+ ///
+ /// @param t the recursive type to consider.
+ void
+ set_does_not_depend_on_recursive_type(const type_base *t)
+ {depends_on_recursive_type_.erase(reinterpret_cast<uintptr_t>(t));}
+
+ /// Flag the current type as not being dependant on any recursive type.
+ void
+ set_does_not_depend_on_recursive_type()
+ {depends_on_recursive_type_.clear();}
+
+ /// Test if the type carries a canonical type that is the result of
+ /// maybe_propagate_canonical_type(), aka, "canonical type
+ /// propagation optimization".
+ ///
+ /// @return true iff the current type carries a canonical type that
+ /// is the result of canonical type propagation.
+ bool
+ canonical_type_propagated()
+ {return canonical_type_propagated_;}
+
+ /// Set the flag that says if the type carries a canonical type that
+ /// is the result of maybe_propagate_canonical_type(), aka,
+ /// "canonical type propagation optimization".
+ ///
+ /// @param f true iff the current type carries a canonical type that
+ /// is the result of canonical type propagation.
+ void
+ set_canonical_type_propagated(bool f)
+ {canonical_type_propagated_ = f;}
+
+ /// If the current canonical type was set as the result of the
+ /// "canonical type propagation optimization", then clear it.
+ void
+ clear_propagated_canonical_type()
+ {
+ if (canonical_type_propagated_)
+ {
+ canonical_type.reset();
+ naked_canonical_type = nullptr;
+ set_canonical_type_propagated(false);
+ }
+ }
+}; // end struct type_base::priv
+
+// <environment definitions>
+/// The private data of the @ref environment type.
+struct environment::priv
+{
+ config config_;
+ canonical_types_map_type canonical_types_;
+ mutable vector<type_base_sptr> sorted_canonical_types_;
+ type_base_sptr void_type_;
+ type_base_sptr variadic_marker_type_;
+ unordered_set<const class_or_union*> classes_being_compared_;
+ unordered_set<const function_type*> fn_types_being_compared_;
+ vector<type_base_sptr> extra_live_types_;
+ interned_string_pool string_pool_;
+ // The two vectors below represent the stack of left and right
+ // operands of the current type comparison operation that is
+ // happening during type canonicalization.
+ //
+ // Basically, that stack of operand looks like below.
+ //
+ // First, suppose we have a type T_L that has two sub-types as this:
+ //
+ // T_L
+ // |
+ // +-- L_OP0
+ // |
+ // +-- L_OP1
+ //
+ // Now suppose that we have another type T_R that has two sub-types
+ // as this:
+ //
+ // T_R
+ // |
+ // +-- R_OP0
+ // |
+ // +-- R_OP1
+ //
+ // Now suppose that we compare T_L against T_R. We are going to
+ // have a stack of pair of types. Each pair of types represents
+ // two (sub) types being compared against each other.
+ //
+ // On the stack, we will thus first have the pair (T_L, T_R)
+ // being compared. Then, we will have the pair (L_OP0, R_OP0)
+ // being compared, and then the pair (L_OP1, R_OP1) being
+ // compared. Like this:
+ //
+ // | T_L | L_OP0 | L_OP1 | <-- this goes into left_type_comp_operands_;
+ // -------- -------------
+ // | T_R | R_OP0 | R_OP1 | <-- this goes into right_type_comp_operands_;
+ //
+ // This "stack of operands of the current type comparison, during
+ // type canonicalization" is used in the context of the @ref
+ // OnTheFlyCanonicalization optimization. It's used to detect if a
+ // sub-type of the type being canonicalized depends on a recursive
+ // type.
+ vector<const type_base*> left_type_comp_operands_;
+ vector<const type_base*> right_type_comp_operands_;
+ // Vector of types that protentially received propagated canonical types.
+ // If the canonical type propagation is confirmed, the potential
+ // canonical types must be promoted as canonical types. Otherwise if
+ // the canonical type propagation is cancelled, the canonical types
+ // must be cleared.
+ pointer_set types_with_non_confirmed_propagated_ct_;
+#ifdef WITH_DEBUG_SELF_COMPARISON
+ // This is used for debugging purposes.
+ // When abidw is used with the option --debug-abidiff, some
+ // libabigail internals need to get a hold on the initial binary
+ // input of abidw, as well as as the abixml file that represents the
+ // ABI of that binary.
+ //
+ // So this one is the corpus for the input binary.
+ corpus_wptr first_self_comparison_corpus_;
+ // This one is the corpus for the ABIXML file representing the
+ // serialization of the input binary.
+ corpus_wptr second_self_comparison_corpus_;
+ // This is also used for debugging purposes, when using
+ // 'abidw --debug-abidiff <binary>'. It holds the set of mapping of
+ // an abixml (canonical) type and its type-id.
+ unordered_map<string, uintptr_t> type_id_canonical_type_map_;
+ // Likewise. It holds a map that associates the pointer to a type read from
+ // abixml and the type-id string it corresponds to.
+ unordered_map<uintptr_t, string> pointer_type_id_map_;
+#endif
+ bool canonicalization_is_done_;
+ bool do_on_the_fly_canonicalization_;
+ bool decl_only_class_equals_definition_;
+ bool use_enum_binary_only_equality_;
+#ifdef WITH_DEBUG_SELF_COMPARISON
+ bool self_comparison_debug_on_;
+#endif
+
+ priv()
+ : canonicalization_is_done_(),
+ do_on_the_fly_canonicalization_(true),
+ decl_only_class_equals_definition_(false),
+ use_enum_binary_only_equality_(true)
+#ifdef WITH_DEBUG_SELF_COMPARISON
+ ,
+ self_comparison_debug_on_(false)
+#endif
+ {}
+
+ /// Push a pair of operands on the stack of operands of the current
+ /// type comparison, during type canonicalization.
+ ///
+ /// For more information on this, please look at the description of
+ /// the right_type_comp_operands_ data member.
+ ///
+ /// @param left the left-hand-side comparison operand to push.
+ ///
+ /// @param right the right-hand-side comparison operand to push.
+ void
+ push_composite_type_comparison_operands(const type_base* left,
+ const type_base* right)
+ {
+ ABG_ASSERT(left && right);
+
+ left_type_comp_operands_.push_back(left);
+ right_type_comp_operands_.push_back(right);
+ }
+
+ /// Pop a pair of operands from the stack of operands to the current
+ /// type comparison.
+ ///
+ /// For more information on this, please look at the description of
+ /// the right_type_comp_operands_ data member.
+ ///
+ /// @param left the left-hand-side comparison operand we expect to
+ /// pop from the top of the stack. If this doesn't match the
+ /// operand found on the top of the stack, the function aborts.
+ ///
+ /// @param right the right-hand-side comparison operand we expect to
+ /// pop from the bottom of the stack. If this doesn't match the
+ /// operand found on the top of the stack, the function aborts.
+ void
+ pop_composite_type_comparison_operands(const type_base* left,
+ const type_base* right)
+ {
+ const type_base *t = left_type_comp_operands_.back();
+ ABG_ASSERT(t == left);
+ t = right_type_comp_operands_.back();
+ ABG_ASSERT(t == right);
+
+ left_type_comp_operands_.pop_back();
+ right_type_comp_operands_.pop_back();
+ }
+
+ /// Mark all the types that comes after a certain one as NOT being
+ /// eligible for the canonical type propagation optimization.
+ ///
+ /// @param type the type that represents the "marker type". All
+ /// types after this one will be marked as being NON-eligible to
+ /// the canonical type propagation optimization.
+ ///
+ /// @param types the set of types to consider. In that vector, all
+ /// types that come after @p type are going to be marked as being
+ /// non-eligible to the canonical type propagation optimization.
+ ///
+ /// @return true iff the operation was successful.
+ bool
+ mark_dependant_types(const type_base* type,
+ vector<const type_base*>& types)
+ {
+ bool found = false;
+ for (auto t : types)
+ {
+ if (!found
+ && (reinterpret_cast<uintptr_t>(t)
+ == reinterpret_cast<uintptr_t>(type)))
+ {
+ found = true;
+ continue;
+ }
+ else if (found)
+ t->priv_->set_depends_on_recursive_type(type);
+ }
+ return found;
+ }
+
+ /// In the stack of the current types being compared (as part of
+ /// type canonicalization), mark all the types that comes after a
+ /// certain one as NOT being eligible to the canonical type
+ /// propagation optimization.
+ ///
+ /// For a starter, please read about the @ref
+ /// OnTheFlyCanonicalization, aka, "canonical type propagation
+ /// optimization".
+ ///
+ /// To implement that optimization, we need, among other things to
+ /// maintain stack of the types (and their sub-types) being
+ /// currently compared as part of type canonicalization.
+ ///
+ /// Note that we only consider the type that is the right-hand-side
+ /// operand of the comparison because it's that one that is being
+ /// canonicalized and thus, that is not yet canonicalized.
+ ///
+ /// The reason why a type is deemed NON-eligible to the canonical
+ /// type propagation optimization is that it "depends" on
+ /// recursively present type. Let me explain.
+ ///
+ /// Suppose we have a type T that has sub-types named ST0 and ST1.
+ /// Suppose ST1 itself has a sub-type that is T itself. In this
+ /// case, we say that T is a recursive type, because it has T
+ /// (itself) as one of its sub-types:
+ ///
+ /// T
+ /// +-- ST0
+ /// |
+ /// +-- ST1
+ /// +
+ /// |
+ /// +-- T
+ ///
+ /// ST1 is said to "depend" on T because it has T as a sub-type.
+ /// But because T is recursive, then ST1 is said to depend on a
+ /// recursive type. Notice however that ST0 does not depend on any
+ /// recursive type.
+ ///
+ /// When we are at the point of comparing the sub-type T of ST1
+ /// against its counterpart, the stack of the right-hand-side
+ /// operands of the type canonicalization is going to look like
+ /// this:
+ ///
+ /// | T | ST1 |
+ ///
+ /// We don't add the type T to the stack as we detect that T was
+ /// already in there (recursive cycle).
+ ///
+ /// So, this function will basically mark ST1 as being NON-eligible
+ /// to being the target of canonical type propagation.
+ ///
+ /// @param right the right-hand-side operand of the type comparison.
+ ///
+ /// @return true iff the operation was successful.
+ bool
+ mark_dependant_types_compared_until(const type_base* right)
+ {
+ bool result = false;
+
+ result |=
+ mark_dependant_types(right,
+ right_type_comp_operands_);
+ return result;
+ }
+
+ /// Propagate the canonical type of a type to another one.
+ ///
+ /// @param src the type to propagate the canonical type from.
+ ///
+ /// @param dest the type to propagate the canonical type of @p src
+ /// to.
+ ///
+ /// @return bool iff the canonical was propagated.
+ bool
+ propagate_ct(const type_base& src, const type_base& dest)
+ {
+ type_base_sptr canonical = src.get_canonical_type();
+ ABG_ASSERT(canonical);
+ dest.priv_->canonical_type = canonical;
+ dest.priv_->naked_canonical_type = canonical.get();
+ dest.priv_->set_canonical_type_propagated(true);
+ return true;
+ }
+
+ /// Mark a set of types that have been the target of canonical type
+ /// propagation and that depend on a recursive type as being
+ /// permanently canonicalized.
+ ///
+ /// To understand the sentence above, please read the description of
+ /// type canonicalization and especially about the "canonical type
+ /// propagation optimization" at @ref OnTheFlyCanonicalization, in
+ /// the src/abg-ir.cc file.
+ void
+ confirm_ct_propagation(const type_base* dependant_type)
+ {
+ pointer_set to_remove;
+ for (auto i : types_with_non_confirmed_propagated_ct_)
+ {
+ type_base *t = reinterpret_cast<type_base*>(i);
+ ABG_ASSERT(t->priv_->depends_on_recursive_type());
+ t->priv_->set_does_not_depend_on_recursive_type(dependant_type);
+ if (!t->priv_->depends_on_recursive_type())
+ to_remove.insert(i);
+ }
+
+ for (auto i : to_remove)
+ types_with_non_confirmed_propagated_ct_.erase(i);
+ }
+
+ /// Collect the types that depends on a given "target" type.
+ ///
+ /// Walk a set of types and if they depend directly or indirectly on
+ /// a "target" type, then collect them into a set.
+ ///
+ /// @param target the target type to consider.
+ ///
+ /// @param types the types to walk to detect those who depend on @p
+ /// target.
+ ///
+ /// @return true iff one or more type from @p types is found to
+ /// depend on @p target.
+ bool
+ collect_types_that_depends_on(const type_base *target,
+ const pointer_set& types,
+ pointer_set& collected)
+ {
+ bool result = false;
+ for (const auto i : types)
+ {
+ type_base *t = reinterpret_cast<type_base*>(i);
+ if (t->priv_->depends_on_recursive_type(target))
+ {
+ collected.insert(i);
+ collect_types_that_depends_on(t, types, collected);
+ result = true;
+ }
+ }
+ return result;
+ }
+
+ /// Reset the canonical type (set it nullptr) of a set of types that
+ /// have been the target of canonical type propagation and that
+ /// depend on a given recursive type.
+ ///
+ /// Once the canonical type of a type in that set is reset, the type
+ /// is marked as non being dependant on a recursive type anymore.
+ ///
+ /// To understand the sentences above, please read the description
+ /// of type canonicalization and especially about the "canonical
+ /// type propagation optimization" at @ref OnTheFlyCanonicalization,
+ /// in the src/abg-ir.cc file.
+ ///
+ /// @param target if a type which has been subject to the canonical
+ /// type propagation optimizationdepends on a this target type, then
+ /// cancel its canonical type.
+ void
+ cancel_ct_propagation(const type_base* target)
+ {
+ pointer_set to_remove;
+ collect_types_that_depends_on(target,
+ types_with_non_confirmed_propagated_ct_,
+ to_remove);
+
+ for (auto i : to_remove)
+ {
+ type_base *t = reinterpret_cast<type_base*>(i);
+ ABG_ASSERT(t->priv_->depends_on_recursive_type());
+ type_base_sptr canonical = t->priv_->canonical_type.lock();
+ if (canonical)
+ {
+ t->priv_->clear_propagated_canonical_type();
+ t->priv_->set_does_not_depend_on_recursive_type();
+ }
+ }
+
+ for (auto i : to_remove)
+ types_with_non_confirmed_propagated_ct_.erase(i);
+ }
+
+ /// Remove a given type from the set of types that have been
+ /// non-confirmed subjects of the canonical type propagation
+ /// optimization.
+ ///
+ /// @param dependant the dependant type to consider.
+ void
+ remove_from_types_with_non_confirmed_propagated_ct(const type_base* dependant)
+ {
+ uintptr_t i = reinterpret_cast<uintptr_t>(dependant);
+ types_with_non_confirmed_propagated_ct_.erase(i);
+ }
+
+};// end struct environment::priv
+
+// <class_or_union::priv definitions>
+struct class_or_union::priv
+{
+ typedef_decl_wptr naming_typedef_;
+ member_types member_types_;
+ data_members data_members_;
+ data_members non_static_data_members_;
+ member_functions member_functions_;
+ // A map that associates a linkage name to a member function.
+ string_mem_fn_sptr_map_type mem_fns_map_;
+ // A map that associates function signature strings to member
+ // function.
+ string_mem_fn_ptr_map_type signature_2_mem_fn_map_;
+ member_function_templates member_function_templates_;
+ member_class_templates member_class_templates_;
+
+ priv()
+ {}
+
+ priv(class_or_union::member_types& mbr_types,
+ class_or_union::data_members& data_mbrs,
+ class_or_union::member_functions& mbr_fns)
+ : member_types_(mbr_types),
+ data_members_(data_mbrs),
+ member_functions_(mbr_fns)
+ {
+ for (data_members::const_iterator i = data_members_.begin();
+ i != data_members_.end();
+ ++i)
+ if (!get_member_is_static(*i))
+ non_static_data_members_.push_back(*i);
+ }
+
+ /// Mark a class or union or union as being currently compared using
+ /// the class_or_union== operator.
+ ///
+ /// Note that is marking business is to avoid infinite loop when
+ /// comparing a class or union or union. If via the comparison of a
+ /// data member or a member function a recursive re-comparison of
+ /// the class or union is attempted, the marking business help to
+ /// detect that infinite loop possibility and avoid it.
+ ///
+ /// @param klass the class or union or union to mark as being
+ /// currently compared.
+ void
+ mark_as_being_compared(const class_or_union& klass) const
+ {
+ const environment* env = klass.get_environment();
+ ABG_ASSERT(env);
+ env->priv_->classes_being_compared_.insert(&klass);
+ }
+
+ /// Mark a class or union as being currently compared using the
+ /// class_or_union== operator.
+ ///
+ /// Note that is marking business is to avoid infinite loop when
+ /// comparing a class or union. If via the comparison of a data
+ /// member or a member function a recursive re-comparison of the
+ /// class or union is attempted, the marking business help to detect
+ /// that infinite loop possibility and avoid it.
+ ///
+ /// @param klass the class or union to mark as being currently
+ /// compared.
+ void
+ mark_as_being_compared(const class_or_union* klass) const
+ {mark_as_being_compared(*klass);}
+
+ /// Mark a class or union as being currently compared using the
+ /// class_or_union== operator.
+ ///
+ /// Note that is marking business is to avoid infinite loop when
+ /// comparing a class or union. If via the comparison of a data
+ /// member or a member function a recursive re-comparison of the
+ /// class or union is attempted, the marking business help to detect
+ /// that infinite loop possibility and avoid it.
+ ///
+ /// @param klass the class or union to mark as being currently
+ /// compared.
+ void
+ mark_as_being_compared(const class_or_union_sptr& klass) const
+ {mark_as_being_compared(*klass);}
+
+ /// If the instance of class_or_union has been previously marked as
+ /// being compared -- via an invocation of mark_as_being_compared()
+ /// this method unmarks it. Otherwise is has no effect.
+ ///
+ /// This method is not thread safe because it uses the static data
+ /// member classes_being_compared_. If you wish to use it in a
+ /// multi-threaded environment you should probably protect the
+ /// access to that static data member with a mutex or somesuch.
+ ///
+ /// @param klass the instance of class_or_union to unmark.
+ void
+ unmark_as_being_compared(const class_or_union& klass) const
+ {
+ const environment* env = klass.get_environment();
+ ABG_ASSERT(env);
+ env->priv_->classes_being_compared_.erase(&klass);
+ }
+
+ /// If the instance of class_or_union has been previously marked as
+ /// being compared -- via an invocation of mark_as_being_compared()
+ /// this method unmarks it. Otherwise is has no effect.
+ ///
+ /// @param klass the instance of class_or_union to unmark.
+ void
+ unmark_as_being_compared(const class_or_union* klass) const
+ {
+ if (klass)
+ return unmark_as_being_compared(*klass);
+ }
+
+ /// Test if a given instance of class_or_union is being currently
+ /// compared.
+ ///
+ ///@param klass the class or union to test.
+ ///
+ /// @return true if @p klass is being compared, false otherwise.
+ bool
+ comparison_started(const class_or_union& klass) const
+ {
+ const environment* env = klass.get_environment();
+ ABG_ASSERT(env);
+ return env->priv_->classes_being_compared_.count(&klass);
+ }
+
+ /// Test if a given instance of class_or_union is being currently
+ /// compared.
+ ///
+ ///@param klass the class or union to test.
+ ///
+ /// @return true if @p klass is being compared, false otherwise.
+ bool
+ comparison_started(const class_or_union* klass) const
+ {
+ if (klass)
+ return comparison_started(*klass);
+ return false;
+ }
+}; // end struct class_or_union::priv
+
} // end namespace ir
} // end namespace abigail
@@ -264,6 +264,118 @@ has_generic_anonymous_internal_type_name(const decl_base *d);
static interned_string
get_generic_anonymous_internal_type_name(const decl_base *d);
+void
+push_composite_type_comparison_operands(const type_base& left,
+ const type_base& right);
+
+void
+pop_composite_type_comparison_operands(const type_base& left,
+ const type_base& right);
+
+bool
+mark_dependant_types_compared_until(const type_base &r);
+
+/// Push a pair of operands on the stack of operands of the current
+/// type comparison, during type canonicalization.
+///
+/// For more information on this, please look at the description of
+/// the environment::priv::right_type_comp_operands_ data member.
+///
+/// @param left the left-hand-side comparison operand to push.
+///
+/// @param right the right-hand-side comparison operand to push.
+void
+push_composite_type_comparison_operands(const type_base& left,
+ const type_base& right)
+{
+ const environment * env = left.get_environment();
+ env->priv_->push_composite_type_comparison_operands(&left, &right);
+}
+
+/// Pop a pair of operands from the stack of operands to the current
+/// type comparison.
+///
+/// For more information on this, please look at the description of
+/// the environment::privright_type_comp_operands_ data member.
+///
+/// @param left the left-hand-side comparison operand we expect to
+/// pop from the top of the stack. If this doesn't match the
+/// operand found on the top of the stack, the function aborts.
+///
+/// @param right the right-hand-side comparison operand we expect to
+/// pop from the bottom of the stack. If this doesn't match the
+/// operand found on the top of the stack, the function aborts.
+void
+pop_composite_type_comparison_operands(const type_base& left,
+ const type_base& right)
+{
+ const environment * env = left.get_environment();
+ env->priv_->pop_composite_type_comparison_operands(&left, &right);
+}
+
+/// In the stack of the current types being compared (as part of type
+/// canonicalization), mark all the types that comes after a certain
+/// one as NOT being eligible to the canonical type propagation
+/// optimization.
+///
+/// For a starter, please read about the @ref
+/// OnTheFlyCanonicalization, aka, "canonical type propagation
+/// optimization".
+///
+/// To implement that optimization, we need, among other things to
+/// maintain stack of the types (and their sub-types) being
+/// currently compared as part of type canonicalization.
+///
+/// Note that we only consider the type that is the right-hand-side
+/// operand of the comparison because it's that one that is being
+/// canonicalized and thus, that is not yet canonicalized.
+///
+/// The reason why a type is deemed NON-eligible to the canonical
+/// type propagation optimization is that it "depends" on
+/// recursively present type. Let me explain.
+///
+/// Suppose we have a type T that has sub-types named ST0 and ST1.
+/// Suppose ST1 itself has a sub-type that is T itself. In this
+/// case, we say that T is a recursive type, because it has T
+/// (itself) as one of its sub-types:
+///
+/// T
+/// +-- ST0
+/// |
+/// +-- ST1
+/// +
+/// |
+/// +-- T
+///
+/// ST1 is said to "depend" on T because it has T as a sub-type.
+/// But because T is recursive, then ST1 is said to depend on a
+/// recursive type. Notice however that ST0 does not depend on any
+/// recursive type.
+///
+/// When we are at the point of comparing the sub-type T of ST1
+/// against its counterpart, the stack of the right-hand-side
+/// operands of the type canonicalization is going to look like
+/// this:
+///
+/// | T | ST1 |
+///
+/// We don't add the type T to the stack as we detect that T was
+/// already in there (recursive cycle).
+///
+/// So, this function will basically mark ST1 as being NON-eligible
+/// to being the target of canonical type propagation, by marking ST1
+/// as being dependant on T.
+///
+/// @param right the right-hand-side operand of the type comparison.
+///
+/// @return true iff the operation was successful.
+bool
+mark_dependant_types_compared_until(const type_base &r)
+{
+ const environment * env = r.get_environment();
+ return env->priv_->mark_dependant_types_compared_until(&r);
+}
+
/// @brief the location of a token represented in its simplest form.
/// Instances of this type are to be stored in a sorted vector, so the
/// type must have proper relational operators.
@@ -723,7 +835,7 @@ notify_equality_failed(const type_or_decl_base *l __attribute__((unused)),
{ \
if (value == false) \
notify_equality_failed(l, r); \
- return value; \
+ return value; \
} while (false)
#else // WITH_DEBUG_SELF_COMPARISON
@@ -752,6 +864,159 @@ try_canonical_compare(const T *l, const T *r)
return equals(*l, *r, 0);
}
+/// Detect if a recursive comparison cycle is detected while
+/// structurally comparing two types (a.k.a member-wise comparison).
+///
+/// @param l the left-hand-side operand of the current comparison.
+///
+/// @param r the right-hand-side operand of the current comparison.
+///
+/// @return true iff a comparison cycle is detected.
+template<typename T>
+bool
+is_comparison_cycle_detected(T& l, T& r)
+{
+ bool result = (l.priv_->comparison_started(l)
+ || l.priv_->comparison_started(r));
+ return result ;
+}
+
+/// This macro is to be used while comparing composite types that
+/// might recursively refer to themselves. Comparing two such types
+/// might get us into a cyle.
+///
+/// Practically, if we detect that we are already into comparing 'l'
+/// and 'r'; then, this is a cycle.
+//
+/// To break the cycle, we assume the result of the comparison is true
+/// for now. Comparing the other sub-types of l & r will tell us later
+/// if l & r are actually different or not.
+///
+/// In the mean time, returning true from this macro should not be
+/// used to propagate the canonical type of 'l' onto 'r' as we don't
+/// know yet if l equals r. All the types that depend on l and r
+/// can't (and that are in the comparison stack currently) can't have
+/// their canonical type propagated either. So this macro disallows
+/// canonical type propagation for those types that depend on a
+/// recursively defined sub-type for now.
+///
+/// @param l the left-hand-side operand of the comparison.
+#define RETURN_TRUE_IF_COMPARISON_CYCLE_DETECTED(l, r) \
+ do \
+ { \
+ if (is_comparison_cycle_detected(l, r)) \
+ { \
+ mark_dependant_types_compared_until(r); \
+ return true; \
+ } \
+ } \
+ while(false)
+
+
+/// Mark a pair of types as being compared.
+///
+/// This is helpful to later detect recursive cycles in the comparison
+/// stack.
+///
+/// @param l the left-hand-side operand of the comparison.
+///
+/// @parm r the right-hand-side operand of the comparison.
+template<typename T>
+void
+mark_types_as_being_compared(T& l, T&r)
+{
+ l.priv_->mark_as_being_compared(l);
+ l.priv_->mark_as_being_compared(r);
+ push_composite_type_comparison_operands(l, r);
+}
+
+/// Return the result of the comparison of two (sub) types.
+///
+/// The function does the necessary book keeping before returning the
+/// result of the comparison of two (sub) types.
+///
+/// The book-keeping done is in the following
+/// areas:
+///
+/// * Management of the Canonical Type Propagation optimization
+/// * type comparison cycle detection
+///
+/// @param l the left-hand-side operand of the type comparison
+///
+/// @param r the right-hand-side operand of the type comparison
+///
+/// @param value the result of the comparison of @p l and @p r.
+///
+/// @return the value @p value.
+template<typename T>
+bool
+return_comparison_result(T& l, T& r, bool value)
+{
+ if (value == true)
+ maybe_propagate_canonical_type(l, r);
+ l.priv_->unmark_as_being_compared(l);
+ l.priv_->unmark_as_being_compared(r);
+
+ pop_composite_type_comparison_operands(l, r);
+ const environment* env = l.get_environment();
+ if (env->do_on_the_fly_canonicalization())
+ // We are instructed to perform the "canonical type propagation"
+ // optimization, making 'r' to possibly get the canonical type of
+ // 'l' if it has one. This mostly means that we are currently
+ // canonicalizing the type that contain the subtype provided in
+ // the 'r' argument.
+ {
+ if (value == true
+ && is_type(&r)->priv_->depends_on_recursive_type()
+ && !env->priv_->right_type_comp_operands_.empty()
+ && is_type(&r)->priv_->canonical_type_propagated())
+ {
+ // Track the object 'r' for which the propagated canonical
+ // type might be re-initialized if the current comparison
+ // eventually fails.
+ env->priv_->types_with_non_confirmed_propagated_ct_.insert
+ (reinterpret_cast<uintptr_t>(is_type(&r)));
+ }
+ else if (value == true && env->priv_->right_type_comp_operands_.empty())
+ {
+ // The type provided in the 'r' argument is the type that is
+ // being canonicalized; 'r' is not a mere subtype being
+ // compared, it's the whole type being canonicalized. And
+ // its canonicalization has just succeeded. So let's
+ // confirm the "canonical type propagation" of all the
+ // sub-types that were compared during the comparison of
+ // 'r'.
+ env->priv_->confirm_ct_propagation(&r);
+ if (is_type(&r)->priv_->depends_on_recursive_type())
+ {
+ is_type(&r)->priv_->set_does_not_depend_on_recursive_type();
+ env->priv_->remove_from_types_with_non_confirmed_propagated_ct(&r);
+ }
+ }
+ else if (value == false)
+ {
+ // The comparison of the current sub-type failed. So all
+ // the types in
+ // env->prix_->types_with_non_confirmed_propagated_ct_
+ // should see their tentatively propagated canonical type
+ // cancelled.
+ env->priv_->cancel_ct_propagation(&r);
+ if (is_type(&r)->priv_->depends_on_recursive_type())
+ {
+ // The right-hand-side operand cannot carry any tentative
+ // canonical type at this point.
+ type_base* canonical_type = r.get_naked_canonical_type();
+ ABG_ASSERT(canonical_type == nullptr);
+ // Reset the marking of the right-hand-side operand as it no
+ // longer carries a tentative canonical type that might be
+ // later cancelled.
+ is_type(&r)->priv_->set_does_not_depend_on_recursive_type();
+ }
+ }
+ }
+ ABG_RETURN(value);
+}
+
/// Getter of all types types sorted by their pretty representation.
///
/// @return a sorted vector of all types sorted by their pretty
@@ -2836,57 +3101,6 @@ dm_context_rel::~dm_context_rel()
typedef unordered_map<interned_string,
bool, hash_interned_string> interned_string_bool_map_type;
-/// The private data of the @ref environment type.
-struct environment::priv
-{
- config config_;
- canonical_types_map_type canonical_types_;
- mutable vector<type_base_sptr> sorted_canonical_types_;
- type_base_sptr void_type_;
- type_base_sptr variadic_marker_type_;
- unordered_set<const class_or_union*> classes_being_compared_;
- unordered_set<const function_type*> fn_types_being_compared_;
- vector<type_base_sptr> extra_live_types_;
- interned_string_pool string_pool_;
-#ifdef WITH_DEBUG_SELF_COMPARISON
- // This is used for debugging purposes.
- // When abidw is used with the option --debug-abidiff, some
- // libabigail internals need to get a hold on the initial binary
- // input of abidw, as well as as the abixml file that represents the
- // ABI of that binary.
- //
- // So this one is the corpus for the input binary.
- corpus_wptr first_self_comparison_corpus_;
- // This one is the corpus for the ABIXML file representing the
- // serialization of the input binary.
- corpus_wptr second_self_comparison_corpus_;
- // This is also used for debugging purposes, when using
- // 'abidw --debug-abidiff <binary>'. It holds the set of mapping of
- // an abixml (canonical) type and its type-id.
- unordered_map<string, uintptr_t> type_id_canonical_type_map_;
- // Likewise. It holds a map that associates the pointer to a type read from
- // abixml and the type-id string it corresponds to.
- unordered_map<uintptr_t, string> pointer_type_id_map_;
-#endif
- bool canonicalization_is_done_;
- bool do_on_the_fly_canonicalization_;
- bool decl_only_class_equals_definition_;
- bool use_enum_binary_only_equality_;
-#ifdef WITH_DEBUG_SELF_COMPARISON
- bool self_comparison_debug_on_;
-#endif
-
- priv()
- : canonicalization_is_done_(),
- do_on_the_fly_canonicalization_(true),
- decl_only_class_equals_definition_(false),
- use_enum_binary_only_equality_(true)
-#ifdef WITH_DEBUG_SELF_COMPARISON
- ,
- self_comparison_debug_on_(false)
-#endif
- {}
-};// end struct environment::priv
/// Default constructor of the @ref environment type.
environment::environment()
@@ -13086,44 +13300,7 @@ global_scope::~global_scope()
{
}
-// <type_base definitions>
-
-/// Definition of the private data of @ref type_base.
-struct type_base::priv
-{
- size_t size_in_bits;
- size_t alignment_in_bits;
- type_base_wptr canonical_type;
- // The data member below holds the canonical type that is managed by
- // the smart pointer referenced by the canonical_type data member
- // above. We are storing this underlying (naked) pointer here, so
- // that users can access it *fast*. Otherwise, accessing
- // canonical_type above implies creating a shared_ptr, and that has
- // been measured to be slow for some performance hot spots.
- type_base* naked_canonical_type;
- // Computing the representation of a type again and again can be
- // costly. So we cache the internal and non-internal type
- // representation strings here.
- interned_string internal_cached_repr_;
- interned_string cached_repr_;
-
- priv()
- : size_in_bits(),
- alignment_in_bits(),
- naked_canonical_type()
- {}
-
- priv(size_t s,
- size_t a,
- type_base_sptr c = type_base_sptr())
- : size_in_bits(s),
- alignment_in_bits(a),
- canonical_type(c),
- naked_canonical_type(c.get())
- {}
-}; // end struct type_base::priv
-
-static void
+static bool
maybe_propagate_canonical_type(const type_base& lhs_type,
const type_base& rhs_type);
@@ -18411,21 +18588,10 @@ equals(const function_type& l,
const function_type& r,
change_kind* k)
{
-#define RETURN(value) \
- do { \
- l.priv_->unmark_as_being_compared(l); \
- l.priv_->unmark_as_being_compared(r); \
- if (value == true) \
- maybe_propagate_canonical_type(l, r); \
- ABG_RETURN(value); \
- } while(0)
-
- if (l.priv_->comparison_started(l)
- || l.priv_->comparison_started(r))
- return true;
+#define RETURN(value) return return_comparison_result(l, r, value)
- l.priv_->mark_as_being_compared(l);
- l.priv_->mark_as_being_compared(r);
+ RETURN_TRUE_IF_COMPARISON_CYCLE_DETECTED(l, r);
+ mark_types_as_being_compared(l, r);
bool result = true;
@@ -19956,145 +20122,6 @@ function_decl::parameter::get_pretty_representation(bool internal,
// </function_decl::parameter definitions>
// <class_or_union definitions>
-struct class_or_union::priv
-{
- typedef_decl_wptr naming_typedef_;
- member_types member_types_;
- data_members data_members_;
- data_members non_static_data_members_;
- member_functions member_functions_;
- // A map that associates a linkage name to a member function.
- string_mem_fn_sptr_map_type mem_fns_map_;
- // A map that associates function signature strings to member
- // function.
- string_mem_fn_ptr_map_type signature_2_mem_fn_map_;
- member_function_templates member_function_templates_;
- member_class_templates member_class_templates_;
-
- priv()
- {}
-
- priv(class_or_union::member_types& mbr_types,
- class_or_union::data_members& data_mbrs,
- class_or_union::member_functions& mbr_fns)
- : member_types_(mbr_types),
- data_members_(data_mbrs),
- member_functions_(mbr_fns)
- {
- for (data_members::const_iterator i = data_members_.begin();
- i != data_members_.end();
- ++i)
- if (!get_member_is_static(*i))
- non_static_data_members_.push_back(*i);
- }
-
- /// Mark a class or union or union as being currently compared using
- /// the class_or_union== operator.
- ///
- /// Note that is marking business is to avoid infinite loop when
- /// comparing a class or union or union. If via the comparison of a
- /// data member or a member function a recursive re-comparison of
- /// the class or union is attempted, the marking business help to
- /// detect that infinite loop possibility and avoid it.
- ///
- /// @param klass the class or union or union to mark as being
- /// currently compared.
- void
- mark_as_being_compared(const class_or_union& klass) const
- {
- const environment* env = klass.get_environment();
- ABG_ASSERT(env);
- env->priv_->classes_being_compared_.insert(&klass);
- }
-
- /// Mark a class or union as being currently compared using the
- /// class_or_union== operator.
- ///
- /// Note that is marking business is to avoid infinite loop when
- /// comparing a class or union. If via the comparison of a data
- /// member or a member function a recursive re-comparison of the
- /// class or union is attempted, the marking business help to detect
- /// that infinite loop possibility and avoid it.
- ///
- /// @param klass the class or union to mark as being currently
- /// compared.
- void
- mark_as_being_compared(const class_or_union* klass) const
- {mark_as_being_compared(*klass);}
-
- /// Mark a class or union as being currently compared using the
- /// class_or_union== operator.
- ///
- /// Note that is marking business is to avoid infinite loop when
- /// comparing a class or union. If via the comparison of a data
- /// member or a member function a recursive re-comparison of the
- /// class or union is attempted, the marking business help to detect
- /// that infinite loop possibility and avoid it.
- ///
- /// @param klass the class or union to mark as being currently
- /// compared.
- void
- mark_as_being_compared(const class_or_union_sptr& klass) const
- {mark_as_being_compared(*klass);}
-
- /// If the instance of class_or_union has been previously marked as
- /// being compared -- via an invocation of mark_as_being_compared()
- /// this method unmarks it. Otherwise is has no effect.
- ///
- /// This method is not thread safe because it uses the static data
- /// member classes_being_compared_. If you wish to use it in a
- /// multi-threaded environment you should probably protect the
- /// access to that static data member with a mutex or somesuch.
- ///
- /// @param klass the instance of class_or_union to unmark.
- void
- unmark_as_being_compared(const class_or_union& klass) const
- {
- const environment* env = klass.get_environment();
- ABG_ASSERT(env);
- env->priv_->classes_being_compared_.erase(&klass);
- }
-
- /// If the instance of class_or_union has been previously marked as
- /// being compared -- via an invocation of mark_as_being_compared()
- /// this method unmarks it. Otherwise is has no effect.
- ///
- /// @param klass the instance of class_or_union to unmark.
- void
- unmark_as_being_compared(const class_or_union* klass) const
- {
- if (klass)
- return unmark_as_being_compared(*klass);
- }
-
- /// Test if a given instance of class_or_union is being currently
- /// compared.
- ///
- ///@param klass the class or union to test.
- ///
- /// @return true if @p klass is being compared, false otherwise.
- bool
- comparison_started(const class_or_union& klass) const
- {
- const environment* env = klass.get_environment();
- ABG_ASSERT(env);
- return env->priv_->classes_being_compared_.count(&klass);
- }
-
- /// Test if a given instance of class_or_union is being currently
- /// compared.
- ///
- ///@param klass the class or union to test.
- ///
- /// @return true if @p klass is being compared, false otherwise.
- bool
- comparison_started(const class_or_union* klass) const
- {
- if (klass)
- return comparison_started(*klass);
- return false;
- }
-}; // end struct class_or_union::priv
/// A Constructor for instances of @ref class_or_union
///
@@ -21030,12 +21057,7 @@ class_or_union::operator==(const class_or_union& other) const
bool
equals(const class_or_union& l, const class_or_union& r, change_kind* k)
{
-#define RETURN(value) \
- do { \
- l.priv_->unmark_as_being_compared(l); \
- l.priv_->unmark_as_being_compared(r); \
- ABG_RETURN(value); \
- } while(0)
+#define RETURN(value) return return_comparison_result(l, r, value);
// if one of the classes is declaration-only, look through it to
// get its definition.
@@ -21116,12 +21138,9 @@ equals(const class_or_union& l, const class_or_union& r, change_kind* k)
}
}
- if (l.priv_->comparison_started(l)
- || l.priv_->comparison_started(r))
- return true;
+ RETURN_TRUE_IF_COMPARISON_CYCLE_DETECTED(l, r);
- l.priv_->mark_as_being_compared(l);
- l.priv_->mark_as_being_compared(r);
+ mark_types_as_being_compared(l, r);
bool val = *def1 == *def2;
if (!val)
@@ -21142,12 +21161,9 @@ equals(const class_or_union& l, const class_or_union& r, change_kind* k)
if (types_defined_same_linux_kernel_corpus_public(l, r))
return true;
- if (l.priv_->comparison_started(l)
- || l.priv_->comparison_started(r))
- return true;
+ RETURN_TRUE_IF_COMPARISON_CYCLE_DETECTED(l, r);
- l.priv_->mark_as_being_compared(l);
- l.priv_->mark_as_being_compared(r);
+ mark_types_as_being_compared(l, r);
bool result = true;
@@ -21329,39 +21345,11 @@ copy_member_function(const class_or_union_sptr& t, const method_decl* method)
// </class_or_union definitions>
-/// Test if two @ref class_decl or @ref function_type are already
-/// being compared.
-///
-/// @param lhs_type the first type that would be involved in a
-/// potential comparison.
-///
-/// @param rhs_type the second type that would involved in a potential
-/// comparison.
-///
-/// @return true iff @p lhs_type and @p rhs_type are being compared.
-static bool
-types_are_being_compared(const type_base& lhs_type,
- const type_base& rhs_type)
-{
- type_base *l = &const_cast<type_base&>(lhs_type);
- type_base *r = &const_cast<type_base&>(rhs_type);
-
- if (class_or_union *l_cou = is_class_or_union_type(l))
- if (class_or_union *r_cou = is_class_or_union_type(r))
- return (l_cou->priv_->comparison_started(*l_cou)
- || l_cou->priv_->comparison_started(*r_cou));
-
- if (function_type *l_fn_type = is_function_type(l))
- if (function_type *r_fn_type = is_function_type(r))
- return (l_fn_type->priv_->comparison_started(*l_fn_type)
- || l_fn_type->priv_->comparison_started(*r_fn_type));
-
- return false;
-}
-
/// @defgroup OnTheFlyCanonicalization On-the-fly Canonicalization
/// @{
///
+/// This optimization is also known as "canonical type propagation".
+///
/// During the canonicalization of a type T (which doesn't yet have a
/// canonical type), T is compared structurally (member-wise) against
/// a type C which already has a canonical type. The comparison
@@ -21382,6 +21370,70 @@ types_are_being_compared(const type_base& lhs_type,
/// For now this on-the-fly canonicalization only happens when
/// comparing @ref class_decl and @ref function_type.
///
+/// Note however that there is a case when a type is *NOT* eligible to
+/// this canonical type propagation optimization.
+///
+/// The reason why a type is deemed NON-eligible to the canonical type
+/// propagation optimization is that it "depends" on recursively
+/// present type. Let me explain.
+///
+/// Suppose we have a type T that has sub-types named ST0 and ST1.
+/// Suppose ST1 itself has a sub-type that is T itself. In this case,
+/// we say that T is a recursive type, because it has T (itself) as
+/// one of its sub-types:
+///
+/// T
+/// +-- ST0
+/// |
+/// +-- ST1
+/// | +
+/// | |
+/// | +-- T
+/// |
+/// +-- ST2
+///
+/// ST1 is said to "depend" on T because it has T as a sub-type. But
+/// because T is recursive, then ST1 is said to depend on a recursive
+/// type. Notice however that ST0 does not depend on any recursive
+/// type.
+///
+/// Now suppose we are comparing T to a type T' that has the same
+/// structure with sub-types ST0', ST1' and ST2'. During the
+/// comparison of ST1 against ST1', their sub-type T is compared
+/// against T'. Because T (resp. T') is a recursive type that is
+/// already being compared, the comparison of T against T' (as a
+/// subtypes of ST1 and ST1') returns true, meaning they are
+/// considered equal. This is done so that we don't enter an infinite
+/// recursion.
+///
+/// That means ST1 is also deemed equal to ST1'. If we are in the
+/// course of the canonicalization of T' and thus if T (as well as as
+/// all of its sub-types) is already canonicalized, then the canonical
+/// type propagation optimization will make us propagate the canonical
+/// type of ST1 onto ST1'. So the canonical type of ST1' will be
+/// equal to the canonical type of ST1 as a result of that
+/// optmization.
+///
+/// But then, later down the road, when ST2 is compared against ST2',
+/// let's suppose that we find out that they are different. Meaning
+/// that ST2 != ST2'. This means that T != T', i.e, the
+/// canonicalization of T' failed for now. But most importantly, it
+/// means that the propagation of the canonical type of ST1 to ST1'
+/// must now be invalidated. Meaning, ST1' must now be considered as
+/// not having any canonical type.
+///
+/// In other words, during type canonicalization, if ST1' depends on a
+/// recursive type T', its propagated canonical type must be
+/// invalidated (set to nullptr) if T' appears to be different from T,
+/// a.k.a, the canonicalization of T' temporarily failed.
+///
+/// This means that any sub-type that depends on recursive types and
+/// that has been the target of the canonical type propagation
+/// optimization must be tracked. If the dependant recursive type
+/// fails its canonicalization, then the sub-type being compared must
+/// have its propagated canonical type cleared. In other words, its
+/// propagated canonical type must be cancelled.
+///
/// @}
@@ -21392,22 +21444,17 @@ types_are_being_compared(const type_base& lhs_type,
/// @param lhs_type the type which canonical type to propagate.
///
/// @param rhs_type the type which canonical type to set.
-static void
+static bool
maybe_propagate_canonical_type(const type_base& lhs_type,
const type_base& rhs_type)
{
-
if (const environment *env = lhs_type.get_environment())
if (env->do_on_the_fly_canonicalization())
if (type_base_sptr canonical_type = lhs_type.get_canonical_type())
- if (!rhs_type.get_canonical_type()
- && !types_are_being_compared(lhs_type, rhs_type))
- {
- const_cast<type_base&>(rhs_type).priv_->canonical_type =
- canonical_type;
- const_cast<type_base&>(rhs_type).priv_->naked_canonical_type =
- canonical_type.get();
- }
+ if (!rhs_type.get_canonical_type())
+ if (env->priv_->propagate_ct(lhs_type, rhs_type))
+ return true;
+ return false;
}
// <class_decl definitions>
@@ -22573,12 +22620,41 @@ method_matches_at_least_one_in_vector(const method_decl_sptr& method,
for (class_decl::member_functions::const_iterator i = fns.begin();
i != fns.end();
++i)
- if (methods_equal_modulo_elf_symbol(*i, method))
+ // Note that the comparison must be done in this order: method ==
+ // *i This is to keep the consistency of the comparison. It's
+ // important especially when doing type canonicalization. The
+ // already canonicalize type is the left operand, and the type
+ // being canonicalized is the right operand. This comes from the
+ // code in type_base::get_canonical_type_for().
+ if (methods_equal_modulo_elf_symbol(method, *i))
return true;
return false;
}
+/// Cancel the canonical type that was propagated.
+///
+/// If we are in the process of comparing a type for the purpose of
+/// canonicalization, and if that type has been the target of the
+/// canonical type propagation optimization, then clear the propagated
+/// canonical type. See @ref OnTheFlyCanonicalization for more about
+/// the canonical type optimization
+///
+/// @param t the type to consider.
+static bool
+maybe_cancel_propagated_canonical_type(const class_or_union& t)
+{
+ const environment* env = t.get_environment();
+ if (env && env->do_on_the_fly_canonicalization())
+ if (is_type(&t)->priv_->canonical_type_propagated())
+ {
+ is_type(&t)->priv_->clear_propagated_canonical_type();
+ env->priv_->remove_from_types_with_non_confirmed_propagated_ct(&t);
+ return true;
+ }
+ return false;
+}
+
/// Compares two instances of @ref class_decl.
///
/// If the two intances are different, set a bitfield to give some
@@ -22608,9 +22684,8 @@ equals(const class_decl& l, const class_decl& r, change_kind* k)
static_cast<const class_or_union&>(r),
k);
- if (l.class_or_union::priv_->comparison_started(l)
- || l.class_or_union::priv_->comparison_started(r))
- return true;
+ RETURN_TRUE_IF_COMPARISON_CYCLE_DETECTED(static_cast<const class_or_union&>(l),
+ static_cast<const class_or_union&>(r));
bool result = true;
if (!equals(static_cast<const class_or_union&>(l),
@@ -22622,17 +22697,19 @@ equals(const class_decl& l, const class_decl& r, change_kind* k)
return result;
}
- l.class_or_union::priv_->mark_as_being_compared(l);
- l.class_or_union::priv_->mark_as_being_compared(r);
+ mark_types_as_being_compared(static_cast<const class_or_union&>(l),
+ static_cast<const class_or_union&>(r));
+
+#define RETURN(value) \
+ return return_comparison_result(static_cast<const class_or_union&>(l), \
+ static_cast<const class_or_union&>(r), \
+ value);
-#define RETURN(value) \
- do { \
- l.class_or_union::priv_->unmark_as_being_compared(l); \
- l.class_or_union::priv_->unmark_as_being_compared(r); \
- if (value == true) \
- maybe_propagate_canonical_type(l, r); \
- ABG_RETURN(value); \
- } while(0)
+ // If comparing the class_or_union 'part' of the type led to
+ // canonical type propagation, then cancel that because it's too
+ // early to do that at this point. We still need to compare bases
+ // virtual members.
+ maybe_cancel_propagated_canonical_type(r);
// Compare bases.
if (l.get_base_specifiers().size() != r.get_base_specifiers().size())
@@ -23739,8 +23816,6 @@ equals(const union_decl& l, const union_decl& r, change_kind* k)
bool result = equals(static_cast<const class_or_union&>(l),
static_cast<const class_or_union&>(r),
k);
- if (result == true)
- maybe_propagate_canonical_type(l, r);
ABG_RETURN(result);
}
@@ -5675,6 +5675,8 @@ build_type(read_context& ctxt,
}
#endif
+ if (t)
+ ctxt.maybe_canonicalize_type(t,/*force_delay=*/false );
return t;
}
@@ -89,7 +89,12 @@ Variables changes summary: 0 Removed, 0 Changed, 3 Added variables
parameter 1 of type 'lttng_action*' has sub-type changes:
in pointed to type 'struct lttng_action':
type size hasn't changed
- 2 data member changes:
+ 3 data member changes:
+ type of 'action_validate_cb validate' changed:
+ underlying type 'bool (lttng_action*)*' changed:
+ in pointed to type 'function type bool (lttng_action*)':
+ parameter 1 of type 'lttng_action*' has sub-type changes:
+ pointed to type 'struct lttng_action' changed, as being reported
type of 'action_serialize_cb serialize' changed:
underlying type 'typedef ssize_t (lttng_action*, char*)*' changed:
in pointed to type 'function type typedef ssize_t (lttng_action*, char*)':
@@ -68,7 +68,7 @@
================ end of changes of 'libssl3.so'===============
================ changes of 'libsmime3.so'===============
- Functions changes summary: 0 Removed, 1 Changed (127 filtered out), 0 Added functions
+ Functions changes summary: 0 Removed, 1 Changed (146 filtered out), 0 Added functions
Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
1 function with some indirect sub-type change:
@@ -82,12 +82,12 @@
type of 'NSSCMSContent content' changed:
underlying type 'union NSSCMSContentUnion' at cmst.h:118:1 changed:
type size hasn't changed
- 1 data member changes (3 filtered):
+ 1 data member changes (4 filtered):
type of 'NSSCMSEncryptedData* encryptedData' changed:
in pointed to type 'typedef NSSCMSEncryptedData' at cmst.h:65:1:
underlying type 'struct NSSCMSEncryptedDataStr' at cmst.h:468:1 changed:
type size hasn't changed
- 1 data member changes (1 filtered):
+ 1 data member changes (2 filtered):
type of 'NSSCMSAttribute** unprotectedAttr' changed:
in pointed to type 'NSSCMSAttribute*':
in pointed to type 'typedef NSSCMSAttribute' at cmst.h:69:1:
@@ -29,7 +29,87 @@
in pointed to type 'typedef QXLState' at spice-qxl.h:35:1:
underlying type 'struct QXLState' at reds.h:93:1 changed:
type size hasn't changed
- 1 data member change:
+ 2 data member changes:
+ type of 'QXLInterface* qif' changed:
+ in pointed to type 'typedef QXLInterface' at spice-qxl.h:33:1:
+ underlying type 'struct QXLInterface' at spice.h:230:1 changed:
+ type size hasn't changed
+ 15 data member changes:
+ type of 'void (QXLInstance*, QXLWorker*)* attache_worker' changed:
+ in pointed to type 'function type void (QXLInstance*, QXLWorker*)':
+ parameter 1 of type 'QXLInstance*' has sub-type changes:
+ in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+ underlying type 'struct QXLInstance' changed, as being reported
+ type of 'void (QXLInstance*, int)* set_compression_level' changed:
+ in pointed to type 'function type void (QXLInstance*, int)':
+ parameter 1 of type 'QXLInstance*' has sub-type changes:
+ in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+ underlying type 'struct QXLInstance' changed, as being reported
+ type of 'void (QXLInstance*, typedef uint32_t)* set_mm_time' changed:
+ in pointed to type 'function type void (QXLInstance*, typedef uint32_t)':
+ parameter 1 of type 'QXLInstance*' has sub-type changes:
+ in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+ underlying type 'struct QXLInstance' changed, as being reported
+ type of 'void (QXLInstance*, QXLDevInitInfo*)* get_init_info' changed:
+ in pointed to type 'function type void (QXLInstance*, QXLDevInitInfo*)':
+ parameter 1 of type 'QXLInstance*' has sub-type changes:
+ in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+ underlying type 'struct QXLInstance' changed, as being reported
+ type of 'int (QXLInstance*, QXLCommandExt*)* get_command' changed:
+ in pointed to type 'function type int (QXLInstance*, QXLCommandExt*)':
+ parameter 1 of type 'QXLInstance*' has sub-type changes:
+ in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+ underlying type 'struct QXLInstance' changed, as being reported
+ type of 'int (QXLInstance*)* req_cmd_notification' changed:
+ in pointed to type 'function type int (QXLInstance*)':
+ parameter 1 of type 'QXLInstance*' has sub-type changes:
+ in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+ underlying type 'struct QXLInstance' changed, as being reported
+ type of 'void (QXLInstance*, struct QXLReleaseInfoExt)* release_resource' changed:
+ in pointed to type 'function type void (QXLInstance*, struct QXLReleaseInfoExt)':
+ parameter 1 of type 'QXLInstance*' has sub-type changes:
+ in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+ underlying type 'struct QXLInstance' changed, as being reported
+ type of 'int (QXLInstance*, QXLCommandExt*)* get_cursor_command' changed:
+ in pointed to type 'function type int (QXLInstance*, QXLCommandExt*)':
+ parameter 1 of type 'QXLInstance*' has sub-type changes:
+ in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+ underlying type 'struct QXLInstance' changed, as being reported
+ type of 'int (QXLInstance*)* req_cursor_notification' changed:
+ in pointed to type 'function type int (QXLInstance*)':
+ parameter 1 of type 'QXLInstance*' has sub-type changes:
+ in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+ underlying type 'struct QXLInstance' changed, as being reported
+ type of 'void (QXLInstance*, typedef uint32_t)* notify_update' changed:
+ in pointed to type 'function type void (QXLInstance*, typedef uint32_t)':
+ parameter 1 of type 'QXLInstance*' has sub-type changes:
+ in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+ underlying type 'struct QXLInstance' changed, as being reported
+ type of 'int (QXLInstance*)* flush_resources' changed:
+ in pointed to type 'function type int (QXLInstance*)':
+ parameter 1 of type 'QXLInstance*' has sub-type changes:
+ in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+ underlying type 'struct QXLInstance' changed, as being reported
+ type of 'void (QXLInstance*, typedef uint64_t)* async_complete' changed:
+ in pointed to type 'function type void (QXLInstance*, typedef uint64_t)':
+ parameter 1 of type 'QXLInstance*' has sub-type changes:
+ in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+ underlying type 'struct QXLInstance' changed, as being reported
+ type of 'void (QXLInstance*, typedef uint32_t, QXLRect*, typedef uint32_t)* update_area_complete' changed:
+ in pointed to type 'function type void (QXLInstance*, typedef uint32_t, QXLRect*, typedef uint32_t)':
+ parameter 1 of type 'QXLInstance*' has sub-type changes:
+ in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+ underlying type 'struct QXLInstance' changed, as being reported
+ type of 'void (QXLInstance*, typedef uint8_t, uint8_t*)* set_client_capabilities' changed:
+ in pointed to type 'function type void (QXLInstance*, typedef uint8_t, uint8_t*)':
+ parameter 1 of type 'QXLInstance*' has sub-type changes:
+ in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+ underlying type 'struct QXLInstance' changed, as being reported
+ type of 'int (QXLInstance*, VDAgentMonitorsConfig*)* client_monitors_config' changed:
+ in pointed to type 'function type int (QXLInstance*, VDAgentMonitorsConfig*)':
+ parameter 1 of type 'QXLInstance*' has sub-type changes:
+ in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+ underlying type 'struct QXLInstance' changed, as being reported
type of 'RedDispatcher* dispatcher' changed:
in pointed to type 'typedef RedDispatcher' at red_worker.h:87:1:
underlying type 'struct RedDispatcher' at red_dispatcher.c:53:1 changed:
@@ -310,7 +390,9 @@
in pointed to type 'typedef QXLState' at spice-qxl.h:35:1:
underlying type 'struct QXLState' at reds.h:93:1 changed:
type size hasn't changed
- 1 data member change:
+ 2 data member changes:
+ type of 'QXLInterface* qif' changed:
+ pointed to type 'typedef QXLInterface' changed at spice.h:102:1, as reported earlier
type of 'RedDispatcher* dispatcher' changed:
pointed to type 'struct RedDispatcher' changed, as being reported
type of 'RedDispatcher* red_dispatcher' changed:
@@ -1,5 +1,5 @@
================ changes of 'libtbb.so.2'===============
- Functions changes summary: 0 Removed, 18 Changed (92 filtered out), 17 Added functions
+ Functions changes summary: 0 Removed, 19 Changed (92 filtered out), 17 Added functions
Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
Function symbols changes summary: 0 Removed, 0 Added function symbol not referenced by debug info
Variable symbols changes summary: 3 Removed, 0 Added variable symbols not referenced by debug info
@@ -24,7 +24,7 @@
[A] 'method void tbb::internal::concurrent_queue_base_v8::move_content(tbb::internal::concurrent_queue_base_v8&)' {_ZN3tbb8internal24concurrent_queue_base_v812move_contentERS1_}
[A] 'method void tbb::task_group_context::capture_fp_settings()' {_ZN3tbb18task_group_context19capture_fp_settingsEv}
- 18 functions with some indirect sub-type change:
+ 19 functions with some indirect sub-type change:
[C] 'method void tbb::filter::set_end_of_input(int)' at pipeline.cpp:700:1 has some indirect sub-type changes:
implicit parameter 0 of type 'tbb::filter*' has sub-type changes:
@@ -112,6 +112,43 @@
class tbb::internal::mutex_copy_deprecated_and_disabled at tbb_stddef.h:334:1
no data member change (1 filtered);
+ [C] 'method void tbb::internal::task_scheduler_observer_v3::observe(bool)' at observer_proxy.cpp:351:1 has some indirect sub-type changes:
+ implicit parameter 0 of type 'tbb::internal::task_scheduler_observer_v3*' has sub-type changes:
+ in pointed to type 'class tbb::internal::task_scheduler_observer_v3' at task_scheduler_observer.h:40:1:
+ type size hasn't changed
+ 1 member function deletion:
+ 'method virtual tbb::internal::task_scheduler_observer_v3::~task_scheduler_observer_v3(int)' at task_scheduler_observer.h:94:1
+ 1 member function insertion:
+ 'method virtual tbb::internal::task_scheduler_observer_v3::~task_scheduler_observer_v3(int)' at task_scheduler_observer.h:86:1
+ no member function changes (2 filtered);
+ 1 data member change:
+ type of 'tbb::internal::observer_proxy* my_proxy' changed:
+ in pointed to type 'class tbb::internal::observer_proxy' at observer_proxy.h:104:1:
+ type size hasn't changed
+ 1 data member changes (3 filtered):
+ type of 'tbb::internal::observer_list* my_list' changed:
+ in pointed to type 'class tbb::internal::observer_list' at observer_proxy.h:34:1:
+ type size hasn't changed
+ 1 data member changes (2 filtered):
+ type of 'tbb::internal::arena* my_arena' changed:
+ in pointed to type 'class tbb::internal::arena' at arena.h:160:1:
+ type size hasn't changed
+ 1 base class deletion:
+ struct tbb::internal::padded<tbb::internal::arena_base> at tbb_stddef.h:261:1
+ 1 base class insertion:
+ struct tbb::internal::padded<tbb::internal::arena_base, 128ul> at tbb_stddef.h:251:1
+ 1 data member change:
+ type of 'tbb::internal::arena_slot my_slots[1]' changed:
+ array element type 'struct tbb::internal::arena_slot' changed:
+ type size hasn't changed
+ 2 base class deletions:
+ struct tbb::internal::padded<tbb::internal::arena_slot_line1> at tbb_stddef.h:261:1
+ struct tbb::internal::padded<tbb::internal::arena_slot_line2> at tbb_stddef.h:261:1
+ 2 base class insertions:
+ struct tbb::internal::padded<tbb::internal::arena_slot_line1, 128ul> at tbb_stddef.h:251:1
+ struct tbb::internal::padded<tbb::internal::arena_slot_line2, 128ul> at tbb_stddef.h:251:1
+ type size hasn't changed
+
[C] 'function void tbb::internal::throw_exception_v4(tbb::internal::exception_id)' at tbb_misc.cpp:119:1 has some indirect sub-type changes:
parameter 1 of type 'enum tbb::internal::exception_id' has sub-type changes:
type size hasn't changed
@@ -1,5 +1,5 @@
================ changes of 'libtbb.so.2'===============
- Functions changes summary: 0 Removed, 16 Changed (94 filtered out), 17 Added functions
+ Functions changes summary: 0 Removed, 16 Changed (95 filtered out), 17 Added functions
Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
Function symbols changes summary: 0 Removed, 0 Added function symbol not referenced by debug info
Variable symbols changes summary: 3 Removed, 0 Added variable symbols not referenced by debug info
@@ -99,7 +99,7 @@
<type-decl name='variadic parameter type' id='2c1145c5'/>
<type-decl name='void' id='48b5725f'/>
<type-decl name='wchar_t' size-in-bits='32' id='c523b9f1'/>
- <class-decl name='__anonymous_struct__2' size-in-bits='64' is-struct='yes' is-anonymous='yes' naming-typedef-id='3d7d8cbf' visibility='default' filepath='bionic/libc/include/bits/mbstate_t.h' line='42' column='1' id='588d60e0'>
+ <class-decl name='__anonymous_struct__2' size-in-bits='64' is-struct='yes' is-anonymous='yes' naming-typedef-id='3d7d8cbf' visibility='default' filepath='bionic/libc/include/bits/mbstate_t.h' line='42' column='1' id='e7f43f72'>
<data-member access='public' layout-offset-in-bits='0'>
<var-decl name='__seq' type-id='09001d3c' visibility='default' filepath='bionic/libc/include/bits/mbstate_t.h' line='43' column='1'/>
</data-member>
@@ -107,7 +107,7 @@
<var-decl name='__reserved' type-id='09001d3c' visibility='default' filepath='bionic/libc/include/bits/mbstate_t.h' line='45' column='1'/>
</data-member>
</class-decl>
- <typedef-decl name='mbstate_t' type-id='588d60e0' filepath='bionic/libc/include/bits/mbstate_t.h' line='47' column='1' id='3d7d8cbf'/>
+ <typedef-decl name='mbstate_t' type-id='e7f43f72' filepath='bionic/libc/include/bits/mbstate_t.h' line='47' column='1' id='3d7d8cbf'/>
<typedef-decl name='wint_t' type-id='f0981eeb' filepath='bionic/libc/include/bits/wctype.h' line='36' column='1' id='6dd37369'/>
<typedef-decl name='wctype_t' type-id='bd54fe1a' filepath='bionic/libc/include/bits/wctype.h' line='56' column='1' id='ba50424d'/>
<typedef-decl name='wctrans_t' type-id='eaa32e2f' filepath='bionic/libc/include/bits/wctype.h' line='60' column='1' id='183c72c3'/>
@@ -119,7 +119,7 @@
<typedef-decl name='int64_t' type-id='0c9942d2' filepath='bionic/libc/include/stdint.h' line='67' column='1' id='9da381c4'/>
<typedef-decl name='fpos_t' type-id='ad707ada' filepath='bionic/libc/include/stdio.h' line='55' column='1' id='e322b6ef'/>
<typedef-decl name='FILE' type-id='8d7b2c1e' filepath='bionic/libc/include/stdio.h' line='59' column='1' id='aa12d1ba'/>
- <class-decl name='__anonymous_struct__1' size-in-bits='128' is-struct='yes' is-anonymous='yes' naming-typedef-id='30397bf8' visibility='default' filepath='bionic/libc/include/stdlib.h' line='136' column='1' id='33a9d652'>
+ <class-decl name='__anonymous_struct__1' size-in-bits='128' is-struct='yes' is-anonymous='yes' naming-typedef-id='30397bf8' visibility='default' filepath='bionic/libc/include/stdlib.h' line='136' column='1' id='e7f43f73'>
<data-member access='public' layout-offset-in-bits='0'>
<var-decl name='quot' type-id='bd54fe1a' visibility='default' filepath='bionic/libc/include/stdlib.h' line='137' column='1'/>
</data-member>
@@ -127,8 +127,8 @@
<var-decl name='rem' type-id='bd54fe1a' visibility='default' filepath='bionic/libc/include/stdlib.h' line='138' column='1'/>
</data-member>
</class-decl>
- <typedef-decl name='ldiv_t' type-id='33a9d652' filepath='bionic/libc/include/stdlib.h' line='139' column='1' id='30397bf8'/>
- <class-decl name='__anonymous_struct__' size-in-bits='128' is-struct='yes' is-anonymous='yes' naming-typedef-id='f5fa3b34' visibility='default' filepath='bionic/libc/include/stdlib.h' line='143' column='1' id='8aac8194'>
+ <typedef-decl name='ldiv_t' type-id='e7f43f73' filepath='bionic/libc/include/stdlib.h' line='139' column='1' id='30397bf8'/>
+ <class-decl name='__anonymous_struct__' size-in-bits='128' is-struct='yes' is-anonymous='yes' naming-typedef-id='f5fa3b34' visibility='default' filepath='bionic/libc/include/stdlib.h' line='143' column='1' id='e7f43f74'>
<data-member access='public' layout-offset-in-bits='0'>
<var-decl name='quot' type-id='1eb56b1e' visibility='default' filepath='bionic/libc/include/stdlib.h' line='144' column='1'/>
</data-member>
@@ -136,7 +136,7 @@
<var-decl name='rem' type-id='1eb56b1e' visibility='default' filepath='bionic/libc/include/stdlib.h' line='145' column='1'/>
</data-member>
</class-decl>
- <typedef-decl name='lldiv_t' type-id='8aac8194' filepath='bionic/libc/include/stdlib.h' line='146' column='1' id='f5fa3b34'/>
+ <typedef-decl name='lldiv_t' type-id='e7f43f74' filepath='bionic/libc/include/stdlib.h' line='146' column='1' id='f5fa3b34'/>
<typedef-decl name='clock_t' type-id='880ebc01' filepath='bionic/libc/include/sys/types.h' line='54' column='1' id='4c3a2c61'/>
<typedef-decl name='__clockid_t' type-id='7eb128d4' filepath='bionic/libc/include/sys/types.h' line='56' column='1' id='08f9a87a'/>
<typedef-decl name='clockid_t' type-id='08f9a87a' filepath='bionic/libc/include/sys/types.h' line='57' column='1' id='a1c3b834'/>