diff --git a/python/tvm/__init__.py b/python/tvm/__init__.py
index 67dab09c2ce78e2d3335e0e28c55e9baa2eeb03f..a3fb11b1919c1259f68676c57c12f493c07e4d58 100644
--- a/python/tvm/__init__.py
+++ b/python/tvm/__init__.py
@@ -18,7 +18,7 @@ from . import ir_builder
 from . import ndarray as nd
 from .ndarray import cpu, gpu, opencl, cl, vpi
 
-from ._ctypes._function import Function
+from ._ffi.function import Function
 
 from ._base import TVMError
 from ._base import __version__
diff --git a/python/tvm/_ctypes/__init__.py b/python/tvm/_ffi/__init__.py
similarity index 100%
rename from python/tvm/_ctypes/__init__.py
rename to python/tvm/_ffi/__init__.py
diff --git a/python/tvm/_ctypes/_function.py b/python/tvm/_ffi/function.py
similarity index 97%
rename from python/tvm/_ctypes/_function.py
rename to python/tvm/_ffi/function.py
index 8051155f72d17ca7e32a959ad77cbb13a5994399..495c3420e479b9bdb1aaf2cdf29573d9771a31a4 100644
--- a/python/tvm/_ctypes/_function.py
+++ b/python/tvm/_ffi/function.py
@@ -10,11 +10,11 @@ from numbers import Number, Integral
 
 from .._base import _LIB, check_call
 from .._base import c_str, py_str, string_types
-from ._types import TVMValue, TypeCode, TVMType, TVMByteArray
-from ._types import TVMPackedCFunc, TVMCFuncFinalizer
-from ._types import RETURN_SWITCH, C_TO_PY_ARG_SWITCH, _wrap_arg_func
-from ._node import NodeBase, NodeGeneric, convert_to_node
-from ._ndarray import NDArrayBase
+from .types import TVMValue, TypeCode, TVMType, TVMByteArray
+from .types import TVMPackedCFunc, TVMCFuncFinalizer
+from .types import RETURN_SWITCH, C_TO_PY_ARG_SWITCH, _wrap_arg_func
+from .node import NodeBase, NodeGeneric, convert_to_node
+from .ndarray import NDArrayBase
 
 FunctionHandle = ctypes.c_void_p
 ModuleHandle = ctypes.c_void_p
@@ -57,7 +57,7 @@ def convert_to_tvm_func(pyfunc):
 
         if rv is not None:
             if isinstance(rv, tuple):
-                raise ValueError("PackedFunction can only support one reurn value")
+                raise ValueError("PackedFunction can only support one return value")
             temp_args = []
             values, tcodes, _ = _make_tvm_args((rv,), temp_args)
             if not isinstance(ret, TVMRetValueHandle):
@@ -84,15 +84,15 @@ def _make_tvm_args(args, temp_args):
     values = (TVMValue * num_args)()
     type_codes = (ctypes.c_int * num_args)()
     for i, arg in enumerate(args):
-        if arg is None:
+        if isinstance(arg, NodeBase):
+            values[i].v_handle = arg.handle
+            type_codes[i] = TypeCode.NODE_HANDLE
+        elif arg is None:
             values[i].v_handle = None
             type_codes[i] = TypeCode.NULL
         elif isinstance(arg, NDArrayBase):
             values[i].v_handle = ctypes.cast(arg.handle, ctypes.c_void_p)
             type_codes[i] = TypeCode.ARRAY_HANDLE
-        elif isinstance(arg, NodeBase):
-            values[i].v_handle = arg.handle
-            type_codes[i] = TypeCode.NODE_HANDLE
         elif isinstance(arg, Integral):
             values[i].v_int64 = arg
             type_codes[i] = TypeCode.INT
diff --git a/python/tvm/_ctypes/_ndarray.py b/python/tvm/_ffi/ndarray.py
similarity index 99%
rename from python/tvm/_ctypes/_ndarray.py
rename to python/tvm/_ffi/ndarray.py
index 72ba6402c15010fa4f5e60734e996a6f6b2b1095..1b1bddfae661cda6f5ba56103ac618f032fc376d 100644
--- a/python/tvm/_ctypes/_ndarray.py
+++ b/python/tvm/_ffi/ndarray.py
@@ -8,7 +8,7 @@ import numpy as np
 
 from .._base import _LIB, check_call
 from .._base import c_array
