The Git repository of the Libabigail Project
Go to file
Dodji Seketeli 39ba859603 Bug 27236 - Fix the canonical type propagation optimization
While working on another bug, it turned out the initial fix for the
bug https://sourceware.org/bugzilla/show_bug.cgi?id=27236 was just
papering over the real issue.

I think the real issue is that "canonical type propagation"
optimization was being done even in cases where it shouldn't have been
done.  This patch recognizes the limits of that optimization and avoid
performing it when we are off limits.

So here is what that optimization is.  The text below is also present
in the comments in the source code.  I am putting it here to explain
the context.

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 expression
is C == T.

During that structural comparison, if a subtype of C (which also
already has a canonical type) is structurally compared to a subtype of
T (which doesn't yet have a canonical type) and if they are equal,
then we can deduce that the canonical type of the subtype of C is the
canonical type of the subtype of C.

Thus, we can canonicalize the sub-type of the T, during the
canonicalization of T itself.  That canonicalization of the sub-type
of T is what we call "propagating the canonical type of the sub-type
of C onto the sub-type of T".  It's also called "on-the-fly
canonicalization".  It's on the fly because it happens during a
comparison -- which itself happens during the canonicalization of T.

So this is the general description of the "canonical type propagation
optimization".

Now we must recognize the limits of that optimization.  Said
otherwise, 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 a 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.

This concept of cancelling the propagated canonical type when needed
is what this patch introduces.

New data members have been introduced to the environment::priv private
structure.  Those are to keep track of the stack of sub-types being
compared so that we can detect if a candidate to the canonical type
propagation optimization depends on a recursive type.

There is also a data structure in there to track the targets of the
canonical type propagation optimization that "might" need to see their
propagated canonical types be cancelled.

Then new functions have been introduced to detect when a type depends
on a recursive type, to cancel or confirm propagated canonical types
etc.

In abg-ir.cc, The RETURN* macros used in the equals() overloads have
been factorized using the newly introduced function templates
return_comparison_result().  This now contains the book keeping that
was previously done (in the RETURN* macros) to detect recursive cycles
in the comparison, as well as triggering the canonical type
propagation.  This i also where the logic of properly limiting the
optimization is implemented now.

	* include/abg-ir.h (pointer_set): This typedef is now for an
	unordered_set<uintptr_t> rather than an unordered_set<size_t>.
	(environment::priv_): Make this public so that code in free form
	function from abg-ir.cc can access it.
	* src/abg-ir-priv.h (struct type_base::priv): Move this private
	structure here, from abg-ir.cc.
	(type_base::priv::{depends_on_recursive_type_,
	canonical_type_propagated_}): Added these two new data members.
	(type_base::priv::priv): Initialize the two new data members.
	(type_base::priv::{depends_on_recursive_type,
	set_depends_on_recursive_type,
	set_does_not_depend_on_recursive_type, canonical_type_propagated,
	set_canonical_type_propagated, clear_propagated_canonical_type}):
	Define new member functions.
	(struct environment::priv): Move this struct here, from abg-ir.cc.
	(environment::priv::{types_with_non_confirmed_propagated_ct_,
	left_type_comp_operands_, right_type_comp_operands_}): New data
	members.
	(environment::priv::{mark_dependant_types,
	mark_dependant_types_compared_until, confirm_ct_propagation,
	collect_types_that_depends_on, cancel_ct_propagation,
	remove_from_types_with_non_confirmed_propagated_ct}): New member
	functions.
	* src/abg-ir.cc (struct environment::priv, struct)
	(type_base::priv, struct class_or_union::priv): Move these struct
	to include/abg-ir-priv.h.
	(push_composite_type_comparison_operands)
	(pop_composite_type_comparison_operands)
	(mark_dependant_types_compared_until)
	(maybe_cancel_propagated_canonical_type): Define new functions.
	(notify_equality_failed, mark_types_as_being_compared): Re-indent.
	(is_comparison_cycle_detected, return_comparison_result): Define
	new function templates.
	(RETURN_TRUE_IF_COMPARISON_CYCLE_DETECTED): Define new macro.
	(equals(const function_type& l, const function_type& r)): Redefine
	the RETURN macro using the new return_comparison_result function
	template.  Use the new RETURN_TRUE_IF_COMPARISON_CYCLE_DETECTED
	and mark_types_as_being_compared functions.
	(equals(const class_or_union& l, const class_or_union&, change_kind*)):
	Likewise.
	(equals(const class_decl& l, const class_decl&, change_kind*)):
	Likewise.  Because this uses another equal() function to compare
	the class_or_union part the type, ensure that no canonical type
	propagation occurs at that point.
	(types_are_being_compared): Remove as it's not used anymore.
	(maybe_propagate_canonical_type): Use the new
	environment::priv::propagate_ct() function here.
	(method_matches_at_least_one_in_vector): Ensure the
	right-hand-side operand of the equality stays on the right.  This
	is important because the equals() functions expect that.
	* src/abg-reader.cc (build_type): Ensure all types are
	canonicalized.
	* tests/data/test-diff-dwarf/PR25058-liblttng-ctl-report-1.txt:
	Adjust.
	* tests/data/test-diff-pkg/nss-3.23.0-1.0.fc23.x86_64-report-0.txt:
	Likewise.
	* tests/data/test-diff-pkg/spice-server-0.12.4-19.el7.x86_64-0.12.8-1.el7.x86_64-report-2.txt:
	Likewise.
	* tests/data/test-diff-pkg/tbb-4.1-9.20130314.fc22.x86_64--tbb-4.3-3.20141204.fc23.x86_64-report-0.txt:
	Likewise.
	* tests/data/test-diff-pkg/tbb-4.1-9.20130314.fc22.x86_64--tbb-4.3-3.20141204.fc23.x86_64-report-1.txt:
	Likewise.
	* tests/data/test-read-dwarf/test-libaaudio.so.abi: Likewise.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
2021-08-11 17:39:11 +02:00
autoconf-archive Replace individual license references with SPDX Identifiers 2020-12-02 11:44:13 +01:00
bash-completion Re-license the project to Apache v2 With LLVM Exception 2020-12-02 11:49:13 +01:00
doc Bug 27995 - Self comparison error from abixml file 2021-08-11 17:38:14 +02:00
include Bug 27236 - Fix the canonical type propagation optimization 2021-08-11 17:39:11 +02:00
m4
relicensing-scripts Bug 27512 - Remove broken zip-archive support 2021-03-19 10:52:57 +01:00
scripts Re-license the project to Apache v2 With LLVM Exception 2020-12-02 11:49:13 +01:00
src Bug 27236 - Fix the canonical type propagation optimization 2021-08-11 17:39:11 +02:00
tests Bug 27236 - Fix the canonical type propagation optimization 2021-08-11 17:39:11 +02:00
tools Bug 27995 - Self comparison error from abixml file 2021-08-11 17:38:14 +02:00
.clang-format clang-format: Minor correction to not break parameters on the first line 2021-04-01 09:51:20 +02:00
.gitignore .gitignore: Add libabigail-?.* *.orig files 2019-05-22 14:34:23 +02:00
abigail.m4 Re-license the project to Apache v2 With LLVM Exception 2020-12-02 11:49:13 +01:00
AUTHORS
ChangeLog Update the Changelog for 1.8 2020-12-01 15:13:57 +01:00
COMMIT-LOG-GUIDELINES Update the COMMIT-LOG-GUIDELINES file 2016-05-22 23:20:12 +02:00
COMPILING Fix typo in COMPILING. 2020-02-03 13:49:54 +01:00
configure.ac Detect failed self comparison in type canonicalization of abixml 2021-05-25 12:16:25 +02:00
CONTRIBUTING Use C++11 for the code base 2020-12-03 14:04:23 +01:00
default.abignore Re-license the project to Apache v2 With LLVM Exception 2020-12-02 11:49:13 +01:00
gen-changelog.py Replace individual license references with SPDX Identifiers 2020-12-02 11:44:13 +01:00
install-sh Replace individual license references with SPDX Identifiers 2020-12-02 11:44:13 +01:00
libabigail.pc.in
license-change-2020.txt Add a license-change-2020.txt file 2020-12-02 11:50:22 +01:00
LICENSE.txt Add the LICENSE.txt file 2020-12-02 11:49:33 +01:00
ltmain.sh Replace individual license references with SPDX Identifiers 2020-12-02 11:44:13 +01:00
Makefile.am Teach Automake that COPYING* files are gone from sources 2020-12-02 17:42:47 +01:00
NEWS Update NEWS file for 1.8 2020-12-01 15:19:12 +01:00
README
release-text-template.txt Update the release text template after 1.0 2017-11-22 15:55:43 +01:00
update-copyright.sh Re-license the project to Apache v2 With LLVM Exception 2020-12-02 11:49:13 +01:00
VISIBILITY Control symbols exported from libabigail.so 2016-07-27 12:51:02 +02:00

This is the Application Binary Interface Generic Analysis and
Instrumentation Library.

It aims at constructing, manipulating, serializing and de-serializing
ABI-relevant artifacts.

The set of artifacts that we are intersted is made of quantities like
types, variable, fonctions and declarations of a given library or
program.  For a given library or program this set of quantities is
called an ABI corpus.

This library aims at (among other things) providing a way to compare
two ABI Corpora (apparently the plural of corpus is copora, heh,
that's cool), provide detailed information about their differences,
and help build tools to infer interesting conclusions about these
differences.

You are welcome to contribute to this project after reading the files
CONTRIBUTING and COMMIT-LOG-GUIDELINES files in the source tree.

Communicating with the maintainers of this project -- including
sending patches to be include to the source code -- happens via email
at libabigail@sourceware.org.