diff options
-rw-r--r-- | src/analyzer.js | 7 | ||||
-rw-r--r-- | src/embind/embind.js | 26 | ||||
-rw-r--r-- | src/embind/emval.js | 15 | ||||
-rw-r--r-- | src/library.js | 32 | ||||
-rw-r--r-- | src/library_sdl.js | 5 | ||||
-rw-r--r-- | system/include/emscripten/bind.h | 56 | ||||
-rw-r--r-- | tests/cases/entry3.ll | 36 | ||||
-rw-r--r-- | tests/cases/entry3.txt | 2 | ||||
-rw-r--r-- | tests/embind/embind.test.js | 19 | ||||
-rw-r--r-- | tests/embind/embind_test.cpp | 24 | ||||
-rwxr-xr-x | tests/runner.py | 63 | ||||
-rw-r--r-- | tools/find_bigfuncs.py | 13 | ||||
-rw-r--r-- | tools/js-optimizer.js | 200 | ||||
-rw-r--r-- | tools/test-js-optimizer-asm-outline1-output.js | 179 | ||||
-rw-r--r-- | tools/test-js-optimizer-asm-outline1.js | 2 | ||||
-rw-r--r-- | tools/test-js-optimizer-asm-outline2-output.js | 574 | ||||
-rw-r--r-- | tools/test-js-optimizer-asm-outline2.js | 2 |
17 files changed, 739 insertions, 516 deletions
diff --git a/src/analyzer.js b/src/analyzer.js index de9a7940..1d32d7fc 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -1433,15 +1433,14 @@ function analyzer(data, sidePass) { func.labelsDict = {}; func.labelIds = {}; func.labelIdsInverse = {}; - func.labelIds[toNiceIdent('%0')] = 1; - func.labelIdsInverse[0] = toNiceIdent('%0'); - func.labelIdCounter = 2; + func.labelIdCounter = 1; func.labels.forEach(function(label) { if (!(label.ident in func.labelIds)) { func.labelIds[label.ident] = func.labelIdCounter++; func.labelIdsInverse[func.labelIdCounter-1] = label.ident; } }); + var entryIdent = func.labels[0].ident; // Minify label ids to numeric ids. func.labels.forEach(function(label) { @@ -1478,7 +1477,7 @@ function analyzer(data, sidePass) { function getActualLabelId(labelId) { if (func.labelsDict[labelId]) return labelId; // If not present, it must be a surprisingly-named entry (or undefined behavior, in which case, still ok to use the entry) - labelId = func.labelIds[ENTRY_IDENT]; + labelId = func.labelIds[entryIdent]; assert(func.labelsDict[labelId]); return labelId; } diff --git a/src/embind/embind.js b/src/embind/embind.js index 91386c69..f0cd0c74 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -5,9 +5,9 @@ /*global __emval_register, _emval_handle_array, __emval_decref*/ /*global ___getTypeName*/ /*jslint sub:true*/ /* The symbols 'fromWireType' and 'toWireType' must be accessed via array notation to be closure-safe since craftInvokerFunction crafts functions as strings that can't be closured. */ -var InternalError = Module.InternalError = extendError(Error, 'InternalError'); -var BindingError = Module.BindingError = extendError(Error, 'BindingError'); -var UnboundTypeError = Module.UnboundTypeError = extendError(BindingError, 'UnboundTypeError'); +var InternalError = Module['InternalError'] = extendError(Error, 'InternalError'); +var BindingError = Module['BindingError'] = extendError(Error, 'BindingError'); +var UnboundTypeError = Module['UnboundTypeError'] = extendError(BindingError, 'UnboundTypeError'); function throwInternalError(message) { throw new InternalError(message); @@ -638,7 +638,7 @@ function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker, var tupleRegistrations = {}; -function __embind_register_tuple(rawType, name, rawConstructor, rawDestructor) { +function __embind_register_value_array(rawType, name, rawConstructor, rawDestructor) { tupleRegistrations[rawType] = { name: readLatin1String(name), rawConstructor: FUNCTION_TABLE[rawConstructor], @@ -647,7 +647,7 @@ function __embind_register_tuple(rawType, name, rawConstructor, rawDestructor) { }; } -function __embind_register_tuple_element( +function __embind_register_value_array_element( rawTupleType, getterReturnType, getter, @@ -666,7 +666,7 @@ function __embind_register_tuple_element( }); } -function __embind_finalize_tuple(rawTupleType) { +function __embind_finalize_value_array(rawTupleType) { var reg = tupleRegistrations[rawTupleType]; delete tupleRegistrations[rawTupleType]; var elements = reg.elements; @@ -725,7 +725,7 @@ function __embind_finalize_tuple(rawTupleType) { var structRegistrations = {}; -function __embind_register_struct( +function __embind_register_value_object( rawType, name, rawConstructor, @@ -739,7 +739,7 @@ function __embind_register_struct( }; } -function __embind_register_struct_field( +function __embind_register_value_object_field( structType, fieldName, getterReturnType, @@ -760,7 +760,7 @@ function __embind_register_struct_field( }); } -function __embind_finalize_struct(structType) { +function __embind_finalize_value_object(structType) { var reg = structRegistrations[structType]; delete structRegistrations[structType]; @@ -879,11 +879,11 @@ var genericPointerToWireType = function(destructors, handle) { if (handle.$$.smartPtrType === this) { ptr = handle.$$.smartPtr; } else { - var clonedHandle = handle.clone(); + var clonedHandle = handle['clone'](); ptr = this.rawShare( ptr, __emval_register(function() { - clonedHandle.delete(); + clonedHandle['delete'](); }) ); if (destructors !== null) { @@ -1088,7 +1088,7 @@ function getInstanceTypeName(handle) { return handle.$$.ptrType.registeredClass.name; } -ClassHandle.prototype.isAliasOf = function(other) { +ClassHandle.prototype['isAliasOf'] = function(other) { if (!(this instanceof ClassHandle)) { return false; } @@ -1118,7 +1118,7 @@ function throwInstanceAlreadyDeleted(obj) { throwBindingError(getInstanceTypeName(obj) + ' instance already deleted'); } -ClassHandle.prototype.clone = function() { +ClassHandle.prototype['clone'] = function() { if (!this.$$.ptr) { throwInstanceAlreadyDeleted(this); } diff --git a/src/embind/emval.js b/src/embind/emval.js index 77270597..0d075188 100644 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -4,6 +4,7 @@ /*global createNamedFunction*/ /*global readLatin1String, writeStringToMemory*/ /*global requireRegisteredType, throwBindingError*/ +/*jslint sub:true*/ /* The symbols 'fromWireType' and 'toWireType' must be accessed via array notation to be closure-safe since craftInvokerFunction crafts functions as strings that can't be closured. */ var Module = Module || {}; @@ -100,7 +101,7 @@ function __emval_new_cstring(v) { function __emval_take_value(type, v) { type = requireRegisteredType(type, '_emval_take_value'); - v = type.fromWireType(v); + v = type['fromWireType'](v); return __emval_register(v); } @@ -203,7 +204,7 @@ function __emval_as(handle, returnType) { returnType = requireRegisteredType(returnType, 'emval::as'); var destructors = []; // caller owns destructing - return returnType.toWireType(destructors, _emval_handle_array[handle].value); + return returnType['toWireType'](destructors, _emval_handle_array[handle].value); } function parseParameters(argCount, argTypes, argWireTypes) { @@ -212,7 +213,7 @@ function parseParameters(argCount, argTypes, argWireTypes) { var argType = requireRegisteredType( HEAP32[(argTypes >> 2) + i], "parameter " + i); - a[i] = argType.fromWireType(argWireTypes[i]); + a[i] = argType['fromWireType'](argWireTypes[i]); } return a; } @@ -223,7 +224,7 @@ function __emval_call(handle, argCount, argTypes) { var args = new Array(argCount); for (var i = 0; i < argCount; ++i) { - args[i] = types[i].fromWireType(arguments[3 + i]); + args[i] = types[i]['fromWireType'](arguments[3 + i]); } var fn = _emval_handle_array[handle].value; @@ -247,8 +248,8 @@ function __emval_get_method_caller(argCount, argTypes) { var retType = types[0]; var signatureName = retType.name + "_$" + types.slice(1).map(function (t) { return t.name; }).join("_") + "$"; - var args1 = ["Runtime", "createNamedFunction", "requireHandle", "getStringOrSymbol", "_emval_handle_array", "retType"]; - var args2 = [Runtime, createNamedFunction, requireHandle, getStringOrSymbol, _emval_handle_array, retType]; + var args1 = ["addFunction", "createNamedFunction", "requireHandle", "getStringOrSymbol", "_emval_handle_array", "retType"]; + var args2 = [Runtime.addFunction, createNamedFunction, requireHandle, getStringOrSymbol, _emval_handle_array, retType]; var argsList = ""; // 'arg0, arg1, arg2, ... , argN' var argsListWired = ""; // 'arg0Wired, ..., argNWired' @@ -260,7 +261,7 @@ function __emval_get_method_caller(argCount, argTypes) { } var invokerFnBody = - "return Runtime.addFunction(createNamedFunction('" + signatureName + "', function (handle, name" + argsListWired + ") {\n" + + "return addFunction(createNamedFunction('" + signatureName + "', function (handle, name" + argsListWired + ") {\n" + "requireHandle(handle);\n" + "name = getStringOrSymbol(name);\n"; diff --git a/src/library.js b/src/library.js index fbfdd9da..17018f5d 100644 --- a/src/library.js +++ b/src/library.js @@ -2565,15 +2565,27 @@ LibraryManager.library = { continue; } - // TODO: Support strings like "%5c" etc. - if (format[formatIndex] === '%' && format[formatIndex+1] == 'c') { - var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}}; - argIndex += Runtime.getAlignSize('void*', null, true); - fields++; - next = get(); - {{{ makeSetValue('argPtr', 0, 'next', 'i8') }}} - formatIndex += 2; - continue; + if (format[formatIndex] === '%') { + var nextC = format.indexOf('c', formatIndex+1); + if (nextC > 0) { + var maxx = 1; + if (nextC > formatIndex+1) { + var sub = format.substring(formatIndex+1, nextC) + maxx = parseInt(sub); + if (maxx != sub) maxx = 0; + } + if (maxx) { + var argPtr = HEAP32[(varargs + argIndex)>>2]; + argIndex += Runtime.getAlignSize('void*', null, true); + fields++; + for (var i = 0; i < maxx; i++) { + next = get(); + {{{ makeSetValue('argPtr++', 0, 'next', 'i8') }}}; + } + formatIndex += nextC - formatIndex + 1; + continue; + } + } } // remove whitespace @@ -5961,7 +5973,7 @@ LibraryManager.library = { return 1; } else { var lib_record = DLFCN_DATA.loadedLibs[handle]; - if (lib_record.refcount-- == 0) { + if (--lib_record.refcount == 0) { delete DLFCN_DATA.loadedLibNames[lib_record.name]; delete DLFCN_DATA.loadedLibs[handle]; } diff --git a/src/library_sdl.js b/src/library_sdl.js index d14e433b..9287bd3e 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -939,7 +939,10 @@ var LibrarySDL = { // TODO }, - SDL_GetKeyboardState: function() { + SDL_GetKeyboardState: function(numKeys) { + if (numKeys) { + {{{ makeSetValue('numKeys', 0, 0x10000, 'i32') }}}; + } return SDL.keyboardState; }, diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index cd465e45..403d8084 100644 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -70,13 +70,13 @@ namespace emscripten { GenericFunction invoker, GenericFunction function); - void _embind_register_tuple( + void _embind_register_value_array( TYPEID tupleType, const char* name, GenericFunction constructor, GenericFunction destructor); - void _embind_register_tuple_element( + void _embind_register_value_array_element( TYPEID tupleType, TYPEID getterReturnType, GenericFunction getter, @@ -85,15 +85,15 @@ namespace emscripten { GenericFunction setter, void* setterContext); - void _embind_finalize_tuple(TYPEID tupleType); + void _embind_finalize_value_array(TYPEID tupleType); - void _embind_register_struct( + void _embind_register_value_object( TYPEID structType, const char* fieldName, GenericFunction constructor, GenericFunction destructor); - void _embind_register_struct_field( + void _embind_register_value_object_field( TYPEID structType, const char* fieldName, TYPEID getterReturnType, @@ -103,7 +103,7 @@ namespace emscripten { GenericFunction setter, void* setterContext); - void _embind_finalize_struct(TYPEID structType); + void _embind_finalize_value_object(TYPEID structType); void _embind_register_smart_ptr( TYPEID pointerType, @@ -531,26 +531,26 @@ namespace emscripten { //////////////////////////////////////////////////////////////////////////////// template<typename ClassType> - class value_tuple : public internal::noncopyable { + class value_array : public internal::noncopyable { public: - value_tuple(const char* name) { + value_array(const char* name) { using namespace internal; - _embind_register_tuple( + _embind_register_value_array( TypeID<ClassType>::get(), name, reinterpret_cast<GenericFunction>(&raw_constructor<ClassType>), reinterpret_cast<GenericFunction>(&raw_destructor<ClassType>)); } - ~value_tuple() { + ~value_array() { using namespace internal; - _embind_finalize_tuple(TypeID<ClassType>::get()); + _embind_finalize_value_array(TypeID<ClassType>::get()); } template<typename InstanceType, typename ElementType> - value_tuple& element(ElementType InstanceType::*field) { + value_array& element(ElementType InstanceType::*field) { using namespace internal; - _embind_register_tuple_element( + _embind_register_value_array_element( TypeID<ClassType>::get(), TypeID<ElementType>::get(), reinterpret_cast<GenericFunction>( @@ -566,11 +566,11 @@ namespace emscripten { } template<typename Getter, typename Setter> - value_tuple& element(Getter getter, Setter setter) { + value_array& element(Getter getter, Setter setter) { using namespace internal; typedef GetterPolicy<Getter> GP; typedef SetterPolicy<Setter> SP; - _embind_register_tuple_element( + _embind_register_value_array_element( TypeID<ClassType>::get(), TypeID<typename GP::ReturnType>::get(), reinterpret_cast<GenericFunction>(&GP::template get<ClassType>), @@ -582,11 +582,11 @@ namespace emscripten { } template<int Index> - value_tuple& element(index<Index>) { + value_array& element(index<Index>) { using namespace internal; ClassType* null = 0; typedef typename std::remove_reference<decltype((*null)[Index])>::type ElementType; - _embind_register_tuple_element( + _embind_register_value_array_element( TypeID<ClassType>::get(), TypeID<ElementType>::get(), reinterpret_cast<GenericFunction>(&internal::get_by_index<ClassType, ElementType>), @@ -603,25 +603,25 @@ namespace emscripten { //////////////////////////////////////////////////////////////////////////////// template<typename ClassType> - class value_struct : public internal::noncopyable { + class value_object : public internal::noncopyable { public: - value_struct(const char* name) { + value_object(const char* name) { using namespace internal; - _embind_register_struct( + _embind_register_value_object( TypeID<ClassType>::get(), name, reinterpret_cast<GenericFunction>(&raw_constructor<ClassType>), reinterpret_cast<GenericFunction>(&raw_destructor<ClassType>)); } - ~value_struct() { - _embind_finalize_struct(internal::TypeID<ClassType>::get()); + ~value_object() { + _embind_finalize_value_object(internal::TypeID<ClassType>::get()); } template<typename InstanceType, typename FieldType> - value_struct& field(const char* fieldName, FieldType InstanceType::*field) { + value_object& field(const char* fieldName, FieldType InstanceType::*field) { using namespace internal; - _embind_register_struct_field( + _embind_register_value_object_field( TypeID<ClassType>::get(), fieldName, TypeID<FieldType>::get(), @@ -638,7 +638,7 @@ namespace emscripten { } template<typename Getter, typename Setter> - value_struct& field( + value_object& field( const char* fieldName, Getter getter, Setter setter @@ -646,7 +646,7 @@ namespace emscripten { using namespace internal; typedef GetterPolicy<Getter> GP; typedef SetterPolicy<Setter> SP; - _embind_register_struct_field( + _embind_register_value_object_field( TypeID<ClassType>::get(), fieldName, TypeID<typename GP::ReturnType>::get(), @@ -659,11 +659,11 @@ namespace emscripten { } template<int Index> - value_struct& field(const char* fieldName, index<Index>) { + value_object& field(const char* fieldName, index<Index>) { using namespace internal; ClassType* null = 0; typedef typename std::remove_reference<decltype((*null)[Index])>::type ElementType; - _embind_register_struct_field( + _embind_register_value_object_field( TypeID<ClassType>::get(), fieldName, TypeID<ElementType>::get(), diff --git a/tests/cases/entry3.ll b/tests/cases/entry3.ll new file mode 100644 index 00000000..a20c6843 --- /dev/null +++ b/tests/cases/entry3.ll @@ -0,0 +1,36 @@ +; ModuleID = '/tmp/tmpKnA2D3/a.out.bc' +target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" +target triple = "i386-pc-linux-gnu" + +@.str = private unnamed_addr constant [11 x i8] c"getgid=%d\0A\00", align 1 +@.str1 = private unnamed_addr constant [6 x i8] c"f=%d\0A\00", align 1 + +define internal i32 @_Z1fii(i32, i32) noinline { +entry: + %3 = tail call i32 @getgid() + %4 = icmp eq i32 %3, 0 + br i1 %4, label %cond.b, label %cond.a + +cond.a: + %6 = tail call i32 @getgid() + br label %cond.end + +cond.b: + br label %cond.end + +cond.end: + %.0 = phi i32 [ 0, %cond.b ], [ 1, %1 ] + ret i32 %.0 +} + +declare i32 @getgid() + +define i32 @main() { + %1 = tail call i32 @getgid() + %2 = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([11 x i8]* @.str, i32 0, i32 0), i32 %1) + %3 = tail call i32 @_Z1fii(i32 undef, i32 undef) + %4 = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str1, i32 0, i32 0), i32 %3) + ret i32 0 +} + +declare i32 @printf(i8* nocapture, ...) nounwind diff --git a/tests/cases/entry3.txt b/tests/cases/entry3.txt new file mode 100644 index 00000000..4060fb06 --- /dev/null +++ b/tests/cases/entry3.txt @@ -0,0 +1,2 @@ +getgid=0 +f=0 diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js index e60e1ab3..da81a81e 100644 --- a/tests/embind/embind.test.js +++ b/tests/embind/embind.test.js @@ -898,10 +898,7 @@ module({ test("can clone handles", function() { var a = cm.emval_test_get_function_ptr(); - assert.equal(1, a.$$.count.value); var b = a.clone(); - assert.equal(2, a.$$.count.value); - assert.equal(2, b.$$.count.value); a.delete(); assert.throws(cm.BindingError, function() { @@ -1149,7 +1146,7 @@ module({ a.set(b); var c = a.get(); - assert.equal(b.$$.ptr, c.$$.ptr); + assert.true(b.isAliasOf(c)); b.delete(); c.delete(); a.delete(); @@ -1747,8 +1744,8 @@ module({ BaseFixture.extend("constants", function() { assert.equal(10, cm.INT_CONSTANT); assert.equal("some string", cm.STRING_CONSTANT); - assert.deepEqual([1, 2, 3, 4], cm.VALUE_TUPLE_CONSTANT); - assert.deepEqual({x:1,y:2,z:3,w:4}, cm.VALUE_STRUCT_CONSTANT); + assert.deepEqual([1, 2, 3, 4], cm.VALUE_ARRAY_CONSTANT); + assert.deepEqual({x:1,y:2,z:3,w:4}, cm.VALUE_OBJECT_CONSTANT); }); BaseFixture.extend("object handle comparison", function() { @@ -1881,6 +1878,16 @@ module({ // setTimeout(fn, 0); // }); }); + + BaseFixture.extend("references", function() { + test("JS object handles can be passed through to C++ by reference", function() { + var sh = new cm.StringHolder("Hello world"); + assert.equal("Hello world", sh.get()); + cm.clear_StringHolder(sh); + assert.equal("", sh.get()); + sh.delete(); + }); + }); }); /* global run_all_tests */ diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp index 3561b8a1..d6b27bce 100644 --- a/tests/embind/embind_test.cpp +++ b/tests/embind/embind_test.cpp @@ -1445,7 +1445,7 @@ EMSCRIPTEN_BINDINGS(tests) { //function("emval_test_take_and_return_CustomStruct", &emval_test_take_and_return_CustomStruct); - value_tuple<TupleVector>("TupleVector") + value_array<TupleVector>("TupleVector") .element(&TupleVector::x) .element(&Vector::getY, &Vector::setY) .element(&readVectorZ, &writeVectorZ) @@ -1455,13 +1455,13 @@ EMSCRIPTEN_BINDINGS(tests) { function("emval_test_return_TupleVector", &emval_test_return_TupleVector); function("emval_test_take_and_return_TupleVector", &emval_test_take_and_return_TupleVector); - value_tuple<TupleVectorTuple>("TupleVectorTuple") + value_array<TupleVectorTuple>("TupleVectorTuple") .element(&TupleVectorTuple::v) ; function("emval_test_return_TupleVectorTuple", &emval_test_return_TupleVectorTuple); - value_struct<StructVector>("StructVector") + value_object<StructVector>("StructVector") .field("x", &StructVector::x) .field("y", &Vector::getY, &Vector::setY) .field("z", &readVectorZ, &writeVectorZ) @@ -1471,7 +1471,7 @@ EMSCRIPTEN_BINDINGS(tests) { function("emval_test_return_StructVector", &emval_test_return_StructVector); function("emval_test_take_and_return_StructVector", &emval_test_take_and_return_StructVector); - value_struct<TupleInStruct>("TupleInStruct") + value_object<TupleInStruct>("TupleInStruct") .field("field", &TupleInStruct::field) ; @@ -2077,12 +2077,12 @@ OrderedStruct getOrderedStruct() { } EMSCRIPTEN_BINDINGS(order) { - value_tuple<OrderedTuple>("OrderedTuple") + value_array<OrderedTuple>("OrderedTuple") .element(&OrderedTuple::first) .element(&OrderedTuple::second) ; - value_struct<OrderedStruct>("OrderedStruct") + value_object<OrderedStruct>("OrderedStruct") .field("first", &OrderedStruct::first) .field("second", &OrderedStruct::second) ; @@ -2215,10 +2215,10 @@ EMSCRIPTEN_BINDINGS(constants) { constant("STRING_CONSTANT", std::string("some string")); TupleVector tv(1, 2, 3, 4); - constant("VALUE_TUPLE_CONSTANT", tv); + constant("VALUE_ARRAY_CONSTANT", tv); StructVector sv(1, 2, 3, 4); - constant("VALUE_STRUCT_CONSTANT", sv); + constant("VALUE_OBJECT_CONSTANT", sv); } class DerivedWithOffset : public DummyDataToTestPointerAdjustment, public Base { @@ -2235,3 +2235,11 @@ EMSCRIPTEN_BINDINGS(with_adjustment) { function("return_Base_from_DerivedWithOffset", &return_Base_from_DerivedWithOffset); } + +void clear_StringHolder(StringHolder& sh) { + sh.set(""); +} + +EMSCRIPTEN_BINDINGS(references) { + function("clear_StringHolder", &clear_StringHolder); +} diff --git a/tests/runner.py b/tests/runner.py index 020f2bdb..3c2ce8f3 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -6785,6 +6785,25 @@ Pass: 0.000012 0.000012''') ''' self.do_run(src, '2, , black\n2, ., #001100\n2, X, #111100'); + def test_sscanf_6(self): + src = r''' + #include <stdio.h> + + int main() + { + char *date = "18.07.2013w"; + char c[10]; + int y, m, d, i; + i = sscanf(date, "%d.%d.%4d%c", &d, &m, &y, c); + printf("date: %s; day %2d, month %2d, year %4d, extra: %c, %d\n", date, d, m, y, c[0], i); + i = sscanf(date, "%d.%d.%3c", &d, &m, c); + printf("date: %s; day %2d, month %2d, year %4d, extra: %s, %d\n", date, d, m, y, c, i); + } + ''' + self.do_run(src, '''date: 18.07.2013w; day 18, month 7, year 2013, extra: w, 4 +date: 18.07.2013w; day 18, month 7, year 2013, extra: 201, 3 +'''); + def test_sscanf_skip(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip("need ta2 for full i64") @@ -10817,6 +10836,50 @@ f.close() args=['-I' + path_from_root('tests', 'bullet', 'src')]) + def test_outline(self): + def test(name, src, libs, expected, expected_ranges, args=[], suffix='cpp'): + print name + + def measure_funcs(filename): + i = 0 + start = -1 + curr = '?' + ret = {} + for line in open(filename): + i += 1 + if line.startswith('function '): + start = i + curr = line + elif line.startswith('}'): + size = i - start + if size > 100: ret[curr] = size + return ret + + for outlining_limit in [1000, 2000, 5000, 0]: + Popen([PYTHON, EMCC, src] + libs + ['-o', 'test.js', '-O2', '-g3', '-s', 'OUTLINING_LIMIT=%d' % outlining_limit] + args).communicate() + assert os.path.exists('test.js') + shutil.copyfile('test.js', '%d_test.js' % outlining_limit) + for engine in JS_ENGINES: + out = run_js('test.js', engine=engine, stderr=PIPE, full_output=True) + self.assertContained(expected, out) + if engine == SPIDERMONKEY_ENGINE: self.validate_asmjs(out) + low = expected_ranges[outlining_limit][0] + seen = max(measure_funcs('test.js').values()) + high = expected_ranges[outlining_limit][1] + print outlining_limit, ' ', low, '<=', seen, '<=', high + assert low <= seen <= high + + test('zlib', path_from_root('tests', 'zlib', 'example.c'), + self.get_library('zlib', os.path.join('libz.a'), make_args=['libz.a']), + open(path_from_root('tests', 'zlib', 'ref.txt'), 'r').read(), + { + 1000: (380, 390), + 2000: (395, 410), + 5000: (800, 1100), + 0: (1500, 1800) + }, + args=['-I' + path_from_root('tests', 'zlib')], suffix='c') + def test_symlink(self): if os.name == 'nt': return self.skip('Windows FS does not need to be tested for symlinks support, since it does not have them.') diff --git a/tools/find_bigfuncs.py b/tools/find_bigfuncs.py index ebff8b6e..31825544 100644 --- a/tools/find_bigfuncs.py +++ b/tools/find_bigfuncs.py @@ -1,15 +1,14 @@ ''' -Simple tool to find big functions in an .ll file. Anything over i64 is of interest. +Simple tool to find big functions in an .ll file. ''' import os, sys, re filename = sys.argv[1] i = 0 -maxx = -1 -maxxest = '?' start = -1 curr = '?' +data = [] for line in open(filename): i += 1 if line.startswith('function '): @@ -17,7 +16,7 @@ for line in open(filename): curr = line elif line.startswith('}'): size = i - start - if size > maxx: - maxx = size - maxxest = curr -print maxx, 'lines in', maxxest + data.append([curr, size]); +data.sort(lambda x, y: x[1] - y[1]) +print ''.join(['%6d : %s' % (x[1], x[0]) for x in data]) + diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index dfc4d5dd..46b9c731 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -3012,6 +3012,8 @@ function outline(ast) { asmData.stackPos[stack[i]] = stackSize + i*8; } // Reserve an extra two spots: one for control flow var, the other for control flow data + // The control variables are zeroed out when calling an outlined function, and after using + // the value after they return. asmData.extraStackSize = (stack.length + 2)*8; asmData.controlStackPos = stackSize + asmData.extraStackSize - 16; asmData.controlDataStackPos = stackSize + asmData.extraStackSize - 8; @@ -3030,8 +3032,8 @@ function outline(ast) { }); var writes = {}; - var appearances = {}; - var hasReturn = false, hasBreak = false, hasContinue = false; + var namings = {}; + var hasReturn = false, hasReturnInt = false, hasReturnDouble = false, hasBreak = false, hasContinue = false; var breaks = {}; // set of labels we break or continue var continues = {}; // to (name -> id, just like labels) var breakCapturers = 0; @@ -3041,16 +3043,21 @@ function outline(ast) { if (type == 'assign' && node[2][0] == 'name') { var name = node[2][1]; if (name in asmData.vars || name in asmData.params) { - writes[name] = 0; - appearances[name] = (appearances[name] || 0) - 1; // this appearance is a definition, offset the counting later + writes[name] = (writes[name] || 0) + 1; } } else if (type == 'name') { var name = node[1]; if (name in asmData.vars || name in asmData.params) { - appearances[name] = (appearances[name] || 0) + 1; + namings[name] = (namings[name] || 0) + 1; } } else if (type == 'return') { - hasReturn = true; + if (!node[1]) { + hasReturn = true; + } else if (detectAsmCoercion(node[1]) == ASM_INT) { + hasReturnInt = true; + } else { + hasReturnDouble = true; + } } else if (type == 'break') { var label = node[1] || 0; if (!label && breakCapturers > 0) return; // no label, and captured @@ -3079,13 +3086,15 @@ function outline(ast) { continueCapturers--; } }); + assert(hasReturn + hasReturnInt + hasReturnDouble <= 1); var reads = {}; - - for (var name in appearances) { - if (appearances[name] > 0) reads[name] = 0; + for (var v in namings) { + var actualReads = namings[v] - (writes[v] || 0); + if (actualReads > 0) reads[v] = actualReads; } - return { writes: writes, reads: reads, hasReturn: hasReturn, hasBreak: hasBreak, hasContinue: hasContinue, breaks: breaks, continues: continues, labels: labels }; + + return { writes: writes, reads: reads, hasReturn: hasReturn, hasReturnInt: hasReturnInt, hasReturnDouble: hasReturnDouble, hasBreak: hasBreak, hasContinue: hasContinue, breaks: breaks, continues: continues, labels: labels }; } function makeAssign(dst, src) { @@ -3111,24 +3120,41 @@ function outline(ast) { var sizeToOutline = extraInfo.sizeToOutline; var level = 0; + var costs = {}; // new function name => overhead cost of outlining + function doOutline(func, asmData, stats, start, end) { - printErr(' do outline ' + [func[1], level, 'range:', start, end |