blob: 2eab15093b72b0544df27f36f2f09a63d66c1dc2 [file] [log] [blame]
/**
* TVM Javascript web runtime library.
*
* @projectname tvm
* @version 0.5.dev
*/
/* eslint no-unused-vars: "off" */
/* eslint no-unexpected-multiline: "off" */
/* eslint indent: "off" */
/* eslint no-console: "off" */
/**
* TVM Runtime namespace.
* Provide tvm_runtime.create to create a {@link tvm.TVMRuntime}.
*
* @namespace tvm_runtime
*/
var tvm_runtime = tvm_runtime || {};
/**
* TVM root namespace.
* The classes inside this namespace need to be constructed by factory functions.
* Use {@link tvm_runtime}.create to get started.
*
* @namespace tvm
*/
(function() {
/**
* TVMRuntime object for interacting with TVM runtime.
* This object can be constructed using {@link tvm_runtime}.create
*
* @class
* @memberof tvm
*/
function TVMRuntime() {
"use strict";
var runtime_ref = this;
// Utility function to throw error
function throwError(message) {
if (typeof runtime_ref.logger !== "undefined") {
runtime_ref.logger(message);
}
if (typeof Error !== "undefined") {
throw new Error(message);
}
throw message;
}
var Module = this.Module;
var Runtime = this.Runtime;
if (typeof Module === "undefined") {
throwError("Emscripten Module is not available");
}
// constants
var SIZEOF_POINTER = 4;
var SIZEOF_SIZE_T = 4;
var SIZEOF_FLOAT = 4;
var SIZEOF_INT = 4;
var SIZEOF_INT8 = 1;
var SIZEOF_INT64 = 8;
var SIZEOF_DOUBLE = 8;
var SIZEOF_TYPE = 4;
var SIZEOF_CTX = SIZEOF_INT + SIZEOF_INT;
var SIZEOF_TVMVALUE = SIZEOF_DOUBLE;
var ARRAY_OFFSET_DATA = 0;
var ARRAY_OFFSET_CTX = ARRAY_OFFSET_DATA + SIZEOF_POINTER;
var ARRAY_OFFSET_DEV_TYPE = ARRAY_OFFSET_CTX;
var ARRAY_OFFSET_DEV_ID = ARRAY_OFFSET_CTX + SIZEOF_INT;
var ARRAY_OFFSET_NDIM = ARRAY_OFFSET_CTX + SIZEOF_CTX;
var ARRAY_OFFSET_DTYPE = ARRAY_OFFSET_NDIM + SIZEOF_INT;
var ARRAY_OFFSET_DTYPE_CODE = ARRAY_OFFSET_DTYPE;
var ARRAY_OFFSET_DTYPE_BITS = ARRAY_OFFSET_DTYPE_CODE + SIZEOF_INT8;
var ARRAY_OFFSET_DTYPE_LANES = ARRAY_OFFSET_DTYPE_BITS + SIZEOF_INT8;
var ARRAY_OFFSET_SHAPE = ARRAY_OFFSET_DTYPE + SIZEOF_TYPE;
var ARRAY_OFFSET_STRIDES = ARRAY_OFFSET_STRIDES + SIZEOF_POINTER;
var ARRAY_OFFSET_BYTE_OFFSET = ARRAY_OFFSET_STRIDES + SIZEOF_POINTER;
// Type codes
var kInt = 0;
var kUInt = 1;
var kFloat = 2;
var kHandle = 3;
var kNull = 4;
var kTVMType = 5;
var kTVMContext = 6;
var kArrayHandle = 7;
var kNodeHandle = 8;
var kModuleHandle = 9;
var kFuncHandle = 10;
var kStr = 11;
var kBytes = 12;
//-----------------------------------------
// TVM CWrap library
// ----------------------------------------
var TVMGetLastError = Module.cwrap(
"TVMGetLastError",
"string", // const char*
[]);
var TVMAPISetLastError = Module.cwrap
("TVMAPISetLastError",
null,
["string" // const char*
]);
var TVMModImport = Module.cwrap
("TVMModImport",
"number",
["number", // TVMModuleHandle mod
"number" // TVMModuleHandle dep
]);
var TVMModGetFunction = Module.cwrap
("TVMModGetFunction",
"number",
["number", // TVMModuleHandle mod
"string", // const char* func_name
"number", // int query_imports
"number" // TVMFunctionHandle *out
]);
var TVMModFree = Module.cwrap
("TVMModFree",
"number",
["number" // TVMModeHandle mod
]);
var TVMFuncFree = Module.cwrap
("TVMFuncFree",
"number",
["number" // TVMFunctionHandle func
]);
var TVMFuncCall = Module.cwrap
("TVMFuncCall",
"number",
["number", // TVMFunctionHandle func
"number", // TVMValue* arg_values
"number", // int* arg_tcodes
"number", // int num_args
"number", // int ret_val
"number" // int ret_type_code
]);
var TVMCFuncSetReturn = Module.cwrap
("TVMCFuncSetReturn",
"number",
["number", // TVMRetValueHandle ret
"number", // TVMValue* value
"number", // int* type_code
"number" // int num_ret
]);
var TVMCbArgToReturn = Module.cwrap
("TVMCbArgToReturn",
"number",
["number", // TVMValue* value
"number" // int code
]);
var TVMFuncCreateFromCFunc = Module.cwrap
("TVMFuncCreateFromCFunc",
"number",
["number", // TVMPackedCFunc func,
"number", // void* resource_handle
"number", // TVMPackedCFuncFinalizer fin
"number" // TVMFunctionHandle *out
]);
var TVMFuncRegisterGlobal = Module.cwrap
("TVMFuncRegisterGlobal",
"number",
["string", // name
"number", // TVMFunctionHandle f
"number" // int override
]);
var TVMFuncGetGlobal = Module.cwrap
("TVMFuncGetGlobal",
"number",
["string", // const char* name
"number" // TVMFunctionHandle* out
]);
var TVMFuncListGlobalNames = Module.cwrap
("TVMFuncListGlobalNames",
"number",
["number", // int* out_size
"number" // const char*** out_array
]);
var TVMArrayAlloc = Module.cwrap
("TVMArrayAlloc",
"number",
["number", // const tvm_index_t* shape
"number", // int ndim
"number", // int dtype_code
"number", // int dtype_bits
"number", // int dtype_lanes
"number", // int device_type
"number", // int device_id
"number" // int TVMArrayHandle* out
]);
var TVMArrayFree = Module.cwrap
("TVMArrayFree",
"number",
["number" // TVMArrayHandle handle
]);
var TVMArrayCopyFromTo = Module.cwrap
("TVMArrayCopyFromTo",
"number",
["number", // TVMArrayHandle from
"number" // TVMArrayHandle to
]);
var TVMArrayCopyFromBytes = Module.cwrap
("TVMArrayCopyFromBytes",
"number",
["number", // TVMArrayHandle handle
"number", // int data
"number" // size_t nbytes
]);
var TVMArrayCopyToBytes = Module.cwrap
("TVMArrayCopyToBytes",
"number",
["number", // TVMArrayHandle handle
"number", // int data
"number" // size_t nbytes
]);
var TVMModLoadFromFile = Module.cwrap
("TVMModLoadFromFile",
"number",
["string", // const char* file_name
"string", // const char* format
"number" // TVMModuleHandle* out
])
//-----------------------------------------
// Static utility functions
// ----------------------------------------
this.assert = function(condition, message) {
if (!condition) {
message = message || "assert failed";
throwError(message);
}
};
/**
* Logging function.
* Override this to change logger behavior.
*
* @param {string} message
*/
this.logger = function(message) {
console.log(message);
};
function logging(message) {
runtime_ref.logger(message);
}
// Override print error to logging
Module.printErr = logging;
var CHECK = this.assert;
function TVM_CALL(ret) {
if (ret != 0) {
throwError(TVMGetLastError());
}
}
function CInt64ArrayToJS(ptr, size) {
var ret = [];
for (var i = 0; i < size; ++i) {
ret.push(Module.getValue(ptr + i * SIZEOF_INT64, "i64"));
}
return ret;
}
function CStringToJS(ptr) {
var ret = [];
var ch = 1;
while (ch != 0) {
ch = Module.getValue(ptr, "i8");
if (ch != 0) {
ret.push(String.fromCharCode(ch));
}
++ptr;
}
return ret.join("");
}
function CBytesToJS(ptr) {
var data = Module.getValue(ptr, "*");
var size = Module.getValue(ptr + SIZEOF_POINTER, "i32");
var ret = new Uint8Array(new ArrayBuffer(size));
ret.set(new Uint8Array(Module.HEAPU8.buffer, data, size));
return ret;
}
function StringToUint8Array(str) {
var arr = new Uint8Array(str.length + 1);
for(var i = 0; i < str.length; ++i) {
arr[i] = str.charCodeAt(i);
}
arr[str.length] = 0;
return arr;
}
//-----------------------------------------
// Class declarations
// ----------------------------------------
function CBuffer(nbytes) {
this.data = Module._malloc(nbytes);
}
function RefTVMValue() {
this.data = Module._malloc(SIZEOF_TVMVALUE);
}
function TVMArgs(nargs) {
this.nargs = nargs;
this.value = Module._malloc(SIZEOF_TVMVALUE * nargs);
this.tcode = Module._malloc(SIZEOF_INT * nargs);
this.temp = [];
}
function TVMType(code, bits, lanes) {
this.code = code;
this.bits = bits;
this.lanes = lanes;
}
/**
* TVM device context.
* @class
* @memberof tvm
*/
function TVMContext(device_type, device_id) {
this.device_type = device_type;
this.device_id = device_id;
}
/**
* TVM n-dimensional array.
*
* Use {@link tvm.TVMRuntime}.empty to create an instance.
* @class
* @memberof tvm
*/
function NDArray(handle) {
this.handle = handle;
this.ndim = Module.getValue(this.handle + ARRAY_OFFSET_NDIM, "i32");
// shape
var cshape = Module.getValue(this.handle + ARRAY_OFFSET_SHAPE, "*");
this.shape = CInt64ArrayToJS(cshape, this.ndim);
// dtype
var code = Module.getValue(this.handle + ARRAY_OFFSET_DTYPE_CODE, "i8");
var bits = Module.getValue(this.handle + ARRAY_OFFSET_DTYPE_BITS, "i8");
var lanes = Module.getValue(this.handle + ARRAY_OFFSET_DTYPE_LANES, "i16");
var dtype = new TVMType(code, bits, lanes);
this.dtype = dtype;
this.BYTES_PER_ELEMENT = (dtype.bits * dtype.lanes / 8);
// ctx
var device_type = Module.getValue(this.handle + ARRAY_OFFSET_DEV_TYPE, "i32");
var device_id = Module.getValue(this.handle + ARRAY_OFFSET_DEV_ID, "i32");
this.context = new TVMContext(device_type, device_id);
// byte_offset
this.byteOffset = Module.getValue(this.handle + ARRAY_OFFSET_BYTE_OFFSET, "i64");
}
function TVMFunction(handle) {
this.handle = handle;
}
/**
* Module container of TVM generated functions.
*
* @class
* @memberof tvm
*/
function TVMModule(handle) {
this.handle = handle;
}
/**
* A typed scalar constant.
* This can be used to pass number as integer types to tvm function.
* Use {@link tvm.TVMRuntime}.constant to create an instance.
* @class
* @memberof tvm
*/
function TVMConstant(value, dtype) {
this.value = value;
this.dtype = dtype;
}
//-----------------------------------------
// Private Functions
// ----------------------------------------
function getTVMType(dtype) {
if (dtype instanceof TVMType) return dtype;
if (typeof dtype == "string") {
var pattern = dtype;
var code, bits = 32, lanes = 1;
if (pattern.substring(0, 5) == "float") {
pattern = pattern.substring(5, pattern.length);
code = kFloat;
} else if (pattern.substring(0, 3) == "int") {
pattern = pattern.substring(3, pattern.length);
code = kInt;
} else if (pattern.substring(0, 4) == "uint") {
pattern = pattern.substring(4, pattern.length);
code = kUInt;
} else if (pattern.substring(0, 6) == "handle") {
pattern = pattern.substring(5, pattern.length);
code = kHandle;
bits = 64;
} else {
throw throwError("Unknown dtype " + dtype);
}
var arr = pattern.split("x");
if (arr.length >= 1) {
var parsed = parseInt(arr[0]);
if (parsed == arr[0]) {
bits = parsed;
}
}
if (arr.length >= 2) {
lanes = parseInt(arr[1]);
}
return new TVMType(code, bits, lanes);
} else {
throw throwError("Unknown dtype " + dtype);
}
}
function TVMRetValueToJS(vptr, tcode) {
switch (tcode) {
case kInt:
case kUInt: return Module.getValue(vptr, "i64");
case kFloat: return Module.getValue(vptr, "double");
case kFuncHandle: return makeTVMFunction(Module.getValue(vptr, "*"));
case kModuleHandle: return new TVMModule(Module.getValue(vptr, "*"));
case kNull: return null;
case kStr: return CStringToJS(Module.getValue(vptr, "*"));
case kBytes: return CBytesToJS(Module.getValue(vptr, "*"));
default: throwError("Unsupported return type code=" + tcode);
}
}
function makeTVMFunction(handle) {
var func = new TVMFunction(handle);
var ret = function () {
// alloc
var args = new TVMArgs(arguments.length);
var rvalue = new RefTVMValue();
var rtcode = new RefTVMValue();
args.setArguments(arguments);
TVM_CALL(TVMFuncCall(handle, args.value, args.tcode,
args.nargs, rvalue.data, rtcode.data));
var rv = TVMRetValueToJS(rvalue.data, rtcode.asInt());
// release
args.release();
rvalue.release();
rtcode.release();
return rv;
};
var release = function() {
func.release();
};
ret._tvm_function = func;
ret.release = release;
return ret;
}
//-----------------------------------------
// Javascript PackedCallback System
// ----------------------------------------
var funcTable = [0];
var freeFuncId = [];
function invokeCallback(arg_value, arg_tcode, nargs, ret, handle) {
var args = [];
for (var i = 0; i < nargs; ++i) {
var vptr = arg_value + i * SIZEOF_TVMVALUE;
var tcode = Module.getValue(arg_tcode + i * SIZEOF_INT, "i32");
if (tcode == kNodeHandle ||
tcode == kFuncHandle ||
tcode == kModuleHandle) {
TVM_CALL(TVMCbArgToReturn(vptr, tcode));
}
args.push(TVMRetValueToJS(vptr, tcode));
}
var rv = funcTable[handle].apply(null, args);
if (typeof rv !== "undefined") {
// alloc
var rarg = new TVMArgs(1);
rarg.setArguments([rv]);
TVM_CALL(TVMCFuncSetReturn(ret, rarg.value, rarg.tcode, 1));
// release
rarg.release();
}
return 0;
}
function freeCallback(handle) {
funcTable[handle] = 0;
freeFuncId.push(handle);
}
var fptrInvokeCallback = null;
var fptrFreeCallback = null;
if (typeof Runtime !== "undefined" &&
typeof Runtime.addFunction !== "undefined") {
fptrInvokeCallback = Runtime.addFunction(invokeCallback);
fptrFreeCallback = Runtime.addFunction(freeCallback);
}
/**
* Check if a function is TVM PackedFunc
* @param {Function} f function to be checked.
* @return {boolean} Whether f is PackedFunc
*/
this.isPackedFunc = function(f) {
return (typeof f == "function") && f.hasOwnProperty("_tvm_function");
};
var isPackedFunc = this.isPackedFunc;
/**
* Convert a javascript function to TVM function.
* @param {Function} f javascript function.
* @return {Function} The created TVMFunction.
*/
this.convertFunc = function(f) {
if (isPackedFunc(f)) return f;
CHECK(fptrInvokeCallback !== null,
"Emscripten Runtime addFunction is not available");
var fid;
if (freeFuncId.length != 0) {
fid = freeFuncId.pop();
} else {
fid = funcTable.length;
funcTable.push(0);
}
funcTable[fid] = f;
// alloc
var out = new RefTVMValue();
TVM_CALL(TVMFuncCreateFromCFunc(
fptrInvokeCallback, fid, fptrFreeCallback, out.data));
var out_handle = out.asHandle();
// release
out.release();
return makeTVMFunction(out_handle);
};
var convertFunc = this.convertFunc;
//-----------------------------------------
// Private Class declarations
// ----------------------------------------
CBuffer.prototype = {
/**
* Finalizer: resources from the object.
*/
release : function() {
if (this.data != 0) {
Module._free(this.data);
this.data = 0;
}
},
};
// RefTVMValue
RefTVMValue.prototype = {
/**
* Finalizer: resources from the object.
*/
release : function() {
if (this.data != 0) {
Module._free(this.data);
this.data = 0;
}
},
asInt : function() {
return Module.getValue(this.data, "i32");
},
asInt64 : function() {
return Module.getValue(this.data, "i64");
},
asDouble : function() {
return Module.getValue(this.data, "double");
},
asHandle : function() {
return Module.getValue(this.data, "*");
}
};
// TVMArgs
TVMArgs.prototype = {
release : function() {
if (this.value != 0) {
Module._free(this.value);
Module._free(this.tcode);
this.value = 0;
for (var i = 0; i< this.temp.length; ++i) {
if (this.temp[i].release instanceof Function) {
this.temp[i].release();
}
}
}
},
setInt : function(index, value) {
Module.setValue(this.tcode + index * SIZEOF_INT, kInt, "i32");
Module.setValue(this.value + index * SIZEOF_TVMVALUE, value, "i64");
},
setDouble : function(index, value) {
Module.setValue(this.tcode + index * SIZEOF_INT, kFloat, "i32");
Module.setValue(this.value + index * SIZEOF_TVMVALUE, value, "double");
},
setHandle : function(index, value, tcode) {
Module.setValue(this.tcode + index * SIZEOF_INT, tcode, "i32");
Module.setValue(this.value + index * SIZEOF_TVMVALUE, value, "*");
},
setString : function(index, value) {
var sdata = new CBuffer(value.length + 1);
Module.HEAPU8.set(StringToUint8Array(value), sdata.data);
this.temp.push(sdata);
Module.setValue(this.tcode + index * SIZEOF_INT, kStr, "i32");
Module.setValue(this.value + index * SIZEOF_TVMVALUE, sdata.data, "*");
},
setBytes : function(index, value) {
CHECK(value instanceof Uint8Array);
var sdata = new CBuffer(value.length);
var sheader = new CBuffer(SIZEOF_POINTER + SIZEOF_SIZE_T);
Module.HEAPU8.set(new Uint8Array(value), sdata.data);
Module.setValue(sheader.data, sdata.data, "*");
Module.setValue(sheader.data + SIZEOF_POINTER, value.length, "i32");
this.temp.push(sdata);
this.temp.push(sheader);
Module.setValue(this.tcode + index * SIZEOF_INT, kBytes, "i32");
Module.setValue(this.value + index * SIZEOF_TVMVALUE, sheader.data, "*");
},
setArguments : function(args) {
for (var i = 0; i < args.length; ++i) {
var v = args[i];
var tp = typeof v;
if (v instanceof NDArray) {
this.setHandle(i, v.handle, kArrayHandle);
} else if (v instanceof TVMConstant) {
var code = getTVMType(v.dtype).code;
if (code == kInt || code == kUInt) {
this.setInt(i, v.value);
} else if (code == kFloat) {
this.setDouble(i, v.value);
} else {
CHECK(code == kHandle);
this.setHandle(i, v.value, kHandle);
}
} else if (tp == "number") {
this.setDouble(i, v);
} else if (tp == "function" && v.hasOwnProperty("_tvm_function")) {
this.setString(i, v._tvm_function.handle, kFuncHandle);
} else if (v === null) {
this.setHandle(i, 0, kNull);
} else if (tp == "string") {
this.setString(i, v);
} else if (v instanceof Uint8Array) {
this.setBytes(i, v);
} else if (v instanceof Function) {
v = convertFunc(v);
this.temp.push(v);
this.setHandle(i, v._tvm_function.handle, kFuncHandle);
} else if (v instanceof TVMModule) {
this.setHandle(i, v.handle, kModuleHandle);
} else {
throwError("Unsupported argument type " + tp);
}
}
}
};
// TVMType
var TYPE_CODE2STR = {
0 : "int",
1 : "uint",
2 : "float",
4 : "handle"
};
TVMType.prototype = {
toString : function() {
var ret = TYPE_CODE2STR[this.code] + this.bits.toString();
if (this.lanes != 1) {
return ret + "x" + this.lanes.toString();
} else {
return ret;
}
}
};
// TVMFunction
TVMFunction.prototype = {
release : function() {
if (this.handle != 0) {
TVM_CALL(TVMFuncFree(this.handle));
this.handle = 0;
}
}
};
// TVMContext
var CTX_MASK2STR = {
1 : "cpu",
2 : "gpu",
4 : "opencl",
7 : "vulkan",
8 : "metal",
9 : "vpi",
11 : "opengl",
};
var CTX_STR2MASK = {
"cpu": 1,
"gpu": 2,
"cuda": 2,
"cl": 4,
"opencl": 4,
"vulkan": 7,
"metal": 8,
"vpi": 9,
"opengl": 11,
};
TVMContext.prototype = {
toString : function() {
return CTX_MASK2STR[this.device_type] + "(" + this.device_id.toString() + ")";
}
};
//-----------------------------------------
// Public Functions
// ----------------------------------------
/**
* Construct a TVMContext given device type and id.
*
* @param {number} device_type, string or int, The device type.
* @param {number} device_id, the device id.
* @return {tvm.TVMContext} The created TVMContext
*/
this.context = function(device_type, device_id) {
if (typeof device_type == "string") {
device_type = CTX_STR2MASK[device_type];
}
return new TVMContext(device_type, device_id);
};
var context = this.context;
/**
* Create empty ndarray with given shape.
*
* @param {Array.<number>} shape The shape of the array.
* @param {string} dtype The data type of the array, optional, default="float32"
* @param {tvm.TVMContext} ctx The context of the array, optional, default=cpu(0).
* @return {tvm.NDArray} The created ndarray.
*/
this.empty = function(shape, dtype, ctx) {
dtype = (typeof dtype !== "undefined") ? dtype: "float32";
ctx = (typeof ctx !== "undefined") ? ctx : context("cpu", 0);
shape = (typeof shape == "number") ? [shape] : shape;
// alloc
var cshape = Module._malloc(SIZEOF_INT64 * shape.length);
var out = new RefTVMValue();
for (var i = 0; i < shape.length; ++i) {
Module.setValue(cshape + i * SIZEOF_INT64, shape[i], "i64");
}
dtype = getTVMType(dtype);
TVM_CALL(TVMArrayAlloc(cshape, shape.length,
dtype.code, dtype.bits, dtype.lanes,
ctx.device_type, ctx.device_id,
out.data));
var out_handle = out.asHandle();
// release
Module._free(cshape);
out.release();
return new NDArray(out_handle);
};
/**
* List all global function names in the TVM runtime.
* @return {Array.<string>} List of global function names.
*/
this.listGlobalFuncNames = function() {
// alloc
var out_size = new RefTVMValue();
var out_array = new RefTVMValue();
TVM_CALL(TVMFuncListGlobalNames(out_size.data, out_array.data));
var length = out_size.asInt();
var base = out_array.asHandle();
var names = [];
for (var i = 0 ; i < length; ++i) {
names.push(
CStringToJS(Module.getValue(base + i * SIZEOF_POINTER, "*")));
}
// release
out_size.release();
out_array.release();
return names;
};
var listGlobalFuncNames = this.listGlobalFuncNames;
/**
* Get a global function from TVM runtime.
*
* @param {string} The name of the function.
* @return {Function} The corresponding function, null if function do not exist
*/
this.getGlobalFunc = function (name) {
// alloc
var out = new RefTVMValue();
TVM_CALL(TVMFuncGetGlobal(name, out.data));
var out_handle = out.asHandle();
// release
out.release();
if (out_handle != 0) {
return makeTVMFunction(out_handle);
} else {
return null;
}
};
var getGlobalFunc = this.getGlobalFunc;
/**
* Register function to be global function in tvm runtime.
* @param {string} name The name of the function.
* @param {Function} f function to be registered.
* @param {boolean} override Whether overwrite function in existing registry.
*/
this.registerFunc = function(name, f, override) {
f = convertFunc(f);
override = (typeof override !== "undefined") ? override: false;
var ioverride = override ? 1 : 0;
TVM_CALL(TVMFuncRegisterGlobal(name, f._tvm_function.handle, ioverride));
};
/**
* Create a typed scalar constant.
* This can be used to pass number as integer types to tvm function.
*
* @param {number} value The value of the data.
* @param {string} dtype The data type.
* @param {tvm.TVMConstant} The created typed scalar.
*/
this.constant = function(value, dtype) {
return new TVMConstant(value, dtype);
};
//-----------------------------------------
// Wrap of TVM Functions.
// ----------------------------------------
var systemFunc = {};
/**
* Get system-wide library module singleton.5A
* System lib is a global module that contains self register functions in startup.
* @return {tvm.TVMModule} The system module singleton.
*/
this.systemLib = function() {
if (typeof systemFunc.fGetSystemLib === "undefined") {
systemFunc.fGetSystemLib = getGlobalFunc("module._GetSystemLib");
}
return systemFunc.fGetSystemLib();
};
this.startRPCServer = function(url, key, counter) {
if (typeof key === "undefined") {
key = "";
}
if (typeof counter === "undefined") {
counter = 1;
}
// Node js, import websocket
var bkey = StringToUint8Array("server:" + key);
bkey = bkey.slice(0, bkey.length - 1);
var server_name = "WebSocketRPCServer[" + key + "]";
var RPC_MAGIC = 0xff271;
function checkEndian() {
var a = new ArrayBuffer(4);
var b = new Uint8Array(a);
var c = new Uint32Array(a);
b[0] = 0x11;
b[1] = 0x22;
b[2] = 0x33;
b[3] = 0x44;
CHECK(c[0] === 0x44332211, "Need little endian to work");
}
checkEndian();
// start rpc
function RPCServer(counter) {
var socket;
if (typeof module !== "undefined" && module.exports) {
// WebSocket for nodejs
const WebSocket = require("ws");
socket = new WebSocket(url);
} else {
socket = new WebSocket(url);
}
var self = this;
socket.binaryType = "arraybuffer";
this.init = true;
this.counter = counter;
if (typeof systemFunc.fcreateServer === "undefined") {
systemFunc.fcreateServer =
getGlobalFunc("rpc._CreateEventDrivenServer");
}
if (systemFunc.fcreateServer == null) {
throwError("RPCServer is not included in runtime");
}
var message_handler = systemFunc.fcreateServer(
function(cbytes) {
if (socket.readyState == 1) {
socket.send(cbytes);
return new TVMConstant(cbytes.length, "int32");
} else {
return new TVMConstant(0, "int32");
}
} , server_name, "%toinit");
function on_open(event) {
var intbuf = new Int32Array(1);
intbuf[0] = RPC_MAGIC;
socket.send(intbuf);
intbuf[0] = bkey.length;
socket.send(intbuf);
socket.send(bkey);
logging(server_name + " connected...");
}
function on_message(event) {
if (self.init) {
var msg = new Uint8Array(event.data);
CHECK(msg.length >= 4, "Need message header to be bigger than 4");
var magic = new Int32Array(event.data)[0];
if (magic == RPC_MAGIC + 1) {
throwError("key: " + key + " has already been used in proxy");
} else if (magic == RPC_MAGIC + 2) {
logging(server_name + ": RPCProxy do not have matching client key " + key);
} else {
CHECK(magic == RPC_MAGIC, url + "is not RPC Proxy");
self.init = false;
}
logging(server_name + "init end...");
if (msg.length > 4) {
if (message_handler(
new Uint8Array(event.data, 4, msg.length -4),
new TVMConstant(3, "int32")) == 0) {
socket.close();
}
}
} else {
if (message_handler(new Uint8Array(event.data),
new TVMConstant(3, "int32")) == 0) {
socket.close();
}
}
}
function on_close(event) {
message_handler.release();
logging(server_name + ": closed finish...");
if (!self.init && self.counter != 0) {
logging(server_name + ":reconnect to serve another request, session left=" + counter);
// start a new server.
new RPCServer(counter - 1);
}
}
socket.addEventListener("open", on_open);
socket.addEventListener("message", on_message);
socket.addEventListener("close", on_close);
}
return new RPCServer(counter);
};
/**
* Load a TVM module from a library file.
* The file must be present in the Emscripten virtual file system.
* For example, you can pass "--preload-file file" or "--preload-file dir/"
* to "emcc" when compiling the TVM library, in order to populate files into
* the file system.
* For more detail, see:
* https://kripken.github.io/emscripten-site/docs/porting/files/packaging_files
* @param {string} file_name Path of the file to be loaded. The path refers
* to the Emscripten virtual file system.
* @param {string} format The format of the file.
* @return {tvm.TVMModule} The loaded module.
*/
this.loadModuleFromFile = function (file_name, format) {
// alloc
var out = new RefTVMValue();
TVM_CALL(TVMModLoadFromFile(file_name, format, out.data));
var out_handle = out.asHandle();
// release
out.release();
if (out_handle != 0) {
return new TVMModule(out_handle);
} else {
return null;
}
};
var loadModuleFromFile = this.loadModuleFromFile;
/**
* Wrapper runtime module.
* Wraps around set_input, load_params, run, and get_output.
*
* @class
* @memberof tvm
*/
function GraphModule(tvm_graph_module, ctx) {
CHECK(tvm_graph_module instanceof TVMModule,
"tvm_graph_module must be TVMModule");
CHECK(ctx instanceof TVMContext, "ctx must be TVMContext");
this.tvm_graph_module = tvm_graph_module;
this.ctx = ctx;
this._set_input = tvm_graph_module.getFunction("set_input");
this._load_params = tvm_graph_module.getFunction("load_params");
this._run = tvm_graph_module.getFunction("run");
this._get_output = tvm_graph_module.getFunction("get_output");
};
GraphModule.prototype = {
/**
* Set input to graph module.
*
* @param {string} key The name of the input.
* @param {NDArray} value The input value.
*/
"set_input" : function(key, value) {
CHECK(typeof key == "string", "key must be string");
CHECK(value instanceof NDArray, "value must be NDArray");
this._set_input(key, value);
},
/**
* Load parameters from serialized byte array of parameter dict.
*
* @param {Uint8Array} params The serialized parameter dict.
*/
"load_params" : function(params) {
CHECK(params instanceof Uint8Array, "params must be Uint8Array");
this._load_params(params);
},
/**
* Load parameters from serialized base64 string of parameter dict.
*
* @param {string} base64_params The serialized parameter dict.
*/
"load_base64_params" : function(base64_params) {
CHECK(typeof base64_params == "string", "base64_params must be string");
var decoded_string = atob(base64_params);
var decoded_u8 = new Uint8Array(decoded_string.length);
for (var i = 0; i < decoded_string.length; i++) {
decoded_u8[i] = decoded_string[i].charCodeAt(0);
}
this.load_params(decoded_u8);
},
/**
* Run forward execution of the graph.
*/
"run" : function() {
this._run();
},
/**
* Get index-th output to out.
*
* @param {NDArray} out The output array container.
* @return {NDArray} The output array container.
*/
"get_output" : function(index, out) {
CHECK(typeof index == "number", "index must be number");
CHECK(out instanceof NDArray, "out must be NDArray");
this._get_output(new TVMConstant(index, "int32"), out);
return out;
}
};
/**
* Create a runtime executor module given a graph and a module.
* @param {string} graph_json_str The Json string of the graph.
* @param {TVMModule} libmod The TVM module.
* @param {TVMContext} ctx The context to deploy the module.
* @return {GraphModule} Runtime graph module for executing the graph.
*/
this.createGraphRuntime = function(graph_json_str, libmod, ctx) {
CHECK(typeof graph_json_str == "string", "graph_json_str must be string");
CHECK(libmod instanceof TVMModule, "libmod must be TVMModule");
CHECK(ctx instanceof TVMContext, "ctx must be TVMContext");
var fcreate = getGlobalFunc("tvm.graph_runtime.create");
CHECK(fcreate != null, "Cannot find tvm.graph_runtime.create");
var tvm_graph_module = fcreate(graph_json_str, libmod,
new TVMConstant(ctx.device_type, "int32"),
new TVMConstant(ctx.device_id, "int32"));
return new GraphModule(tvm_graph_module, ctx);
};
//-----------------------------------------
// Class defintions
// ----------------------------------------
// NDArray.
NDArray.prototype = {
/**
* Finalizer: resources from the object.
*/
release : function() {
if (this.handle != 0) {
TVM_CALL(TVMArrayFree(this.handle));
this.handle = 0;
}
},
/**
* Copy data from another NDArray or javascript array.
* The number of elements must match.
*
* @param {Array} data The source data array.
*/
copyFrom : function(data) {
if (data instanceof NDArray) {
TVM_CALL(TVMArrayCopyFromTo(data.handle, this.handle));
} else {
var size = this.shape.reduce(function(a, b) { return a * b; }, 1);
if (data.length != size) {
throwError("data size and shape mismatch data.length" + data.length + " vs " + size);
}
if (this.dtype == "float32") {
data = Float32Array.from(data);
} else if (this.dtype == "float64") {
data = Float64Array.from(data);
} else if (this.dtype == "int32") {
data = Int32Array.from(data);
} else if (this.dtype == "int8") {
data = Int8Array.from(data);
} else if (this.dtype == "uint8") {
data = Uint8Array.from(data);
} else {
throwError("Unsupported data type " + this.dtype);
}
return this.copyFromRawBytes(new Uint8Array(data.buffer));
}
},
/**
* Copy data from raw bytes.
* @param {Uint8Array} data Uint8Array of bytes.
*/
copyFromRawBytes : function(data) {
var size = this.shape.reduce(function(a, b) { return a * b; }, 1);
var dtype = getTVMType(this.dtype);
var nbytes = this.BYTES_PER_ELEMENT * size;
CHECK(data instanceof Uint8Array);
CHECK(data.length == nbytes,
"Data length and bytes do not match " + data.length +
" vs " + nbytes);
var temp = Module._malloc(nbytes);
Module.HEAPU8.set(data, temp);
TVM_CALL(TVMArrayCopyFromBytes(this.handle, temp, nbytes));
Module._free(temp);
return this;
},
/**
* Return a copied Uint8Array of the raw bytes in the NDArray.
* @return {Uint8Array} The created array.
*/
asRawBytes : function() {
var size = this.shape.reduce(function(a, b) { return a * b; }, 1);
var nbytes = this.BYTES_PER_ELEMENT * size;
var temp = Module._malloc(nbytes);
TVM_CALL(TVMArrayCopyToBytes(this.handle, temp, nbytes));
var ret = new Uint8Array(new ArrayBuffer(nbytes));
ret.set(new Uint8Array(Module.HEAPU8.buffer, temp, nbytes));
Module._free(temp);
return ret;
},
/**
* Return Array data content as javascript typed array.
* @return {TypedArray} The created array.
*/
asArray : function() {
if (this.dtype == "float32") {
return new Float32Array(this.asRawBytes().buffer);
} else if (this.dtype == "float64") {
return new Float64Array(this.asRawBytes().buffer);
} else if (this.dtype == "int32") {
return new Int32Array(this.asRawBytes().buffer);
} else if (this.dtype == "int8") {
return new Int8Array(this.asRawBytes().buffer);
} else if (this.dtype == "uint8") {
return new Uint8Array(this.asRawBytes().buffer);
} else {
throwError("Unsupported data type " + this.dtype);
}
}
};
TVMModule.prototype = {
/**
* Finalizer: resources from the object.
*/
release : function() {
if (this.handle != 0) {
TVM_CALL(TVMModFree(this.handle));
this.handle = 0;
}
},
/**
* Get function from the module.
* @param {string} name The name of the function.
* @return {Function} The correspondin function.
*/
getFunction : function(name) {
// alloc
var out = new RefTVMValue();
TVM_CALL(TVMModGetFunction(this.handle, name, 0, out.data));
var out_handle = out.asHandle();
// release
out.release();
if (out_handle == 0) {
throwError("Module has no function " + name);
}
return makeTVMFunction(out_handle);
},
/**
* Add module to the import list of current one.
* @param {tvm.TVMModule} mod The other module to be imported.
*/
import_module : function(mod) {
CHECK(mod instanceof TVMModule, "mod must be instance of TVMModule");
TVM_CALL(TVMModImport(this.handle, mod.handle));
}
};
//-----------------------------------------
// Static variables.
// ----------------------------------------
/** Float32 type */
this.float32 = "float32";
/** Int32 type */
this.int32 = "int32";
}
/**
* Create a TVM runtime given emscripten module.
* @property {string} create
* @memberof tvm_runtime
* @param Module The emscripten module.
* @return {tvm.TVMRuntime} The created TVM runtime.
*/
this.create = function(Module) {
var tvm = {};
tvm.Module = Module;
if (typeof Module.addFunction !== "undefined") {
tvm.Runtime = Module;
} else {
tvm.Runtime = Module.Runtime;
}
TVMRuntime.apply(tvm);
return tvm;
};
}).apply(tvm_runtime);
// export things in node
if (typeof module !== "undefined" && module.exports) {
module.exports = tvm_runtime;
}