Skip to content

Commit

Permalink
[JS][WEB][BACKEND] Javascript(webassembly) backend. (apache#239)
Browse files Browse the repository at this point in the history
  • Loading branch information
tqchen authored and ZihengJiang committed Jul 11, 2017
1 parent 619e529 commit 0a07411
Show file tree
Hide file tree
Showing 31 changed files with 1,551 additions and 31 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,5 @@ Win32
*.dir
perf
nnvm
*.wasm
.emscripten
37 changes: 37 additions & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,31 @@ stage('Build') {
pack_lib('i386')
}
}
},
'web': {
node('emcc') {
ws('workspace/tvm/build-weblib') {
init_git()
sh """
cp make/config.mk .
echo USE_CUDA=0 >> config.mk
echo USE_OPENCL=0 >> config.mk
echo LLVM_CONFIG=llvm-config >> config.mk
echo USE_RPC=0 >> config.mk
"""
sh "${docker_run} emscripten echo testing javascript..."
timeout(time: max_time, unit: 'MINUTES') {
try {
sh "${docker_run} emscripten ./tests/scripts/task_web_build.sh"
} catch (exc) {
echo 'Incremental compilation failed. Fall back to build from scratch'
sh "${docker_run} emscripten make clean"
sh "${docker_run} emscripten ./tests/scripts/task_web_build.sh"
}
}
pack_lib('weblib')
}
}
}
}

