Does frontend.from_xxx do anything like tvm.lower()?

I’d like to know tvm compiling flow and I wrote some test code to see every step tvm does. I read the Example Compilation Flow( Design and Architecture — tvm 0.8.dev0 documentation) which show that

  • tvm load frontend module to relay IR
  • tvm lower the relay IR to tir IR

so I wrote test code like:

mod, params = relay.frontend.from_onnx(onnx_model)
lower_mod = tvm.lower(mod)

But I find that there is no difference between mod and lower_mod with print(mod.astext(show_meta_data=False))

why? Does it mean frontend.from_xxx has done what tvm.lower does?

Hi @chiuchiu, I don’t think the relay importer does lowering. Could you provide the onnx_model or your entire test code?

@yuchenj ,thanks for your reply, my test code is as follows:

def write_file(filename, mod):
    with open(filename, mode='w', encoding='utf-8') as f:
        print(mod.astext(show_meta_data=False), file = f)

def load_onnx(model_path):
    onnx_model = onnx.load(model_path)
    graphviz = GraphvizOutput(output_file='tvm_from_onnx_callstack.png')
    with PyCallGraph(output=graphviz):
        mod, params = relay.frontend.from_onnx(onnx_model) # no shape param passing is also OK
    return mod

def show_graph_orig_irmodule(mod):
    write_file('orig_ir.mod', mod)
    return mod

def show_graph_lower_irmodule(mod):
    graphviz = GraphvizOutput(output_file='tvm_lower_callstack.png')
    with PyCallGraph(output=graphviz):
        lower_mod = tvm.lower(mod)
    write_file('lower_ir.mod', lower_mod)
    return lower_mod

def show_tvm_proc():
    # show onnx local model
    mod = load_onnx('resnet50-v2-7.onnx')
    mod = show_graph_orig_irmodule(mod)
    show_graph_lower_irmodule(mod)

def main():
    show_tvm_proc()

the onnx model is one trained model which was downloaded from somewhere. is there any bug in my test code? Thanks!

@chiuchiu, I took a look at the tvm.lower() api. Usually, it takes a TE schedule and lowers that as shown in the use case here. It can also take a PrimFunc or a IRModule as the first argument. When you pass an IRModule to it, a set of passes will be created, it seems to me that these passes created are TIR passes instead of Relay passes.

So my understanding is that if you give it a Relay IRModule, it will do nothing; while if you give it a TIR IRmodule, it will do the lowering. I’m not sure if I understand correctly, @junrushao might be able to correct me. :slight_smile:

1 Like

@yuchenj ,thanks for your reply!

  1. If tvm.lower() will do nothing about Relay IR, why does it accept the type as its parameter?

  2. I took a look at some user example using tvm.relay.build(mod ...) compiling computational graph into llvm file. I think relay.build will transform Relay IR to Tir IR then transform into target llvm, and I’d like to know which way could print the content of Relay IR and Tir IR , that might could help me to understand the TVM compiling flow.

Thanks for your suggestion.

Hi @chiuchiu,

  1. In TVM, an IRModule can represent both high-level relay.Function and low-level tir.PrimFunc, as shown in the graph in the Design and Architecture. My understanding is that tvm.lower() only does the lowering when the IRModule contains tir.PrimFunc. I think it’s worth adding comments around the API code to clarify it.

  2. Yes, relay.build transforms Relay IR to Tir IR and does the codegen. I think today it’s hard to inspect the compilation flow of each stage. There is a work in progress to replace the compile engine with TEcompiler to have an intermediate stage where Relay is lowered to TIR. I think once it’s done, it’s easier to print the content of TIR after lowering.

Hi @yuchenj ,

Thanks a lot for your reply. I need read more source code for understanding TVM compiling .