Semantics of generated LLVM code



I would like to work with the LLVM IRs generated by TVM when importing and compiling a module from ONNX, but I have some trouble understanding the output .ll files. I know everything I need is written somewhere in the codebase, but it is not so easy to find things when you do not know exactly what you are looking for :sweat_smile:.

Right now what puzzles me is how the inputs of a function are modified. My simplest example is an ONNX node that perform element-by-element multiplication, this is what I get by printing mod.astext after importing the model:

def @main(%X: Tensor[(1, 8), float32], %Y: Tensor[(1, 8), float32]) -> Tensor[(1, 8), float32] {
  multiply(%X, %Y) /* ty=Tensor[(1, 8), float32] */

Instead this is the top function in the generated .ll file after compilation:

define dllexport i32 @fused_multiply(i8* noalias nocapture readonly, i8* noalias nocapture readnone, i32)

So, what now? There used to be two 1x8 tensors, now there are two pointers to 8-bit ints and one 32-bit int. What type of input am I supposed to give to the LLVM function to obtain the same behavior?

Where is a good starting point to find this kind of information in TVM’s source code?


If it can be useful, here is the complete script I am using:

import numpy as np
import onnx
import tvm
from tvm import relay

onnx_model = onnx.load('vecmul.onnx')
shape_dict = {'X': (1,8), 'Y': (1,8)}

mod, params = relay.frontend.from_onnx(model=onnx_model, shape=shape_dict)

opt_level = 3
target = 'llvm'
with relay.build_config(opt_level=opt_level):
    graph, lib, params =
        mod, target, params=params)

out_file = open("vecmul.ll", "w")


Look in src/runtime/module_util.h:

typedef int (*BackendPackedCFunc)(void* args,
                                  int* type_codes,
                                  int num_args);

The args parameter is really of type TVMValue[], i.e. an address of an array of TVMValue objects, and type_codes is the array of the corresponding types.