# Porting Relay Passes to Pass Manager
As the pass manager framework has been merged, we should start to move passes 
to the pass manager. This RFC proposes the plans to move the Relay passes.

## Proposal (take constant folding as an example):
The proposal needs to solve problems from both the backend and the frontend. As 
this could be a lot of refactoring work, we will mainly focus on the API 
changes of the pass functions first, but leave others (like resolving 
dependency) as follow-up work.

### C++ Backend
The C++ backend implements the functionality of a certain pass as the 
`pass_func` for `module_pass` and `function_pass`, i.e. constant folding in 
this example. Some passes are passed with a `Module` to identify global vars 
and/or help report errors, but others are not.

1. Pass `Module` to every pass, or have a global `Module`? When `Module` is not 
passed, we can call `GetModule`, like `FromExpr`. For backward compatibility, 
we can default it to null. Therefore, users can still invoke the pass as 
before. All frontend converters should return a module as well.
2. Previously, many passes are not aware of PassContext. We probably should 
pass PassContext into a pass so that the internal context, e.g. ErrorReporter, 
can be used by each pass. 
3. How can a pass provide the frontend with the most convenient API so that it 
could be invoked easily?

```c++
class ConstantFolder : public ExprMutator {
 private:
  PassContext pass_ctx;
  // Other members are omitted..
};

Expr FoldConstant(const Expr& expr, const PassContext& ctx) {
  return ConstantFolder(CreateInterpreter(...), ctx).Mutate(expr);  
}  
  
TVM_REGISTER_API("relay._ir_pass.FoldConstant")  
.set_body([](TVMArgs args, TVMRetValue *ret) {
        PassContext ctx = args[1];
        if (!ctx.defined()) {
                ctx = PassContext();
        }  
    *ret = FoldConstant(args[0], ctx);  
});
```
### Python frontend

Python frontend performs the following tasks
- Link to a pass function, i.e. `ir_pass.fold_constant`
- Register the pass using flattened pass info, e.g. `opt_level`, `name`, and 
`required` passes.
- Setup `PassContext` when necessary. Otherwise, the backend will create it.
- Create a module for pass execution if it is not passed. 
- Have an API to invoke pass functions in different manner?
        - The old style invocation, `mypass(mod/expr)`, depending on the type 
of pass, e.g. `ir_pass.fold_constant(expr)`
        - Through pass manager 
```python
        # Create a module for all passes?
        mod = relay.Module({})
        
        # Method 1, Use decorator:
        @ir_pass.function_pass(opt_level=opt_level, name=pass_name)
        def mypass(expr, ctx):
                class Transform(ExprVisitor):
                        def __init__():
                        def __call__(expr, ctx):
                return Transform(expr, ctx)
        mypass(mod)
                
        # Method 2, pass registered passes directly:    
        pass_func = ir_pass.fold_constant
        cf = ir_pass.function_pass(pass_func, opt_level=2, name=”FoldConstant”, 
required=[])
        cf(mod) 
```

## Next Steps
- One pass may call another pass directly. We should setup the `required` field 
and resolve dependencies. After that, this pass should be able to access the 
returned analysis/optimization results.
- How to represent a `Pass` using pass info, particularly pass name. To resolve 
pass dependency, we may only have the `required` passes. We then need to create 
theses pass and execute them.


-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/dmlc/tvm/issues/3146

Reply via email to