These are the elementary steps of the DataLayoutAnalysis (that is still
to be defined), along with a manager to handle them, their dependencies,
their executions and their invalidations.
In principle, this design could have been implemented inheriting from
`llvm::Pass` and `llvm::legacy::PassManager` (or `llvm::PassInfoMixin`
and `llvm::PMDataManager` for the new step manager).
However, LLVM's passes and managers provide far more complex features
than we need for now, so I decided to keep it simple and roll our own
for now.
We could potentially switch to LLVM stuff later if we need their power.
Some examples of features we don't need for now are:
- capability to pass results from a pass to another that depends on it
- capability to dynamically compute pass dependencies
- capability to dynamically invalidate passes
If some of these become necessary in future, it's a sign that it might
be time to switch to an LLVM-based design
The design is structured as follows.
1) Adding `Step`s to the `StepManager`
The `StepManager` owns the `Step`s, that can be added via the `addStep`
method.
Whenever a new `Step` is added, four properties are checked:
- that all its dependencies have already been added before it;
- that non of its dependencies have been invalidated by the insertion of
another step that invalidate them.
- that it does not depend on itself
- that it does not invalidate itself
If any of these conditions fail, the `Step` is not added successfully.
Whenever a new `Step` A is added and it invalidates any `Step` B, the
last instance of B added to the `StepManager` before A (if present) is
marked as invalidated.
If one wants to add a new `Step` C that depends on B, it will be
necessary to explicitly add a new instance of A (say A*) before adding
C.
Otherwise, there will be no guarantee that an instance of A executes
before C without being invalidated.
These conditions on invalidations are restrictive but the allow to
specify the dependencies and invalidations that we need for now.
If it turns out we need more complex dependencies and invalidations we
should explore implementing the LLVM-based design.
2) Executing the added `Step`s
After adding the `Step`s to the `StepManger`, they can be executed all
together with the `dla::StepManger::run()` method.
This method runs all the steps in the order they were added by the user
with calls to `addStep`.
The fact that `addStep` strictly ensures that dependencies and
invalidations are respected, guarantees that the execution always works
in the order of `Step`s specified by the user.
It also means that there is no need to check and propagate invalidations
across dependencies between the execution of the `Step`s, because there
is the guarantee that if a `Step` A invalidates `Step` B, another
instance of B will be executed later if some `Step` C needs it, or if
the user has added it explicitly.
When the `dla::StepManager::run()` method returns, all the `Step`s that
were successfully added to the manager have finished running.
3) Implementing a `Step`
Each `Step` has a virtual `runOnTypeSystem` method, that executes the
`Step` and returns true if some change was made, false otherwise.
`TypeSystem` for now is just a forward declaration and all the
implementations of `runOnTypeSystem` simply return true.