Skip to content
Snippets Groups Projects
Commit 0a07411d authored by Tianqi Chen's avatar Tianqi Chen Committed by ziheng
Browse files

[JS][WEB][BACKEND] Javascript(webassembly) backend. (#239)

parent 619e529a
No related branches found
No related tags found
No related merge requests found
Showing
with 218 additions and 31 deletions
......@@ -98,3 +98,5 @@ Win32
*.dir
perf
nnvm
*.wasm
.emscripten
......@@ -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')
}
}
}
}
......@@ -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') {
......
......@@ -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
......@@ -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
......@@ -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
......
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>`_
......@@ -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)
......@@ -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
......@@ -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++.
......
......@@ -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
......@@ -46,8 +46,8 @@ 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
......@@ -55,7 +55,7 @@ The configuration of xgboost can be modified by ```config.mk```
## 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
......
......@@ -3,6 +3,7 @@ TVM Documentation
Welcome to TVM documentation.
Contents
--------
......@@ -13,6 +14,7 @@ Contents
how_to/install
tutorials/index
faq
api/python/index
how_to/contribute
api/python/index
api_links
genindex
......@@ -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)
......@@ -34,6 +39,7 @@
#else
#define TVM_DLL
#endif
#endif
// TVM Runtime is DLPack compatible.
#include <dlpack/dlpack.h>
......@@ -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
......
......@@ -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()
......
......@@ -5,9 +5,14 @@ import os
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)
......@@ -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
......
"""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)
......@@ -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")
......@@ -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;
......@@ -110,6 +113,8 @@ GetLLVMTargetMachine(const std::string& target_str) {
return tm;
}
} // namespace codegen
} // namespace tvm
#endif // TVM_LLVM_VERSION
......@@ -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
......
......@@ -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
......@@ -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;
}
......
# For CPU
FROM ubuntu:14.04
RUN apt-get update --fix-missing
COPY install/ubuntu_install_core.sh /install/ubuntu_install_core.sh
RUN bash /install/ubuntu_install_core.sh
COPY install/ubuntu_install_python.sh /install/ubuntu_install_python.sh
RUN bash /install/ubuntu_install_python.sh
COPY install/ubuntu_install_emscripten.sh /install/ubuntu_install_emscripten.sh
RUN bash /install/ubuntu_install_emscripten.sh
RUN cp /root/.emscripten /emsdk-portable/
\ No newline at end of file
......@@ -28,7 +28,14 @@ RUN cd recommonmark; python setup.py install
COPY install/ubuntu_install_java.sh /install/ubuntu_install_java.sh
RUN bash /install/ubuntu_install_java.sh
COPY install/ubuntu_install_nodejs.sh /install/ubuntu_install_nodejs.sh
RUN bash /install/ubuntu_install_nodejs.sh
# Enable doxygen for c++ doc build
RUN apt-get install -y doxygen graphviz
# Environment variables
ENV PATH=/node_modules/.bin:${PATH}
ENV PATH=/usr/local/nvidia/bin:${PATH}
ENV PATH=/usr/clang+llvm-4.0.0-x86_64-linux-gnu-ubuntu-14.04/bin:${PATH}
ENV PATH=/usr/local/cuda/bin:${PATH}
......
alias make="make -j4"
# Get latest cmake
wget https://cmake.org/files/v3.8/cmake-3.8.2-Linux-x86_64.tar.gz
tar xf cmake-3.8.2-Linux-x86_64.tar.gz
export PATH=/cmake-3.8.2-Linux-x86_64/bin/:${PATH}
wget https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-portable.tar.gz
tar xf emsdk-portable.tar.gz
cd emsdk-portable
./emsdk update
./emsdk install latest
./emsdk activate latest
# Clone and pull latest sdk
./emsdk install clang-incoming-64bit
./emsdk activate clang-incoming-64bit
cd ..
apt-get update && apt-get install -y curl
curl -sL https://deb.nodesource.com/setup_6.x | bash -
apt-get update && apt-get install -y nodejs
npm install eslint jsdoc
......@@ -36,11 +36,11 @@ def test_llvm_add_pipeline():
verify_elf(path, 0x03)
def build_arm():
if not tvm.module.enabled("llvm"):
print("Skip because llvm is not enabled..")
target = "llvm -target=armv7-none-linux-gnueabihf"
if not tvm.module.enabled(target):
print("Skip because %s is not enabled.." % target)
return
temp = util.tempdir()
target = "llvm -target=armv7-none-linux-gnueabihf"
f = tvm.build(s, [A, B, C], target)
path = temp.relpath("myadd.o")
f.save(path)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment