You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mxnet.apache.org by zh...@apache.org on 2019/09/30 19:12:19 UTC

[incubator-mxnet] branch ir-patch updated: [IR-Patch] IR Bridge (#16290)

This is an automated email from the ASF dual-hosted git repository.

zhengda pushed a commit to branch ir-patch
in repository https://gitbox.apache.org/repos/asf/incubator-mxnet.git


The following commit(s) were added to refs/heads/ir-patch by this push:
     new 6af2611  [IR-Patch] IR Bridge (#16290)
6af2611 is described below

commit 6af26118a837096d24ad29fd8b463529de4fd018
Author: Junru Shao <ju...@gmail.com>
AuthorDate: Mon Sep 30 12:11:35 2019 -0700

    [IR-Patch] IR Bridge (#16290)
    
    * ir converter
    
    Add license
    
    Missed something
    
    lint
    
    lintlintlint
    
    * Restore cryptic part of CachedOp
    
    * Update Makefile
    
    * try again for libtvm.so...
    
    * try again
    
    * try once once again
    
    * let's try to fix julia's issue first
    
    * Remove AsText which is not an exposed symbol
    
    * try to bypass amalgamation
    
    * try again
    
    * boy try this
    
    * blacklist tvm to amalgamation.py
---
 3rdparty/tvm                                       |   2 +-
 CMakeLists.txt                                     |   2 +-
 Makefile                                           |  17 +-
 amalgamation/Makefile                              |   4 +-
 amalgamation/amalgamation.py                       |   4 +-
 ci/jenkins/Jenkins_steps.groovy                    |  20 +--
 .../assembly/src/main/assembly/assembly.xml        |   2 +-
 .../apache/mxnet/util/NativeLibraryLoader.scala    |   2 +-
 src/imperative/cached_op.cc                        |  16 +-
 src/v3/src/nnvm_relay_bridge.cc                    | 182 +++++++++++++++++++++
 tests/nightly/JenkinsfileForBinaries               |   4 +-
 .../JenkinsfileForMBCC                             |   2 +-
 12 files changed, 228 insertions(+), 29 deletions(-)

diff --git a/3rdparty/tvm b/3rdparty/tvm
index afd4b3e..18188f4 160000
--- a/3rdparty/tvm
+++ b/3rdparty/tvm
@@ -1 +1 @@
-Subproject commit afd4b3e4450984358e9d79a7e8e578483cb7b017
+Subproject commit 18188f4ba3f53cc1dab765b8a0d932d21db0ae8a
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f441e9b..051dc91 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -744,7 +744,7 @@ endif()
 
 if(USE_TVM_OP)
   add_definitions(-DMXNET_USE_TVM_OP=1)
-  list(APPEND mxnet_LINKER_LIBS ${CMAKE_CURRENT_BINARY_DIR}/3rdparty/tvm/libtvm_runtime.so)
+  list(APPEND mxnet_LINKER_LIBS ${CMAKE_CURRENT_BINARY_DIR}/3rdparty/tvm/libtvm.so)
   include(cmake/BuildTVM.cmake)
   add_subdirectory("3rdparty/tvm")
 
diff --git a/Makefile b/Makefile
index b3b188a..bd580ef 100644
--- a/Makefile
+++ b/Makefile
@@ -468,9 +468,9 @@ CUSRC = $(wildcard src/*/*/*/*.cu src/*/*/*.cu src/*/*.cu src/*.cu)
 CUOBJ = $(patsubst %.cu, build/%_gpu.o, $(CUSRC))
 
 ifeq ($(USE_TVM_OP), 1)
-LIB_DEP += lib/libtvm_runtime.so lib/libtvmop.so
+LIB_DEP += lib/libtvm.so lib/libtvmop.so
 CFLAGS += -I$(TVM_PATH)/include -DMXNET_USE_TVM_OP=1
-LDFLAGS += -L$(ROOTDIR)/lib -ltvm_runtime -Wl,-rpath,'$${ORIGIN}'
+LDFLAGS += -L$(ROOTDIR)/lib -ltvm -Wl,-rpath,'$${ORIGIN}'
 
 TVM_USE_CUDA := OFF
 ifeq ($(USE_CUDA), 1)
@@ -618,19 +618,20 @@ $(DMLC_CORE)/libdmlc.a: DMLCCORE
 DMLCCORE:
 	+ cd $(DMLC_CORE); $(MAKE) libdmlc.a USE_SSE=$(USE_SSE) config=$(ROOTDIR)/$(config); cd $(ROOTDIR)
 
-lib/libtvm_runtime.so:
+lib/libtvm.so:
 	echo "Compile TVM"
 	[ -e $(LLVM_PATH)/bin/llvm-config ] || sh $(ROOTDIR)/contrib/tvmop/prepare_tvm.sh; \
 	cd $(TVM_PATH)/build; \
-	cmake -DUSE_LLVM="$(LLVM_PATH)/bin/llvm-config" \
+	cmake -DUSE_LLVM="$(LLVM_PATH)/bin/llvm-config --ignore-libllvm" -DHIDE_PRIVATE_SYMBOLS=ON \
+			-DCMAKE_SHARED_LINKER_FLAGS="-Wl,--exclude-libs,ALL" \
 		  -DUSE_SORT=OFF -DUSE_CUDA=$(TVM_USE_CUDA) -DUSE_CUDNN=OFF ..; \
 	$(MAKE) VERBOSE=1; \
 	mkdir -p $(ROOTDIR)/lib; \
-	cp $(TVM_PATH)/build/libtvm_runtime.so $(ROOTDIR)/lib/libtvm_runtime.so; \
+	cp $(TVM_PATH)/build/libtvm.so $(ROOTDIR)/lib/libtvm.so; \
 	ls $(ROOTDIR)/lib; \
 	cd $(ROOTDIR)
 
-lib/libtvmop.so: lib/libtvm_runtime.so $(wildcard contrib/tvmop/*/*.py contrib/tvmop/*.py)
+lib/libtvmop.so: lib/libtvm.so $(wildcard contrib/tvmop/*/*.py contrib/tvmop/*.py)
 	echo "Compile TVM operators"
 	PYTHONPATH=$(TVM_PATH)/python:$(TVM_PATH)/topi/python:$(ROOTDIR)/contrib \
 		LD_LIBRARY_PATH=$(ROOTDIR)/lib \
@@ -696,8 +697,8 @@ rpkg:
 		cp -rf lib/libmklml_intel.so R-package/inst/libs; \
 	fi
 
-	if [ -e "lib/libtvm_runtime.so" ]; then \
-		cp -rf lib/libtvm_runtime.so R-package/inst/libs; \
+	if [ -e "lib/libtvm.so" ]; then \
+		cp -rf lib/libtvm.so R-package/inst/libs; \
 	fi
 
 	mkdir -p R-package/inst/include
diff --git a/amalgamation/Makefile b/amalgamation/Makefile
index 701c1f1..f45ebfc 100644
--- a/amalgamation/Makefile
+++ b/amalgamation/Makefile
@@ -49,7 +49,7 @@ endif
 .PHONY: all clean
 
 DEFS+=-DMSHADOW_USE_CUDA=0 -DMSHADOW_USE_MKL=0 -DMSHADOW_RABIT_PS=0 -DMSHADOW_DIST_PS=0 -DDMLC_LOG_STACK_TRACE=0
-DEFS+=-DMSHADOW_FORCE_STREAM -DMXNET_USE_OPENCV=0 -DMXNET_PREDICT_ONLY=1
+DEFS+=-DMSHADOW_FORCE_STREAM -DMXNET_USE_OPENCV=0 -DMXNET_PREDICT_ONLY=1 -DMXNET_AMALGAMATION=1
 CFLAGS=-std=c++11 -Wno-unknown-pragmas -Wall $(DEFS)
 
 # if architecture of the CPU supports F16C instruction set, enable USE_F16C for fast fp16 computation on CPU
@@ -120,7 +120,7 @@ else
 endif
 
 libmxnet_predict.js: mxnet_predict-all.cc
-	${EMCC} -std=c++11 -O2 $(DEFS) -DMSHADOW_USE_SSE=0 -D__MXNET_JS__  -o $@ $+ \
+	${EMCC} -std=c++11 -O2 $(DEFS) -DMSHADOW_USE_SSE=0 -D__MXNET_JS__ -o $@ $+ \
 	-s EXPORTED_FUNCTIONS="['_MXPredCreate', \
 	                        '_MXPredGetOutputShape', \
 	                        '_MXPredSetInput', \
diff --git a/amalgamation/amalgamation.py b/amalgamation/amalgamation.py
index 5f825de..8d1cd6f 100644
--- a/amalgamation/amalgamation.py
+++ b/amalgamation/amalgamation.py
@@ -170,6 +170,7 @@ def expand(x, pending, stage):
             if not source:
                 if (h not in blacklist and
                     h not in sysheaders and
+                    'tvm' not in h and
                     'mkl' not in h and
                     'nnpack' not in h and
                     'tensorrt' not in h and
@@ -190,7 +191,8 @@ expand.fileCount = 0
 
 # Expand the stages
 expand(sys.argv[2], [], "3rdparty/dmlc-core")
-expand(sys.argv[3], [], "3rdparty/tvm/nnvm")
+expand(sys.argv[3], [], "3rdparty/tvm")
+expand(sys.argv[3], [], "3rdparty/nnvm")
 expand(sys.argv[4], [], "src")
 
 # Write to amalgamation file
diff --git a/ci/jenkins/Jenkins_steps.groovy b/ci/jenkins/Jenkins_steps.groovy
index 30db322..48cabeb 100644
--- a/ci/jenkins/Jenkins_steps.groovy
+++ b/ci/jenkins/Jenkins_steps.groovy
@@ -23,22 +23,22 @@
 utils = load('ci/Jenkinsfile_utils.groovy')
 
 // mxnet libraries
-mx_lib = 'lib/libmxnet.so, lib/libmxnet.a, lib/libtvm_runtime.so, lib/libtvmop.so, libsample_lib.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a'
-mx_lib_cython = 'lib/libmxnet.so, lib/libmxnet.a, lib/libtvm_runtime.so, lib/libtvmop.so, libsample_lib.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a, python/mxnet/_cy2/*.so, python/mxnet/_cy3/*.so'
+mx_lib = 'lib/libmxnet.so, lib/libmxnet.a, lib/libtvm.so, lib/libtvmop.so, libsample_lib.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a'
+mx_lib_cython = 'lib/libmxnet.so, lib/libmxnet.a, lib/libtvm.so, lib/libtvmop.so, libsample_lib.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a, python/mxnet/_cy2/*.so, python/mxnet/_cy3/*.so'
 
 // Python wheels
 mx_pip = 'build/*.whl'
 
 // mxnet cmake libraries, in cmake builds we do not produce a libnvvm static library by default.
-mx_cmake_lib = 'build/libmxnet.so, build/libmxnet.a, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/3rdparty/dmlc-core/libdmlc.a, build/tests/mxnet_unit_tests, build/3rdparty/openmp/runtime/src/libomp.so'
-mx_cmake_lib_cython = 'build/libmxnet.so, build/libmxnet.a, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/3rdparty/dmlc-core/libdmlc.a, build/tests/mxnet_unit_tests, build/3rdparty/openmp/runtime/src/libomp.so, python/mxnet/_cy2/*.so, python/mxnet/_cy3/*.so'
+mx_cmake_lib = 'build/libmxnet.so, build/libmxnet.a, build/3rdparty/tvm/libtvm.so, build/libtvmop.so, build/3rdparty/dmlc-core/libdmlc.a, build/tests/mxnet_unit_tests, build/3rdparty/openmp/runtime/src/libomp.so'
+mx_cmake_lib_cython = 'build/libmxnet.so, build/libmxnet.a, build/3rdparty/tvm/libtvm.so, build/libtvmop.so, build/3rdparty/dmlc-core/libdmlc.a, build/tests/mxnet_unit_tests, build/3rdparty/openmp/runtime/src/libomp.so, python/mxnet/_cy2/*.so, python/mxnet/_cy3/*.so'
 // mxnet cmake libraries, in cmake builds we do not produce a libnvvm static library by default.
-mx_cmake_lib_debug = 'build/libmxnet.so, build/libmxnet.a, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/libsample_lib.so, build/3rdparty/dmlc-core/libdmlc.a, build/tests/mxnet_unit_tests'
-mx_cmake_mkldnn_lib = 'build/libmxnet.so, build/libmxnet.a, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/3rdparty/dmlc-core/libdmlc.a, build/tests/mxnet_unit_tests, build/3rdparty/openmp/runtime/src/libomp.so, build/3rdparty/mkldnn/src/libmkldnn.so.0'
-mx_mkldnn_lib = 'lib/libmxnet.so, lib/libmxnet.a, lib/libtvm_runtime.so, lib/libtvmop.so, libsample_lib.so, lib/libiomp5.so, lib/libmkldnn.so.0, lib/libmklml_intel.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a'
-mx_tensorrt_lib = 'build/libmxnet.so, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, lib/libnvonnxparser_runtime.so.0, lib/libnvonnxparser.so.0, lib/libonnx_proto.so, lib/libonnx.so'
-mx_lib_cpp_examples = 'lib/libmxnet.so, lib/libmxnet.a, lib/libtvm_runtime.so, lib/libtvmop.so, libsample_lib.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a, 3rdparty/ps-lite/build/libps.a, deps/lib/libprotobuf-lite.a, deps/lib/libzmq.a, build/cpp-package/example/*, python/mxnet/_cy2/*.so, python/mxnet/_cy3/*.so'
-mx_lib_cpp_examples_cpu = 'build/libmxnet.so, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/cpp-package/example/*'
+mx_cmake_lib_debug = 'build/libmxnet.so, build/libmxnet.a, build/3rdparty/tvm/libtvm.so, build/libtvmop.so, build/libsample_lib.so, build/3rdparty/dmlc-core/libdmlc.a, build/tests/mxnet_unit_tests'
+mx_cmake_mkldnn_lib = 'build/libmxnet.so, build/libmxnet.a, build/3rdparty/tvm/libtvm.so, build/libtvmop.so, build/3rdparty/dmlc-core/libdmlc.a, build/tests/mxnet_unit_tests, build/3rdparty/openmp/runtime/src/libomp.so, build/3rdparty/mkldnn/src/libmkldnn.so.0'
+mx_mkldnn_lib = 'lib/libmxnet.so, lib/libmxnet.a, lib/libtvm.so, lib/libtvmop.so, libsample_lib.so, lib/libiomp5.so, lib/libmkldnn.so.0, lib/libmklml_intel.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a'
+mx_tensorrt_lib = 'build/libmxnet.so, build/3rdparty/tvm/libtvm.so, build/libtvmop.so, lib/libnvonnxparser_runtime.so.0, lib/libnvonnxparser.so.0, lib/libonnx_proto.so, lib/libonnx.so'
+mx_lib_cpp_examples = 'lib/libmxnet.so, lib/libmxnet.a, lib/libtvm.so, lib/libtvmop.so, libsample_lib.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a, 3rdparty/ps-lite/build/libps.a, deps/lib/libprotobuf-lite.a, deps/lib/libzmq.a, build/cpp-package/example/*, python/mxnet/_cy2/*.so, python/mxnet/_cy3/*.so'
+mx_lib_cpp_examples_cpu = 'build/libmxnet.so, build/3rdparty/tvm/libtvm.so, build/libtvmop.so, build/cpp-package/example/*'
 
 // Python unittest for CPU
 // Python 2
diff --git a/scala-package/assembly/src/main/assembly/assembly.xml b/scala-package/assembly/src/main/assembly/assembly.xml
index bcc5408..0588244 100644
--- a/scala-package/assembly/src/main/assembly/assembly.xml
+++ b/scala-package/assembly/src/main/assembly/assembly.xml
@@ -54,7 +54,7 @@
       <directory>${MXNET_DIR}/lib</directory>
       <includes>
         <include>libmxnet.so</include>
-        <include>libtvm_runtime.so</include>
+        <include>libtvm.so</include>
         <include>libgfortran.so.3</include>
         <include>libquadmath.so.0</include>
         <include>libiomp5.so</include>
diff --git a/scala-package/core/src/main/scala/org/apache/mxnet/util/NativeLibraryLoader.scala b/scala-package/core/src/main/scala/org/apache/mxnet/util/NativeLibraryLoader.scala
index 9609ba2..5d95745 100644
--- a/scala-package/core/src/main/scala/org/apache/mxnet/util/NativeLibraryLoader.scala
+++ b/scala-package/core/src/main/scala/org/apache/mxnet/util/NativeLibraryLoader.scala
@@ -86,7 +86,7 @@ private[mxnet] object NativeLibraryLoader {
     logger.debug(s"Attempting to load $loadLibname")
     val libFileInJar = libPathInJar + loadLibname
     saveLibraryToTemp("libmxnet.so", "/lib/native/libmxnet.so", true)
-    saveLibraryToTemp("libtvm_runtime.so", "/lib/native/libtvm_runtime.so", false)
+    saveLibraryToTemp("libtvm.so", "/lib/native/libtvm.so", false)
     saveLibraryToTemp("libgfortran.so.3", "/lib/native/libgfortran.so.3", false)
     saveLibraryToTemp("libquadmath.so.0", "/lib/native/libquadmath.so.0", false)
     saveLibraryToTemp("libiomp5.so", "/lib/native/libiomp5.so", false)
diff --git a/src/imperative/cached_op.cc b/src/imperative/cached_op.cc
index 6818d75..14e9527 100644
--- a/src/imperative/cached_op.cc
+++ b/src/imperative/cached_op.cc
@@ -25,6 +25,18 @@
 #include "../operator/operator_common.h"
 #include "../operator/subgraph/common.h"
 
+#if MXNET_USE_TVM_OP
+#ifndef MXNET_AMALGAMATION
+#include <tvm/node/node.h>
+namespace mxnet {
+namespace v3 {
+namespace nnvm_relay_bridge {
+tvm::NodeRef NNVMToRelay(const nnvm::Graph &g);
+}  // namespace nnvm_relay_bridge
+}  // namespace v3
+}  // namespace mxnet
+#endif  // MXNET_AMALGAMATION
+#endif  // MXNET_USE_TVM_OP
 
 namespace mxnet {
 
@@ -312,7 +324,9 @@ bool CachedOp::SetForwardGraph(
   using namespace imperative;
   CHECK_EQ(inputs.size(), num_inputs());
   nnvm::Graph& g = info->fwd_graph;
-
+#if MXNET_USE_TVM_OP && !defined MXNET_AMALGAMATION
+  v3::nnvm_relay_bridge::NNVMToRelay(g);
+#endif  // MXNET_USE_TVM_OP && !define MXNET_AMALGAMATION
   ShapeVector shape_inputs;
   DTypeVector dtype_inputs;
   StorageTypeVector storage_type_inputs;
diff --git a/src/v3/src/nnvm_relay_bridge.cc b/src/v3/src/nnvm_relay_bridge.cc
new file mode 100644
index 0000000..298ce65
--- /dev/null
+++ b/src/v3/src/nnvm_relay_bridge.cc
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*!
+ * Copyright (c) 2019 by Contributors
+ * \file nnvm_relay_bridge.cc
+ * \author Junru Shao
+ */
+#if MXNET_USE_TVM_OP
+#ifndef MXNET_AMALGAMATION
+#include <nnvm/graph.h>
+#include <tvm/relay/expr.h>
+#include <tvm/relay/op.h>
+#include <tvm/node/container.h>
+#include <tvm/node/node.h>
+
+namespace mxnet {
+namespace v3 {
+namespace nnvm_relay_bridge {
+
+using tvm::relay::Expr;
+using tvm::relay::TupleGetItemNode;
+using tvm::relay::FunctionNode;
+using tvm::relay::Var;
+using tvm::relay::VarNode;
+using tvm::relay::CallNode;
+using tvm::relay::TupleNode;
+using tvm::relay::LetNode;
+using tvm::NodeRef;
+using tvm::Array;
+
+static void PrintIndexedGraph(const nnvm::Graph &g) {
+  const auto &idx = g.indexed_graph();
+  std::unordered_set<int> input_nodes(idx.input_nodes().begin(),
+                                      idx.input_nodes().end());
+  std::cout << idx.num_nodes() << " nodes, " << input_nodes.size()
+            << " input nodes" << std::endl;
+  int n_nodes = idx.num_nodes();
+  for (int i = 0, input_cnt = 0; i < n_nodes; ++i) {
+    const nnvm::Node *node = idx[i].source;
+    const nnvm::Op *op = node->op();
+    std::string op_name = op ? op->name : "None";
+    if (input_nodes.count(i)) {
+      input_cnt += 1;
+      op_name = (op ? op->name + " [input " : "[input ") + std::to_string(input_cnt) + "]";
+    } else {
+      op_name = op ? op->name : "None";
+    }
+    std::cout << "  i = " << i << ", op = " << op_name
+              << ", #(input node entries) = " << idx[i].inputs.size()
+              << std::endl;
+    int j_cnt = 0;
+    for (const nnvm::IndexedGraph::NodeEntry &j : idx[i].inputs) {
+      std::cout << "    input entry #" << ++j_cnt
+                << ", entry_id = " << idx.entry_id(j)
+                << ", (node_id = " << j.node_id << ", index = " << j.index
+                << ", version = " << j.version << ")"
+                << std::endl;
+    }
+    for (int j_cnt = 0, n_out = node->num_outputs(); j_cnt < n_out; ++j_cnt) {
+      uint32_t entry_id = idx.entry_id(i, j_cnt);
+      std::cout << "    output entry #" << j_cnt + 1
+                << ", entry_id = " << entry_id
+                << std::endl;
+    }
+  }
+  std::cout << idx.outputs().size() << " output node entries: "
+            << std::endl;
+  int j_cnt = 0;
+  for (const nnvm::IndexedGraph::NodeEntry &j : idx.outputs()) {
+    std::cout << "  output entry #" << ++j_cnt
+              << ", entry_id = " << idx.entry_id(j)
+              << ", (node_id = " << j.node_id << ", index = " << j.index
+              << ", version = " << j.version << ")"
+              << std::endl;
+  }
+}
+
+NodeRef NNVMToRelay(const nnvm::Graph &g) {
+  PrintIndexedGraph(g);
+  const auto &idx = g.indexed_graph();
+  int n_nodes = idx.num_nodes();
+  // maps: node -> var
+  std::vector<Var> node2var(n_nodes);
+  // maps: (node, output_index) -> var
+  std::vector<std::vector<Var> > entries(n_nodes);
+  // maps: node -> #outputs of the node
+  std::vector<int> n_outputs(n_nodes);
+  for (int node_id = 0, input_cnt = 0, compute_cnt = 0; node_id < n_nodes; ++node_id) {
+    const nnvm::Node *node = idx[node_id].source;
+    int n_out = node->num_outputs();
+    n_outputs[node_id] = n_out;
+    std::string name = node->is_variable() ?
+      "arg_" + std::to_string(++input_cnt) :
+      "x_" + std::to_string(++compute_cnt);
+    Var var = node2var[node_id] = VarNode::make(name, {});
+    std::vector<Var> &outputs = entries[node_id];
+    if (n_out == 1) {
+      outputs.push_back(var);
+    } else {
+      outputs.reserve(n_out);
+      for (int i = 0; i < n_out; ++i) {
+        outputs.push_back(VarNode::make(name + "#" + std::to_string(i), {}));
+      }
+    }
+  }
+  // Create the let list
+  std::vector<std::pair<Var, Expr> > let_list;
+  for (int node_id = 0; node_id < n_nodes; ++node_id) {
+    const Var &var = node2var[node_id];
+    const nnvm::IndexedGraph::Node &node = idx[node_id];
+    int n_out = n_outputs[node_id];
+    if (node.source->is_variable()) {
+      CHECK_EQ(n_out, 1) << "InternalError: internal assumption violation";
+      continue;
+    }
+    // Create call_args
+    std::vector<Expr> call_args;
+    for (const nnvm::IndexedGraph::NodeEntry &input : node.inputs) {
+      CHECK_LT((int)input.node_id, node_id) << "InternalError: IndexedGraph is not topo-sorted";
+      call_args.push_back(entries[input.node_id][input.index]);
+    }
+    // TODO(@junrushao1994): map attrs
+    // Add a CallNode
+    let_list.push_back({var, CallNode::make(tvm::relay::Op::Get("add"), call_args)});
+    // Add logic for de-tuple
+    if (n_out > 1) {
+      for (int index = 0; index < n_out; ++index) {
+        let_list.push_back(std::make_pair(
+          entries[node_id][index],
+          TupleGetItemNode::make(var, index)));
+      }
+    }
+  }
+  // Find input arguments to the function
+  Array<Var> params;
+  for (int node_id = 0; node_id < n_nodes; ++node_id) {
+    const nnvm::Node *node = idx[node_id].source;
+    if (node->is_variable()) {
+      params.push_back(node2var[node_id]);
+    }
+  }
+  // Find outputs of the function
+  Expr body;
+  {
+    // 1) Find outputs
+    Array<Expr> outputs;
+    for (const nnvm::IndexedGraph::NodeEntry &j : idx.outputs()) {
+      outputs.push_back(entries[j.node_id][j.index]);
+    }
+    body = TupleNode::make(std::move(outputs));
+    // 2) Construct let out of let-list
+    for ( ; !let_list.empty(); let_list.pop_back()) {
+      const std::pair<Var, Expr> &last = let_list.back();
+      body = LetNode::make(last.first, last.second, body);
+    }
+  }
+  // Then we are able to construct the function
+  return FunctionNode::make(std::move(params), std::move(body), {}, {}, {});
+}
+
+}  // namespace nnvm_relay_bridge
+}  // namespace v3
+}  // namespace mxnet
+#endif  // MXNET_AMALGAMATION
+#endif  // MXNET_USE_TVM_OP
diff --git a/tests/nightly/JenkinsfileForBinaries b/tests/nightly/JenkinsfileForBinaries
index 5158274..e825492 100755
--- a/tests/nightly/JenkinsfileForBinaries
+++ b/tests/nightly/JenkinsfileForBinaries
@@ -18,8 +18,8 @@
 //
 //This is a Jenkinsfile for nightly tests. The format and some functions have been picked up from the top-level Jenkinsfile
 
-mx_lib = 'lib/libmxnet.so, lib/libmxnet.a, lib/libtvm_runtime.so, lib/libtvmop.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a'
-mx_cmake_lib = 'build/libmxnet.so, build/libmxnet.a, build/3rdparty/tvm/libtvm_runtime.so, build/libtvmop.so, build/3rdparty/dmlc-core/libdmlc.a, build/tests/mxnet_unit_tests, build/3rdparty/openmp/runtime/src/libomp.so'
+mx_lib = 'lib/libmxnet.so, lib/libmxnet.a, lib/libtvm.so, lib/libtvmop.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a'
+mx_cmake_lib = 'build/libmxnet.so, build/libmxnet.a, build/3rdparty/tvm/libtvm.so, build/libtvmop.so, build/3rdparty/dmlc-core/libdmlc.a, build/tests/mxnet_unit_tests, build/3rdparty/openmp/runtime/src/libomp.so'
 
 node('utility') {
   // Loading the utilities requires a node context unfortunately
diff --git a/tests/nightly/model_backwards_compatibility_check/JenkinsfileForMBCC b/tests/nightly/model_backwards_compatibility_check/JenkinsfileForMBCC
index 725261d..7d95e3c 100644
--- a/tests/nightly/model_backwards_compatibility_check/JenkinsfileForMBCC
+++ b/tests/nightly/model_backwards_compatibility_check/JenkinsfileForMBCC
@@ -18,7 +18,7 @@
 //
 //This is a Jenkinsfile for the model backwards compatibility checker. The format and some functions have been picked up from the top-level Jenkinsfile.
 
-mx_lib = 'lib/libmxnet.so, lib/libmxnet.a, lib/libtvm_runtime.so,lib/libtvmop.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a'
+mx_lib = 'lib/libmxnet.so, lib/libmxnet.a, lib/libtvm.so,lib/libtvmop.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a'
 
 node('restricted-utility') {
   // Loading the utilities requires a node context unfortunately