Default Relay Passes in PathContext

I have a very basic question about the application of Relay Passes. Every Relay-Pass has a PassInfo defining the name, optimization_level and other required paths.

When using the PassContext like in this very simple example:

target = tvm.target.Target("llvm", host="llvm")
dev = tvm.cpu(0)
with tvm.transform.PassContext(opt_level=3):
     lib = relay.build(mod, target=target, params=params)

Is there a standard portfolio of Relay-Passes that is applied (e.g. Constant Folding, OperatorFusion, etc.)?

How are the related Relay-Compiler Passes selected? I guess there might be a few options, but I did not find any description about it yet.

  • Are no Relay-passes applied automatically at all?
  • Are all Relay-passes applied which are registered an match the optimization level?
  • Is there a list of default Relay-Passes that are applied when using the PassContext? If yes, where is it specified. I did not find this step in the code yet.

I already red a few articles in the documantation of TVM about the Relay-Pass-Infrastructure but I did not find any information regarding this topic.

I would really appreciate if someone could help me to understand the application mechanism.

One quick way to debug this is via pass instrument hook. https://github.com/apache/tvm/blob/main/gallery/how_to/extend_tvm/use_pass_infra.py

@tvm.instrument.pass_instrument
class PrintIR:
    """Print the name of the pass, the IR, only before passes execute."""

    def run_before_pass(self, mod, info):
        print("Running pass: {}", info)
        print(mod.astext(False))

Default relay optimization order I believe could be currently viewed in https://github.com/apache/tvm/blob/main/src/relay/backend/build_module.cc#L334 and https://github.com/apache/tvm/blob/main/src/relay/backend/utils.cc#L206

2 Likes

Thank you very much @wrongtest for this precise answer. It helped me a lot to understand the Workflow of the high-level optimization of TMV. I really appreciate it.

Just one more question: Regarding the check of the opt_level. I just found the function PassContext::PassEnabled (https://github.com/apache/tvm/blob/b555bf5481d3eb261427850cea286c162aa3d2e3/src/ir/transform.cc#L93) that checks if the opt_level of the pass context is greater or equal the opt_level of the pass. It seems like this is checked only while execute a Sequential Node meaning a Sequence of passes (https://github.com/apache/tvm/blob/b555bf5481d3eb261427850cea286c162aa3d2e3/src/ir/transform.cc#L445).

I just tried to verify it with the example shown in “Use Sequential to Apply a Sequence of Passes” (How to Use TVM Pass Infra — tvm 0.9.dev0 documentation) If the operations of FoldConstant(), EliminateCommonSubexpr() and FuseOps(fuse_opt_level=2) are performed inside a Sequential, the opt_level of the the pass EliminateCommonSubexpr() is below the PassContext opt_level of 2 and is not executed.

seq = tvm.transform.Sequential(
[
    relay.transform.FoldConstant(),              #opt_level:2		
    relay.transform.EliminateCommonSubexpr(),    #opt_level:3
    relay.transform.FuseOps(fuse_opt_level=2),   #opt_level:0
]
with tvm.transform.PassContext(opt_level=2):
    mod2 = seq(mod)

If I apply the passes individually in the same order inside the PassContext all of three passes are executed.

with tvm.transform.PassContext(opt_level=2):
   mod2 = relay.transform.FoldConstant()(mod)
   mod3 = relay.transform.EliminateCommonSubexpr()(mod2)
   mod4 = relay.transform.FuseOps(fuse_opt_level=2)(mod3)

I suppose this means, that only the execution of Sequentials (like the ones that are prepared in the build process) are checked by the PassContext and individual passes are executed anyway?

Is there a specific reason for this behavior?

1 Like