-from ._types import TVMType, tvm_shape_index_t
+from .types import TVMType, tvm_shape_index_t
 
 class TVMContext(ctypes.Structure):
     """TVM context strucure."""
diff --git a/python/tvm/_ctypes/_node.py b/python/tvm/_ffi/node.py
similarity index 98%
rename from python/tvm/_ctypes/_node.py
rename to python/tvm/_ffi/node.py
index 0c706923e60ec101a3da621bdf447a0a2222d848..085ff56fb7fb52797abd9c7f9141654d5ae77402 100644
--- a/python/tvm/_ctypes/_node.py
+++ b/python/tvm/_ffi/node.py
@@ -10,8 +10,8 @@ from numbers import Number, Integral
 from .._base import _LIB, check_call
 from .._base import c_str, py_str, string_types
 from .. import _api_internal
-from ._types import TVMValue, TypeCode
-from ._types import RETURN_SWITCH, C_TO_PY_ARG_SWITCH, _wrap_arg_func
+from .types import TVMValue, TypeCode
+from .types import RETURN_SWITCH, C_TO_PY_ARG_SWITCH, _wrap_arg_func
 
 
 NodeHandle = ctypes.c_void_p
diff --git a/python/tvm/_ctypes/_types.py b/python/tvm/_ffi/types.py
similarity index 100%
rename from python/tvm/_ctypes/_types.py
rename to python/tvm/_ffi/types.py
diff --git a/python/tvm/addon/verilog.py b/python/tvm/addon/verilog.py
index 1eb83afd4bb78b5e36eec1f7ccd84a5aaa2ad685..62e070aca9d6dda671918751783164a00ee0a690 100644
--- a/python/tvm/addon/verilog.py
+++ b/python/tvm/addon/verilog.py
@@ -8,8 +8,8 @@ import ctypes
 
 from .. import _api_internal
 from .._base import string_types
-from .._ctypes._node import NodeBase, register_node
-from .._ctypes._function import register_func
+from .._ffi.node import NodeBase, register_node
+from .._ffi.function import register_func
 from . import testing
 
 @register_node
diff --git a/python/tvm/api.py b/python/tvm/api.py
index 8bb243bf4c2b14bacc64e3c7bf194f8f41e16ff5..ca43ed7d423e47f46169496f55a116331c35cfcf 100644
--- a/python/tvm/api.py
+++ b/python/tvm/api.py
@@ -4,12 +4,11 @@ from __future__ import absolute_import as _abs
 
 from numbers import Integral as _Integral
 
-from ._ctypes._types import TVMType
-from ._ctypes._node import register_node, NodeBase
-from ._ctypes._node import convert_to_node as _convert_to_node
-from ._ctypes._function import Function
-from ._ctypes._function import _init_api, register_func, get_global_func
-from ._ctypes._function import convert_to_tvm_func as _convert_tvm_func
+from ._ffi.node import register_node, NodeBase
+from ._ffi.node import convert_to_node as _convert_to_node
+from ._ffi.function import Function
+from ._ffi.function import _init_api, register_func, get_global_func
+from ._ffi.function import convert_to_tvm_func as _convert_tvm_func
 from . import _api_internal
 from . import _base
 from . import make as _make
diff --git a/python/tvm/arith.py b/python/tvm/arith.py
index ed086bfdebe594ff5c22e20e8b3d195e98d88499..980c87d90316de5ed91bfa16eb313eb6b9a778e4 100644
--- a/python/tvm/arith.py
+++ b/python/tvm/arith.py
@@ -1,8 +1,8 @@
 """Arithmetic data structure and utility"""
 from __future__ import absolute_import as _abs
 
-from ._ctypes._node import NodeBase, register_node
-from ._ctypes._function import _init_api
+from ._ffi.node import NodeBase, register_node
+from ._ffi.function import _init_api
 from . import _api_internal
 
 class IntSet(NodeBase):
