diff --git a/hpvm/projects/onnx/frontend/config.py b/hpvm/projects/onnx/frontend/config.py
index 8f42dafa4ee5591f095f410f1bfb390c6e12ace4..8eac060c2838aca3820a83c05bf7633d6ecb9146 100644
--- a/hpvm/projects/onnx/frontend/config.py
+++ b/hpvm/projects/onnx/frontend/config.py
@@ -1,4 +1,4 @@
-model_name = "alexnet"
+model_name = "lenet"
 compile_type = 1 # 0 for HPVM Tensor Runtime, 1 for HPVM C Interface
 input_size = [1,2,3,4]
 onnx_file_dir = "../models/keras/lenet.onnx"
diff --git a/hpvm/projects/onnx/frontend/graph_builder.py b/hpvm/projects/onnx/frontend/graph_builder.py
index 737eb1d3e178267a76bbccdf13f29c9739007453..fb5d97f4ac03b830d057bbd636a2f9e23263a4fd 100644
--- a/hpvm/projects/onnx/frontend/graph_builder.py
+++ b/hpvm/projects/onnx/frontend/graph_builder.py
@@ -153,33 +153,36 @@ class DFG(object):
     def build_dfg(self):
         print("\n\n ****** Traversing and Printing DFG ******* \n\n")
         for node in self.graph.node:
-            self.nodes.append(self.emit_node(node))
+            self.nodes.extend(self.emit_node(node))
 
     # This should be the place where partial evaluation happens
     def emit_node(self, onnx_node):
         if onnx_node.op_type == "Conv":
-            return Conv2DNode(onnx_node)
+            if len(onnx_node.input) == 2:
+                return [Conv2DNode(onnx_node)]
+            else:
+                return [Conv2DNode(onnx_node), BiasAddNode(onnx_node)]
         elif onnx_node.op_type == "MaxPool":
-            return MaxPool2DNode(onnx_node)
+            return [MaxPool2DNode(onnx_node)]
         elif onnx_node.op_type == "AveragePool":
-            return AveragePool2DNode(onnx_node)
+            return [AveragePool2DNode(onnx_node)]
         elif onnx_node.op_type == "MatMul":
-            return MatMulNode(onnx_node)
+            return [MatMulNode(onnx_node)]
         elif onnx_node.op_type == "Add":
-            return AddNode(onnx_node)
+            return [AddNode(onnx_node)]
         elif onnx_node.op_type == "Softmax":
-            return SoftMaxNode(onnx_node)
+            return [SoftMaxNode(onnx_node)]
         elif onnx_node.op_type == "Relu":
-            return ReluNode(onnx_node)
+            return [ReluNode(onnx_node)]
         elif onnx_node.op_type == "Tanh":
-            return TanhNode(onnx_node)
+            return [TanhNode(onnx_node)]
         elif onnx_node.op_type == "BatchNormalization":
-            return BatchNormalizationNode(onnx_node)
+            return [BatchNormalizationNode(onnx_node)]
         elif onnx_node.op_type == "Pad":
-            return PadNode(onnx_node)
+            return [PadNode(onnx_node)]
         elif onnx_node.op_type == "Identity":
-            return IdentityNode(onnx_node)
+            return [IdentityNode(onnx_node)]
         elif onnx_node.op_type == "Flatten":
-            return FlattenNode(onnx_node)
+            return [FlattenNode(onnx_node)]
         else:
             raise ValueError("Unsupported operator type: {}!".format(onnx_node.op_type))
diff --git a/hpvm/projects/onnx/frontend/graph_codegen.py b/hpvm/projects/onnx/frontend/graph_codegen.py
index df02685386817133a8dba3012132b99b5636238b..a9db0ad92b40e541afcc9b71d13cb0da233b2c9e 100644
--- a/hpvm/projects/onnx/frontend/graph_codegen.py
+++ b/hpvm/projects/onnx/frontend/graph_codegen.py
@@ -11,7 +11,7 @@ class GraphCodeGen(object):
         self.graph = dfg.graph
         self.tensors = dfg.tensors
         self.nodes = dfg.nodes
-        self.var_cnt = 0
+        self.var_cnt = -1
         self.weights_dir = weights_dir
         self.test_data_shape = test_data_shape
 
