The copier is instantiated when a dwarf_output object is
instantiated
and it creates the compile_units, which will call copier::make_unit ()
through the cu_maker for each cu. make_unit () will create a
copier::unit_copier for each cu using the
compile_units_type::const_iterator, and then calls final_unit () on it,
which just does a sanity check on the result, making sure there are no
undefined entries left, and returns the final die_info_pair.
Correct. I think this spot is where the main control flow of the second
walk will be done.
An entry_copier lives as long as the copier is transforming the
input_die. It holds the copier, the copier::entry input die, and a
copier::pending_entry output which gets created in the constructor from
the input die tag. The constructor set the input copier::entry
_m_building field to the entry_copier to indicate it is being
constructed. The destructor clears it again. It doesn't hold the actual
input_die as state, all methods take an input_die as argument.
Correct. The main reason for this kind of object is that running its
destructor is the unwind-protect mechanism. That is, the destructor gets
run even when an exception is thrown during the copying. It's also
possible to replace use of a temporary object that has a destructor with
just using 'try { ... } finally { ... }' clauses. Feel free to do that
when it seems simpler.
We also take advantage of the fact that we have this temporary object to
use a pointer to it as the circularity detection mechanism (_m_building).
entry_copier::populate (as called from the unit_copier constructor)
takes an input_die, sets the _m_pending field of the input entry to the
output pending_entry of the entry_copier. It will populate the
_m_attributes if the _m_out entry from the given input_die and call
add_child() for each child of the given input_die. After that it will
clear the _m_out field to indicate the we are done with the
pending_entry. And call entry::defined_self () on the _m_in entry.
Right. defined_self triggers the final steps of state changes based on the
reference counts. All that will be changing and hopefully become far
simpler in the new plan.
When the _m_attributes set of the entry is populated the value maker
will call back into the entry_copier::add_reference() for each reference
value. add_reference() will setup the copier to enter the input_die to
which the attribute is pointing and provide a "backptr" to the
entry::refer() method that fill it in with either the finalized entry
itself (if it is finalized) or to the entry itself and pushes the
backptr to the _m_pending_refs of pending_entry.
Correct.
entry_copier::add_child will setup the copier to start copying the
given
child die and create a new copier::entry for it, add the child to the
_m_out pending_entry. If the child is already know, but not yet final
(because it comes from an DW_TAG_imported_unit in a logical walk) it
will update the _m_parent and create a new entry_copier and populate
that.
Right. That entry_copier is a temporary that lives only inside that
add_child frame, during the recursion into entry_copier::populate.
entry_copier::final_unit() as called from make_unit() will do sanity
checks to make sure the _m_in entry has really been finalized and return
the die_info_pair stored in the collector.
Right. It may make sense to remove this method and change the details of
the unit_copier class and what methods it has, or perhaps final_unit can
stay as the place where all the finalization happens. Whatever works.
A copier::entry holds the following state: _m_offset, the Dwarff_Off
of
the original die, _m_cost, the cost of the original die,
Right. _m_offset serves the original_offset method, and is only kept to
use in the debugging output and the collector's stats output. Nothing
about the actual workings uses it. Likewise _m_cost is the "cost", meaning
bytes occupied by the encoding, and is just for the statistics output where
we can show how much we saved by reducing duplicates. I have some thought
that eventually this cost calculation could be used in the output routines
to decide when it's worthwhile to generate a shared partial_unit vs just
copying very small DIEs in the output.
Thanks,
Roland