Ok, I see what you mean now. Let me clarify a few things then:
1. Op - includes its data dependencies via `inputs` which can be inspected.
2. Block - a single level of hierarchy with a sequence of statements (symbol + op pairs) in program declaration order
I should clarify that you can create a custom traversal direction without building a new graph if you absolutely need to. For instance, when visiting a block, you can visit its statements in reverse order.
In my experience there is never a need to build an entirely new graph. The existing program graph should have all of the information you need.
Depending on what you're trying to do, there's several ways of inspecting data dependencies. See Spatial's AccessPatternAnalyzer, for example, which looks for localized patterns like (a * b) + (c * d) by pattern matching on inputs.
In other cases, reverse traversal actually isn't required at all, since you can flag nodes as you reach them, propagate those flags, and check to see if a node's inputs are flagged.
Even in the worst case, it's fairly simple to set up a traversal that would approximate reversed topological sort simply by traversing blocks in the reverse order like I mentioned. Building a new graph should absolutely be avoided.
It would help here to know more details about what analysis you're trying to achieve. I don't know how much compiler experience you have, but it sounds like there's a good chance that the solution you're thinking of is much more complicated than it needs to be.