For different topological ordering within a Block, I think I can expose buildDispatchTable, which finds the implementations in the op-to-implementation mapping and builds the dispatch table, as a virtual function.
For graph region, I think I will start with an error message (i.e. no support) in the first revision. It is unclear to me how to build a general interpreter for that (especially the operations can have mutual dependencies that don’t guarantee causal consistency). I am still open to future improvement, but probably not in the first revision.
For region control flow overwritting, I an concerned about the concept. Although it is still possible to do so by overriding buildDispatchTable (proposed two paragraphs earlier) at region level, it sounds like a non-trivial transformation. Furthermore, to provide an interface to op implementations, the mapping between Block/Region in the input program and corresponding interpreter data structure must be bijective mapping. Skipping a few non-terminating ops within a Block might be fine (semantically equivalent to using a no-op implementation, which returns poison values for the results). But combining two Blocks that used to be conditionally executed into sequentially unconditionally executed Blocks sounds more like a need for an instrumentation pass that runs before the interpreter starts.
If you want to do data flow analyses (e.g. run every paths and compute the meet over all paths (MOP) result, traverse Blocks with some other order (e.g. reverse post order), etc), this framework is probably not the best option for you. You might be interested in Data Flow Analysis Framework proposed by @Mogball.
Fair point. I will remove the dependency on OpInterface and build my own lookup map (with allows the same MLIRContext with the same operation being mapping to different implementation when different lookup map is provided). (I have to remove OpInterface dependency to implement other idea as well.)