diff --git a/python/tvm/codegen.py b/python/tvm/codegen.py
index fbb102d464e8f9685605c0278b859cdc82c62f90..2021c30a364da8f3d914d105ce5d9872218a6ec4 100644
--- a/python/tvm/codegen.py
+++ b/python/tvm/codegen.py
@@ -1,5 +1,5 @@
 """Code generation related functions."""
-from ._ctypes._function import _init_api
+from ._ffi.function import _init_api
 
 def build_module(lowered_func, target):
     """Build lowered_func into Module.
diff --git a/python/tvm/collections.py b/python/tvm/collections.py
index 4910d36925a81066fd4326a2382ccb5ac2084296..f9af60035a655d8d82b15bea782ab3b4147da4b4 100644
--- a/python/tvm/collections.py
+++ b/python/tvm/collections.py
@@ -1,6 +1,6 @@
 """Collections contains data structures used in TVM DSL."""
 from __future__ import absolute_import as _abs
-from ._ctypes._node import NodeBase, register_node
+from ._ffi.node import NodeBase, register_node
 from . import _api_internal
 
 @register_node
diff --git a/python/tvm/expr.py b/python/tvm/expr.py
index 6b196ca2ec0209ca13a567e8454c6e32d61596b8..045353b2c41b79bd65941d570f51ec4b4cfac9c6 100644
--- a/python/tvm/expr.py
+++ b/python/tvm/expr.py
@@ -16,7 +16,7 @@ For example, you can use addexp.a to get the left operand of an Add node.
 """
 # pylint: disable=missing-docstring
 from __future__ import absolute_import as _abs
-from ._ctypes._node import NodeBase, register_node
+from ._ffi.node import NodeBase, register_node
 from . import make as _make
 
 class ExprOp(object):
diff --git a/python/tvm/intrin.py b/python/tvm/intrin.py
index cdc4a4c38d16a038b3d4b5b19aa4e661a566b77d..eeb0de4f68d943972d5f92a78c99302fa7f76c16 100644
--- a/python/tvm/intrin.py
+++ b/python/tvm/intrin.py
@@ -3,7 +3,7 @@ from __future__ import absolute_import as _abs
 
 from .expr import Call as _Call
 from . import make as _make
-from ._ctypes._function import register_func as _register_func
+from ._ffi.function import register_func as _register_func
 from .api import convert
 
 def call_packed(*args):
diff --git a/python/tvm/ir_builder.py b/python/tvm/ir_builder.py
index ac414b8e165127abe75a9836d2a9db89eb9a7c16..d2b9d74acf578f1b5a0ae8775c42a946204c21eb 100644
--- a/python/tvm/ir_builder.py
+++ b/python/tvm/ir_builder.py
@@ -8,7 +8,7 @@ from . import make as _make
 from . import ir_pass as _pass
 from . import collections as _collections
 from ._base import string_types
-from ._ctypes._node import NodeGeneric
+from ._ffi.node import NodeGeneric
 
 class WithScope(object):
     """Auxiliary scope  with"""
diff --git a/python/tvm/ir_pass.py b/python/tvm/ir_pass.py
index b43a69700010cf1f88d24b66543fb47f249cecf3..2f47ac4c6898792a4282316dae07fae986455546 100644
--- a/python/tvm/ir_pass.py
+++ b/python/tvm/ir_pass.py
@@ -6,6 +6,6 @@ The functions are automatically exported from C++ side via PackedFunc.
 Each api is a PackedFunc that can be called in a positional argument manner.
 You can read "include/tvm/pass.h" for the function signature of these functions.
 """
-from ._ctypes._function import _init_api
+from ._ffi.function import _init_api
 
 _init_api("tvm.ir_pass")
diff --git a/python/tvm/make.py b/python/tvm/make.py
index 50613701cc231977ba8583c3fa6d68d9e354b5ab..6633552f155fb61607076f0c094927e4eb134855 100644
--- a/python/tvm/make.py
+++ b/python/tvm/make.py
@@ -6,6 +6,6 @@ The functions are automatically exported from C++ side via PackedFunc.
 Each api is a PackedFunc that can be called in a positional argument manner.
 You can use make function to build the IR node.
 """
-from ._ctypes._function import _init_api
+from ._ffi.function import _init_api
 
 _init_api("tvm.make")