Expand Down Expand Up @@ -174,6 +199,18 @@ stage('Integration Test') {
}
}
},
'web': {
node('emcc') {
ws('workspace/tvm/it-weblib') {
init_git()
unpack_lib('weblib')
sh "${docker_run} emscripten echo testing javascript..."
timeout(time: max_time, unit: 'MINUTES') {
sh "${docker_run} emscripten ./tests/scripts/task_web_test.sh"
}
}
}
},
'docs': {
node('GPU' && 'linux') {
ws('workspace/tvm/docs-python-gpu') {
Expand Down
18 changes: 15 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ endif

include $(config)

.PHONY: clean all test doc pylint cpplint lint verilog cython cython2 cython3
.PHONY: clean all test doc pylint cpplint lint verilog cython cython2 cython3 web runtime

BUILD_TARGETS ?= lib/libtvm.so lib/libtvm_runtime.so
all: ${BUILD_TARGETS}
runtime: lib/libtvm_runtime.so
web: lib/libtvm_web_runtime.js lib/libtvm_web_runtime.bc

ifndef DMLC_CORE_PATH
DMLC_CORE_PATH = $(ROOTDIR)/dmlc-core
Expand Down Expand Up @@ -52,11 +53,13 @@ RUNTIME_DEP = $(RUNTIME_OBJ)

# The flags
LDFLAGS = -pthread -lm -ldl
CFLAGS = -std=c++11 -Wall -O2\
-Iinclude -I$(DLPACK_PATH)/include -I$(DMLC_CORE_PATH)/include -IHalideIR/src -Itopi/include -fPIC
INCLUDE_FLAGS = -Iinclude -I$(DLPACK_PATH)/include -I$(DMLC_CORE_PATH)/include -IHalideIR/src -Itopi/include
CFLAGS = -std=c++11 -Wall -O2 $(INCLUDE_FLAGS) -fPIC
LLVM_CFLAGS= -fno-rtti -DDMLC_ENABLE_RTTI=0
FRAMEWORKS =
OBJCFLAGS = -fno-objc-arc
EMCC_FLAGS= -s RESERVED_FUNCTION_POINTERS=2 -s NO_EXIT_RUNTIME=1 -DDMLC_LOG_STACK_TRACE=0\
-std=c++11 -Oz $(INCLUDE_FLAGS)

# Dependency specific rules
ifdef CUDA_PATH
Expand Down Expand Up @@ -149,6 +152,15 @@ lib/libtvm_runtime.so: $(RUNTIME_DEP)
@mkdir -p $(@D)
$(CXX) $(CFLAGS) $(FRAMEWORKS) -shared -o $@ $(filter %.o %.a, $^) $(LDFLAGS)

lib/libtvm_web_runtime.bc: web/web_runtime.cc
@mkdir -p build/web
@mkdir -p $(@D)
$(CXX) $(CFLAGS) -MM -MT lib/libtvm_web_runtime.bc $< >build/web/web_runtime.d
emcc $(EMCC_FLAGS) -o $@ web/web_runtime.cc

lib/libtvm_web_runtime.js: lib/libtvm_web_runtime.bc
@mkdir -p $(@D)
emcc $(EMCC_FLAGS) -o $@ lib/libtvm_web_runtime.bc

$(LIB_HALIDEIR): LIBHALIDEIR

Expand Down
7 changes: 7 additions & 0 deletions docs/api_links.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Links to API References
=======================

This page contains links to API references that are build with different doc build system.

* `C++ doyxgen API <doxygen/index.html>`_
* `Javascript jsdoc API <jsdoc/index.html>`_
9 changes: 4 additions & 5 deletions docs/how_to/contribute.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
TVM has been developed by community members.
Everyone is more than welcome to contribute. It is a way to make the project better and more accessible to more users.

- Please add your name to [CONTRIBUTORS.md](../../CONTRIBUTORS.md)
- Please update [NEWS.md](../../NEWS.md) to add note on your changes to the API or added a new document.
- Please add your name to [CONTRIBUTORS.md](https://github.com/dmlc/tvm/blob/master/CONTRIBUTORS.md)
- Please update [NEWS.md](https://github.com/dmlc/tvm/blob/master/NEWS.md) to add note on your changes to the API or added a new document.

## Guidelines
* [Submit Pull Request](#submit-pull-request)
Expand All @@ -29,7 +29,7 @@ git rebase upstream/master
it might be good to merge them together(use git rebase then squash) into more meaningful groups.
* Send the pull request!
- Fix the problems reported by automatic checks
- If you are contributing a new module, consider add a testcase in [tests](../tests)
- If you are contributing a new module or new function, add a test.

## Git Workflow Howtos
### How to resolve conflict with master
Expand Down Expand Up @@ -87,8 +87,7 @@ The previous two tips requires force push, this is because we altered the path o
It is fine to force push to your own fork, as long as the commits changed are only yours.

## Testcases
- All the testcases are in [tests](../../tests)
- We use python nose for python test cases.
- All the testcases are in tests

## Core Library
- Follow Google C style for C++.
Expand Down
8 changes: 4 additions & 4 deletions docs/how_to/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ You can edit `make/config.mk` to change the compile options, and then build by
### Building on Windows

TVM support build via MSVC using cmake. To build with Visual Studio 2015 use cmake.
Make sure you have a recent version of cmake added to your path and then from the xgboost directory:
Make sure you have a recent version of cmake added to your path and then from the tvm directory:

```bash
mkdir build
Expand All @@ -46,16 +46,16 @@ This specifies an out of source build using the MSVC 12 64 bit generator. Open t

### Customized Building

The configuration of xgboost can be modified by ```config.mk```
- First copy [make/config.mk](../make/config.mk) to the project root, on which
The configuration of tvm can be modified by ```config.mk```
- First copy make/config.mk to the project root, on which
any local modification will be ignored by git, then modify the according flags.
- TVM optionally depends on LLVM. LLVM is required for CPU codegen that needs LLVM.
- LLVM 4.0 is needed for build with LLVM
- By default CUDA and OpenCL code generator do not require llvm.

## Python Package Installation

The python package is located at [python](../python).
The python package is located at python
There are several ways to install the package:

1. Set the environment variable `PYTHONPATH` to tell python where to find
Expand Down
4 changes: 3 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ TVM Documentation

Welcome to TVM documentation.


Contents
--------

Expand All @@ -13,6 +14,7 @@ Contents
how_to/install
tutorials/index
faq
api/python/index
how_to/contribute
api/python/index
api_links
genindex
10 changes: 8 additions & 2 deletions include/tvm/runtime/c_runtime_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@
#define TVM_EXTERN_C
#endif

/*! \brief TVM_DLL prefix for windows */
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#define TVM_DLL EMSCRIPTEN_KEEPALIVE
#endif

#ifndef TVM_DLL
#ifdef _WIN32
#ifdef TVM_EXPORTS
#define TVM_DLL __declspec(dllexport)
Expand All @@ -34,6 +39,7 @@
#else
#define TVM_DLL
#endif
#endif

// TVM Runtime is DLPack compatible.
#include <dlpack/dlpack.h>
Expand Down Expand Up @@ -331,7 +337,7 @@ TVM_DLL int TVMFuncGetGlobal(const char* name, TVMFunctionHandle* out);
* \param out_array The array of function names.
* \return 0 when success, -1 when failure happens
*/
TVM_DLL int TVMFuncListGlobalNames(int *out_size,
TVM_DLL int TVMFuncListGlobalNames(int* out_size,
const char*** out_array);

// Array related apis for quick proptyping
Expand Down
1 change: 1 addition & 0 deletions python/tvm/_ffi/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class TVMError(Exception):
"""Error thrown by TVM function"""
pass


def _load_lib():
"""Load libary by searching possible path."""
lib_path = libinfo.find_lib_path()
Expand Down
22 changes: 15 additions & 7 deletions python/tvm/_ffi/libinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@
import platform


def find_lib_path():
def find_lib_path(name=None):
"""Find dynamic library files.
Parameters
----------
name : list of str
List of names to be found.
Returns
-------
lib_path : list(string)
Expand All @@ -30,13 +35,16 @@ def find_lib_path():
elif os.name == "posix" and os.environ.get('LD_LIBRARY_PATH', None):
dll_path.extend([p.strip() for p in os.environ['LD_LIBRARY_PATH'].split(":")])
dll_path = [os.path.abspath(x) for x in dll_path]

if os.name == 'nt':
lib_dll_path = [os.path.join(p, 'libtvm.dll') for p in dll_path]
runtime_dll_path = [os.path.join(p, 'libtvm_runtime.dll') for p in dll_path]
if name is not None:
lib_dll_path = [os.path.join(p, name) for p in dll_path]
runtime_dll_path = []
else:
lib_dll_path = [os.path.join(p, 'libtvm.so') for p in dll_path]
runtime_dll_path = [os.path.join(p, 'libtvm_runtime.so') for p in dll_path]
if os.name == 'nt':
lib_dll_path = [os.path.join(p, 'libtvm.dll') for p in dll_path]
runtime_dll_path = [os.path.join(p, 'libtvm_runtime.dll') for p in dll_path]
else:
lib_dll_path = [os.path.join(p, 'libtvm.so') for p in dll_path]
runtime_dll_path = [os.path.join(p, 'libtvm_runtime.so') for p in dll_path]

if not use_runtime:
# try to find lib_dll_path
Expand Down
57 changes: 57 additions & 0 deletions python/tvm/contrib/emscripten.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""Util to invoke emscripten compilers in the system."""
# pylint: disable=invalid-name
from __future__ import absolute_import as _abs
import subprocess
from .._ffi.libinfo import find_lib_path

def create_js(output,
objects,
options=None,
cc="emcc"):
"""Create emscripten javascript library.
Parameters
----------
output : str
The target shared library.
objects : list
List of object files.
options : str
The additional options.
cc : str, optional
The compile string.
"""
cmd = [cc]
cmd += ["-s", "RESERVED_FUNCTION_POINTERS=2"]
cmd += ["-s", "NO_EXIT_RUNTIME=1"]
cmd += ["-Oz"]
cmd += ["-o", output]

objects = [objects] if isinstance(objects, str) else objects
with_runtime = False
for obj in objects:
if obj.find("libtvm_web_runtime.bc") != -1:
with_runtime = True

if not with_runtime:
objects += [find_lib_path("libtvm_web_runtime.bc")[0]]

cmd += objects

if options:
cmd += options

args = ' '.join(cmd)
proc = subprocess.Popen(
args, shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
(out, _) = proc.communicate()

if proc.returncode != 0:
msg = "Compilation error:\n"
msg += out
raise RuntimeError(msg)
9 changes: 7 additions & 2 deletions src/codegen/llvm/llvm_common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ void InitializeLLVM() {
}

llvm::TargetMachine*
GetLLVMTargetMachine(const std::string& target_str) {
GetLLVMTargetMachine(const std::string& target_str, bool allow_null) {
// setup target triple
CHECK(target_str.length() >= 4 &&
target_str.substr(0, 4) == "llvm")
Expand Down Expand Up @@ -91,7 +91,10 @@ GetLLVMTargetMachine(const std::string& target_str) {
std::string err;
const llvm::Target* target =
llvm::TargetRegistry::lookupTarget(target_triple, err);
CHECK(target) << err << " target_triple=" << target_triple;
if (target == nullptr) {
CHECK(allow_null) << err << " target_triple=" << target_triple;
return nullptr;
}
// set target option
llvm::TargetOptions opt;
opt.LessPreciseFPMADOption = true;
Expand All @@ -110,6 +113,8 @@ GetLLVMTargetMachine(const std::string& target_str) {
return tm;
}



} // namespace codegen
} // namespace tvm
#endif // TVM_LLVM_VERSION
4 changes: 2 additions & 2 deletions src/codegen/llvm/llvm_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ void InitializeLLVM();
/*!
* \brief Get target machine from target_str string.
* \param target_str Target string, in format "llvm -target=xxx -mcpu=xxx"
*
* \param allow_null Whether allow null to be returned.
* \return target machine
*/
llvm::TargetMachine*
GetLLVMTargetMachine(const std::string& target_str);
GetLLVMTargetMachine(const std::string& target_str, bool allow_null = false);

} // namespace codegen
} // namespace tvm
Expand Down
6 changes: 6 additions & 0 deletions src/codegen/llvm/llvm_module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,12 @@ TVM_REGISTER_API("module.loadfile_ll")
n->LoadIR(args[0]);
*rv = runtime::Module(n);
});

TVM_REGISTER_API("codegen.llvm_target_enabled")
.set_body([](TVMArgs args, TVMRetValue* rv) {
InitializeLLVM();
*rv = (GetLLVMTargetMachine(args[0], true) != nullptr);
});
} // namespace codegen
} // namespace tvm
#endif // TVM_LLVM_VERSION
6 changes: 4 additions & 2 deletions src/runtime/module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,14 @@ bool RuntimeEnabled(const std::string& target) {
f_name = "device_api.metal";
} else if (target == "stackvm") {
f_name = "codegen.build_stackvm";
} else if (target == "llvm") {
f_name = "codegen.build_llvm";
} else if (target == "rpc") {
f_name = "device_api.rpc";
} else if (target == "vpi" || target == "verilog") {
f_name = "device_api.vpi";
} else if (target.length() >= 4 && target.substr(0, 4) == "llvm") {
const PackedFunc* pf = runtime::Registry::Get("codegen.llvm_target_enabled");
if (pf == nullptr) return false;
return (*pf)(target);
} else {
LOG(FATAL) << "Unknown optional runtime " << target;
}
Expand Down
Loading

0 comments on commit 0a07411

Please sign in to comment.