@@ -56,17 +56,16 @@ class GraphCodeGen(object):
     def emit_graph(self):
         for node in self.nodes:
             # check if all inputs of this node is mapped
-            cur_node = node.onnx_node
-            for i in cur_node.input:
+            for i in node.input:
                 self.tensors[i].get_mapped_name() 
             # set var name for output node
-            if len(cur_node.output) > 1:
+            if len(node.output) > 1:
                 raise ValueError("Output number for a single layer larger than 1!")
-            if cur_node.op_type in skip_layer:
+            if node.op_type in skip_layer or node.op_type == "BiasAdd":
                 mapped_output_name = self.get_last_var()
             else:
                 mapped_output_name = self.get_new_var()
-            self.tensors[cur_node.output[0]].set_mapped_name(mapped_output_name)
+            self.tensors[node.output[0]].set_mapped_name(mapped_output_name)
             self.program_str += node.codegen(self.tensors)
 
     def emit_header(self):
diff --git a/hpvm/projects/onnx/frontend/graph_ir.py b/hpvm/projects/onnx/frontend/graph_ir.py
index b14de79439b3a7f6c28cf8a88594af94dc773fe7..a221baf34fb1b9ad9a579ec68018c556918f193b 100644
--- a/hpvm/projects/onnx/frontend/graph_ir.py
+++ b/hpvm/projects/onnx/frontend/graph_ir.py
@@ -8,13 +8,14 @@ class DFGNode(object):
         self.onnx_node = onnx_node
         self.name = onnx_node.name
         self.op_type = onnx_node.op_type
-        self.inst_str = ""
+        self.input = onnx_node.input
+        self.output = onnx_node.output
 
     def codegen(self, tensors):
-        return self.inst_str
+        return "Not Implemented."
 
     def hpvm_codegen(self, tensors):