diff --git a/python/tvm/module.py b/python/tvm/module.py
index a64b8b437fa7b372f67fc91c2364cc46b0892779..610bfdc3917a089e9449cd39ef50ccedd593f17d 100644
--- a/python/tvm/module.py
+++ b/python/tvm/module.py
@@ -1,7 +1,7 @@
 """Container of compiled functions of TVM."""
 from __future__ import absolute_import as _abs
-from ._ctypes._function import ModuleBase, _init_module_module
-from ._ctypes._function import _init_api
+from ._ffi.function import ModuleBase, _init_module_module
+from ._ffi.function import _init_api
 
 
 class Module(ModuleBase):
diff --git a/python/tvm/ndarray.py b/python/tvm/ndarray.py
index 238b3f6359f4cd2f164ecd34537415a76e64a86a..d345073c312756e4be69942bdb0f540421edab2d 100644
--- a/python/tvm/ndarray.py
+++ b/python/tvm/ndarray.py
@@ -7,9 +7,9 @@ the correctness of the program.
 from __future__ import absolute_import as _abs
 import numpy as _np
 
-from ._ctypes._ndarray import TVMContext, TVMType, NDArrayBase
-from ._ctypes._ndarray import cpu, gpu, opencl, vpi, empty, sync
-from ._ctypes._ndarray import _init_ndarray_module
+from ._ffi.ndarray import TVMContext, TVMType, NDArrayBase
+from ._ffi.ndarray import cpu, gpu, opencl, vpi, empty, sync
+from ._ffi.ndarray import _init_ndarray_module
 
 cl = opencl
 
diff --git a/python/tvm/node.py b/python/tvm/node.py
index b87ce510cc91cdc9e5e7005a568d3894cab721e4..b47087773d95c2ffb85579ab48006b98c2d84612 100644
--- a/python/tvm/node.py
+++ b/python/tvm/node.py
@@ -4,6 +4,6 @@ Normally user do not need to touch this api.
 """
 # pylint: disable=unused-import
 from __future__ import absolute_import as _abs
-from ._ctypes._node import NodeBase, register_node
+from ._ffi.node import NodeBase, register_node
 
 Node = NodeBase
diff --git a/python/tvm/schedule.py b/python/tvm/schedule.py
index 0783f260b2a8260952be67c3f447cbdeb745c98e..6406b7cd3cb4dd519183a5d3319051916b7298b6 100644
--- a/python/tvm/schedule.py
+++ b/python/tvm/schedule.py
@@ -1,11 +1,11 @@
 """The computation schedule api of TVM."""
 from __future__ import absolute_import as _abs
-from ._ctypes._node import NodeBase, register_node
+from ._ffi.node import NodeBase, register_node
 from . import _api_internal
 from . import tensor as _tensor
 from . import expr as _expr
 from . import collections as _collections
-from ._ctypes._function import _init_api
+from ._ffi.function import _init_api
 
 
 @register_node
diff --git a/python/tvm/stmt.py b/python/tvm/stmt.py
index 6c7ec8d6cfef8a4b455f8bd973738a5245c1036b..a2dfdc6b9a4867172d7cb513317ce1db318c0b3b 100644
--- a/python/tvm/stmt.py
+++ b/python/tvm/stmt.py
@@ -14,7 +14,7 @@ Each statement node have subfields that can be visited from python side.
     assert(st.buffer_var == a)
 """
 from __future__ import absolute_import as _abs
-from ._ctypes._node import NodeBase, register_node
+from ._ffi.node import NodeBase, register_node
 
 class Stmt(NodeBase):
     pass
diff --git a/python/tvm/tensor.py b/python/tvm/tensor.py
index 848d64fec72e345f71f83a0b18bd286a871fcda6..db4faf23015529c91031dae84857d9c4b64218d8 100644
--- a/python/tvm/tensor.py
+++ b/python/tvm/tensor.py
@@ -1,7 +1,7 @@
 """Tensor and Operation class for computation declaration."""
 # pylint: disable=invalid-name
 from __future__ import absolute_import as _abs
-from ._ctypes._node import NodeBase, NodeGeneric, register_node, convert_to_node
+from ._ffi.node import NodeBase, NodeGeneric, register_node, convert_to_node
 from . import _api_internal
 from . import make as _make
 from . import expr as _expr