Skip to content

Instantly share code, notes, and snippets.

@kripken
Created July 30, 2013 22:20
Show Gist options
  • Save kripken/6117576 to your computer and use it in GitHub Desktop.
Save kripken/6117576 to your computer and use it in GitHub Desktop.
embind asm
diff --git a/emcc b/emcc
index 91b109b..e40bfef 100755
--- a/emcc
+++ b/emcc
@@ -1079,14 +1079,17 @@ try:
assert not memory_init_file, 'memory init file is not supported with module linking'
shared.Settings.LINKABLE = 1 # TODO: add FORCE_DCE option for the brave people that do want to dce here and in side modules
debug_level = max(debug_level, 2)
if shared.Settings.DLOPEN_SUPPORT:
shared.Settings.LINKABLE = 1
+ if bind:
+ shared.Settings.EMBIND = 1
+
## Compile source code to bitcode
logging.debug('compiling to bitcode')
temp_files = []
# First, generate LLVM bitcode. For each input file, we get base.o with bitcode
diff --git a/src/embind/embind.js b/src/embind/embind.js
index f0cd0c7..18bae90 100644
--- a/src/embind/embind.js
+++ b/src/embind/embind.js
@@ -510,14 +510,22 @@ function new_(constructor, argumentList) {
dummy.prototype = constructor.prototype;
var obj = new dummy;
var r = constructor.apply(obj, argumentList);
return (r instanceof Object) ? r : obj;
}
+function craftFunctionTableCaller(fun) {
+ if (!Runtime.ASM_JS) return FUNCTION_TABLE[fun];
+ return function() {
+ var sig = 'f' + Array.prototype.map.call(arguments, function() { return 'f' }).join('');
+ return Runtime.dynCall(sig, fun, arguments);
+ };
+}
+
// The path to interop from JS code to C++ code:
// (hand-written JS code) -> (autogenerated JS invoker) -> (template-generated C++ invoker) -> (target C++ function)
// craftInvokerFunction generates the JS invoker function for each function exposed to JS through embind.
function craftInvokerFunction(humanName, argTypes, classType, cppInvokerFunc, cppTargetFunc) {
// humanName: a human-readable string name for the function to be generated.
// argTypes: An array that contains the embind type objects for all types in the function signature.
// argTypes[0] is the type object for the function return value.
@@ -530,17 +538,17 @@ function craftInvokerFunction(humanName, argTypes, classType, cppInvokerFunc, cp
if (argCount < 2) {
throwBindingError("argTypes array size mismatch! Must at least get return value and 'this' types!");
}
var isClassMethodFunc = (argTypes[1] !== null && classType !== null);
- if (!isClassMethodFunc && !FUNCTION_TABLE[cppTargetFunc]) {
- throwBindingError('Global function '+humanName+' is not defined!');
- }
+ //if (!isClassMethodFunc && !FUNCTION_TABLE[cppTargetFunc]) {
+ // throwBindingError('Global function '+humanName+' is not defined!');
+ //}
// Free functions with signature "void function()" do not need an invoker that marshalls between wire types.
// TODO: This omits argument count check - enable only at -O3 or similar.
// if (ENABLE_UNSAFE_OPTS && argCount == 2 && argTypes[0].name == "void" && !isClassMethodFunc) {
// return FUNCTION_TABLE[fn];
// }
@@ -619,15 +627,15 @@ function craftInvokerFunction(humanName, argTypes, classType, cppInvokerFunc, cp
var invokerFunction = new_(Function, args1).apply(null, args2);
return invokerFunction;
}
function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker, fn) {
var argTypes = heap32VectorToArray(argCount, rawArgTypesAddr);
name = readLatin1String(name);
- rawInvoker = FUNCTION_TABLE[rawInvoker];
+ rawInvoker = craftFunctionTableCaller(rawInvoker);
exposePublicSymbol(name, function() {
throwUnboundTypeError('Cannot call ' + name + ' due to unbound types', argTypes);
}, argCount - 1);
whenDependentTypesAreResolved([], argTypes, function(argTypes) {
var invokerArgsArray = [argTypes[0] /* return value */, null /* no class 'this'*/].concat(argTypes.slice(1) /* actual params */);
@@ -637,16 +645,16 @@ function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker,
}
var tupleRegistrations = {};
function __embind_register_value_array(rawType, name, rawConstructor, rawDestructor) {
tupleRegistrations[rawType] = {
name: readLatin1String(name),
- rawConstructor: FUNCTION_TABLE[rawConstructor],
- rawDestructor: FUNCTION_TABLE[rawDestructor],
+ rawConstructor: craftFunctionTableCaller(rawConstructor),
+ rawDestructor: craftFunctionTableCaller(rawDestructor),
elements: [],
};
}
function __embind_register_value_array_element(
rawTupleType,
getterReturnType,
@@ -654,18 +662,18 @@ function __embind_register_value_array_element(
getterContext,
setterArgumentType,
setter,
setterContext
) {
tupleRegistrations[rawTupleType].elements.push({
getterReturnType: getterReturnType,
- getter: FUNCTION_TABLE[getter],
+ getter: craftFunctionTableCaller(getter),
getterContext: getterContext,
setterArgumentType: setterArgumentType,
- setter: FUNCTION_TABLE[setter],
+ setter: craftFunctionTableCaller(setter),
setterContext: setterContext,
});
}
function __embind_finalize_value_array(rawTupleType) {
var reg = tupleRegistrations[rawTupleType];
delete tupleRegistrations[rawTupleType];
@@ -729,16 +737,16 @@ function __embind_register_value_object(
rawType,
name,
rawConstructor,
rawDestructor
) {
structRegistrations[rawType] = {
name: readLatin1String(name),
- rawConstructor: FUNCTION_TABLE[rawConstructor],
- rawDestructor: FUNCTION_TABLE[rawDestructor],
+ rawConstructor: craftFunctionTableCaller(rawConstructor),
+ rawDestructor: craftFunctionTableCaller(rawDestructor),
fields: [],
};
}
function __embind_register_value_object_field(
structType,
fieldName,
@@ -748,18 +756,18 @@ function __embind_register_value_object_field(
setterArgumentType,
setter,
setterContext
) {
structRegistrations[structType].fields.push({
fieldName: readLatin1String(fieldName),
getterReturnType: getterReturnType,
- getter: FUNCTION_TABLE[getter],
+ getter: craftFunctionTableCaller(getter),
getterContext: getterContext,
setterArgumentType: setterArgumentType,
- setter: FUNCTION_TABLE[setter],
+ setter: craftFunctionTableCaller(setter),
setterContext: setterContext,
});
}
function __embind_finalize_value_object(structType) {
var reg = structRegistrations[structType];
delete structRegistrations[structType];
@@ -1232,18 +1240,18 @@ function __embind_register_class(
getActualType,
upcast,
downcast,
name,
rawDestructor
) {
name = readLatin1String(name);
- rawDestructor = FUNCTION_TABLE[rawDestructor];
- getActualType = FUNCTION_TABLE[getActualType];
- upcast = FUNCTION_TABLE[upcast];
- downcast = FUNCTION_TABLE[downcast];
+ rawDestructor = craftFunctionTableCaller(rawDestructor);
+ getActualType = craftFunctionTableCaller(getActualType);
+ upcast = craftFunctionTableCaller(upcast);
+ downcast = craftFunctionTableCaller(downcast);
var legalFunctionName = makeLegalFunctionName(name);
exposePublicSymbol(legalFunctionName, function() {
// this code cannot run if baseClassRawType is zero
throwUnboundTypeError('Cannot construct ' + name + ' due to unbound types', [baseClassRawType]);
});
@@ -1329,15 +1337,15 @@ function __embind_register_class_constructor(
rawClassType,
argCount,
rawArgTypesAddr,
invoker,
rawConstructor
) {
var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr);
- invoker = FUNCTION_TABLE[invoker];
+ invoker = craftFunctionTableCaller(invoker);
whenDependentTypesAreResolved([], [rawClassType], function(classType) {
classType = classType[0];
var humanName = 'constructor ' + classType.name;
if (undefined === classType.registeredClass.constructor_body) {
classType.registeredClass.constructor_body = [];
@@ -1419,15 +1427,15 @@ function __embind_register_class_function(
argCount,
rawArgTypesAddr, // [ReturnType, ThisType, Args...]
rawInvoker,
context
) {
var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr);
methodName = readLatin1String(methodName);
- rawInvoker = FUNCTION_TABLE[rawInvoker];
+ rawInvoker = craftFunctionTableCaller(rawInvoker);
whenDependentTypesAreResolved([], [rawClassType], function(classType) {
classType = classType[0];
var humanName = classType.name + '.' + methodName;
var unboundTypesHandler = function() {
throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes);
@@ -1470,15 +1478,15 @@ function __embind_register_class_class_function(
argCount,
rawArgTypesAddr,
rawInvoker,
fn
) {
var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr);
methodName = readLatin1String(methodName);
- rawInvoker = FUNCTION_TABLE[rawInvoker];
+ rawInvoker = craftFunctionTableCaller(rawInvoker);
whenDependentTypesAreResolved([], [rawClassType], function(classType) {
classType = classType[0];
var humanName = classType.name + '.' + methodName;
var unboundTypesHandler = function() {
throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes);
};
@@ -1517,15 +1525,15 @@ function __embind_register_class_property(
getter,
getterContext,
setterArgumentType,
setter,
setterContext
) {
fieldName = readLatin1String(fieldName);
- getter = FUNCTION_TABLE[getter];
+ getter = craftFunctionTableCaller(getter);
whenDependentTypesAreResolved([], [classType], function(classType) {
classType = classType[0];
var humanName = classType.name + '.' + fieldName;
var desc = {
get: function() {
throwUnboundTypeError('Cannot access ' + humanName + ' due to unbound types', [getterReturnType, setterArgumentType]);
@@ -1555,15 +1563,15 @@ function __embind_register_class_property(
var ptr = validateThis(this, classType, humanName + ' getter');
return getterReturnType['fromWireType'](getter(getterContext, ptr));
},
enumerable: true
};
if (setter) {
- setter = FUNCTION_TABLE[setter];
+ setter = craftFunctionTableCaller(setter);
var setterArgumentType = types[1];
desc.set = function(v) {
var ptr = validateThis(this, classType, humanName + ' setter');
var destructors = [];
setter(setterContext, ptr, setterArgumentType['toWireType'](destructors, v));
runDestructors(destructors);
};
@@ -1596,18 +1604,18 @@ function __embind_register_smart_ptr(
sharingPolicy,
rawGetPointee,
rawConstructor,
rawShare,
rawDestructor
) {
name = readLatin1String(name);
- rawGetPointee = FUNCTION_TABLE[rawGetPointee];
- rawConstructor = FUNCTION_TABLE[rawConstructor];
- rawShare = FUNCTION_TABLE[rawShare];
- rawDestructor = FUNCTION_TABLE[rawDestructor];
+ rawGetPointee = craftFunctionTableCaller(rawGetPointee);
+ rawConstructor = craftFunctionTableCaller(rawConstructor);
+ rawShare = craftFunctionTableCaller(rawShare);
+ rawDestructor = craftFunctionTableCaller(rawDestructor);
whenDependentTypesAreResolved([rawType], [rawPointeeType], function(pointeeType) {
pointeeType = pointeeType[0];
var registeredPointer = new RegisteredPointer(
name,
pointeeType.registeredClass,
diff --git a/src/modules.js b/src/modules.js
index 9f41923..e6f5007 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -319,14 +319,24 @@ var Functions = {
if (!tables[sig]) tables[sig] = zeros(firstTableIndex);
var index = this.indexedFunctions[ident];
for (var i = tables[sig].length; i < index; i++) {
tables[sig][i] = 0; // keep flat
}
tables[sig][index] = ident;
}
+ if (ASM_JS && EMBIND) {
+ // ensure f..f functions for each size, so embind can always call f..f sigs
+ keys(tables).forEach(function(t) {
+ var fsig = t.replace(/i/g, 'f').replace('v', 'f');
+ if (!(fsig in tables)) {
+printErr('add ' + [t, fsig]);
+ tables[fsig] = zeros(firstTableIndex);
+ }
+ });
+ }
var generated = false;
var wrapped = {};
var maxTable = 0;
for (var t in tables) {
if (t == 'pre') continue;
generated = true;
var table = tables[t];
diff --git a/src/runtime.js b/src/runtime.js
index e07d505..4a8a130 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -179,14 +179,16 @@ var Runtime = {
dedup: dedup,
set: set,
STACK_ALIGN: {{{ STACK_ALIGN }}},
+ ASM_JS: {{{ ASM_JS }}},
+
// type can be a native type or a struct (or null, for structs we only look at size here)
getAlignSize: function(type, size, vararg) {
// we align i64s and doubles on 64-bit boundaries, unlike x86
#if TARGET_LE32
if (type == 'i64' || type == 'double' || vararg) return 8;
if (!type) return Math.min(size, 8); // align structures internally to 64 bits
#endif
@@ -372,20 +374,21 @@ var Runtime = {
}
},
funcWrappers: {},
getFuncWrapper: function(func, sig) {
assert(sig);
- if (!Runtime.funcWrappers[func]) {
- Runtime.funcWrappers[func] = function() {
+ var id = func + '|' + sig;
+ if (!Runtime.funcWrappers[id]) {
+ Runtime.funcWrappers[id] = function() {
return Runtime.dynCall(sig, func, arguments);
};
}
- return Runtime.funcWrappers[func];
+ return Runtime.funcWrappers[id];
},
// Returns a processor of UTF.
// processCChar() receives characters from a C-like UTF representation and returns JS string fragments.
// processJSString() receives a JS string and returns a C-like UTF representation in an array
UTF8Processor: function() {
var buffer = [];
diff --git a/src/settings.js b/src/settings.js
index ed2afdb..297a2ff 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -259,14 +259,15 @@ var EXPORTED_FUNCTIONS = ['_main', '_malloc'];
// through LLVM dead code elimination, and also made accessible outside of
// the generated code even after running closure compiler (on "Module").
// Note the necessary prefix of "_".
var EXPORT_ALL = 0; // If true, we export all the symbols. Note that this does *not* affect LLVM, so it can
// still eliminate functions as dead. This just exports them on the Module object.
var EXPORT_BINDINGS = 0; // Export all bindings generator functions (prefixed with emscripten_bind_). This
// is necessary to use the bindings generator with asm.js
+var EMBIND = 0; // Whether the embind bindings approach is used
// JS library functions (C functions implemented in JS)
// that we include by default. If you want to make sure
// something is included by the JS compiler, add it here.
// For example, if you do not use some emscripten_*
// C API call from C, but you want to call it from JS,
// add it here (and in EXPORTED FUNCTIONS with prefix
diff --git a/tests/runner.py b/tests/runner.py
index 47e18ed..976772c 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -9328,14 +9328,34 @@ def process(filename):
printf("abs(-11): %d\n", Math["abs"](-11).as<int>());
return 0;
}
'''
self.do_run(src, 'abs(-10): 10\nabs(-11): 11');
+ def test_embind_2(self):
+ if self.emcc_args is None: return self.skip('requires emcc')
+ Building.COMPILER_TEST_OPTS += ['--bind', '--post-js', 'post.js']
+ open('post.js', 'w').write('''
+ Module.print('lerp ' + Module.lerp(1, 2, 0.66) + '.');
+ ''')
+ src = r'''
+ #include <stdio.h>
+ #include <SDL/SDL.h>
+ #include <emscripten/bind.h>
+ using namespace emscripten;
+ float lerp(float a, float b, float t) {
+ return (1 - t) * a + t * b;
+ }
+ EMSCRIPTEN_BINDINGS(my_module) {
+ function("lerp", &lerp);
+ }
+ '''
+ self.do_run(src, 'lerp 1.66');
+
def test_scriptaclass(self):
if self.emcc_args is None: return self.skip('requires emcc')
Settings.EXPORT_BINDINGS = 1
header_filename = os.path.join(self.get_dir(), 'header.h')
header = '''
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment