Our compiler is organized as a series of filters that form a pipeline. We serialize the tree into a sequence of instructions and pipe these through the compiler pipeline. We already have some 12 or so filters, and its getting difficult to keep track of dependencies between them. Invariant code motion, for example, needs information from the invariant code analysis filter.
We introduced a CompilerStatus object that is passed along the pipeline. Each filter notifies the status object when it starts processing the tree. Since filters are truely pipelined and process instructions in an overlapping manner, there is no guarantee when a filter terminates. If such a termination property is needed, we insert Barrier filters into the pipeline which wait for all instructions to come in before allowing instructions to flow out and continue along the pipeline. Whenever a Barrier filter has received all instructions and thus all instructions have been processed by all previous filters, it clears the list of pending filters and moves them to the “completed” filter set.
Filters can query these two sets to ensure that any dependencies have been fulfilled. Currently the granularities are “must have been started”, and “has been completed”.