Skip to content

Commit

Permalink
[BACKEND][CODEGEN] C codegen with tests (apache#2161)
Browse files Browse the repository at this point in the history
* Implement C code generation with tests

* Code cleanup

* Implement C code generation with tests

* Code cleanup

* tabs to spaces

* make lint compliant

* update export_library and reserve unique C keywords

* move ReserveKeywordsAsUnique to codegen_c

* some documentation and code cleanup

* use tvm.contrib.util for tempdir in testcases
  • Loading branch information
Mutinifni authored and Wei Chen committed Feb 20, 2019
1 parent b939d61 commit 936323b
Show file tree
Hide file tree
Showing 11 changed files with 544 additions and 9 deletions.
60 changes: 60 additions & 0 deletions python/tvm/_ffi/libinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,66 @@ def find_lib_path(name=None, search_path=None, optional=False):
return lib_found


def find_include_path(name=None, search_path=None, optional=False):
"""Find header files for C compilation.
Parameters
----------
name : list of str
List of directory names to be searched.
Returns
-------
include_path : list(string)
List of all found paths to header files.
"""
ffi_dir = os.path.dirname(os.path.abspath(os.path.expanduser(__file__)))
source_dir = os.path.join(ffi_dir, "..", "..", "..")
install_include_dir = os.path.join(ffi_dir, "..", "..", "..", "..")
third_party_dir = os.path.join(source_dir, "3rdparty")

header_path = []

if os.environ.get('TVM_INCLUDE_PATH', None):
header_path.append(os.environ['TVM_INCLUDE_PATH'])

header_path.append(install_include_dir)
header_path.append(source_dir)
header_path.append(third_party_dir)

header_path = [os.path.abspath(x) for x in header_path]
if search_path is not None:
if search_path is list:
header_path = header_path + search_path
else:
header_path.append(search_path)
if name is not None:
if isinstance(name, list):
tvm_include_path = []
for n in name:
tvm_include_path += [os.path.join(p, n) for p in header_path]
else:
tvm_include_path = [os.path.join(p, name) for p in header_path]
dlpack_include_path = []
else:
tvm_include_path = [os.path.join(p, 'include') for p in header_path]
dlpack_include_path = [os.path.join(p, 'dlpack/include') for p in header_path]

# try to find include path
include_found = [p for p in tvm_include_path if os.path.exists(p) and os.path.isdir(p)]
include_found += [p for p in dlpack_include_path if os.path.exists(p) and os.path.isdir(p)]

if not include_found:
message = ('Cannot find the files.\n' +
'List of candidates:\n' +
str('\n'.join(tvm_include_path + dlpack_include_path)))
if not optional:
raise RuntimeError(message)
return None

return include_found


# current version
# We use the version of the incoming release for code
# that is under development.
Expand Down
1 change: 1 addition & 0 deletions python/tvm/_ffi/runtime_ctypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ class TVMContext(ctypes.Structure):
'llvm': 1,
'stackvm': 1,
'cpu': 1,
'c': 1,
'gpu': 2,
'cuda': 2,
'nvptx': 2,
Expand Down
2 changes: 2 additions & 0 deletions python/tvm/contrib/cc.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from .._ffi.base import py_str
from .util import tempdir
from .._ffi.libinfo import find_include_path


def create_shared(output,
Expand Down Expand Up @@ -49,6 +50,7 @@ def _linux_shared(output, objects, options, cc="g++"):
cmd += objects
if options:
cmd += options
cmd += ["-I" + path for path in find_include_path()]
proc = subprocess.Popen(
cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
(out, _) = proc.communicate()
Expand Down
12 changes: 8 additions & 4 deletions python/tvm/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,17 +115,21 @@ def export_library(self,
self.save(file_name)
return

if self.type_key != "llvm":
raise ValueError("Module[%s]: Only llvm support export shared" % self.type_key)
if not (self.type_key == "llvm" or self.type_key == "c"):
raise ValueError("Module[%s]: Only llvm and c support export shared" % self.type_key)
temp = _util.tempdir()
if fcompile is not None and hasattr(fcompile, "object_format"):
object_format = fcompile.object_format
else:
object_format = "o"
if self.type_key == "llvm":
object_format = "o"
else:
assert self.type_key == "c"
object_format = "cc"
path_obj = temp.relpath("lib." + object_format)
self.save(path_obj)
files = [path_obj]
is_system_lib = self.get_function("__tvm_is_system_module")()
is_system_lib = self.type_key == "llvm" and self.get_function("__tvm_is_system_module")()
if self.imported_modules:
path_cc = temp.relpath("devc.cc")
with open(path_cc, "w") as f:
Expand Down
44 changes: 39 additions & 5 deletions src/codegen/codegen_c.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,43 @@ void CodeGenC::InitFuncState(LoweredFunc f) {
handle_data_type_.clear();
CodeGenSourceBase::ClearFuncState();
}
void CodeGenC::AddFunction(LoweredFunc f) {
// clear previous generated state.
this->InitFuncState(f);

void CodeGenC::ReserveKeywordsAsUnique() {
// skip the first underscore, so SSA variable starts from _1
GetUniqueName("_");
GetUniqueName("extern");
GetUniqueName("void");
GetUniqueName("int");
GetUniqueName("float");
GetUniqueName("double");
GetUniqueName("char");
GetUniqueName("unsigned");
GetUniqueName("short");
GetUniqueName("long");
GetUniqueName("if");
GetUniqueName("else");
GetUniqueName("switch");
GetUniqueName("case");
GetUniqueName("default");
GetUniqueName("for");
GetUniqueName("do");
GetUniqueName("while");
GetUniqueName("goto");
GetUniqueName("register");
GetUniqueName("continue");
GetUniqueName("break");
GetUniqueName("typedef");
GetUniqueName("struct");
GetUniqueName("enum");
GetUniqueName("union");
GetUniqueName("return");
}

void CodeGenC::AddFunction(LoweredFunc f) {
// clear previous generated state.
this->InitFuncState(f);
// reserve keywords
ReserveKeywordsAsUnique();
// add to alloc buffer type.
for (const auto & kv : f->handle_data_type) {
RegisterHandleType(kv.first.get(), kv.second.type());
Expand Down Expand Up @@ -187,6 +218,7 @@ std::string CodeGenC::GetStructRef(
case intrinsic::kArrNDim: os << "ndim"; break;
case intrinsic::kArrTypeCode: os << "dtype.code"; break;
case intrinsic::kArrTypeBits: os << "dtype.bits"; break;
case intrinsic::kArrByteOffset: os << "byte_offset"; break;
case intrinsic::kArrTypeLanes: os << "dtype.lanes"; break;
case intrinsic::kArrDeviceId: os << "ctx.device_id"; break;
case intrinsic::kArrDeviceType: os << "ctx.device_type"; break;
Expand Down Expand Up @@ -834,8 +866,10 @@ void CodeGenC::VisitStmt_(const Evaluate *op) {
}
}
std::string vid = this->PrintExpr(op->value);
this->PrintIndent();
this->stream << "(void)" << vid << ";\n";
if (vid != "") {
this->PrintIndent();
this->stream << "(void)" << vid << ";\n";
}
}

void CodeGenC::VisitStmt_(const ProducerConsumer *op) {
Expand Down
2 changes: 2 additions & 0 deletions src/codegen/codegen_c.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ class CodeGenC :
std::unordered_map<const Variable*, std::string> alloc_storage_scope_;
/*! \brief the data type of allocated buffers */
std::unordered_map<const Variable*, Type> handle_data_type_;
/*! \brief reserves common C keywords */
void ReserveKeywordsAsUnique();

private:
/*! \brief whether to print in SSA form */
Expand Down
Loading

0 comments on commit 936323b

Please sign in to comment.