-        return self.inst_str
+        return "Not Implemented."
 
 
 '''
@@ -44,30 +45,49 @@ class LogicalOpNode(DFGNode):
 class AddNode(DFGNode):
 
     def codegen(self, tensors):
-        cur_node = self.onnx_node
-        left_input = tensors[cur_node.input[0]].get_mapped_name()
-        right_input = tensors[cur_node.input[1]].get_mapped_name()
-        mapped_output_name = tensors[cur_node.output[0]].get_mapped_name()
-        self.inst_str += "void* " + mapped_output_name + " = "
-        self.inst_str += "tensorAdd(" + left_input + ", " + right_input + "); \n"
-        return self.inst_str
+        inst_str = ""
+        left_input = tensors[self.input[0]].get_mapped_name()
+        right_input = tensors[self.input[1]].get_mapped_name()
+        mapped_output_name = tensors[self.output[0]].get_mapped_name()
+        inst_str += "void* " + mapped_output_name + " = "
+        inst_str += "tensorAdd(" + left_input + ", " + right_input + "); \n"
+        return inst_str
 
     def hpvm_codegen(self, tensors):
         return "  void *r = __visc__tensor_add(t1, t2); \n"
 
+class BiasAddNode(DFGNode):
+
+    def __init__(self, onnx_conv_node):
+        super().__init__(onnx_conv_node)
+        self.op_type = "BiasAdd"
+        self.input = list()
+        self.input.append(self.output[0])
+        self.input.append(onnx_conv_node.input[2])
+
+    def codegen(self, tensors):
+        inst_str = ""
+        left_input = tensors[self.input[0]].get_mapped_name()
+        right_input = tensors[self.input[1]].get_mapped_name()
+        mapped_output_name = tensors[self.output[0]].get_mapped_name()
+        inst_str += mapped_output_name + " = "
+        inst_str += "tensorAdd(" + left_input + ", " + right_input + "); \n"
+        return inst_str
 
+    def hpvm_codegen(self, tensors):
+        return "  void *r = __visc__tensor_add(t1, t2); \n"
 
 class MatMulNode(DFGNode):
 
     def codegen(self, tensors):
-        cur_node = self.onnx_node
-        left_input = tensors[cur_node.input[0]].get_mapped_name()
-        right_input = tensors[cur_node.input[1]].get_mapped_name()
-        mapped_output_name = tensors[cur_node.output[0]].get_mapped_name()
-        self.inst_str += "void* " + mapped_output_name + " = "
-        self.inst_str += "tensorGemmGPU(" + left_input + \
+        inst_str = ""
+        left_input = tensors[self.input[0]].get_mapped_name()
+        right_input = tensors[self.input[1]].get_mapped_name()
+        mapped_output_name = tensors[self.output[0]].get_mapped_name()
+        inst_str += "void* " + mapped_output_name + " = "
+        inst_str += "tensorGemmGPU(" + left_input + \
             ", " + right_input + "); \n"
-        return self.inst_str
+        return inst_str
 
     def hpvm_codegen(self, tensors):
         return "  void *r = __visc__tensor_mul(t1, t2); \n"
@@ -76,12 +96,12 @@ class MatMulNode(DFGNode):
 class SoftMaxNode(DFGNode):
 
     def codegen(self, tensors):
-        cur_node = self.onnx_node
-        mapped_input_name = tensors[cur_node.input[0]].get_mapped_name()
-        mapped_output_name = tensors[cur_node.output[0]].get_mapped_name()
-        self.inst_str += "void* " + mapped_output_name + " = "
-        self.inst_str += "tensorSoftmax(" + mapped_input_name + "); \n"
-        return self.inst_str
+        inst_str = ""
+        mapped_input_name = tensors[self.input[0]].get_mapped_name()
+        mapped_output_name = tensors[self.output[0]].get_mapped_name()
+        inst_str += "void* " + mapped_output_name + " = "
+        inst_str += "tensorSoftmax(" + mapped_input_name + "); \n"
+        return inst_str
 
     def hpvm_codegen(self, tensors):
         return "  void* r = __visc__tensor_softmax(t1); \n"
@@ -89,159 +109,128 @@ class SoftMaxNode(DFGNode):
 
 class Conv2DNode(DFGNode):
 
-    def codegen(self, tensors):
-        cur_node = self.onnx_node
-        mapped_input_name  = tensors[cur_node.input[0]].get_mapped_name()
-        mapped_output_name = tensors[cur_node.output[0]].get_mapped_name()
-        weight = cur_node.input[1]
-        strides = list()
-        padding = 0
-        for attr in cur_node.attribute:
+    def __init__(self, onnx_node):
+        super().__init__(onnx_node)
+        if len(self.input) == 3:
+            tmp_input = list()
+            for i in self.input:
+                tmp_input.append(i)
+            self.input = tmp_input
+            self.input.pop() # remove the last index for bias add
+        self.padding = 0
+        self.strides = list()
+        for attr in onnx_node.attribute:
             if attr.name == "pads":
-                padding = attr.ints[0]
+                self.padding = attr.ints[0]
             elif attr.name == "strides":
                 for stride in attr.ints:
-                    strides.append(stride)
-
-        self.inst_str += "void* " + mapped_output_name + " = "
-        self.inst_str += "tensorConvolution(" + mapped_input_name  + ", "
-        self.inst_str += tensors[cur_node.input[1]].get_mapped_name() + ", "
-        self.inst_str += str(padding) + ", "
-        self.inst_str += str(padding) + ", "
-        self.inst_str += str(strides[0]) + ", "
-        self.inst_str += str(strides[1]) + ", "
-        self.inst_str += "1, 1); \n"
-
-        # check if has bias add (Optional)
-        # in ONNX it is only in Conv
-        # in Keras bias add could exist in Dense
-        if len(cur_node.input) == 3:
-            self.inst_str += mapped_output_name + " = "
-            self.inst_str += "tensorAdd(" + mapped_output_name + ", "
-            self.inst_str += tensors[cur_node.input[2]].get_mapped_name() + ""
-            self.inst_str += "); \n"
-        return self.inst_str
+                    self.strides.append(stride)
+
+    def codegen(self, tensors):
+        inst_str = ""
+        mapped_input_name  = tensors[self.input[0]].get_mapped_name()
+        mapped_output_name = tensors[self.output[0]].get_mapped_name()
+
+        inst_str += "void* " + mapped_output_name + " = "
+        inst_str += "tensorConvolution(" + mapped_input_name  + ", "
+        inst_str += tensors[self.input[1]].get_mapped_name() + ", "
+        inst_str += str(self.padding) + ", "
+        inst_str += str(self.padding) + ", "
+        inst_str += str(self.strides[0]) + ", "
+        inst_str += str(self.strides[1]) + ", "
+        inst_str += "1, 1); \n"
+        return inst_str
 
     def hpvm_codegen(self, tensors):
-        strides = list()
-        padding = 0
-        cur_node = self.onnx_node
-        for attr in cur_node.attribute:
-            if attr.name == "pads":
-                padding = attr.ints[0]
-            elif attr.name == "strides":
-                for stride in attr.ints:
-                    strides.append(stride)
         inst_str = "  void *r = __visc__tensor_convolution(t1, t2, "
-        inst_str += str(padding) + ", "
-        inst_str += str(padding) + ", "
-        inst_str += str(strides[0]) + ", "
-        inst_str += str(strides[1]) 
+        inst_str += str(self.padding) + ", "
+        inst_str += str(self.padding) + ", "
+        inst_str += str(self.strides[0]) + ", "
+        inst_str += str(self.strides[1]) 
         inst_str += "); \n"
         return inst_str
 
 
 class MaxPool2DNode(DFGNode):
 
-    def codegen(self, tensors):
-        cur_node = self.onnx_node
-        mapped_input_name  = tensors[cur_node.input[0]].get_mapped_name()
-        mapped_output_name = tensors[cur_node.output[0]].get_mapped_name()
-        strides = list()
-        pool_size = list()
-        for attr in cur_node.attribute:
+    def __init__(self, onnx_node):
+        super().__init__(onnx_node)
+        self.strides = list()
+        self.pool_size = list()
+        self.padding = 0
+        self.pool_type = "0"
+        for attr in onnx_node.attribute:
             if attr.name == "kernel_shape":
                 for pool in attr.ints:
-                    pool_size.append(pool)
+                    self.pool_size.append(pool)
             elif attr.name == "strides":
                 for stride in attr.ints:
-                    strides.append(stride)
-        # FIXME: Non-same padding is *NOT* currently supported
-        padding = 0
-        pool_type = "0"
+                    self.strides.append(stride)
+
+
+    def codegen(self, tensors):
+        mapped_input_name  = tensors[self.input[0]].get_mapped_name()
+        mapped_output_name = tensors[self.output[0]].get_mapped_name()
         # tensorPooling(input, pool_type, pool_h, pool_w, v_pad, h_pad, v_stride, h_stride)
-        self.inst_str += "void* " + mapped_output_name + " = "
-        self.inst_str += "tensorPooling(" + mapped_input_name  + "," + \
-            pool_type + "," + str(pool_size[0]) + "," + str(pool_size[1])
-        self.inst_str += "," + str(padding) + "," + str(padding) + \
-            "," + str(strides[0]) + "," + str(strides[1])
-        self.inst_str += "); \n"
-        return self.inst_str
+        inst_str = "void* " + mapped_output_name + " = "
+        inst_str += "tensorPooling(" + mapped_input_name  + "," + \
+            self.pool_type + "," + str(self.pool_size[0]) + "," + str(self.pool_size[1])
+        inst_str += "," + str(self.padding) + "," + str(self.padding) + \
+            "," + str(self.strides[0]) + "," + str(self.strides[1])
+        inst_str += "); \n"
+        return inst_str
 
     def hpvm_codegen(self, tensors):
-        cur_node = self.onnx_node
-        padding = 0
-        strides = list()
-        pool_size = list()
-        for attr in cur_node.attribute:
-            if attr.name == "kernel_shape":
-                for pool in attr.ints:
-                    pool_size.append(pool)
-            elif attr.name == "strides":
-                for stride in attr.ints:
-                    strides.append(stride)
         inst_str = "  void* r = __visc__tensor_pool_max(t1, "
-        inst_str += str(pool_size[0]) + ", " + str(pool_size[1]) + ", "
-        inst_str += str(padding) + ", " + str(padding) + ", "
-        inst_str += str(strides[0]) + ", " + str(strides[1]) + "); \n"
+        inst_str += str(self.pool_size[0]) + ", " + str(self.pool_size[1]) + ", "
+        inst_str += str(self.padding) + ", " + str(self.padding) + ", "
+        inst_str += str(self.strides[0]) + ", " + str(self.strides[1]) + "); \n"
         return inst_str
 
 
 class AveragePool2DNode(DFGNode):
 
-    def codegen(self, tensors):
-        cur_node = self.onnx_node
-        mapped_input_name  = tensors[cur_node.input[0]].get_mapped_name()
-        mapped_output_name = tensors[cur_node.output[0]].get_mapped_name()
-        strides = list()
-        pool_size = list()
-        for attr in cur_node.attribute:
+    def __init__(self, onnx_node):
+        super().__init__(onnx_node)
+        self.strides = list()
+        self.pool_size = list()
+        self.padding = 0
+        self.pool_type = "1"
+        for attr in onnx_node.attribute:
             if attr.name == "kernel_shape":
                 for pool in attr.ints:
-                    pool_size.append(pool)
+                    self.pool_size.append(pool)
             elif attr.name == "strides":
                 for stride in attr.ints:
-                    strides.append(stride)
-        # FIXME: Non-same padding is *NOT* currently supported
-        padding = 0
-        pool_type = "1"
-        # tensorPooling(input, pool_type, pool_h, pool_w, v_pad, h_pad, v_stride, h_stride)
-        self.inst_str += "void* " + mapped_output_name + " = "
-        self.inst_str += "tensorPooling(" + mapped_input_name  + "," + \
-            pool_type + "," + str(pool_size[0]) + "," + str(pool_size[1])
-        self.inst_str += "," + str(padding) + "," + str(padding) + \
-            "," + str(strides[0]) + "," + str(strides[1])
-        self.inst_str += "); \n"
-        return self.inst_str
+                    self.strides.append(stride)
+
+    def codegen(self, tensors):
+        mapped_input_name  = tensors[self.input[0]].get_mapped_name()
+        mapped_output_name = tensors[self.output[0]].get_mapped_name()
+        inst_str = "void* " + mapped_output_name + " = "
+        inst_str += "tensorPooling(" + mapped_input_name  + "," + \
+            self.pool_type + "," + str(self.pool_size[0]) + "," + str(self.pool_size[1])
+        inst_str += "," + str(self.padding) + "," + str(self.padding) + \
+            "," + str(self.strides[0]) + "," + str(self.strides[1])
+        inst_str += "); \n"
+        return inst_str
 
     def hpvm_codegen(self, tensors):
-        cur_node = self.onnx_node
-        padding = 0
-        strides = list()
-        pool_size = list()
-        for attr in cur_node.attribute:
-            if attr.name == "kernel_shape":
-                for pool in attr.ints:
-                    pool_size.append(pool)
-            elif attr.name == "strides":
-                for stride in attr.ints:
-                    strides.append(stride)
         inst_str = "  void* r = __visc__tensor_pool_avg(t1, "
-        inst_str += str(pool_size[0]) + ", " + str(pool_size[1]) + ", "
-        inst_str += str(padding) + ", " + str(padding) + ", "
-        inst_str += str(strides[0]) + ", " + str(strides[1]) + "); \n"
+        inst_str += str(self.pool_size[0]) + ", " + str(self.pool_size[1]) + ", "
+        inst_str += str(self.padding) + ", " + str(self.padding) + ", "
+        inst_str += str(self.strides[0]) + ", " + str(self.strides[1]) + "); \n"
         return inst_str
 
 
 class ReluNode(DFGNode):
 
     def codegen(self, tensors):
-        cur_node = self.onnx_node
-        mapped_input_name = tensors[cur_node.input[0]].get_mapped_name()
-        mapped_output_name = tensors[cur_node.output[0]].get_mapped_name()
-        self.inst_str += "void* " + mapped_output_name + " = "
-        self.inst_str += "tensorRelu(" + mapped_input_name + "); \n"
-        return self.inst_str
+        mapped_input_name = tensors[self.input[0]].get_mapped_name()
+        mapped_output_name = tensors[self.output[0]].get_mapped_name()
+        inst_str = "void* " + mapped_output_name + " = "
+        inst_str += "tensorRelu(" + mapped_input_name + "); \n"
+        return inst_str
 
     def hpvm_codegen(self, tensors):
         return "  void* r = __visc__tensor_relu(t1); \n"
@@ -249,12 +238,11 @@ class ReluNode(DFGNode):
 class TanhNode(DFGNode):
 
     def codegen(self, tensors):
-        cur_node = self.onnx_node
-        mapped_input_name = tensors[cur_node.input[0]].get_mapped_name()
-        mapped_output_name = tensors[cur_node.output[0]].get_mapped_name()
-        self.inst_str += "void* " + mapped_output_name + " = "
-        self.inst_str += "tensorTanh(" + mapped_input_name + "); \n"
-        return self.inst_str
+        mapped_input_name = tensors[self.input[0]].get_mapped_name()
+        mapped_output_name = tensors[self.output[0]].get_mapped_name()
+        inst_str = "void* " + mapped_output_name + " = "
+        inst_str += "tensorTanh(" + mapped_input_name + "); \n"
+        return inst_str
 
     def hpvm_codegen(self, tensors):
         return "  void* r = __visc__tensor_tanh(t1); \n"
@@ -262,43 +250,52 @@ class TanhNode(DFGNode):
 
 class BatchNormalizationNode(DFGNode):
 
-    def codegen(self, tensors):
-        cur_node = self.onnx_node
-        mapped_input_name = tensors[cur_node.input[0]].get_mapped_name()
-        mapped_output_name = tensors[cur_node.output[0]].get_mapped_name()
-        epsilon = ""
-        for attr in cur_node.attribute:
+    def __init__(self, onnx_node):
+        super().__init__(onnx_node)
+        self.epsilon = ""
+        for attr in onnx_node.attribute:
             if attr.name == "epsilon":
-                epsilon = str(attr.f)
-        self.inst_str += "void* " + mapped_output_name + " = "
-        self.inst_str += "tensorBatchNorm(" + mapped_input_name + ", "
-        self.inst_str += tensors[cur_node.input[1]].get_mapped_name() + ", "
-        self.inst_str += tensors[cur_node.input[2]].get_mapped_name() + ", "
-        self.inst_str += tensors[cur_node.input[3]].get_mapped_name() + ", "
-        self.inst_str += tensors[cur_node.input[4]].get_mapped_name() + ", "
-        self.inst_str += str(epsilon)
-        self.inst_str += "); \n"
-        return self.inst_str
+                self.epsilon = str(attr.f)
+
+    def codegen(self, tensors):
+        mapped_input_name = tensors[self.input[0]].get_mapped_name()
+        mapped_output_name = tensors[self.output[0]].get_mapped_name()
+        inst_str = "void* " + mapped_output_name + " = "
+        inst_str += "tensorBatchNorm(" + mapped_input_name + ", "
+        inst_str += tensors[self.input[1]].get_mapped_name() + ", "
+        inst_str += tensors[self.input[2]].get_mapped_name() + ", "
+        inst_str += tensors[self.input[3]].get_mapped_name() + ", "
+        inst_str += tensors[self.input[4]].get_mapped_name() + ", "
+        inst_str += str(self.epsilon)
+        inst_str += "); \n"
+        return inst_str
 
     def hpvm_codegen(self, tensors):
-        epsilon = ""
-        cur_node = self.onnx_node
-        for attr in cur_node.attribute:
-            if attr.name == "epsilon":
-                epsilon = str(attr.f)
         inst_str = "  void *r = __visc__tensor_batchnorm(t1, t2, t3, t4, t5, "
-        inst_str += str(epsilon) + "); \n"
+        inst_str += str(self.epsilon) + "); \n"
         return inst_str
 
 
 class PadNode(DFGNode):
-    pass
+    def codegen(self, tensors):
+        return ""
+
+    def hpvm_codegen(self, tensors):
+        return ""
 
 class IdentityNode(DFGNode):
-    pass
+    def codegen(self, tensors):
+        return ""
+
+    def hpvm_codegen(self, tensors):
+        return ""
 
 class FlattenNode(DFGNode):
-    pass
+    def codegen(self, tensors):
+        return ""
+
+    def hpvm_codegen(self, tensors):
+        return ""
 
 class ZeroPadding2DNode(DFGNode):
     pass
diff --git a/hpvm/projects/onnx/frontend/hpvm_codegen.py b/hpvm/projects/onnx/frontend/hpvm_codegen.py
index 3d14d97b09fc8b45bc80a2d3ea7acd1baf58cc67..e12c7c99ea06e76741023082e063293ad35c8fbd 100644
--- a/hpvm/projects/onnx/frontend/hpvm_codegen.py
+++ b/hpvm/projects/onnx/frontend/hpvm_codegen.py
@@ -7,17 +7,20 @@ class HpvmCodeGen:
         self.graph = DFG.graph
         self.tensors = DFG.tensors
         self.nodes = DFG.nodes
-        self.var_cnt = 0
+        self.var_cnt = -1
         self.weights_dir = weights_dir
         self.test_data_shape = test_data_shape
         # filter_names is essentially weight & 1st input tensor(s)
         # TODO: Replace manually adding input to filter_names
-        self.filter_names = dict()  
-        self.filter_names["input"] = 1 
+        self.hpvm_graph_str = ""
+        self.filter_names = dict()
+        self.hpvm_node_names = dict()
+        for i in self.graph.input:
+            self.filter_names[self.tensors[i.name].get_mapped_name()] = 1
         for tensor in self.tensors.values():
             if isinstance(tensor, WeightTensor):
                 self.filter_names[tensor.get_mapped_name()] = 1
-
+                self.filter_names["input"] = 1 
         print(self.filter_names)
 
     ################################################
@@ -46,6 +49,7 @@ class HpvmCodeGen:
         self.program_str += headers
 
     def emit_hpvm_node_structures(self):
+
         def emit_hpvm_node_header(new_var, input_size):
             node_header_str = "void " + new_var + "_node("
             for i in range(input_size):
@@ -83,23 +87,89 @@ class HpvmCodeGen:
                 mapped_output_var + ", 1, 1, 0); \n"
             root_footer_str += "\n}\n\n"
             return root_footer_str
+
+        def genHpvmNodeEdges(out_var_name, input_var_name, input_var_name2):
+            print ("input_var_name2 = ", input_var_name2)
+            print ("input_var_name = ", input_var_name)
+
+            hpvm_edge_str = "\n  void* " + out_var_name + " = "
+            hpvm_edge_str += "__visc__createNodeND(0, " + out_var_name + "_node); \n\n"
+
+            if input_var_name in self.filter_names:
+              input_index = self.filter_names[input_var_name]
+              index1 = input_index * 2
+              index2 = index1 + 1      
+              hpvm_edge_str += "  __visc__bindIn(" + out_var_name + ", " + str(index1) + ", 0, 0); \n"
+              hpvm_edge_str += "  __visc__bindIn(" + out_var_name + ", " + str(index2) + ", 1, 0); \n"
+
+            elif input_var_name in self.hpvm_node_names:
+              hpvm_edge_str += "  __visc__edge(" + input_var_name + ", " + out_var_name + ", 1, 0, 0, 0); \n"
+              hpvm_edge_str += "  __visc__edge(" + input_var_name + ", " + out_var_name + ", 1, 1, 1, 0); \n"
+
+
+            if input_var_name2 in self.filter_names:
+              input_index = self.filter_names[input_var_name2]
+              index1 = input_index * 2
+              index2 = index1 + 1
+              hpvm_edge_str += "  __visc__bindIn(" + out_var_name + ", " + str(index1) + ", 2, 0); \n"
+              hpvm_edge_str += "  __visc__bindIn(" + out_var_name + ", " + str(index2) + ", 3, 0); \n"
+
+            elif input_var_name2 in self.hpvm_node_names:
+              hpvm_edge_str += "  __visc__edge(" + input_var_name2 + ", " + out_var_name + ", 1, 0, 2, 0); \n"
+              hpvm_edge_str += "  __visc__edge(" + input_var_name2 + ", " + out_var_name + ", 1, 1, 3, 0); \n"
+              
+            return hpvm_edge_str
+        
+        def genHpvmNodeEdges2(out_var_name, input_vars):
+
+            hpvm_edge_str = "\n  void* " + hpvm_node_id + " = "
+            hpvm_edge_str += "__visc__createNodeND(0, " + hpvm_node_id + "_node); \n\n"
+
+            it = 0
+            for input_var_name in input_vars:
+              if input_var_name in self.filter_names:
+                input_index = self.filter_names[input_var_name]
+                index1 = input_index * 2
+                index2 = index1 + 1      
+                hpvm_edge_str += "  __visc__bindIn(" + hpvm_node_id + ", " + str(index1) + ", " + str(it*2) + ", 0); \n"
+                hpvm_edge_str += "  __visc__bindIn(" + hpvm_node_id + ", " + str(index2) + ", " + str(it*2+1) + ", 0); \n"
+
+              elif input_var_name in self.hpvm_node_names:
+                hpvm_edge_str += "  __visc__edge(" + input_var_name + ", " + hpvm_node_id + ", 1, 0, " + str(it*2) + ", 0); \n"
+                hpvm_edge_str += "  __visc__edge(" + input_var_name + ", " + hpvm_node_id + ", 1, 1, " + str(it*2+1) + ", 0); \n"
+
+              it += 1
+              
+            return hpvm_edge_str
         
 
         node_str = ""
         for node in self.nodes:
-            cur_node = node.onnx_node
-            if node.name in skip_layer:
+            if node.op_type in skip_layer:
                 mapped_output_name = self.get_last_var()
             else:
                 mapped_output_name = self.get_new_var()
-            self.tensors[cur_node.output[0]].set_mapped_name(mapped_output_name)
-            if node.name in skip_layer:
+            self.tensors[node.output[0]].set_mapped_name(mapped_output_name)
+            self.hpvm_node_names[mapped_output_name] = 1
+            if node.op_type in skip_layer:
                 continue
-            node_str += emit_hpvm_node_header(mapped_output_name, len(cur_node.input))
+            node_str += emit_hpvm_node_header(mapped_output_name, len(node.input))
             node_str += node.hpvm_codegen(self.tensors)
             node_str += emit_hpvm_node_footer(2) # Hardcoded as in Keras frontend
-       
-        node_str += emit_root_node_footer(self)
+            if node.op_type == "BatchNormalization":
+                out_var_name = self.tensors[node.output[0]].get_mapped_name()
+                input_vars = list()
+                for i in node.input:
+                    input_vars.append(self.tensors[i].get_mapped_name())
+                self.hpvm_graph_str += genHpvmNodeEdges2(out_var_name, input_vars)
+            else:
+                out_var_name = self.tensors[node.output[0]].get_mapped_name()
+                input_var_name = self.tensors[node.input[0]].get_mapped_name()
+                input_var_name2 = ""
+                if len(node.input) == 2:
+                    input_var_name2 = self.tensors[node.input[1]].get_mapped_name()
+                self.hpvm_graph_str += genHpvmNodeEdges(out_var_name, input_var_name, input_var_name2)
+        self.hpvm_graph_str += emit_root_node_footer(self)
         self.program_str += node_str
 
     def emit_root_node_header(self):
@@ -142,24 +212,7 @@ class HpvmCodeGen:
         self.program_str += root_struct
 
     def emit_hpvm_graph(self):
-        
-        def emit_hpvm_edge(node):
-            return ""
-
-        hpvm_graph_str = ""
-        for node in self.nodes:
-            hpvm_graph_str += emit_hpvm_edge(node)
-        return hpvm_graph_str
-
-    def emit_root_node_footer(self):
-        mapped_output_var = self.tensors[self.graph.output[0].name].get_mapped_name()
-        # Binding output of last DFG node to the Root Node output
-        root_footer_str = "\n  __visc__bindOut(" + \
-            mapped_output_var + ", 0, 0, 0); \n"
-        root_footer_str += "  __visc__bindOut(" + \
-            mapped_output_var + ", 1, 1, 0); \n"
-        root_footer_str += "\n}\n\n"
-        self.program_str += root_footer_str
+        self.program_str += self.hpvm_graph_str
 
     def emit_weights(self):
         weights_str = "\n"
@@ -216,9 +269,8 @@ class HpvmCodeGen:
         self.emit_header()
         self.emit_hpvm_node_structures()
         self.emit_root_node_header()
-        self.emit_root_structure()
         self.emit_hpvm_graph()
-        self.emit_root_node_footer()
+        self.emit_root_structure()
         self.emit_main()
         # dump generated program string to source file
         self.emit_source(self.weights_dir)