diff options
55 files changed, 4190 insertions, 1504 deletions
@@ -87,3 +87,7 @@ a license to everyone to use it as detailed in LICENSE.) * Manfred Manik Nerurkar <nerurkar*at*made-apps.biz> (copyright owned by MADE, GmbH) * Joseph Gentle <me@josephg.com> * Douglas T. Crosher <dtc-moz@scieneer.com> (copyright owned by Mozilla Founcation) +* Soeren Balko <soeren.balko@gmail.com> +* Ryan Kelly (ryan@rfk.id.au) +* Michael Lelli <toadking@toadking.com> + @@ -302,6 +302,9 @@ Options that are modified or new in %s include: your main compiled code (or run it before in some other way). + For more docs on the options --preload-file + accepts, see https://github.com/kripken/emscripten/wiki/Filesystem-Guide + --compression <codec> Compress both the compiled code and embedded/ preloaded files. <codec> should be a triple, @@ -1046,7 +1049,7 @@ try: if shared.Settings.CORRECT_SIGNS >= 2 or shared.Settings.CORRECT_OVERFLOWS >= 2 or shared.Settings.CORRECT_ROUNDINGS >= 2: debug_level = 4 # must keep debug info to do line-by-line operations - if debug_level > 0 and closure: + if debug_level > 1 and closure: logging.warning('disabling closure because debug info was requested') closure = False @@ -1077,6 +1080,9 @@ try: 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 + ## Compile source code to bitcode logging.debug('compiling to bitcode') @@ -1535,14 +1541,17 @@ try: # It is useful to run several js optimizer passes together, to save on unneeded unparsing/reparsing js_optimizer_queue = [] + js_optimizer_extra_info = {} def flush_js_optimizer_queue(): - global final, js_optimizer_queue + global final, js_optimizer_queue, js_optimizer_extra_info + if len(js_optimizer_extra_info) == 0: + js_optimizer_extra_info = None if len(js_optimizer_queue) > 0 and not(len(js_optimizer_queue) == 1 and js_optimizer_queue[0] == 'last'): if DEBUG != '2': if shared.Settings.ASM_JS: js_optimizer_queue = ['asm'] + js_optimizer_queue logging.debug('applying js optimization passes: %s', js_optimizer_queue) - final = shared.Building.js_optimizer(final, js_optimizer_queue, jcache, debug_level >= 4) + final = shared.Building.js_optimizer(final, js_optimizer_queue, jcache, debug_level >= 4, js_optimizer_extra_info) js_transform_tempfiles.append(final) if DEBUG: save_intermediate('js_opts') else: @@ -1551,10 +1560,11 @@ try: if shared.Settings.ASM_JS: passes = ['asm'] + passes logging.debug('applying js optimization pass: %s', passes) - final = shared.Building.js_optimizer(final, passes, jcache, debug_level >= 4) + final = shared.Building.js_optimizer(final, passes, jcache, debug_level >= 4, js_optimizer_extra_info) js_transform_tempfiles.append(final) save_intermediate(name) js_optimizer_queue = [] + js_optimizer_extra_info = {} if opt_level >= 1: logging.debug('running pre-closure post-opts') @@ -1571,7 +1581,7 @@ try: else: return 'eliminate' - js_optimizer_queue += [get_eliminate(), 'simplifyExpressionsPre'] + js_optimizer_queue += [get_eliminate(), 'simplifyExpressions'] if shared.Settings.RELOOP and not shared.Settings.ASM_JS: js_optimizer_queue += ['optimizeShiftsAggressive', get_eliminate()] # aggressive shifts optimization requires loops, it breaks on switches @@ -1585,9 +1595,9 @@ try: final = shared.Building.closure_compiler(final) if DEBUG: save_intermediate('closure') - if opt_level >= 1: - logging.debug('running post-closure post-opts') - js_optimizer_queue += ['simplifyExpressionsPost'] + if shared.Settings.OUTLINING_LIMIT > 0: + js_optimizer_queue += ['outline'] + js_optimizer_extra_info['sizeToOutline'] = shared.Settings.OUTLINING_LIMIT if (not closure or shared.Settings.ASM_JS) and shared.Settings.RELOOP and debug_level < 3: js_optimizer_queue += ['registerize'] diff --git a/emscripten.py b/emscripten.py index d9367566..3e3538e9 100755 --- a/emscripten.py +++ b/emscripten.py @@ -9,7 +9,7 @@ header files (so that the JS compiler can see the constants in those headers, for the libc implementation in JS). ''' -import os, sys, json, optparse, subprocess, re, time, multiprocessing, functools +import os, sys, json, optparse, subprocess, re, time, multiprocessing, string from tools import shared from tools import jsrun, cache as cache_module, tempfiles @@ -44,20 +44,25 @@ MIN_CHUNK_SIZE = 1024*1024 MAX_CHUNK_SIZE = float(os.environ.get('EMSCRIPT_MAX_CHUNK_SIZE') or 'inf') # configuring this is just for debugging purposes def process_funcs((i, funcs, meta, settings_file, compiler, forwarded_file, libraries, compiler_engine, temp_files, DEBUG)): - funcs_file = temp_files.get('.func_%d.ll' % i).name - f = open(funcs_file, 'w') - f.write(funcs) - funcs = None - f.write('\n') - f.write(meta) - f.close() - out = jsrun.run_js( - compiler, - engine=compiler_engine, - args=[settings_file, funcs_file, 'funcs', forwarded_file] + libraries, - stdout=subprocess.PIPE, - cwd=path_from_root('src')) - tempfiles.try_delete(funcs_file) + try: + funcs_file = temp_files.get('.func_%d.ll' % i).name + f = open(funcs_file, 'w') + f.write(funcs) + funcs = None + f.write('\n') + f.write(meta) + f.close() + out = jsrun.run_js( + compiler, + engine=compiler_engine, + args=[settings_file, funcs_file, 'funcs', forwarded_file] + libraries, + stdout=subprocess.PIPE, + cwd=path_from_root('src')) + except KeyboardInterrupt: + # Python 2.7 seems to lock up when a child process throws KeyboardInterrupt + raise Exception() + finally: + tempfiles.try_delete(funcs_file) if DEBUG: print >> sys.stderr, '.' return out @@ -620,6 +625,18 @@ Runtime.stackRestore = function(top) { asm['stackRestore'](top) }; // EMSCRIPTEN_END_FUNCS '''] + # Create symbol table for self-dlopen + if settings.get('DLOPEN_SUPPORT'): + symbol_table = { k:v+forwarded_json['Runtime']['GLOBAL_BASE'] + for k,v in forwarded_json['Variables']['indexedGlobals'].iteritems() + if forwarded_json['Variables']['globals'][k]['named'] } + for raw in last_forwarded_json['Functions']['tables'].itervalues(): + if raw == '': continue + table = map(string.strip, raw[raw.find('[')+1:raw.find(']')].split(",")) + symbol_table.update(map(lambda x: (x[1], x[0]), + filter(lambda x: x[1] != '0', enumerate(table)))) + outfile.write("var SYMBOL_TABLE = %s;" % json.dumps(symbol_table)) + for funcs_js_item in funcs_js: # do this loop carefully to save memory funcs_js_item = indexize(funcs_js_item) funcs_js_item = blockaddrsize(funcs_js_item) diff --git a/src/analyzer.js b/src/analyzer.js index de9a7940..b1f0b585 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -502,18 +502,38 @@ function analyzer(data, sidePass) { { intertype: 'value', ident: j.toString(), type: 'i32' } ] }); - var actualSizeType = 'i' + element.bits; // The last one may be smaller than 32 bits - toAdd.push({ + var newItem = { intertype: 'load', assignTo: element.ident, - pointerType: actualSizeType + '*', - valueType: actualSizeType, - type: actualSizeType, // XXX why is this missing from intertyper? - pointer: { intertype: 'value', ident: tempVar, type: actualSizeType + '*' }, + pointerType: 'i32*', + valueType: 'i32', + type: 'i32', + pointer: { intertype: 'value', ident: tempVar, type: 'i32*' }, ident: tempVar, - pointerType: actualSizeType + '*', align: value.align - }); + }; + var newItem2 = null; + // The last one may be smaller than 32 bits + if (element.bits < 32) { + newItem.assignTo += '$preadd$'; + newItem2 = { + intertype: 'mathop', + op: 'and', + assignTo: element.ident, + type: 'i32', + params: [{ + intertype: 'value', + type: 'i32', + ident: newItem.assignTo + }, { + intertype: 'value', + type: 'i32', + ident: (0xffffffff >>> (32 - element.bits)).toString() + }], + }; + } + toAdd.push(newItem); + if (newItem2) toAdd.push(newItem2); j++; }); Types.needAnalysis['[0 x i32]'] = 0; @@ -1433,15 +1453,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 +1497,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/intertyper.js b/src/intertyper.js index 94d937e1..abfbdacb 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -503,6 +503,7 @@ function intertyper(data, sidePass, baseLineNums) { // variable var ident = item.tokens[0].text; var private_ = findTokenText(item, 'private') >= 0 || findTokenText(item, 'internal') >= 0; + var named = findTokenText(item, 'unnamed_addr') < 0; cleanOutTokens(LLVM.GLOBAL_MODIFIERS, item.tokens, [2, 3]); var external = false; if (item.tokens[2].text === 'external') { @@ -516,6 +517,7 @@ function intertyper(data, sidePass, baseLineNums) { type: item.tokens[2].text, external: external, private_: private_, + named: named, lineNum: item.lineNum }; if (!NAMED_GLOBALS) { diff --git a/src/jsifier.js b/src/jsifier.js index 82b78d0a..b377202d 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -286,6 +286,8 @@ function JSify(data, functionsOnly, givenFunctions) { allocator = 'ALLOC_NONE'; } + Variables.globals[item.ident].named = item.named; + if (ASM_JS && (MAIN_MODULE || SIDE_MODULE) && !item.private_ && !NAMED_GLOBALS && isIndexableGlobal(item.ident)) { // We need this to be named (and it normally would not be), so that it can be linked to and used from other modules Variables.globals[item.ident].linkable = 1; @@ -602,6 +604,8 @@ function JSify(data, functionsOnly, givenFunctions) { var associatedSourceFile = "NO_SOURCE"; } + if (DLOPEN_SUPPORT) Functions.getIndex(func.ident); + func.JS += 'function ' + func.ident + '(' + paramIdents.join(', ') + ') {\n'; if (PGO) { @@ -1207,7 +1211,7 @@ function JSify(data, functionsOnly, givenFunctions) { // in an assignment var disabled = DISABLE_EXCEPTION_CATCHING == 2 && !(item.funcData.ident in EXCEPTION_CATCHING_WHITELIST); var phiSets = calcPhiSets(item); - var call_ = makeFunctionCall(item.ident, item.params, item.funcData, item.type, ASM_JS && !disabled, !!item.assignTo || !item.standalone); + var call_ = makeFunctionCall(item.ident, item.params, item.funcData, item.type, ASM_JS && !disabled, !!item.assignTo || !item.standalone, true); var ret; @@ -1364,7 +1368,7 @@ function JSify(data, functionsOnly, givenFunctions) { return ret; }); - function makeFunctionCall(ident, params, funcData, type, forceByPointer, hasReturn) { + function makeFunctionCall(ident, params, funcData, type, forceByPointer, hasReturn, invoke) { // We cannot compile assembly. See comment in intertyper.js:'Call' assert(ident != 'asm', 'Inline assembly cannot be compiled to JavaScript!'); @@ -1429,7 +1433,7 @@ function JSify(data, functionsOnly, givenFunctions) { args = args.map(function(arg, i) { return indexizeFunctions(arg, argsTypes[i]) }); if (ASM_JS) { - if (shortident in Functions.libraryFunctions || simpleIdent in Functions.libraryFunctions || byPointerForced || funcData.setjmpTable) { + if (shortident in Functions.libraryFunctions || simpleIdent in Functions.libraryFunctions || byPointerForced || invoke || funcData.setjmpTable) { args = args.map(function(arg, i) { return asmCoercion(arg, argsTypes[i]) }); } else { args = args.map(function(arg, i) { return asmEnsureFloat(arg, argsTypes[i]) }); @@ -1470,7 +1474,6 @@ function JSify(data, functionsOnly, givenFunctions) { } args = args.concat(varargs); - var argsText = args.join(', '); // Inline if either we inline whenever we can (and we can), or if there is no noninlined version var inline = LibraryManager.library[simpleIdent + '__inline']; @@ -1492,16 +1495,35 @@ function JSify(data, functionsOnly, givenFunctions) { } } + if (callIdent in Functions.implementedFunctions) { + // LLVM sometimes bitcasts for no reason. We must call using the exact same type as the actual function is generated as. + var numArgs = Functions.implementedFunctions[callIdent].length - 1; + if (numArgs !== args.length) { + if (VERBOSE) warnOnce('Fixing function call arguments based on signature, on ' + [callIdent, args.length, numArgs]); + while (args.length > numArgs) { args.pop(); argsTypes.pop() } + while (args.length < numArgs) { args.push('0'); argsTypes.push('i32') } + } + } + var returnType = 'void'; if ((byPointer || ASM_JS) && hasReturn) { returnType = getReturnType(type); + if (callIdent in Functions.implementedFunctions) { + // LLVM sometimes bitcasts for no reason. We must call using the exact same type as the actual function is generated as + var trueType = Functions.getSignatureReturnType(Functions.implementedFunctions[callIdent]); + if (trueType !== returnType && !isIdenticallyImplemented(trueType, returnType)) { + if (VERBOSE) warnOnce('Fixing function call based on return type from signature, on ' + [callIdent, returnType, trueType]); + returnType = trueType; + } + } } if (byPointer) { var sig = Functions.getSignature(returnType, argsTypes, hasVarArgs); if (ASM_JS) { assert(returnType.search(/\("'\[,/) == -1); // XXX need isFunctionType(type, out) - if (!byPointerForced && !funcData.setjmpTable) { + var functionTableCall = !byPointerForced && !funcData.setjmpTable && !invoke; + if (functionTableCall) { // normal asm function pointer call callIdent = '(' + callIdent + ')&{{{ FTM_' + sig + ' }}}'; // the function table mask is set in emscripten.py Functions.neededTables[sig] = 1; @@ -1516,7 +1538,7 @@ function JSify(data, functionsOnly, givenFunctions) { assert(!ASM_JS, 'cannot emit safe dyncalls in asm'); callIdent = '(tempInt=' + callIdent + ',tempInt < 0 || tempInt >= FUNCTION_TABLE.length-1 || !FUNCTION_TABLE[tempInt] ? abort("dyncall error: ' + sig + ' " + FUNCTION_TABLE_NAMES[tempInt]) : tempInt)'; } - if (!ASM_JS || (!byPointerForced && !funcData.setjmpTable)) callIdent = Functions.getTable(sig) + '[' + callIdent + ']'; + if (!ASM_JS || functionTableCall) callIdent = Functions.getTable(sig) + '[' + callIdent + ']'; } var ret = callIdent + '(' + args.join(', ') + ')'; diff --git a/src/library.js b/src/library.js index 20a163c6..e650a545 100644 --- a/src/library.js +++ b/src/library.js @@ -559,25 +559,28 @@ LibraryManager.library = { }; } var utf8 = new Runtime.UTF8Processor(); - function simpleOutput(val) { - if (val === null || val === {{{ charCode('\n') }}}) { - output.printer(output.buffer.join('')); - output.buffer = []; - } else { - output.buffer.push(utf8.processCChar(val)); - } + function createSimpleOutput() { + var fn = function (val) { + if (val === null || val === {{{ charCode('\n') }}}) { + fn.printer(fn.buffer.join('')); + fn.buffer = []; + } else { + fn.buffer.push(utf8.processCChar(val)); + } + }; + return fn; } if (!output) { stdoutOverridden = false; - output = simpleOutput; + output = createSimpleOutput(); } if (!output.printer) output.printer = Module['print']; if (!output.buffer) output.buffer = []; if (!error) { stderrOverridden = false; - error = simpleOutput; + error = createSimpleOutput(); } - if (!error.printer) error.printer = Module['print']; + if (!error.printer) error.printer = Module['printErr']; if (!error.buffer) error.buffer = []; // Create the temporary folder, if not already created @@ -1045,27 +1048,45 @@ LibraryManager.library = { mknod: function(path, mode, dev) { // int mknod(const char *path, mode_t mode, dev_t dev); // http://pubs.opengroup.org/onlinepubs/7908799/xsh/mknod.html - if (dev !== 0 || !(mode & 0xC000)) { // S_IFREG | S_IFDIR. - // Can't create devices or pipes through mknod(). + path = Pointer_stringify(path); + var fmt = (mode & {{{ cDefine('S_IFMT') }}}); + if (fmt !== {{{ cDefine('S_IFREG') }}} && fmt !== {{{ cDefine('S_IFCHR') }}} && + fmt !== {{{ cDefine('S_IFBLK') }}} && fmt !== {{{ cDefine('S_IFIFO') }}} && + fmt !== {{{ cDefine('S_IFSOCK') }}}) { + // not valid formats for mknod ___setErrNo(ERRNO_CODES.EINVAL); return -1; - } else { - var properties = {contents: [], isFolder: Boolean(mode & 0x4000)}; // S_IFDIR. - path = FS.analyzePath(Pointer_stringify(path)); - try { - FS.createObject(path.parentObject, path.name, properties, - mode & 0x100, mode & 0x80); // S_IRUSR, S_IWUSR. - return 0; - } catch (e) { - return -1; - } + } + if (fmt === {{{ cDefine('S_IFCHR') }}} || fmt === {{{ cDefine('S_IFBLK') }}} || + fmt === {{{ cDefine('S_IFIFO') }}} || fmt === {{{ cDefine('S_IFSOCK') }}}) { + // not supported currently + ___setErrNo(ERRNO_CODES.EPERM); + return -1; + } + path = FS.analyzePath(path); + var properties = { contents: [], isFolder: false }; // S_IFDIR. + try { + FS.createObject(path.parentObject, path.name, properties, + mode & 0x100, mode & 0x80); // S_IRUSR, S_IWUSR. + return 0; + } catch (e) { + return -1; } }, mkdir__deps: ['mknod'], mkdir: function(path, mode) { // int mkdir(const char *path, mode_t mode); // http://pubs.opengroup.org/onlinepubs/7908799/xsh/mkdir.html - return _mknod(path, 0x4000 | (mode & 0x180), 0); // S_IFDIR, S_IRUSR | S_IWUSR. + path = Pointer_stringify(path); + path = FS.analyzePath(path); + var properties = { contents: [], isFolder: true }; + try { + FS.createObject(path.parentObject, path.name, properties, + mode & 0x100, mode & 0x80); // S_IRUSR, S_IWUSR. + return 0; + } catch (e) { + return -1; + } }, mkfifo__deps: ['__setErrNo', '$ERRNO_CODES'], mkfifo: function(path, mode) { @@ -1079,10 +1100,13 @@ LibraryManager.library = { return -1; }, chmod__deps: ['$FS'], - chmod: function(path, mode) { + chmod: function(path, mode, dontResolveLastLink) { // int chmod(const char *path, mode_t mode); // http://pubs.opengroup.org/onlinepubs/7908799/xsh/chmod.html - var obj = FS.findObject(Pointer_stringify(path)); + // NOTE: dontResolveLastLink is a shortcut for lchmod(). It should never be + // used in client code. + path = typeof path !== 'string' ? Pointer_stringify(path) : path; + var obj = FS.findObject(path, dontResolveLastLink); if (obj === null) return -1; obj.read = mode & 0x100; // S_IRUSR. obj.write = mode & 0x80; // S_IWUSR. @@ -1093,15 +1117,16 @@ LibraryManager.library = { fchmod: function(fildes, mode) { // int fchmod(int fildes, mode_t mode); // http://pubs.opengroup.org/onlinepubs/7908799/xsh/fchmod.html - if (!FS.streams[fildes]) { + var stream = FS.streams[fildes]; + if (!stream) { ___setErrNo(ERRNO_CODES.EBADF); return -1; - } else { - var pathArray = intArrayFromString(FS.streams[fildes].path); - return _chmod(allocate(pathArray, 'i8', ALLOC_STACK), mode); } + return _chmod(stream.path, mode); + }, + lchmod: function(path, mode) { + return _chmod(path, mode, true); }, - lchmod: function() { throw 'TODO: lchmod' }, umask__deps: ['$FS'], umask: function(newMask) { @@ -1115,6 +1140,7 @@ LibraryManager.library = { }, stat64: 'stat', fstat64: 'fstat', + lstat64: 'lstat', __01fstat64_: 'fstat', __01stat64_: 'stat', __01lstat64_: 'lstat', @@ -1775,6 +1801,8 @@ LibraryManager.library = { } else if (nbyte < 0 || offset < 0) { ___setErrNo(ERRNO_CODES.EINVAL); return -1; + } else if (offset >= stream.object.contents.length) { + return 0; } else { var bytesRead = 0; while (stream.ungotten.length && nbyte > 0) { @@ -1784,6 +1812,8 @@ LibraryManager.library = { } var contents = stream.object.contents; var size = Math.min(contents.length - offset, nbyte); + assert(size >= 0); + #if USE_TYPED_ARRAYS == 2 if (contents.subarray) { // typed array HEAPU8.set(contents.subarray(offset, offset+size), buf); @@ -1851,6 +1881,7 @@ LibraryManager.library = { } else { var ungotSize = stream.ungotten.length; bytesRead = _pread(fildes, buf, nbyte, stream.position); + assert(bytesRead >= -1); if (bytesRead != -1) { stream.position += (stream.ungotten.length - ungotSize) + bytesRead; } @@ -1867,42 +1898,42 @@ LibraryManager.library = { rmdir: function(path) { // int rmdir(const char *path); // http://pubs.opengroup.org/onlinepubs/000095399/functions/rmdir.html - path = FS.analyzePath(Pointer_stringify(path)); + path = Pointer_stringify(path); + path = FS.analyzePath(path, true); if (!path.parentExists || !path.exists) { ___setErrNo(path.error); return -1; - } else if (!path.object.write || path.isRoot) { + } else if (!path.parentObject.write) { ___setErrNo(ERRNO_CODES.EACCES); return -1; } else if (!path.object.isFolder) { ___setErrNo(ERRNO_CODES.ENOTDIR); return -1; + } else if (path.isRoot || path.path == FS.currentPath) { + ___setErrNo(ERRNO_CODES.EBUSY); + return -1; } else { for (var i in path.object.contents) { ___setErrNo(ERRNO_CODES.ENOTEMPTY); return -1; } - if (path.path == FS.currentPath) { - ___setErrNo(ERRNO_CODES.EBUSY); - return -1; - } else { - delete path.parentObject.contents[path.name]; - return 0; - } + delete path.parentObject.contents[path.name]; + return 0; } }, unlink__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], unlink: function(path) { // int unlink(const char *path); // http://pubs.opengroup.org/onlinepubs/000095399/functions/unlink.html - path = FS.analyzePath(Pointer_stringify(path)); + path = Pointer_stringify(path); + path = FS.analyzePath(path, true); if (!path.parentExists || !path.exists) { ___setErrNo(path.error); return -1; } else if (path.object.isFolder) { - ___setErrNo(ERRNO_CODES.EISDIR); + ___setErrNo(ERRNO_CODES.EPERM); return -1; - } else if (!path.object.write) { + } else if (!path.parentObject.write) { ___setErrNo(ERRNO_CODES.EACCES); return -1; } else { @@ -2559,15 +2590,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 = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}}; + 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 @@ -3183,7 +3226,7 @@ LibraryManager.library = { var flush = function(filedes) { // Right now we write all data directly, except for output devices. if (FS.streams[filedes] && FS.streams[filedes].object.output) { - if (!FS.streams[filedes].isTerminal) { // don't flush terminals, it would cause a \n to also appear + if (!FS.streams[filedes].object.isTerminal) { // don't flush terminals, it would cause a \n to also appear FS.streams[filedes].object.output(null); } } @@ -4898,7 +4941,9 @@ LibraryManager.library = { isprint: function(chr) { return 0x1F < chr && chr < 0x7F; }, - isgraph: 'isprint', + isgraph: function(chr) { + return 0x20 < chr && chr < 0x7F; + }, // Lookup tables for glibc ctype implementation. __ctype_b_loc: function() { // http://refspecs.freestandards.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/baselib---ctype-b-loc.html @@ -5735,8 +5780,15 @@ LibraryManager.library = { rintf: 'rint', lrint: 'rint', lrintf: 'rint', +#if USE_TYPED_ARRAYS == 2 + llrint: function(x) { + x = (x < 0) ? -Math.round(-x) : Math.round(x); + {{{ makeStructuralReturn(splitI64('x')) }}}; + }, +#else llrint: 'rint', - llrintf: 'rint', +#endif + llrintf: 'llrint', nearbyint: 'rint', nearbyintf: 'rint', trunc: function(x) { @@ -5878,7 +5930,7 @@ LibraryManager.library = { dlopen: function(filename, flag) { // void *dlopen(const char *file, int mode); // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlopen.html - filename = (ENV['LD_LIBRARY_PATH'] || '/') + Pointer_stringify(filename); + filename = filename === 0 ? '__self__' : (ENV['LD_LIBRARY_PATH'] || '/') + Pointer_stringify(filename); if (DLFCN_DATA.loadedLibNames[filename]) { // Already loaded; increment ref count and return. @@ -5887,48 +5939,55 @@ LibraryManager.library = { return handle; } - var target = FS.findObject(filename); - if (!target || target.isFolder || target.isDevice) { - DLFCN_DATA.errorMsg = 'Could not find dynamic lib: ' + filename; - return 0; + if (filename === '__self__') { + var handle = -1; + var lib_module = Module; + var cached_functions = SYMBOL_TABLE; } else { - FS.forceLoadFile(target); - var lib_data = intArrayToString(target.contents); - } + var target = FS.findObject(filename); + if (!target || target.isFolder || target.isDevice) { + DLFCN_DATA.errorMsg = 'Could not find dynamic lib: ' + filename; + return 0; + } else { + FS.forceLoadFile(target); + var lib_data = intArrayToString(target.contents); + } - try { - var lib_module = eval(lib_data)({{{ Functions.getTable('x') }}}.length); - } catch (e) { + try { + var lib_module = eval(lib_data)({{{ Functions.getTable('x') }}}.length); + } catch (e) { #if ASSERTIONS - Module.printErr('Error in loading dynamic library: ' + e); + Module.printErr('Error in loading dynamic library: ' + e); #endif - DLFCN_DATA.errorMsg = 'Could not evaluate dynamic lib: ' + filename; - return 0; - } + DLFCN_DATA.errorMsg = 'Could not evaluate dynamic lib: ' + filename; + return 0; + } - // Not all browsers support Object.keys(). - var handle = 1; - for (var key in DLFCN_DATA.loadedLibs) { - if (DLFCN_DATA.loadedLibs.hasOwnProperty(key)) handle++; - } + // Not all browsers support Object.keys(). + var handle = 1; + for (var key in DLFCN_DATA.loadedLibs) { + if (DLFCN_DATA.loadedLibs.hasOwnProperty(key)) handle++; + } + + // We don't care about RTLD_NOW and RTLD_LAZY. + if (flag & 256) { // RTLD_GLOBAL + for (var ident in lib_module) { + if (lib_module.hasOwnProperty(ident)) { + Module[ident] = lib_module[ident]; + } + } + } + var cached_functions = {}; + } DLFCN_DATA.loadedLibs[handle] = { refcount: 1, name: filename, module: lib_module, - cached_functions: {} + cached_functions: cached_functions }; DLFCN_DATA.loadedLibNames[filename] = handle; - // We don't care about RTLD_NOW and RTLD_LAZY. - if (flag & 256) { // RTLD_GLOBAL - for (var ident in lib_module) { - if (lib_module.hasOwnProperty(ident)) { - Module[ident] = lib_module[ident]; - } - } - } - return handle; }, // int dlclose(void* handle); @@ -5941,7 +6000,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]; } @@ -5960,13 +6019,15 @@ LibraryManager.library = { return 0; } else { var lib = DLFCN_DATA.loadedLibs[handle]; - if (!lib.module.hasOwnProperty(symbol)) { - DLFCN_DATA.errorMsg = ('Tried to lookup unknown symbol "' + symbol + - '" in dynamic lib: ' + lib.name); - return 0; + // self-dlopen means that lib.module is not a superset of + // cached_functions, so check the latter first + if (lib.cached_functions.hasOwnProperty(symbol)) { + return lib.cached_functions[symbol]; } else { - if (lib.cached_functions.hasOwnProperty(symbol)) { - return lib.cached_functions[symbol]; + if (!lib.module.hasOwnProperty(symbol)) { + DLFCN_DATA.errorMsg = ('Tried to lookup unknown symbol "' + symbol + + '" in dynamic lib: ' + lib.name); + return 0; } else { var result = lib.module[symbol]; if (typeof result == 'function') { @@ -6207,18 +6268,582 @@ LibraryManager.library = { return -1; }, - strftime: function(s, maxsize, format, timeptr) { + _MONTH_DAYS_REGULAR: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], + _MONTH_DAYS_LEAP: [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], + + _isLeapYear: function(year) { + return year%4 === 0 && (year%100 !== 0 || year%400 === 0); + }, + + _arraySum: function(array, index) { + var sum = 0; + for (var i = 0; i <= index; sum += array[i++]); + return sum; + }, + + _addDays__deps: ['_isLeapYear', '_MONTH_DAYS_LEAP', '_MONTH_DAYS_REGULAR'], + _addDays: function(date, days) { + var newDate = new Date(date.getTime()); + while(days > 0) { + var leap = __isLeapYear(newDate.getFullYear()); + var currentMonth = newDate.getMonth(); + var daysInCurrentMonth = (leap ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR)[currentMonth]; + + if (days > daysInCurrentMonth-newDate.getDate()) { + // we spill over to next month + days -= (daysInCurrentMonth-newDate.getDate()+1); + newDate.setDate(1); + if (currentMonth < 11) { + newDate.setMonth(currentMonth+1) + } else { + newDate.setMonth(0); + newDate.setFullYear(newDate.getFullYear()+1); + } + } else { + // we stay in current month + newDate.setDate(newDate.getDate()+days); + return newDate; + } + } + + return newDate; + }, + + strftime__deps: ['__tm_struct_layout', '_isLeapYear', '_arraySum', '_addDays', '_MONTH_DAYS_REGULAR', '_MONTH_DAYS_LEAP'], + strftime: function(s, maxsize, format, tm) { // size_t strftime(char *restrict s, size_t maxsize, const char *restrict format, const struct tm *restrict timeptr); // http://pubs.opengroup.org/onlinepubs/009695399/functions/strftime.html - // TODO: Implement. - return 0; + + var date = { + tm_sec: {{{ makeGetValue('tm', '___tm_struct_layout.tm_sec', 'i32') }}}, + tm_min: {{{ makeGetValue('tm', '___tm_struct_layout.tm_min', 'i32') }}}, + tm_hour: {{{ makeGetValue('tm', '___tm_struct_layout.tm_hour', 'i32') }}}, + tm_mday: {{{ makeGetValue('tm', '___tm_struct_layout.tm_mday', 'i32') }}}, + tm_mon: {{{ makeGetValue('tm', '___tm_struct_layout.tm_mon', 'i32') }}}, + tm_year: {{{ makeGetValue('tm', '___tm_struct_layout.tm_year', 'i32') }}}, + tm_wday: {{{ makeGetValue('tm', '___tm_struct_layout.tm_wday', 'i32') }}}, + tm_yday: {{{ makeGetValue('tm', '___tm_struct_layout.tm_yday', 'i32') }}}, + tm_isdst: {{{ makeGetValue('tm', '___tm_struct_layout.tm_isdst', 'i32') }}} + }; + + var pattern = Pointer_stringify(format); + + // expand format + var EXPANSION_RULES_1 = { + '%c': '%a %b %d %H:%M:%S %Y', // Replaced by the locale's appropriate date and time representation - e.g., Mon Aug 3 14:02:01 2013 + '%D': '%m/%d/%y', // Equivalent to %m / %d / %y + '%F': '%Y-%m-%d', // Equivalent to %Y - %m - %d + '%h': '%b', // Equivalent to %b + '%r': '%I:%M:%S %p', // Replaced by the time in a.m. and p.m. notation + '%R': '%H:%M', // Replaced by the time in 24-hour notation + '%T': '%H:%M:%S', // Replaced by the time + '%x': '%m/%d/%y', // Replaced by the locale's appropriate date representation + '%X': '%H:%M:%S', // Replaced by the locale's appropriate date representation + }; + for (var rule in EXPANSION_RULES_1) { + pattern = pattern.replace(new RegExp(rule, 'g'), EXPANSION_RULES_1[rule]); + } + + var WEEKDAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; + var MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; + + var leadingSomething = function(value, digits, character) { + var str = typeof value === 'number' ? value.toString() : (value || ''); + while (str.length < digits) { + str = character[0]+str; + } + return str; + }; + + var leadingNulls = function(value, digits) { + return leadingSomething(value, digits, '0'); + }; + + var compareByDay = function(date1, date2) { + var sgn = function(value) { + return value < 0 ? -1 : (value > 0 ? 1 : 0); + }; + + var compare; + if ((compare = sgn(date1.getFullYear()-date2.getFullYear())) === 0) { + if ((compare = sgn(date1.getMonth()-date2.getMonth())) === 0) { + compare = sgn(date1.getDate()-date2.getDate()); + } + } + return compare; + }; + + var getFirstWeekStartDate = function(janFourth) { + switch (janFourth.getDay()) { + case 0: // Sunday + return new Date(janFourth.getFullYear()-1, 11, 29); + case 1: // Monday + return janFourth; + case 2: // Tuesday + return new Date(janFourth.getFullYear(), 0, 3); + case 3: // Wednesday + return new Date(janFourth.getFullYear(), 0, 2); + case 4: // Thursday + return new Date(janFourth.getFullYear(), 0, 1); + case 5: // Friday + return new Date(janFourth.getFullYear()-1, 11, 31); + case 6: // Saturday + return new Date(janFourth.getFullYear()-1, 11, 30); + } + }; + + var getWeekBasedYear = function(date) { + var thisDate = __addDays(new Date(date.tm_year+1900, 0, 1), date.tm_yday); + + var janFourthThisYear = new Date(thisDate.getFullYear(), 0, 4); + var janFourthNextYear = new Date(thisDate.getFullYear()+1, 0, 4); + + var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear); + var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear); + + if (compareByDay(firstWeekStartThisYear, thisDate) <= 0) { + // this date is after the start of the first week of this year + if (compareByDay(firstWeekStartNextYear, thisDate) <= 0) { + return thisDate.getFullYear()+1; + } else { + return thisDate.getFullYear(); + } + } else { + return thisDate.getFullYear()-1; + } + }; + + var EXPANSION_RULES_2 = { + '%a': function(date) { + return WEEKDAYS[date.tm_wday].substring(0,3); + }, + '%A': function(date) { + return WEEKDAYS[date.tm_wday]; + }, + '%b': function(date) { + return MONTHS[date.tm_mon].substring(0,3); + }, + '%B': function(date) { + return MONTHS[date.tm_mon]; + }, + '%C': function(date) { + var year = date.tm_year+1900; + return leadingNulls(Math.floor(year/100),2); + }, + '%d': function(date) { + return leadingNulls(date.tm_mday, 2); + }, + '%e': function(date) { + return leadingSomething(date.tm_mday, 2, ' '); + }, + '%g': function(date) { + // %g, %G, and %V give values according to the ISO 8601:2000 standard week-based year. + // In this system, weeks begin on a Monday and week 1 of the year is the week that includes + // January 4th, which is also the week that includes the first Thursday of the year, and + // is also the first week that contains at least four days in the year. + // If the first Monday of January is the 2nd, 3rd, or 4th, the preceding days are part of + // the last week of the preceding year; thus, for Saturday 2nd January 1999, + // %G is replaced by 1998 and %V is replaced by 53. If December 29th, 30th, + // or 31st is a Monday, it and any following days are part of week 1 of the following year. + // Thus, for Tuesday 30th December 1997, %G is replaced by 1998 and %V is replaced by 01. + + return getWeekBasedYear(date).toString().substring(2); + }, + '%G': function(date) { + return getWeekBasedYear(date); + }, + '%H': function(date) { + return leadingNulls(date.tm_hour, 2); + }, + '%I': function(date) { + return leadingNulls(date.tm_hour < 13 ? date.tm_hour : date.tm_hour-12, 2); + }, + '%j': function(date) { + // Day of the year (001-366) + return leadingNulls(date.tm_mday+__arraySum(__isLeapYear(date.tm_year+1900) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, date.tm_mon-1), 3); + }, + '%m': function(date) { + return leadingNulls(date.tm_mon+1, 2); + }, + '%M': function(date) { + return leadingNulls(date.tm_min, 2); + }, + '%n': function() { + return '\n'; + }, + '%p': function(date) { + if (date.tm_hour > 0 && date.tm_hour < 13) { + return 'AM'; + } else { + return 'PM'; + } + }, + '%S': function(date) { + return leadingNulls(date.tm_sec, 2); + }, + '%t': function() { + return '\t'; + }, + '%u': function(date) { + var day = new Date(date.tm_year+1900, date.tm_mon+1, date.tm_mday, 0, 0, 0, 0); + return day.getDay() || 7; + }, + '%U': function(date) { + // Replaced by the week number of the year as a decimal number [00,53]. + // The first Sunday of January is the first day of week 1; + // days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday] + var janFirst = new Date(date.tm_year+1900, 0, 1); + var firstSunday = janFirst.getDay() === 0 ? janFirst : __addDays(janFirst, 7-janFirst.getDay()); + var endDate = new Date(date.tm_year+1900, date.tm_mon, date.tm_mday); + + // is target date after the first Sunday? + if (compareByDay(firstSunday, endDate) < 0) { + // calculate difference in days between first Sunday and endDate + var februaryFirstUntilEndMonth = __arraySum(__isLeapYear(endDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, endDate.getMonth()-1)-31; + var firstSundayUntilEndJanuary = 31-firstSunday.getDate(); + var days = firstSundayUntilEndJanuary+februaryFirstUntilEndMonth+endDate.getDate(); + return leadingNulls(Math.ceil(days/7), 2); + } + + return compareByDay(firstSunday, janFirst) === 0 ? '01': '00'; + }, + '%V': function(date) { + // Replaced by the week number of the year (Monday as the first day of the week) + // as a decimal number [01,53]. If the week containing 1 January has four + // or more days in the new year, then it is considered week 1. + // Otherwise, it is the last week of the previous year, and the next week is week 1. + // Both January 4th and the first Thursday of January are always in week 1. [ tm_year, tm_wday, tm_yday] + var janFourthThisYear = new Date(date.tm_year+1900, 0, 4); + var janFourthNextYear = new Date(date.tm_year+1901, 0, 4); + + var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear); + var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear); + + var endDate = __addDays(new Date(date.tm_year+1900, 0, 1), date.tm_yday); + + if (compareByDay(endDate, firstWeekStartThisYear) < 0) { + // if given date is before this years first week, then it belongs to the 53rd week of last year + return '53'; + } + + if (compareByDay(firstWeekStartNextYear, endDate) <= 0) { + // if given date is after next years first week, then it belongs to the 01th week of next year + return '01'; + } + + // given date is in between CW 01..53 of this calendar year + var daysDifference; + if (firstWeekStartThisYear.getFullYear() < date.tm_year+1900) { + // first CW of this year starts last year + daysDifference = date.tm_yday+32-firstWeekStartThisYear.getDate() + } else { + // first CW of this year starts this year + daysDifference = date.tm_yday+1-firstWeekStartThisYear.getDate(); + } + return leadingNulls(Math.ceil(daysDifference/7), 2); + }, + '%w': function(date) { + var day = new Date(date.tm_year+1900, date.tm_mon+1, date.tm_mday, 0, 0, 0, 0); + return day.getDay(); + }, + '%W': function(date) { + // Replaced by the week number of the year as a decimal number [00,53]. + // The first Monday of January is the first day of week 1; + // days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday] + var janFirst = new Date(date.tm_year, 0, 1); + var firstMonday = janFirst.getDay() === 1 ? janFirst : __addDays(janFirst, janFirst.getDay() === 0 ? 1 : 7-janFirst.getDay()+1); + var endDate = new Date(date.tm_year+1900, date.tm_mon, date.tm_mday); + + // is target date after the first Monday? + if (compareByDay(firstMonday, endDate) < 0) { + var februaryFirstUntilEndMonth = __arraySum(__isLeapYear(endDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, endDate.getMonth()-1)-31; + var firstMondayUntilEndJanuary = 31-firstMonday.getDate(); + var days = firstMondayUntilEndJanuary+februaryFirstUntilEndMonth+endDate.getDate(); + return leadingNulls(Math.ceil(days/7), 2); + } + return compareByDay(firstMonday, janFirst) === 0 ? '01': '00'; + }, + '%y': function(date) { + // Replaced by the last two digits of the year as a decimal number [00,99]. [ tm_year] + return (date.tm_year+1900).toString().substring(2); + }, + '%Y': function(date) { + // Replaced by the year as a decimal number (for example, 1997). [ tm_year] + return date.tm_year+1900; + }, + '%z': function(date) { + // Replaced by the offset from UTC in the ISO 8601:2000 standard format ( +hhmm or -hhmm ), + // or by no characters if no timezone is determinable. + // For example, "-0430" means 4 hours 30 minutes behind UTC (west of Greenwich). + // If tm_isdst is zero, the standard time offset is used. + // If tm_isdst is greater than zero, the daylight savings time offset is used. + // If tm_isdst is negative, no characters are returned. + // FIXME: we cannot determine time zone (or can we?) + return ''; + }, + '%Z': function(date) { + // Replaced by the timezone name or abbreviation, or by no bytes if no timezone information exists. [ tm_isdst] + // FIXME: we cannot determine time zone (or can we?) + return ''; + }, + '%%': function() { + return '%'; + } + }; + for (var rule in EXPANSION_RULES_2) { + if (pattern.indexOf(rule) >= 0) { + pattern = pattern.replace(new RegExp(rule, 'g'), EXPANSION_RULES_2[rule](date)); + } + } + + var bytes = intArrayFromString(pattern, false); + if (bytes.length > maxsize) { + return 0; + } + + writeArrayToMemory(bytes, s); + return bytes.length-1; }, strftime_l: 'strftime', // no locale support yet + strptime__deps: ['__tm_struct_layout', '_isLeapYear', '_arraySum', '_addDays', '_MONTH_DAYS_REGULAR', '_MONTH_DAYS_LEAP'], strptime: function(buf, format, tm) { // char *strptime(const char *restrict buf, const char *restrict format, struct tm *restrict tm); // http://pubs.opengroup.org/onlinepubs/009695399/functions/strptime.html - // TODO: Implement. + var pattern = Pointer_stringify(format); + + // escape special characters + // TODO: not sure we really need to escape all of these in JS regexps + var SPECIAL_CHARS = '\\!@#$^&*()+=-[]/{}|:<>?,.'; + for (var i=0, ii=SPECIAL_CHARS.length; i<ii; ++i) { + pattern = pattern.replace(new RegExp('\\'+SPECIAL_CHARS[i], 'g'), '\\'+SPECIAL_CHARS[i]); + } + + // reduce number of matchers + var EQUIVALENT_MATCHERS = { + '%A': '%a', + '%B': '%b', + '%c': '%x\\s+%X', + '%D': '%m\\/%d\\/%y', + '%e': '%d', + '%h': '%b', + '%R': '%H\\:%M', + '%r': '%I\\:%M\\:%S\\s%p', + '%T': '%H\\:%M\\:%S', + '%x': '%m\\/%d\\/(?:%y|%Y)', + '%X': '%H\\:%M\\:%S' + }; + for (var matcher in EQUIVALENT_MATCHERS) { + pattern = pattern.replace(matcher, EQUIVALENT_MATCHERS[matcher]); + } + + // TODO: take care of locale + + var DATE_PATTERNS = { + /* weeday name */ '%a': '(?:Sun(?:day)?)|(?:Mon(?:day)?)|(?:Tue(?:sday)?)|(?:Wed(?:nesday)?)|(?:Thu(?:rsday)?)|(?:Fri(?:day)?)|(?:Sat(?:urday)?)', + /* month name */ '%b': '(?:Jan(?:uary)?)|(?:Feb(?:ruary)?)|(?:Mar(?:ch)?)|(?:Apr(?:il)?)|May|(?:Jun(?:e)?)|(?:Jul(?:y)?)|(?:Aug(?:ust)?)|(?:Sep(?:tember)?)|(?:Oct(?:ober)?)|(?:Nov(?:ember)?)|(?:Dec(?:ember)?)', + /* century */ '%C': '\\d\\d', + /* day of month */ '%d': '0[1-9]|[1-9](?!\\d)|1\\d|2\\d|30|31', + /* hour (24hr) */ '%H': '\\d(?!\\d)|[0,1]\\d|20|21|22|23', + /* hour (12hr) */ '%I': '\\d(?!\\d)|0\\d|10|11|12', + /* day of year */ '%j': '00[1-9]|0?[1-9](?!\\d)|0?[1-9]\\d(?!\\d)|[1,2]\\d\\d|3[0-6]\\d', + /* month */ '%m': '0[1-9]|[1-9](?!\\d)|10|11|12', + /* minutes */ '%M': '0\\d|\\d(?!\\d)|[1-5]\\d', + /* whitespace */ '%n': '\\s', + /* AM/PM */ '%p': 'AM|am|PM|pm|A\\.M\\.|a\\.m\\.|P\\.M\\.|p\\.m\\.', + /* seconds */ '%S': '0\\d|\\d(?!\\d)|[1-5]\\d|60', + /* week number */ '%U': '0\\d|\\d(?!\\d)|[1-4]\\d|50|51|52|53', + /* week number */ '%W': '0\\d|\\d(?!\\d)|[1-4]\\d|50|51|52|53', + /* weekday number */ '%w': '[0-6]', + /* 2-digit year */ '%y': '\\d\\d', + /* 4-digit year */ '%Y': '\\d\\d\\d\\d', + /* % */ '%%': '%', + /* whitespace */ '%t': '\\s', + }; + + var MONTH_NUMBERS = {JAN: 0, FEB: 1, MAR: 2, APR: 3, MAY: 4, JUN: 5, JUL: 6, AUG: 7, SEP: 8, OCT: 9, NOV: 10, DEC: 11}; + var DAY_NUMBERS_SUN_FIRST = {SUN: 0, MON: 1, TUE: 2, WED: 3, THU: 4, FRI: 5, SAT: 6}; + var DAY_NUMBERS_MON_FIRST = {MON: 0, TUE: 1, WED: 2, THU: 3, FRI: 4, SAT: 5, SUN: 6}; + + for (var datePattern in DATE_PATTERNS) { + pattern = pattern.replace(datePattern, '('+datePattern+DATE_PATTERNS[datePattern]+')'); + } + + // take care of capturing groups + var capture = []; + for (var i=pattern.indexOf('%'); i>=0; i=pattern.indexOf('%')) { + capture.push(pattern[i+1]); + pattern = pattern.replace(new RegExp('\\%'+pattern[i+1], 'g'), ''); + } + + var matches = new RegExp('^'+pattern).exec(Pointer_stringify(buf)) + // Module['print'](Pointer_stringify(buf)+ ' is matched by '+((new RegExp('^'+pattern)).source)+' into: '+JSON.stringify(matches)); + + var initDate = function() { + var fixup = function(value, min, max) { + return (typeof value !== 'number' || isNaN(value)) ? min : (value>=min ? (value<=max ? value: max): min); + }; + return { + year: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_year', 'i32', 0, 0, 1) }}} + 1900 , 1970, 9999), + month: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_mon', 'i32', 0, 0, 1) }}}, 0, 11), + day: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_mday', 'i32', 0, 0, 1) }}}, 1, 31), + hour: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_hour', 'i32', 0, 0, 1) }}}, 0, 23), + min: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_min', 'i32', 0, 0, 1) }}}, 0, 59), + sec: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_sec', 'i32', 0, 0, 1) }}}, 0, 59) + }; + }; + + if (matches) { + var date = initDate(); + var value; + + var getMatch = function(symbol) { + var pos = capture.indexOf(symbol); + // check if symbol appears in regexp + if (pos >= 0) { + // return matched value or null (falsy!) for non-matches + return matches[pos+1]; + } + return; + } + + // seconds + if ((value=getMatch('S'))) { + date.sec = parseInt(value); + } + + // minutes + if ((value=getMatch('M'))) { + date.min = parseInt(value); + } + + // hours + if ((value=getMatch('H'))) { + // 24h clock + date.hour = parseInt(value); + } else if ((value = getMatch('I'))) { + // AM/PM clock + var hour = parseInt(value); + if ((value=getMatch('p'))) { + hour += value.toUpperCase()[0] === 'P' ? 12 : 0; + } + date.hour = hour; + } + + // year + if ((value=getMatch('Y'))) { + // parse from four-digit year + date.year = parseInt(value); + } else if ((value=getMatch('y'))) { + // parse from two-digit year... + var year = parseInt(value); + if ((value=getMatch('C'))) { + // ...and century + year += parseInt(value)*100; + } else { + // ...and rule-of-thumb + year += year<69 ? 2000 : 1900; + } + date.year = year; + } + + // month + if ((value=getMatch('m'))) { + // parse from month number + date.month = parseInt(value)-1; + } else if ((value=getMatch('b'))) { + // parse from month name + date.month = MONTH_NUMBERS[value.substring(0,3).toUpperCase()] || 0; + // TODO: derive month from day in year+year, week number+day of week+year + } + + // day + if ((value=getMatch('d'))) { + // get day of month directly + date.day = parseInt(value); + } else if ((value=getMatch('j'))) { + // get day of month from day of year ... + var day = parseInt(value); + var leapYear = __isLeapYear(date.year); + for (var month=0; month<12; ++month) { + var daysUntilMonth = __arraySum(leapYear ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, month-1); + if (day<=daysUntilMonth+(leapYear ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR)[month]) { + date.day = day-daysUntilMonth; + } + } + } else if ((value=getMatch('a'))) { + // get day of month from weekday ... + var weekDay = value.substring(0,3).toUpperCase(); + if ((value=getMatch('U'))) { + // ... and week number (Sunday being first day of week) + // Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. + // All days in a new year preceding the first Sunday are considered to be in week 0. + var weekDayNumber = DAY_NUMBERS_SUN_FIRST[weekDay]; + var weekNumber = parseInt(value); + + // January 1st + var janFirst = new Date(date.year, 0, 1); + var endDate; + if (janFirst.getDay() === 0) { + // Jan 1st is a Sunday, and, hence in the 1st CW + endDate = __addDays(janFirst, weekDayNumber+7*(weekNumber-1)); + } else { + // Jan 1st is not a Sunday, and, hence still in the 0th CW + endDate = __addDays(janFirst, 7-janFirst.getDay()+weekDayNumber+7*(weekNumber-1)); + } + date.day = endDate.getDate(); + date.month = endDate.getMonth(); + } else if ((value=getMatch('W'))) { + // ... and week number (Monday being first day of week) + // Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. + // All days in a new year preceding the first Monday are considered to be in week 0. + var weekDayNumber = DAY_NUMBERS_MON_FIRST[weekDay]; + var weekNumber = parseInt(value); + + // January 1st + var janFirst = new Date(date.year, 0, 1); + var endDate; + if (janFirst.getDay()===1) { + // Jan 1st is a Monday, and, hence in the 1st CW + endDate = __addDays(janFirst, weekDayNumber+7*(weekNumber-1)); + } else { + // Jan 1st is not a Monday, and, hence still in the 0th CW + endDate = __addDays(janFirst, 7-janFirst.getDay()+1+weekDayNumber+7*(weekNumber-1)); + } + + date.day = endDate.getDate(); + date.month = endDate.getMonth(); + } + } + + /* + tm_sec int seconds after the minute 0-61* + tm_min int minutes after the hour 0-59 + tm_hour int hours since midnight 0-23 + tm_mday int day of the month 1-31 + tm_mon int months since January 0-11 + tm_year int years since 1900 + tm_wday int days since Sunday 0-6 + tm_yday int days since January 1 0-365 + tm_isdst int Daylight Saving Time flag + */ + + var fullDate = new Date(date.year, date.month, date.day, date.hour, date.min, date.sec, 0); + {{{ makeSetValue('tm', '___tm_struct_layout.tm_sec', 'fullDate.getSeconds()', 'i32') }}} + {{{ makeSetValue('tm', '___tm_struct_layout.tm_min', 'fullDate.getMinutes()', 'i32') }}} + {{{ makeSetValue('tm', '___tm_struct_layout.tm_hour', 'fullDate.getHours()', 'i32') }}} + {{{ makeSetValue('tm', '___tm_struct_layout.tm_mday', 'fullDate.getDate()', 'i32') }}} + {{{ makeSetValue('tm', '___tm_struct_layout.tm_mon', 'fullDate.getMonth()', 'i32') }}} + {{{ makeSetValue('tm', '___tm_struct_layout.tm_year', 'fullDate.getFullYear()-1900', 'i32') }}} + {{{ makeSetValue('tm', '___tm_struct_layout.tm_wday', 'fullDate.getDay()', 'i32') }}} + {{{ makeSetValue('tm', '___tm_struct_layout.tm_yday', '__arraySum(__isLeapYear(fullDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, fullDate.getMonth()-1)+fullDate.getDate()-1', 'i32') }}} + {{{ makeSetValue('tm', '___tm_struct_layout.tm_isdst', '0', 'i32') }}} + + // we need to convert the matched sequence into an integer array to take care of UTF-8 characters > 0x7F + // TODO: not sure that intArrayFromString handles all unicode characters correctly + return buf+intArrayFromString(matches[0]).length-1; + } + return 0; }, strptime_l: 'strptime', // no locale support yet @@ -6328,15 +6953,15 @@ LibraryManager.library = { // NOTE: These are fake, since we don't support the C device creation API. // http://www.kernel.org/doc/man-pages/online/pages/man3/minor.3.html makedev: function(maj, min) { - return 0; + return ((maj) << 8 | (min)); }, gnu_dev_makedev: 'makedev', major: function(dev) { - return 0; + return ((dev) >> 8); }, gnu_dev_major: 'major', minor: function(dev) { - return 0; + return ((dev) & 0xff); }, gnu_dev_minor: 'minor', diff --git a/src/library_gl.js b/src/library_gl.js index 959773bc..ee3f8581 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -1076,7 +1076,9 @@ var LibraryGL = { } {{{ makeSetValue('count', '0', 'len', 'i32') }}}; for (var i = 0; i < len; ++i) { - {{{ makeSetValue('shaders', 'i*4', 'GL.shaders[result[i]]', 'i32') }}}; + var id = GL.shaders.indexOf(result[i]); + assert(id !== -1, 'shader not bound to local id'); + {{{ makeSetValue('shaders', 'i*4', 'id', 'i32') }}}; } }, diff --git a/src/library_sdl.js b/src/library_sdl.js index da682eab..7078aa9d 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -940,7 +940,10 @@ var LibrarySDL = { // TODO }, - SDL_GetKeyboardState: function() { + SDL_GetKeyboardState: function(numKeys) { + if (numKeys) { + {{{ makeSetValue('numKeys', 0, 0x10000, 'i32') }}}; + } return SDL.keyboardState; }, @@ -1097,7 +1100,11 @@ var LibrarySDL = { return ret; }, + rotozoomSurface__deps: ['zoomSurface'], rotozoomSurface: function(src, angle, zoom, smooth) { + if (angle % 360 === 0) { + return _zoomSurface(src, zoom, zoom, smooth); + } var srcData = SDL.surfaces[src]; var w = srcData.width * zoom; var h = srcData.height * zoom; @@ -1828,6 +1835,11 @@ var LibrarySDL = { return Math.floor(fontData.size*0.02); // XXX }, + TTF_FontHeight: function(font) { + var fontData = SDL.fonts[font]; + return fontData.size; + }, + // SDL gfx $SDL_gfx: { diff --git a/src/modules.js b/src/modules.js index a6aa2644..9f419234 100644 --- a/src/modules.js +++ b/src/modules.js @@ -252,12 +252,26 @@ var Functions = { for (var i = 0; i < argTypes.length; i++) { var type = argTypes[i]; if (!type) break; // varargs - sig += isIntImplemented(type) ? (getBits(type) == 64 ? 'ii' : 'i') : 'f'; // legalized i64s will be i32s + if (type in Runtime.FLOAT_TYPES) { + sig += 'f'; + } else { + var chunks = getNumIntChunks(type); + for (var j = 0; j < chunks; j++) sig += 'i'; + } } if (hasVarArgs) sig += 'i'; return sig; }, + getSignatureReturnType: function(sig) { + switch(sig[0]) { + case 'v': return 'void'; + case 'i': return 'i32'; + case 'f': return 'double'; + default: throw 'what is this sig? ' + sig; + } + }, + // Mark a function as needing indexing. Python will coordinate them all getIndex: function(ident, doNotCreate, sig) { if (doNotCreate && !(ident in this.indexedFunctions)) { @@ -449,7 +463,8 @@ var PassManager = { Types: Types, Variables: Variables, Functions: Functions, - EXPORTED_FUNCTIONS: EXPORTED_FUNCTIONS // needed for asm.js global constructors (ctors) + EXPORTED_FUNCTIONS: EXPORTED_FUNCTIONS, // needed for asm.js global constructors (ctors) + Runtime: { GLOBAL_BASE: Runtime.GLOBAL_BASE } })); } else if (phase == 'funcs') { print('\n//FORWARDED_DATA:' + JSON.stringify({ diff --git a/src/parseTools.js b/src/parseTools.js index eb200c65..72166592 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -969,6 +969,12 @@ function generateStructTypes(type) { } ret[index++] = type; } else { + if (Runtime.isStructType(type) && type[1] === '0') { + // this is [0 x something]. When inside another structure like here, it must be at the end, + // and it does nothing + assert(i === typeData.fields.length-1); + return; + } add(Types.types[type]); } var more = (i+1 < typeData.fields.length ? typeData.flatIndexes[i+1] : typeData.flatSize) - (index - start); @@ -1231,7 +1237,7 @@ function indexizeFunctions(value, type) { // add signature to library functions that we now know need indexing var sig = Functions.implementedFunctions[value] || Functions.unimplementedFunctions[value]; if (!sig) { - sig = Functions.unimplementedFunctions[value] = Functions.getSignature(out.returnType, out.segments ? out.segments.map(function(segment) { return segment[0].text }) : []); + sig = Functions.unimplementedFunctions[value] = Functions.getSignature(out.returnType, out.segments ? out.segments.map(function(segment) { return segment[0].text }) : [], isVarArgsFunctionType(type)); } return Functions.getIndex(value, undefined, sig); } @@ -1694,7 +1700,7 @@ function makeGetSlabs(ptr, type, allowMultiple, unsigned) { } case 'float': return ['HEAPF32']; default: { - throw 'what, exactly, can we do for unknown types in TA2?! ' + new Error().stack; + throw 'what, exactly, can we do for unknown types in TA2?! ' + [new Error().stack, ptr, type, allowMultiple, unsigned]; } } } diff --git a/src/preamble.js b/src/preamble.js index c9e7e4eb..411abce6 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -796,6 +796,7 @@ Math['imul'] = function(a, b) { return (a*b)|0; // fast but imprecise }; #endif +Math.imul = Math['imul']; // A counter of dependencies for calling run(). If we need to // do asynchronous work before running, increment this and diff --git a/src/relooper/Relooper.cpp b/src/relooper/Relooper.cpp index ca9c6ab1..a457914b 100644 --- a/src/relooper/Relooper.cpp +++ b/src/relooper/Relooper.cpp @@ -180,7 +180,7 @@ void Block::Render(bool InLoop) { Block *DefaultTarget(NULL); // The block we branch to without checking the condition, if none of the other conditions held. - // We must do this here, because blocks can be split and even comparing their Ids is not enough. We must check the conditions. + // Find the default target, the one without a condition for (BlockBranchMap::iterator iter = ProcessedBranchesOut.begin(); iter != ProcessedBranchesOut.end(); iter++) { if (!iter->second->Condition) { assert(!DefaultTarget); // Must be exactly one default diff --git a/src/runtime.js b/src/runtime.js index 684f11e7..8c2c8f4d 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -201,14 +201,24 @@ var Runtime = { type.alignSize = 0; var diffs = []; var prev = -1; + var index = 0; type.flatIndexes = type.fields.map(function(field) { + index++; var size, alignSize; if (Runtime.isNumberType(field) || Runtime.isPointerType(field)) { size = Runtime.getNativeTypeSize(field); // pack char; char; in structs, also char[X]s. alignSize = Runtime.getAlignSize(field, size); } else if (Runtime.isStructType(field)) { - size = Types.types[field].flatSize; - alignSize = Runtime.getAlignSize(null, Types.types[field].alignSize); + if (field[1] === '0') { + // this is [0 x something]. When inside another structure like here, it must be at the end, + // and it adds no size + assert(index === type.fields.length); + size = 0; + alignSize = type.alignSize || QUANTUM_SIZE; + } else { + size = Types.types[field].flatSize; + alignSize = Runtime.getAlignSize(null, Types.types[field].alignSize); + } } else if (field[0] == 'b') { // bN, large number field, like a [N x i8] size = field.substr(1)|0; diff --git a/src/settings.js b/src/settings.js index 7f9dca3b..b7460cf2 100644 --- a/src/settings.js +++ b/src/settings.js @@ -125,6 +125,11 @@ var INLINING_LIMIT = 0; // A limit on inlining. If 0, we will inline normally i // we will prevent inlining of functions of this size or larger // in closure. 50 is a reasonable setting if you do not want // inlining +var OUTLINING_LIMIT = 0; // A function size above which we try to automatically break up + // functions into smaller ones, to avoid the downsides of very + // large functions (JS engines often compile them very slowly, + // compile them with lower optimizations, or do not optimize them + // at all). If 0, we do not perform outlining at all. // Generated code debugging options var SAFE_HEAP = 0; // Check each write to the heap, for example, this will give a clear @@ -238,6 +243,8 @@ var FS_LOG = 0; // Log all FS operations. This is especially helpful when you'r // a new project and want to see a list of file system operations happening // so that you can create a virtual file system with all of the required files. +var USE_OLD_FS = 1; // Switch to toggle the new / old FS code. Currently only used for testing purposes. + var USE_BSS = 1; // https://en.wikipedia.org/wiki/.bss // When enabled, 0-initialized globals are sorted to the end of the globals list, // enabling us to not explicitly store the initialization value for each 0 byte. @@ -327,6 +334,10 @@ var LINKABLE = 0; // If set to 1, this file can be linked with others, either as // LINKABLE of 0 is very useful in that we can reduce the size of the // generated code very significantly, by removing everything not actually used. +var DLOPEN_SUPPORT = 0; // Whether to support dlopen(NULL, ...) which enables dynamic access to the + // module's functions and globals. Implies LINKABLE=1, because we do not want + // dead code elimination. + var RUNTIME_TYPE_INFO = 0; // Whether to expose type info to the script at run time. This // increases the size of the generated script, but allows you // to more easily perform operations from handwritten JS on @@ -403,7 +414,6 @@ var DEBUG_TAGS_SHOWING = []; // metadata // legalizer - // A cached set of defines, generated from the header files. This // lets the emscripten libc (library.js) see the right values. // If you the headers or use different ones, you will need to override 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/system/include/libc/sys/stat.h b/system/include/libc/sys/stat.h index e2b20187..2285b294 100644 --- a/system/include/libc/sys/stat.h +++ b/system/include/libc/sys/stat.h @@ -179,7 +179,8 @@ struct stat64 #endif int _EXFUN(chmod,( const char *__path, mode_t __mode )); -int _EXFUN(fchmod,(int __fd, mode_t __mode)); +int _EXFUN(lchmod,( const char *__path, mode_t __mode )); +int _EXFUN(fchmod,(int __fd, mode_t __mode)); int _EXFUN(fstat,( int __fd, struct stat *__sbuf )); int _EXFUN(fstat64,( int __fd, struct stat64 *__sbuf )); /* XXX Emscripten */ int _EXFUN(mkdir,( const char *_path, mode_t __mode )); diff --git a/system/include/libc/sys/types.h b/system/include/libc/sys/types.h index c36f724c..fe5d552a 100644 --- a/system/include/libc/sys/types.h +++ b/system/include/libc/sys/types.h @@ -159,6 +159,10 @@ typedef __gid_t gid_t; typedef __id_t id_t ; /* can hold a uid_t or pid_t */ #endif +__int32_t major(__uint32_t _x); +__int32_t minor(__uint32_t _x); +dev_t makedev(__uint32_t _major, __uint32_t _minor); + #if defined(__XMK__) typedef signed char pid_t; #else diff --git a/system/include/net/netinet/in.h b/system/include/net/netinet/in.h index cf324f3d..fba1a7b3 100644 --- a/system/include/net/netinet/in.h +++ b/system/include/net/netinet/in.h @@ -55,6 +55,13 @@ extern const struct in6_addr in6addr_interfacelocal_allnodes; extern const struct in6_addr in6addr_interfacelocal_allrouters; extern const struct in6_addr in6addr_sitelocal_allrouters; +#define IN6ADDR_ANY_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } } +#define IN6ADDR_LOOPBACK_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } } +#define IN6ADDR_LINKLOCAL_ALLNODES_INIT \ + { { { 0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } } +#define IN6ADDR_LINKLOCAL_ALLROUTERS_INIT \ + { { { 0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2 } } } + struct sockaddr_in6 { int sin6_family; unsigned short sin6_port; diff --git a/system/include/sys/socket.h b/system/include/sys/socket.h index 3168f85b..9650bb9a 100644 --- a/system/include/sys/socket.h +++ b/system/include/sys/socket.h @@ -34,7 +34,8 @@ extern "C" { #define SO_LINGER 130 #define SO_BSDCOMPAT 140 -#define SHUT_RD 1 +#define SHUT_RD 0 +#define SHUT_WR 1 #define SHUT_RDWR 2 typedef unsigned int sa_family_t; 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/cases/i32_mem.ll b/tests/cases/i32_mem.ll new file mode 100644 index 00000000..e50014ca --- /dev/null +++ b/tests/cases/i32_mem.ll @@ -0,0 +1,23 @@ +; ModuleID = 'tests/hello_world.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 [15 x i8] c".%x.\0A\00", align 1 ; [#uses=1 type=[5 x i8]*] + +define i32 @main() { +entry: + %mem = alloca i32 + store i32 4279383126, i32* %mem + %i24 = bitcast i32* %mem to i24* + %load = load i24* %i24, align 4 + %load32 = zext i24 %load to i32 + %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([5 x i8]* @.str, i32 0, i32 0), i32 %load32) + %val_24 = trunc i32 4041265344 to i24 + store i24 %val_24, i24* %i24, align 4 + %load32b = load i32* %mem, align 4 + %call2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([5 x i8]* @.str, i32 0, i32 0), i32 %load32b) + ret i32 1 +} + +; [#uses=1] +declare i32 @printf(i8*, ...) diff --git a/tests/cases/i32_mem.txt b/tests/cases/i32_mem.txt new file mode 100644 index 00000000..683e58e2 --- /dev/null +++ b/tests/cases/i32_mem.txt @@ -0,0 +1,2 @@ +.123456. +.ffe0d0c0. diff --git a/tests/cases/sillyfuncast.ll b/tests/cases/sillyfuncast.ll new file mode 100644 index 00000000..36c26720 --- /dev/null +++ b/tests/cases/sillyfuncast.ll @@ -0,0 +1,23 @@ +; ModuleID = 'tests/hello_world.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 [15 x i8] c"hello, world!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*] + +define void @doit() { + %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0 type=i32] + ret void +} + +define i32 @main() { +entry: + %retval = alloca i32, align 4 ; [#uses=1 type=i32*] + store i32 0, i32* %retval + %58 = tail call i32 bitcast (void ()* @doit to i32 ()*)() nounwind + ret i32 1 +} + +declare i32 @printf(i8*, ...) + + + diff --git a/tests/cases/sillyfuncast2.ll b/tests/cases/sillyfuncast2.ll new file mode 100644 index 00000000..f72ebe28 --- /dev/null +++ b/tests/cases/sillyfuncast2.ll @@ -0,0 +1,21 @@ +; ModuleID = 'tests/hello_world.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 [15 x i8] c"hello, world!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*] + +define void @doit(i32 %one, i32 %two) { + %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0 type=i32] + ret void +} + +define i32 @main() { +entry: + %retval = alloca i32, align 4 ; [#uses=1 type=i32*] + store i32 0, i32* %retval + call i32 bitcast (void (i32, i32)* @doit to i32 (i32, i64)*)(i32 0, i64 0) nounwind + ret i32 1 +} + +declare i32 @printf(i8*, ...) + diff --git a/tests/cases/zeroembedded.ll b/tests/cases/zeroembedded.ll new file mode 100644 index 00000000..6a4f6073 --- /dev/null +++ b/tests/cases/zeroembedded.ll @@ -0,0 +1,23 @@ +; a.ll +%struct.pypy_str = type { i32, [0 x i8] } +%struct.pypy_strval = type { i32, [13 x i8] } + +%union.pypy_array3_len0u = type { %struct.pypy_array3_len0 } +%struct.pypy_array3_len0 = type { i32, i32, [0 x i8] } + +@pypy_g_strval = global %struct.pypy_strval { i32 13, [13 x i8] c"hello world\0A\00" } +@pypy_g_strval2 = global %struct.pypy_array3_len0 { i32 13, i32 111, [0 x i8] c"" } + +declare i32 @printf(i8*, ...) + +define i32 @main(i32 %argc, i8** nocapture %argv) { + %waka = alloca %struct.pypy_array3_len0 + %1 = bitcast %struct.pypy_strval* @pypy_g_strval to %struct.pypy_str* + %2 = getelementptr inbounds %struct.pypy_str* %1, i32 1 + %3 = bitcast %struct.pypy_str* %2 to i8* + call i32 (i8*, ...)* @printf(i8* %3) + %unneeded = bitcast %struct.pypy_str* %2 to %struct.pypy_array3_len0* + call i32 (i8*, ...)* @printf(i8* %3, %struct.pypy_array3_len0* %unneeded) + ret i32 0 +} + diff --git a/tests/cases/zeroembedded.txt b/tests/cases/zeroembedded.txt new file mode 100644 index 00000000..3b18e512 --- /dev/null +++ b/tests/cases/zeroembedded.txt @@ -0,0 +1 @@ +hello world 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/glgetattachedshaders.c b/tests/glgetattachedshaders.c new file mode 100644 index 00000000..303e0f92 --- /dev/null +++ b/tests/glgetattachedshaders.c @@ -0,0 +1,93 @@ +#include <GLES2/gl2.h> +#include <EGL/egl.h> +#include <stdio.h> +#include <stdlib.h> + +static void die(const char *msg) +{ + printf("%s\n", msg); + abort(); +} + +static void create_context(void) +{ + EGLint num_config; + EGLContext g_egl_ctx; + EGLDisplay g_egl_dpy; + EGLConfig g_config; + + static const EGLint attribute_list[] = + { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE + }; + + static const EGLint context_attributes[] = + { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + g_egl_dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (!g_egl_dpy) + die("failed to create display"); + + if (!eglInitialize(g_egl_dpy, NULL, NULL)) + die("failed to initialize egl"); + + if (!eglChooseConfig(g_egl_dpy, attribute_list, &g_config, 1, &num_config)) + die("failed to choose config"); + + g_egl_ctx = eglCreateContext(g_egl_dpy, g_config, EGL_NO_CONTEXT, context_attributes); + if (!g_egl_ctx) + die("failed to create context"); +} + +int main(int argc, char *argv[]) +{ + unsigned i; + + create_context(); + + GLuint prog = glCreateProgram(); + if (glGetError()) + die("failed to create program"); + + GLuint vertex = glCreateShader(GL_VERTEX_SHADER); + if (glGetError()) + die("failed to create vertex shader"); + glAttachShader(prog, vertex); + + GLuint fragment = glCreateShader(GL_FRAGMENT_SHADER); + if (glGetError()) + die("failed to create fragment shader"); + glAttachShader(prog, fragment); + + GLuint shaders[2]; + GLsizei count; + + glGetAttachedShaders(prog, 2, &count, shaders); + if (glGetError()) + die("failed to get attached shaders"); + if (count != 2) + die("unknown number of shaders returned"); + if (shaders[0] == shaders[1]) + die("returned identical shaders"); + + for (i = 0; i < count; i++) + { + if (shaders[i] == 0) + die("returned 0"); + if (shaders[i] != vertex && shaders[i] != fragment) + die("unknown shader returned"); + } + + int result = 1; + REPORT_RESULT(); + + return 0; +} diff --git a/tests/runner.py b/tests/runner.py index 06d3a190..4a9bf623 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -27,6 +27,7 @@ Running the main part of the test suite. Don't forget to run the other parts! benchmark - run before and after each set of changes before pushing to master, verify no regressions browser - runs pages in a web browser + browser audio - runs audio tests in a web browser (requires human verification) To run one of those parts, do something like @@ -270,6 +271,8 @@ process(sys.argv[1]) print >> sys.stderr, "[was asm.js'ified]" elif 'asm.js' in err: # if no asm.js error, then not an odin build raise Exception("did NOT asm.js'ify") + err = '\n'.join(filter(lambda line: 'successfully compiled asm.js code' not in line, err.split('\n'))) + return err def run_generated_code(self, engine, filename, args=[], check_timeout=True, output_nicerizer=None): stdout = os.path.join(self.get_dir(), 'stdout') # use files, as PIPE can get too full and hang us @@ -285,7 +288,7 @@ process(sys.argv[1]) out = open(stdout, 'r').read() err = open(stderr, 'r').read() if engine == SPIDERMONKEY_ENGINE and Settings.ASM_JS: - self.validate_asmjs(err) + err = self.validate_asmjs(err) if output_nicerizer: ret = output_nicerizer(out, err) else: @@ -1981,6 +1984,18 @@ Succeeded! self.do_run(src, "1.0 2.0 -1.0 -2.0 2.0 3.0 -2.0 -3.0 " "1 2 -1 -2 2 2 -2 -2") + def test_llrint(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2') + src = r''' + #include <stdio.h> + #include <math.h> + int main() { + printf("%lld\n%lld\n%lld\n%lld\n", llrint(0.1), llrint(0.6), llrint(1.25), llrint(1099511627776.667)); + return 0; + } + ''' + self.do_run(src, '0\n1\n1\n1099511627777\n') + def test_getgep(self): # Generated code includes getelementptr (getelementptr, 0, 1), i.e., GEP as the first param to GEP src = ''' @@ -2954,6 +2969,37 @@ back self.emcc_args.pop() ; self.emcc_args.pop() # disable closure to work around a closure bug self.do_run(src, 'Throw...Construct...Catched...Destruct...Throw...Construct...Copy...Catched...Destruct...Destruct...') + def test_exception_2(self): + if self.emcc_args is None: return self.skip('need emcc to add in libcxx properly') + Settings.DISABLE_EXCEPTION_CATCHING = 0 + src = r''' + #include <stdexcept> + #include <stdio.h> + + typedef void (*FuncPtr)(); + + void ThrowException() + { + throw std::runtime_error("catch me!"); + } + + FuncPtr ptr = ThrowException; + + int main() + { + try + { + ptr(); + } + catch(...) + { + printf("Exception caught successfully!\n"); + } + return 0; + } + ''' + self.do_run(src, 'Exception caught successfully!') + def test_white_list_exception(self): Settings.DISABLE_EXCEPTION_CATCHING = 2 Settings.EXCEPTION_CATCHING_WHITELIST = ["__Z12somefunctionv"] @@ -4509,6 +4555,22 @@ The current address of a is: 0x12345678 The current type of b is: 9 ''') + def test_functionpointer_libfunc_varargs(self): + src = r''' + #include <stdio.h> + #include <fcntl.h> + typedef int (*fp_t)(int, int, ...); + int main(int argc, char **argv) { + fp_t fp = &fcntl; + if (argc == 1337) fp = (fp_t)&main; + (*fp)(0, 10); + (*fp)(0, 10, 5); + printf("waka\n"); + return 0; + } + ''' + self.do_run(src, '''waka''') + def test_structbyval(self): Settings.INLINING_LIMIT = 50 @@ -4958,6 +5020,286 @@ The current type of b is: 9 ''' self.do_run(src, 'time: ') # compilation check, mainly + + def test_strptime_tm(self): + src=r''' + #include <time.h> + #include <stdio.h> + #include <string.h> + + int main() { + struct tm tm; + char *ptr = strptime("17410105012000", "%H%M%S%d%m%Y", &tm); + + printf("%s: %s, %d/%d/%d %d:%d:%d", + (ptr != NULL && *ptr=='\0') ? "OK" : "ERR", + tm.tm_wday == 0 ? "Sun" : (tm.tm_wday == 1 ? "Mon" : (tm.tm_wday == 2 ? "Tue" : (tm.tm_wday == 3 ? "Wed" : (tm.tm_wday == 4 ? "Thu" : (tm.tm_wday == 5 ? "Fri" : (tm.tm_wday == 6 ? "Sat" : "ERR")))))), + tm.tm_mon+1, + tm.tm_mday, + tm.tm_year+1900, + tm.tm_hour, + tm.tm_min, + tm.tm_sec + ); + } + ''' + self.do_run(src, 'OK: Wed, 1/5/2000 17:41:1') + + def test_strptime_days(self): + src = r''' + #include <time.h> + #include <stdio.h> + #include <string.h> + + static const struct { + const char *input; + const char *format; + } day_tests[] = { + { "2000-01-01", "%Y-%m-%d"}, + { "03/03/00", "%D"}, + { "9/9/99", "%x"}, + { "19990502123412", "%Y%m%d%H%M%S"}, + { "2001 20 Mon", "%Y %U %a"}, + { "2006 4 Fri", "%Y %U %a"}, + { "2001 21 Mon", "%Y %W %a"}, + { "2013 29 Wed", "%Y %W %a"}, + { "2000-01-01 08:12:21 AM", "%Y-%m-%d %I:%M:%S %p"}, + { "2000-01-01 08:12:21 PM", "%Y-%m-%d %I:%M:%S %p"}, + { "2001 17 Tue", "%Y %U %a"}, + { "2001 8 Thursday", "%Y %W %a"}, + }; + + int main() { + struct tm tm; + + for (int i = 0; i < sizeof (day_tests) / sizeof (day_tests[0]); ++i) { + memset (&tm, '\0', sizeof (tm)); + char *ptr = strptime(day_tests[i].input, day_tests[i].format, &tm); + + printf("%s: %d/%d/%d (%dth DoW, %dth DoY)\n", (ptr != NULL && *ptr=='\0') ? "OK" : "ERR", tm.tm_mon+1, tm.tm_mday, 1900+tm.tm_year, tm.tm_wday, tm.tm_yday); + } + } + ''' + self.do_run(src, 'OK: 1/1/2000 (6th DoW, 0th DoY)\n'\ + 'OK: 3/3/2000 (5th DoW, 62th DoY)\n'\ + 'OK: 9/9/1999 (4th DoW, 251th DoY)\n'\ + 'OK: 5/2/1999 (0th DoW, 121th DoY)\n'\ + 'OK: 5/21/2001 (1th DoW, 140th DoY)\n'\ + 'OK: 1/27/2006 (5th DoW, 26th DoY)\n'\ + 'OK: 5/21/2001 (1th DoW, 140th DoY)\n'\ + 'OK: 7/24/2013 (3th DoW, 204th DoY)\n'\ + 'OK: 1/1/2000 (6th DoW, 0th DoY)\n'\ + 'OK: 1/1/2000 (6th DoW, 0th DoY)\n'\ + 'OK: 5/1/2001 (2th DoW, 120th DoY)\n'\ + 'OK: 2/22/2001 (4th DoW, 52th DoY)\n'\ + ) + + def test_strptime_reentrant(self): + src=r''' + #include <time.h> + #include <stdio.h> + #include <string.h> + #include <stdlib.h> + + int main () { + int result = 0; + struct tm tm; + + memset (&tm, 0xaa, sizeof (tm)); + + /* Test we don't crash on uninitialized struct tm. + Some fields might contain bogus values until everything + needed is initialized, but we shouldn't crash. */ + if (strptime ("2007", "%Y", &tm) == NULL + || strptime ("12", "%d", &tm) == NULL + || strptime ("Feb", "%b", &tm) == NULL + || strptime ("13", "%M", &tm) == NULL + || strptime ("21", "%S", &tm) == NULL + || strptime ("16", "%H", &tm) == NULL) { + printf("ERR: returned NULL"); + exit(EXIT_FAILURE); + } + + if (tm.tm_sec != 21 || tm.tm_min != 13 || tm.tm_hour != 16 + || tm.tm_mday != 12 || tm.tm_mon != 1 || tm.tm_year != 107 + || tm.tm_wday != 1 || tm.tm_yday != 42) { + printf("ERR: unexpected tm content (1) - %d/%d/%d %d:%d:%d", tm.tm_mon+1, tm.tm_mday, tm.tm_year+1900, tm.tm_hour, tm.tm_min, tm.tm_sec); + exit(EXIT_FAILURE); + } + + if (strptime ("8", "%d", &tm) == NULL) { + printf("ERR: strptime failed"); + exit(EXIT_FAILURE); + } + + if (tm.tm_sec != 21 || tm.tm_min != 13 || tm.tm_hour != 16 + || tm.tm_mday != 8 || tm.tm_mon != 1 || tm.tm_year != 107 + || tm.tm_wday != 4 || tm.tm_yday != 38) { + printf("ERR: unexpected tm content (2) - %d/%d/%d %d:%d:%d", tm.tm_mon+1, tm.tm_mday, tm.tm_year+1900, tm.tm_hour, tm.tm_min, tm.tm_sec); + exit(EXIT_FAILURE); + } + + printf("OK"); + } + ''' + self.do_run(src, 'OK') + + def test_strftime(self): + src=r''' + #include <time.h> + #include <stdio.h> + #include <string.h> + #include <stdlib.h> + + void test(int result, const char* comment, const char* parsed = "") { + printf("%d",result); + if (!result) { + printf("\nERROR: %s (\"%s\")\n", comment, parsed); + } + } + + int cmp(const char *s1, const char *s2) { + for ( ; *s1 == *s2 ; s1++,s2++ ) { + if ( *s1 == '\0' ) + break; + } + + return (*s1 - *s2); + } + + int main() { + struct tm tm; + char s[1000]; + size_t size; + + tm.tm_sec = 4; + tm.tm_min = 23; + tm.tm_hour = 20; + tm.tm_mday = 21; + tm.tm_mon = 1; + tm.tm_year = 74; + tm.tm_wday = 4; + tm.tm_yday = 51; + tm.tm_isdst = 0; + + size = strftime(s, 1000, "", &tm); + test((size==0) && (*s=='\0'), "strftime test #1", s); + + size = strftime(s, 1000, "%a", &tm); + test((size==3) && !cmp(s, "Thu"), "strftime test #2", s); + + size = strftime(s, 1000, "%A", &tm); + test((size==8) && !cmp(s, "Thursday"), "strftime test #3", s); + + size = strftime(s, 1000, "%b", &tm); + test((size==3) && !cmp(s, "Feb"), "strftime test #4", s); + + size = strftime(s, 1000, "%B", &tm); + test((size==8) && !cmp(s, "February"), + "strftime test #5", s); + + size = strftime(s, 1000, "%d", &tm); + test((size==2) && !cmp(s, "21"), + "strftime test #6", s); + + size = strftime(s, 1000, "%H", &tm); + test((size==2) && !cmp(s, "20"), + "strftime test #7", s); + + size = strftime(s, 1000, "%I", &tm); + test((size==2) && !cmp(s, "08"), + "strftime test #8", s); + + size = strftime(s, 1000, "%j", &tm); + test((size==3) && !cmp(s, "052"), + "strftime test #9", s); + + size = strftime(s, 1000, "%m", &tm); + test((size==2) && !cmp(s, "02"), + "strftime test #10", s); + + size = strftime(s, 1000, "%M", &tm); + test((size==2) && !cmp(s, "23"), + "strftime test #11", s); + + size = strftime(s, 1000, "%p", &tm); + test((size==2) && !cmp(s, "PM"), + "strftime test #12", s); + + size = strftime(s, 1000, "%S", &tm); + test((size==2) && !cmp(s, "04"), + "strftime test #13", s); + + size = strftime(s, 1000, "%U", &tm); + test((size==2) && !cmp(s, "07"), + "strftime test #14", s); + + size = strftime(s, 1000, "%w", &tm); + test((size==1) && !cmp(s, "4"), + "strftime test #15", s); + + size = strftime(s, 1000, "%W", &tm); + test((size==2) && !cmp(s, "07"), + "strftime test #16", s); + + size = strftime(s, 1000, "%y", &tm); + test((size==2) && !cmp(s, "74"), + "strftime test #17", s); + + size = strftime(s, 1000, "%Y", &tm); + test((size==4) && !cmp(s, "1974"), + "strftime test #18", s); + + size = strftime(s, 1000, "%%", &tm); + test((size==1) && !cmp(s, "%"), + "strftime test #19", s); + + size = strftime(s, 5, "%Y", &tm); + test((size==4) && !cmp(s, "1974"), + "strftime test #20", s); + + size = strftime(s, 4, "%Y", &tm); + test((size==0), "strftime test #21", s); + + tm.tm_mon = 0; + tm.tm_mday = 1; + size = strftime(s, 10, "%U", &tm); + test((size==2) && !cmp(s, "00"), "strftime test #22", s); + + size = strftime(s, 10, "%W", &tm); + test((size==2) && !cmp(s, "00"), "strftime test #23", s); + + // 1/1/1973 was a Sunday and is in CW 1 + tm.tm_year = 73; + size = strftime(s, 10, "%W", &tm); + test((size==2) && !cmp(s, "01"), "strftime test #24", s); + + // 1/1/1978 was a Monday and is in CW 1 + tm.tm_year = 78; + size = strftime(s, 10, "%U", &tm); + test((size==2) && !cmp(s, "01"), "strftime test #25", s); + + // 2/1/1999 + tm.tm_year = 99; + tm.tm_yday = 1; + size = strftime(s, 10, "%G (%V)", &tm); + test((size==9) && !cmp(s, "1998 (53)"), "strftime test #26", s); + + size = strftime(s, 10, "%g", &tm); + test((size==2) && !cmp(s, "98"), "strftime test #27", s); + + // 30/12/1997 + tm.tm_year = 97; + tm.tm_yday = 363; + size = strftime(s, 10, "%G (%V)", &tm); + test((size==9) && !cmp(s, "1998 (01)"), "strftime test #28", s); + + size = strftime(s, 10, "%g", &tm); + test((size==2) && !cmp(s, "98"), "strftime test #29", s); + } + ''' + self.do_run(src, '11111111111111111111111111111') + def test_intentional_fault(self): # Some programs intentionally segfault themselves, we should compile that into a throw src = r''' @@ -5885,6 +6227,50 @@ def process(filename): self.do_run(src, '100\n200\n13\n42\n', post_build=add_pre_run_and_checks) + def test_dlfcn_self(self): + if Settings.USE_TYPED_ARRAYS == 1: return self.skip('Does not work with USE_TYPED_ARRAYS=1') + Settings.DLOPEN_SUPPORT = 1 + + src = r''' +#include <stdio.h> +#include <dlfcn.h> + +int global = 123; + +extern "C" __attribute__((noinline)) void foo(int x) { + printf("%d\n", x); +} + +extern "C" __attribute__((noinline)) void repeatable() { + void* self = dlopen(NULL, RTLD_LAZY); + int* global_ptr = (int*)dlsym(self, "global"); + void (*foo_ptr)(int) = (void (*)(int))dlsym(self, "foo"); + foo_ptr(*global_ptr); + dlclose(self); +} + +int main() { + repeatable(); + repeatable(); + return 0; +}''' + def post(filename): + with open(filename) as f: + for line in f: + if 'var SYMBOL_TABLE' in line: + table = line + break + else: + raise Exception('Could not find symbol table!') + import json + table = json.loads(table[table.find('{'):table.rfind('}')+1]) + actual = list(sorted(table.keys())) + # ensure there aren't too many globals; we don't want unnamed_addr + assert actual == ['_foo', '_global', '_main', '_repeatable'], \ + "Symbol table does not match: %s" % actual + + self.do_run(src, '123\n123', post_build=(None, post)) + def test_rand(self): return self.skip('rand() is now random') # FIXME @@ -6605,6 +6991,26 @@ 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> + #include <string.h> + int main() + { + char *date = "18.07.2013w"; + char c[10]; + memset(c, 0, 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") @@ -6673,7 +7079,7 @@ def process(filename): other.close() src = open(path_from_root('tests', 'files.cpp'), 'r').read() - self.do_run(src, 'size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\ntexte\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\nok.\n', + self.do_run(src, 'size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\nok.\ntexte\n', post_build=post, extra_emscripten_args=['-H', 'libc/fcntl.h']) def test_files_m(self): @@ -6705,7 +7111,7 @@ def process(filename): return 0; } ''' - self.do_run(src, 'isatty? 0,0,1\ngot: 35\ngot: 45\ngot: 25\ngot: 15\n', post_build=post) + self.do_run(src, 'got: 35\ngot: 45\ngot: 25\ngot: 15\nisatty? 0,0,1\n', post_build=post) def test_fwrite_0(self): src = r''' @@ -6799,23 +7205,19 @@ def process(filename): self.do_run(src, 'success', force_c=True) def test_stat(self): - add_pre_run = ''' -def process(filename): - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - \'\'\' - var f1 = FS.createFolder('/', 'test', true, true); - var f2 = FS.createDataFile(f1, 'file', 'abcdef', true, true); - var f3 = FS.createLink(f1, 'link', 'file', true, true); - var f4 = FS.createDevice(f1, 'device', function(){}, function(){}); - f1.timestamp = f2.timestamp = f3.timestamp = f4.timestamp = new Date(1200000000000); - \'\'\' - ) - open(filename, 'w').write(src) -''' - src = open(path_from_root('tests', 'stat', 'src.c'), 'r').read() - expected = open(path_from_root('tests', 'stat', 'output.txt'), 'r').read() - self.do_run(src, expected, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h']) + Building.COMPILER_TEST_OPTS += ['-DUSE_OLD_FS='+str(Settings.USE_OLD_FS)] + src = open(path_from_root('tests', 'stat', 'test_stat.c'), 'r').read() + self.do_run(src, 'success', force_c=True) + + def test_stat_chmod(self): + Building.COMPILER_TEST_OPTS += ['-DUSE_OLD_FS='+str(Settings.USE_OLD_FS)] + src = open(path_from_root('tests', 'stat', 'test_chmod.c'), 'r').read() + self.do_run(src, 'success', force_c=True) + + def test_stat_mknod(self): + Building.COMPILER_TEST_OPTS += ['-DUSE_OLD_FS='+str(Settings.USE_OLD_FS)] + src = open(path_from_root('tests', 'stat', 'test_mknod.c'), 'r').read() + self.do_run(src, 'success', force_c=True) def test_fcntl(self): add_pre_run = ''' @@ -7197,18 +7599,8 @@ def process(filename): self.do_run(src, expected) def test_unistd_unlink(self): - add_pre_run = ''' -def process(filename): - import tools.shared as shared - src = open(filename, 'r').read().replace( - '// {{PRE_RUN_ADDITIONS}}', - open(shared.path_from_root('tests', 'unistd', 'unlink.js'), 'r').read() - ) - open(filename, 'w').write(src) -''' src = open(path_from_root('tests', 'unistd', 'unlink.c'), 'r').read() - expected = open(path_from_root('tests', 'unistd', 'unlink.out'), 'r').read() - self.do_run(src, expected, post_build=add_pre_run) + self.do_run(src, 'success', force_c=True) def test_unistd_links(self): add_pre_run = ''' @@ -9664,7 +10056,7 @@ def process(filename): Settings.CORRECT_ROUNDINGS = 0 self.do_run(src.replace('TYPE', 'long long'), '*-3**2**-6**5*') # JS floor operations, always to the negative. This is an undetected error here! self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # We get these right, since they are 32-bit and we can shortcut using the |0 trick - self.do_run(src.replace('TYPE', 'unsigned int'), '*-3**2**-6**5*') # We fail, since no fast shortcut for 32-bit unsigneds + self.do_run(src.replace('TYPE', 'unsigned int'), '*-2**2**-6**5*') Settings.CORRECT_ROUNDINGS = 1 Settings.CORRECT_SIGNS = 1 # To be correct here, we need sign corrections as well @@ -9678,7 +10070,7 @@ def process(filename): Settings.CORRECT_ROUNDINGS_LINES = ["src.cpp:13"] # Fix just the last mistake self.do_run(src.replace('TYPE', 'long long'), '*-3**2**-5**5*') self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # Here we are lucky and also get the first one right - self.do_run(src.replace('TYPE', 'unsigned int'), '*-3**2**-5**5*') # No such luck here + self.do_run(src.replace('TYPE', 'unsigned int'), '*-2**2**-5**5*') # And reverse the check with = 2 if Settings.USE_TYPED_ARRAYS != 2: # the errors here are very specific to non-i64 mode 1 @@ -9854,6 +10246,11 @@ finalizing 3 (global == 0) for k, v in self.env.iteritems(): del os.environ[k] + # clear global changes to Building + Building.COMPILER_TEST_OPTS = [] + Building.COMPILER = CLANG + Building.LLVM_OPTS = 0 + TT.tearDown = tearDown def setUp(self): @@ -10451,7 +10848,7 @@ f.close() Popen([PYTHON, EMLINK, 'main.js', 'side.js', 'together.js'], stdout=PIPE).communicate() assert os.path.exists('together.js') for engine in JS_ENGINES: - out = run_js('together.js', engine=SPIDERMONKEY_ENGINE, stderr=PIPE, full_output=True) + out = run_js('together.js', engine=engine, stderr=PIPE, full_output=True) self.assertContained(expected, out) if engine == SPIDERMONKEY_ENGINE: self.validate_asmjs(out) if first: @@ -10642,6 +11039,51 @@ 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 [500, 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(), + { + 500: (340, 345), # too big, need if-else chain flattening + 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.') @@ -11366,11 +11808,11 @@ f.close() def test_js_optimizer(self): for input, expected, passes in [ (path_from_root('tools', 'test-js-optimizer.js'), open(path_from_root('tools', 'test-js-optimizer-output.js')).read(), - ['hoistMultiples', 'loopOptimizer', 'removeAssignsToUndefined', 'simplifyExpressionsPre', 'simplifyExpressionsPost']), + ['hoistMultiples', 'loopOptimizer', 'removeAssignsToUndefined', 'simplifyExpressions']), (path_from_root('tools', 'test-js-optimizer-t2c.js'), open(path_from_root('tools', 'test-js-optimizer-t2c-output.js')).read(), - ['simplifyExpressionsPre', 'optimizeShiftsConservative']), + ['simplifyExpressions', 'optimizeShiftsConservative']), (path_from_root('tools', 'test-js-optimizer-t2.js'), open(path_from_root('tools', 'test-js-optimizer-t2-output.js')).read(), - ['simplifyExpressionsPre', 'optimizeShiftsAggressive']), + ['simplifyExpressions', 'optimizeShiftsAggressive']), # Make sure that optimizeShifts handles functions with shift statements. (path_from_root('tools', 'test-js-optimizer-t3.js'), open(path_from_root('tools', 'test-js-optimizer-t3-output.js')).read(), ['optimizeShiftsAggressive']), @@ -11387,13 +11829,15 @@ f.close() (path_from_root('tools', 'test-js-optimizer-asm-regs-min.js'), open(path_from_root('tools', 'test-js-optimizer-asm-regs-min-output.js')).read(), ['asm', 'registerize']), (path_from_root('tools', 'test-js-optimizer-asm-pre.js'), open(path_from_root('tools', 'test-js-optimizer-asm-pre-output.js')).read(), - ['asm', 'simplifyExpressionsPre']), + ['asm', 'simplifyExpressions']), (path_from_root('tools', 'test-js-optimizer-asm-last.js'), open(path_from_root('tools', 'test-js-optimizer-asm-last-output.js')).read(), ['asm', 'last']), (path_from_root('tools', 'test-js-optimizer-asm-relocate.js'), open(path_from_root('tools', 'test-js-optimizer-asm-relocate-output.js')).read(), ['asm', 'relocate']), - #(path_from_root('tools', 'test-js-optimizer-asm-outline.js'), open(path_from_root('tools', 'test-js-optimizer-asm-outline-output.js')).read(), - # ['asm', 'outline']), + (path_from_root('tools', 'test-js-optimizer-asm-outline1.js'), open(path_from_root('tools', 'test-js-optimizer-asm-outline1-output.js')).read(), + ['asm', 'outline']), + (path_from_root('tools', 'test-js-optimizer-asm-outline2.js'), open(path_from_root('tools', 'test-js-optimizer-asm-outline2-output.js')).read(), + ['asm', 'outline']), ]: print input output = Popen(listify(NODE_JS) + [path_from_root('tools', 'js-optimizer.js'), input] + passes, stdin=PIPE, stdout=PIPE).communicate()[0] @@ -11630,6 +12074,24 @@ elif 'browser' in str(sys.argv): print 'Running the browser tests. Make sure the browser allows popups from localhost.' print + if 'audio' in sys.argv: + print + print 'Running the browser audio tests. Make sure to listen to hear the correct results!' + print + + i = sys.argv.index('audio') + sys.argv = sys.argv[:i] + sys.argv[i+1:] + i = sys.argv.index('browser') + sys.argv = sys.argv[:i] + sys.argv[i+1:] + sys.argv += [ + 'browser.test_sdl_audio', + 'browser.test_sdl_audio_mix_channels', + 'browser.test_sdl_audio_mix', + 'browser.test_sdl_audio_quickload', + 'browser.test_openal_playback', + 'browser.test_freealut' + ] + # Run a server and a web page. When a test runs, we tell the server about it, # which tells the web page, which then opens a window with the test. Doing # it this way then allows the page to close() itself when done. @@ -12642,8 +13104,9 @@ Press any key to continue.''' self.run_browser('page.html', 'Should print "(300, 150)" -- the size of the canvas in pixels', '/report_result?1') def test_freealut(self): - programs = self.get_library('freealut', os.path.join('examples', 'hello_world.bc'), make_args=['EXEEXT=.bc']) + programs = self.get_library('freealut', os.path.join('examples', '.libs', 'hello_world.bc'), make_args=['EXEEXT=.bc']) for program in programs: + assert os.path.exists(program) Popen([PYTHON, EMCC, '-O2', program, '-o', 'page.html']).communicate() self.run_browser('page.html', 'You should hear "Hello World!"') @@ -12907,6 +13370,9 @@ Press any key to continue.''' def test_glshaderinfo(self): self.btest('glshaderinfo.cpp', '1') + def test_glgetattachedshaders(self): + self.btest('glgetattachedshaders.c', '1') + def test_sdlglshader(self): self.btest('sdlglshader.c', reference='sdlglshader.png', args=['-O2', '--closure', '1']) diff --git a/tests/sdl_rotozoom.c b/tests/sdl_rotozoom.c index e81258d9..cdbdcc6f 100644 --- a/tests/sdl_rotozoom.c +++ b/tests/sdl_rotozoom.c @@ -6,7 +6,7 @@ #include "emscripten.h" #endif -const int numSprites = 8; +const int numSprites = 9; SDL_Surface *screen; SDL_Surface *sprite[numSprites]; @@ -41,6 +41,7 @@ int main(int argc, char **argv) { sprite[5] = rotozoomSurface(sprite[1], 45, 0.5, SMOOTHING_ON); sprite[6] = zoomSurface(sprite[0], -0.5, 0.5, SMOOTHING_ON); sprite[7] = zoomSurface(sprite[0], -0.5, -0.5, SMOOTHING_ON); + sprite[8] = rotozoomSurface(sprite[1], 0, 0.5, SMOOTHING_ON); mainloop(); diff --git a/tests/sdl_rotozoom.png b/tests/sdl_rotozoom.png Binary files differindex 5c5dec2f..288dd303 100644 --- a/tests/sdl_rotozoom.png +++ b/tests/sdl_rotozoom.png diff --git a/tests/stat/output.txt b/tests/stat/output.txt deleted file mode 100644 index 1e6ae74e..00000000 --- a/tests/stat/output.txt +++ /dev/null @@ -1,202 +0,0 @@ ---stat FOLDER-- -ret: 0 -errno: 0 -st_dev: 1 -st_ino: 2 -st_mode: 040777 -st_nlink: 1 -st_rdev: 0 -st_size: 4096 -st_atime: 1200000000 -st_mtime: 1200000000 -st_ctime: 1200000000 -st_blksize: 4096 -st_blocks: 1 -S_ISBLK: 0 -S_ISCHR: 0 -S_ISDIR: 1 -S_ISFIFO: 0 -S_ISREG: 0 -S_ISLNK: 0 -S_ISSOCK: 0 - ---stat FILE-- -ret: 0 -errno: 0 -st_dev: 1 -st_ino: 3 -st_mode: 0100777 -st_nlink: 1 -st_rdev: 0 -st_size: 6 -st_atime: 1200000000 -st_mtime: 1200000000 -st_ctime: 1200000000 -st_blksize: 4096 -st_blocks: 1 -S_ISBLK: 0 -S_ISCHR: 0 -S_ISDIR: 0 -S_ISFIFO: 0 -S_ISREG: 1 -S_ISLNK: 0 -S_ISSOCK: 0 - ---stat DEVICE-- -ret: 0 -errno: 0 -st_dev: 5 -st_ino: 5 -st_mode: 020777 -st_nlink: 1 -st_rdev: 5 -st_size: 0 -st_atime: 1200000000 -st_mtime: 1200000000 -st_ctime: 1200000000 -st_blksize: 4096 -st_blocks: 0 -S_ISBLK: 0 -S_ISCHR: 1 -S_ISDIR: 0 -S_ISFIFO: 0 -S_ISREG: 0 -S_ISLNK: 0 -S_ISSOCK: 0 - ---stat LINK-- -ret: 0 -errno: 0 -st_dev: 1 -st_ino: 3 -st_mode: 0100777 -st_nlink: 1 -st_rdev: 0 -st_size: 6 -st_atime: 1200000000 -st_mtime: 1200000000 -st_ctime: 1200000000 -st_blksize: 4096 -st_blocks: 1 -S_ISBLK: 0 -S_ISCHR: 0 -S_ISDIR: 0 -S_ISFIFO: 0 -S_ISREG: 1 -S_ISLNK: 0 -S_ISSOCK: 0 - ---lstat LINK-- -ret: 0 -errno: 0 -st_dev: 1 -st_ino: 4 -st_mode: 0120777 -st_nlink: 1 -st_rdev: 0 -st_size: 4 -st_atime: 1200000000 -st_mtime: 1200000000 -st_ctime: 1200000000 -st_blksize: 4096 -st_blocks: 1 -S_ISBLK: 0 -S_ISCHR: 0 -S_ISDIR: 0 -S_ISFIFO: 0 -S_ISREG: 0 -S_ISLNK: 1 -S_ISSOCK: 0 - ---fstat FILE-- -ret: 0 -errno: 0 -st_dev: 1 -st_ino: 3 -st_mode: 0100777 -st_nlink: 1 -st_rdev: 0 -st_size: 6 -st_atime: 1200000000 -st_mtime: 1200000000 -st_ctime: 1200000000 -st_blksize: 4096 -st_blocks: 1 -S_ISBLK: 0 -S_ISCHR: 0 -S_ISDIR: 0 -S_ISFIFO: 0 -S_ISREG: 1 -S_ISLNK: 0 -S_ISSOCK: 0 - ---chmod FILE-- -ret: 0 -errno: 0 -st_mode: 0100222 -st_mtime changed: 1 - ---fchmod FILE-- -ret: 0 -errno: 0 -st_mode: 0100777 -st_mtime changed: 1 - ---chmod FOLDER-- -ret: 0 -errno: 0 -st_mode: 040555 -st_mtime changed: 1 - ---chmod LINK-- -ret: 0 -errno: 0 -st_mode: 0100000 - ---mkdir-- -ret: 0 -errno: 0 -st_mode: 040777 -S_ISBLK: 0 -S_ISCHR: 0 -S_ISDIR: 1 -S_ISFIFO: 0 -S_ISREG: 0 -S_ISLNK: 0 -S_ISSOCK: 0 - ---mknod FILE-- -ret: 0 -errno: 0 -st_mode: 0100777 -S_ISBLK: 0 -S_ISCHR: 0 -S_ISDIR: 0 -S_ISFIFO: 0 -S_ISREG: 1 -S_ISLNK: 0 -S_ISSOCK: 0 - ---mknod FOLDER-- -ret: 0 -errno: 0 -st_mode: 040777 -S_ISBLK: 0 -S_ISCHR: 0 -S_ISDIR: 1 -S_ISFIFO: 0 -S_ISREG: 0 -S_ISLNK: 0 -S_ISSOCK: 0 - ---mknod FIFO-- -ret: -1 -errno: 22 - ---mknod DEVICE-- -ret: -1 -errno: 22 - ---mkfifo-- -ret: -1 -errno: 30 diff --git a/tests/stat/src.c b/tests/stat/src.c deleted file mode 100644 index dc5a0198..00000000 --- a/tests/stat/src.c +++ /dev/null @@ -1,242 +0,0 @@ -/* -Note: Hardcoded st_ino values etc. may change with minor changes to the library impl. - In such an event, we will need to update output.txt here. -*/ - -#include <stdio.h> -#include <string.h> -#include <errno.h> -#include <fcntl.h> -#include <sys/stat.h> - -int main() { - struct stat s; - - printf("--stat FOLDER--\n"); - printf("ret: %d\n", stat("/test", &s)); - printf("errno: %d\n", errno); - printf("st_dev: %lu\n", s.st_dev); - printf("st_ino: %lu\n", s.st_ino); - printf("st_mode: 0%o\n", s.st_mode); - printf("st_nlink: %d\n", s.st_nlink); - printf("st_rdev: %lu\n", s.st_rdev); - printf("st_size: %ld\n", s.st_size); - printf("st_atime: %ld\n", s.st_atime); - printf("st_mtime: %ld\n", s.st_mtime); - printf("st_ctime: %ld\n", s.st_ctime); - printf("st_blksize: %ld\n", s.st_blksize); - printf("st_blocks: %ld\n", s.st_blocks); - printf("S_ISBLK: %d\n", S_ISBLK(s.st_mode)); - printf("S_ISCHR: %d\n", S_ISCHR(s.st_mode)); - printf("S_ISDIR: %d\n", S_ISDIR(s.st_mode)); - printf("S_ISFIFO: %d\n", S_ISFIFO(s.st_mode)); - printf("S_ISREG: %d\n", S_ISREG(s.st_mode)); - printf("S_ISLNK: %d\n", S_ISLNK(s.st_mode)); - printf("S_ISSOCK: %d\n", S_ISSOCK(s.st_mode)); - memset(&s, 0, sizeof s); - - printf("\n--stat FILE--\n"); - printf("ret: %d\n", stat("/test/file", &s)); - printf("errno: %d\n", errno); - printf("st_dev: %lu\n", s.st_dev); - printf("st_ino: %lu\n", s.st_ino); - printf("st_mode: 0%o\n", s.st_mode); - printf("st_nlink: %d\n", s.st_nlink); - printf("st_rdev: %lu\n", s.st_rdev); - printf("st_size: %ld\n", s.st_size); - printf("st_atime: %ld\n", s.st_atime); - printf("st_mtime: %ld\n", s.st_mtime); - printf("st_ctime: %ld\n", s.st_ctime); - printf("st_blksize: %ld\n", s.st_blksize); - printf("st_blocks: %ld\n", s.st_blocks); - printf("S_ISBLK: %d\n", S_ISBLK(s.st_mode)); - printf("S_ISCHR: %d\n", S_ISCHR(s.st_mode)); - printf("S_ISDIR: %d\n", S_ISDIR(s.st_mode)); - printf("S_ISFIFO: %d\n", S_ISFIFO(s.st_mode)); - printf("S_ISREG: %d\n", S_ISREG(s.st_mode)); - printf("S_ISLNK: %d\n", S_ISLNK(s.st_mode)); - printf("S_ISSOCK: %d\n", S_ISSOCK(s.st_mode)); - memset(&s, 0, sizeof s); - - printf("\n--stat DEVICE--\n"); - printf("ret: %d\n", stat("/test/device", &s)); - printf("errno: %d\n", errno); - printf("st_dev: %lu\n", s.st_dev); - printf("st_ino: %lu\n", s.st_ino); - printf("st_mode: 0%o\n", s.st_mode); - printf("st_nlink: %d\n", s.st_nlink); - printf("st_rdev: %lu\n", s.st_rdev); - printf("st_size: %ld\n", s.st_size); - printf("st_atime: %ld\n", s.st_atime); - printf("st_mtime: %ld\n", s.st_mtime); - printf("st_ctime: %ld\n", s.st_ctime); - printf("st_blksize: %ld\n", s.st_blksize); - printf("st_blocks: %ld\n", s.st_blocks); - printf("S_ISBLK: %d\n", S_ISBLK(s.st_mode)); - printf("S_ISCHR: %d\n", S_ISCHR(s.st_mode)); - printf("S_ISDIR: %d\n", S_ISDIR(s.st_mode)); - printf("S_ISFIFO: %d\n", S_ISFIFO(s.st_mode)); - printf("S_ISREG: %d\n", S_ISREG(s.st_mode)); - printf("S_ISLNK: %d\n", S_ISLNK(s.st_mode)); - printf("S_ISSOCK: %d\n", S_ISSOCK(s.st_mode)); - memset(&s, 0, sizeof s); - - printf("\n--stat LINK--\n"); - printf("ret: %d\n", stat("/test/link", &s)); - printf("errno: %d\n", errno); - printf("st_dev: %lu\n", s.st_dev); - printf("st_ino: %lu\n", s.st_ino); - printf("st_mode: 0%o\n", s.st_mode); - printf("st_nlink: %d\n", s.st_nlink); - printf("st_rdev: %lu\n", s.st_rdev); - printf("st_size: %ld\n", s.st_size); - printf("st_atime: %ld\n", s.st_atime); - printf("st_mtime: %ld\n", s.st_mtime); - printf("st_ctime: %ld\n", s.st_ctime); - printf("st_blksize: %ld\n", s.st_blksize); - printf("st_blocks: %ld\n", s.st_blocks); - printf("S_ISBLK: %d\n", S_ISBLK(s.st_mode)); - printf("S_ISCHR: %d\n", S_ISCHR(s.st_mode)); - printf("S_ISDIR: %d\n", S_ISDIR(s.st_mode)); - printf("S_ISFIFO: %d\n", S_ISFIFO(s.st_mode)); - printf("S_ISREG: %d\n", S_ISREG(s.st_mode)); - printf("S_ISLNK: %d\n", S_ISLNK(s.st_mode)); - printf("S_ISSOCK: %d\n", S_ISSOCK(s.st_mode)); - memset(&s, 0, sizeof s); - - printf("\n--lstat LINK--\n"); - printf("ret: %d\n", lstat("/test/link", &s)); - printf("errno: %d\n", errno); - printf("st_dev: %lu\n", s.st_dev); - printf("st_ino: %lu\n", s.st_ino); - printf("st_mode: 0%o\n", s.st_mode); - printf("st_nlink: %d\n", s.st_nlink); - printf("st_rdev: %lu\n", s.st_rdev); - printf("st_size: %ld\n", s.st_size); - printf("st_atime: %ld\n", s.st_atime); - printf("st_mtime: %ld\n", s.st_mtime); - printf("st_ctime: %ld\n", s.st_ctime); - printf("st_blksize: %ld\n", s.st_blksize); - printf("st_blocks: %ld\n", s.st_blocks); - printf("S_ISBLK: %d\n", S_ISBLK(s.st_mode)); - printf("S_ISCHR: %d\n", S_ISCHR(s.st_mode)); - printf("S_ISDIR: %d\n", S_ISDIR(s.st_mode)); - printf("S_ISFIFO: %d\n", S_ISFIFO(s.st_mode)); - printf("S_ISREG: %d\n", S_ISREG(s.st_mode)); - printf("S_ISLNK: %d\n", S_ISLNK(s.st_mode)); - printf("S_ISSOCK: %d\n", S_ISSOCK(s.st_mode)); - memset(&s, 0, sizeof s); - - printf("\n--fstat FILE--\n"); - printf("ret: %d\n", fstat(open("/test/file", O_RDONLY, 0777), &s)); - printf("errno: %d\n", errno); - printf("st_dev: %lu\n", s.st_dev); - printf("st_ino: %lu\n", s.st_ino); - printf("st_mode: 0%o\n", s.st_mode); - printf("st_nlink: %d\n", s.st_nlink); - printf("st_rdev: %lu\n", s.st_rdev); - printf("st_size: %ld\n", s.st_size); - printf("st_atime: %ld\n", s.st_atime); - printf("st_mtime: %ld\n", s.st_mtime); - printf("st_ctime: %ld\n", s.st_ctime); - printf("st_blksize: %ld\n", s.st_blksize); - printf("st_blocks: %ld\n", s.st_blocks); - printf("S_ISBLK: %d\n", S_ISBLK(s.st_mode)); - printf("S_ISCHR: %d\n", S_ISCHR(s.st_mode)); - printf("S_ISDIR: %d\n", S_ISDIR(s.st_mode)); - printf("S_ISFIFO: %d\n", S_ISFIFO(s.st_mode)); - printf("S_ISREG: %d\n", S_ISREG(s.st_mode)); - printf("S_ISLNK: %d\n", S_ISLNK(s.st_mode)); - printf("S_ISSOCK: %d\n", S_ISSOCK(s.st_mode)); - memset(&s, 0, sizeof s); - - printf("\n--chmod FILE--\n"); - printf("ret: %d\n", chmod("/test/file", 0200)); - printf("errno: %d\n", errno); - stat("/test/file", &s); - printf("st_mode: 0%o\n", s.st_mode); - printf("st_mtime changed: %d\n", s.st_mtime != 1200000000l); - memset(&s, 0, sizeof s); - - printf("\n--fchmod FILE--\n"); - printf("ret: %d\n", fchmod(open("/test/file", O_WRONLY, 0777), 0777)); - printf("errno: %d\n", errno); - stat("/test/file", &s); - printf("st_mode: 0%o\n", s.st_mode); - printf("st_mtime changed: %d\n", s.st_mtime != 1200000000l); - memset(&s, 0, sizeof s); - - printf("\n--chmod FOLDER--\n"); - printf("ret: %d\n", chmod("/test", 0400)); - printf("errno: %d\n", errno); - stat("/test", &s); - printf("st_mode: 0%o\n", s.st_mode); - printf("st_mtime changed: %d\n", s.st_mtime != 1200000000l); - memset(&s, 0, sizeof s); - - printf("\n--chmod LINK--\n"); - printf("ret: %d\n", chmod("/test/link", 0000)); - printf("errno: %d\n", errno); - stat("/test/file", &s); - printf("st_mode: 0%o\n", s.st_mode); - memset(&s, 0, sizeof s); - - // Make sure we can create stuff in the root. - chmod("/", 0777); - - printf("\n--mkdir--\n"); - printf("ret: %d\n", mkdir("/test-mkdir", 0777)); - printf("errno: %d\n", errno); - stat("/test-mkdir", &s); - printf("st_mode: 0%o\n", s.st_mode); - printf("S_ISBLK: %d\n", S_ISBLK(s.st_mode)); - printf("S_ISCHR: %d\n", S_ISCHR(s.st_mode)); - printf("S_ISDIR: %d\n", S_ISDIR(s.st_mode)); - printf("S_ISFIFO: %d\n", S_ISFIFO(s.st_mode)); - printf("S_ISREG: %d\n", S_ISREG(s.st_mode)); - printf("S_ISLNK: %d\n", S_ISLNK(s.st_mode)); - printf("S_ISSOCK: %d\n", S_ISSOCK(s.st_mode)); - memset(&s, 0, sizeof s); - - printf("\n--mknod FILE--\n"); - printf("ret: %d\n", mknod("/test-mknod-file", S_IFREG | 0777, 0)); - printf("errno: %d\n", errno); - stat("/test-mknod-file", &s); - printf("st_mode: 0%o\n", s.st_mode); - printf("S_ISBLK: %d\n", S_ISBLK(s.st_mode)); - printf("S_ISCHR: %d\n", S_ISCHR(s.st_mode)); - printf("S_ISDIR: %d\n", S_ISDIR(s.st_mode)); - printf("S_ISFIFO: %d\n", S_ISFIFO(s.st_mode)); - printf("S_ISREG: %d\n", S_ISREG(s.st_mode)); - printf("S_ISLNK: %d\n", S_ISLNK(s.st_mode)); - printf("S_ISSOCK: %d\n", S_ISSOCK(s.st_mode)); - memset(&s, 0, sizeof s); - - printf("\n--mknod FOLDER--\n"); - printf("ret: %d\n", mknod("/test-mknod-dir", S_IFDIR | 0777, 0)); - printf("errno: %d\n", errno); - stat("/test-mknod-dir", &s); - printf("st_mode: 0%o\n", s.st_mode); - printf("S_ISBLK: %d\n", S_ISBLK(s.st_mode)); - printf("S_ISCHR: %d\n", S_ISCHR(s.st_mode)); - printf("S_ISDIR: %d\n", S_ISDIR(s.st_mode)); - printf("S_ISFIFO: %d\n", S_ISFIFO(s.st_mode)); - printf("S_ISREG: %d\n", S_ISREG(s.st_mode)); - printf("S_ISLNK: %d\n", S_ISLNK(s.st_mode)); - printf("S_ISSOCK: %d\n", S_ISSOCK(s.st_mode)); - memset(&s, 0, sizeof s); - - printf("\n--mknod FIFO--\n"); - printf("ret: %d\n", mknod("/test-mknod-fifo", S_IFIFO | 0777, 0)); - printf("errno: %d\n", errno); - - printf("\n--mknod DEVICE--\n"); - printf("ret: %d\n", mknod("/test-mknod-device", S_IFCHR | 0777, 123)); - printf("errno: %d\n", errno); - - printf("\n--mkfifo--\n"); - printf("ret: %d\n", mkfifo("/test-mkfifo", 0777)); - printf("errno: %d\n", errno); - - return 0; -} diff --git a/tests/stat/test_chmod.c b/tests/stat/test_chmod.c new file mode 100644 index 00000000..94e6c12b --- /dev/null +++ b/tests/stat/test_chmod.c @@ -0,0 +1,153 @@ +#include <assert.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <utime.h> +#include <sys/stat.h> +#include <sys/types.h> + +void create_file(const char *path, const char *buffer, int mode) { + int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, mode); + assert(fd >= 0); + + int err = write(fd, buffer, sizeof(char) * strlen(buffer)); + assert(err == (sizeof(char) * strlen(buffer))); + + close(fd); +} + +void setup() { + create_file("file", "abcdef", 0777); + symlink("file", "file-link"); + // some platforms use 777, some use 755 by default for symlinks + // make sure we're using 777 for the test + lchmod("file-link", 0777); + mkdir("folder", 0777); +} + +void cleanup() { + unlink("file-link"); + unlink("file"); + rmdir("folder"); +} + +void test() { + int err; + int lastctime; + struct stat s; + + // + // chmod a file + // + // get the current ctime for the file + memset(&s, 0, sizeof s); + stat("file", &s); + lastctime = s.st_ctime; + sleep(1); + + // do the actual chmod + err = chmod("file", 0200); + assert(!err); + + memset(&s, 0, sizeof s); + stat("file", &s); +#if USE_OLD_FS + assert(s.st_mode == (0222 | S_IFREG)); +#else + assert(s.st_mode == (0200 | S_IFREG)); +#endif + assert(s.st_ctime != lastctime); + + // + // fchmod a file + // + lastctime = s.st_ctime; + sleep(1); + + err = fchmod(open("file", O_WRONLY), 0100); + assert(!err); + + memset(&s, 0, sizeof s); + stat("file", &s); +#if USE_OLD_FS + assert(s.st_mode == (0000 | S_IFREG)); +#else + assert(s.st_mode == (0100 | S_IFREG)); +#endif + assert(s.st_ctime != lastctime); + + // + // chmod a folder + // + // get the current ctime for the folder + memset(&s, 0, sizeof s); + stat("folder", &s); + lastctime = s.st_ctime; + sleep(1); + + // do the actual chmod + err = chmod("folder", 0300); + assert(!err); + memset(&s, 0, sizeof s); + stat("folder", &s); +#if USE_OLD_FS + assert(s.st_mode == (0222 | S_IFDIR)); +#else + assert(s.st_mode == (0300 | S_IFDIR)); +#endif + assert(s.st_ctime != lastctime); + + // + // chmod a symlink's target + // + err = chmod("file-link", 0400); + assert(!err); + + // make sure the file it references changed + stat("file-link", &s); +#if USE_OLD_FS + assert(s.st_mode == (0555 | S_IFREG)); +#else + assert(s.st_mode == (0400 | S_IFREG)); +#endif + + // but the link didn't + lstat("file-link", &s); + assert(s.st_mode == (0777 | S_IFLNK)); + + // + // chmod the actual symlink + // + err = lchmod("file-link", 0500); + assert(!err); + + // make sure the file it references didn't change + stat("file-link", &s); +#if USE_OLD_FS + assert(s.st_mode == (0555 | S_IFREG)); +#else + assert(s.st_mode == (0400 | S_IFREG)); +#endif + + // but the link did + lstat("file-link", &s); +#if USE_OLD_FS + assert(s.st_mode == (0555 | S_IFLNK)); +#else + assert(s.st_mode == (0500 | S_IFLNK)); +#endif + + puts("success"); +} + +int main() { + atexit(cleanup); + signal(SIGABRT, cleanup); + setup(); + test(); + return EXIT_SUCCESS; +}
\ No newline at end of file diff --git a/tests/stat/test_mknod.c b/tests/stat/test_mknod.c new file mode 100644 index 00000000..4cff57d9 --- /dev/null +++ b/tests/stat/test_mknod.c @@ -0,0 +1,96 @@ +#include <assert.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <utime.h> +#include <sys/stat.h> +#include <sys/types.h> + +void setup() { + mkdir("folder-readonly", 0555); +} + +void cleanup() { + unlink("mknod-file"); + unlink("mknod-device"); + rmdir("folder"); + rmdir("folder-readonly"); +} + +void test() { + int err; + struct stat s; + + // + // mknod + // mknod is _extremely_ unportable for anything other + // than a FIFO. so, the tests are disabled when running + // natively as they'd be utterly inconsistent. + // +#if EMSCRIPTEN + + // mknod a folder + err = mknod("mknod-folder", S_IFDIR | 0777, 0); + assert(err); + assert(errno == EINVAL); + + // mknod fifo + err = mknod("mknod-fifo", S_IFIFO | 0777, 0); + assert(err); + assert(errno == EPERM); + + // mknod a file + err = mknod("mknod-file", S_IFREG | 0777, 0); + assert(!err); + memset(&s, 0, sizeof s); + stat("mknod-file", &s); + assert(S_ISREG(s.st_mode)); + + // mknod a character device + err = mknod("mknod-device", S_IFCHR | 0777, 123); +#if USE_OLD_FS + assert(err); + assert(errno == EPERM); +#else + assert(!err); + memset(&s, 0, sizeof s); + stat("mknod-device", &s); + assert(S_ISCHR(s.st_mode)); +#endif + +#endif + + // + // mkdir + // + // can't mkdir in a readonly dir + err = mkdir("folder-readonly/subfolder", 0777); + assert(err); + assert(errno == EACCES); + + // regular creation + err = mkdir("folder", 0777); + assert(!err); + memset(&s, 0, sizeof s); + stat("folder", &s); + assert(S_ISDIR(s.st_mode)); + + // try to re-create the same folder + err = mkdir("folder", 0777); + assert(err); + assert(errno == EEXIST); + + puts("success"); +} + +int main() { + atexit(cleanup); + signal(SIGABRT, cleanup); + setup(); + test(); + return EXIT_SUCCESS; +}
\ No newline at end of file diff --git a/tests/stat/test_stat.c b/tests/stat/test_stat.c new file mode 100644 index 00000000..14e88370 --- /dev/null +++ b/tests/stat/test_stat.c @@ -0,0 +1,167 @@ +#include <assert.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <utime.h> +#include <sys/stat.h> +#include <sys/types.h> + +void create_file(const char *path, const char *buffer, int mode) { + int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, mode); + assert(fd >= 0); + + int err = write(fd, buffer, sizeof(char) * strlen(buffer)); + assert(err == (sizeof(char) * strlen(buffer))); + + close(fd); +} + +void setup() { + struct utimbuf t = {1200000000, 1200000000}; + + mkdir("folder", 0777); + create_file("folder/file", "abcdef", 0777); + symlink("file", "folder/file-link"); + + utime("folder/file", &t); + utime("folder", &t); +} + +void cleanup() { + unlink("folder/file"); + unlink("folder/file-link"); + rmdir("folder"); +} + +void test() { + int err; + struct stat s; + + // stat a folder + memset(&s, 0, sizeof(s)); + err = stat("folder", &s); + assert(!err); + assert(s.st_dev); + assert(s.st_ino); + assert(S_ISDIR(s.st_mode)); + assert(s.st_nlink); + assert(s.st_rdev == 0); + assert(s.st_size); + assert(s.st_atime == 1200000000); + assert(s.st_mtime == 1200000000); + assert(s.st_ctime); +#ifdef EMSCRIPTEN + assert(s.st_blksize == 4096); + assert(s.st_blocks == 1); +#endif + + // stat a file + memset(&s, 0, sizeof(s)); + err = stat("folder/file", &s); + assert(!err); + assert(s.st_dev); + assert(s.st_ino); + assert(S_ISREG(s.st_mode)); + assert(s.st_nlink); + assert(s.st_rdev == 0); + assert(s.st_size == 6); + assert(s.st_atime == 1200000000); + assert(s.st_mtime == 1200000000); + assert(s.st_ctime); +#if EMSCRIPTEN + assert(s.st_blksize == 4096); + assert(s.st_blocks == 1); +#endif + + // fstat a file (should match file stat from above) + memset(&s, 0, sizeof(s)); + err = fstat(open("folder/file", O_RDONLY), &s); + assert(!err); + assert(s.st_dev); + assert(s.st_ino); + assert(S_ISREG(s.st_mode)); + assert(s.st_nlink); + assert(s.st_rdev == 0); + assert(s.st_size == 6); + assert(s.st_atime == 1200000000); + assert(s.st_mtime == 1200000000); + assert(s.st_ctime); +#if EMSCRIPTEN + assert(s.st_blksize == 4096); + assert(s.st_blocks == 1); +#endif + + // stat a device + memset(&s, 0, sizeof(s)); + err = stat("/dev/null", &s); + assert(!err); + assert(s.st_dev); + assert(s.st_ino); + assert(S_ISCHR(s.st_mode)); + assert(s.st_nlink); +#if !USE_OLD_FS + // old FS doesn't store proper device ids +#ifndef __APPLE__ + // mac uses makedev(3, 2) for /dev/null + assert(s.st_rdev == makedev(1, 3)); +#endif +#endif + assert(!s.st_size); + assert(s.st_atime); + assert(s.st_mtime); + assert(s.st_ctime); +#if EMSCRIPTEN + assert(s.st_blksize == 4096); + assert(s.st_blocks == 0); +#endif + + // stat a link (should match the file stat from above) + memset(&s, 0, sizeof(s)); + err = stat("folder/file-link", &s); + assert(!err); + assert(s.st_dev); + assert(s.st_ino); + assert(S_ISREG(s.st_mode)); + assert(s.st_nlink); + assert(s.st_rdev == 0); + assert(s.st_size == 6); + assert(s.st_atime == 1200000000); + assert(s.st_mtime == 1200000000); + assert(s.st_ctime); +#if EMSCRIPTEN + assert(s.st_blksize == 4096); + assert(s.st_blocks == 1); +#endif + + // lstat a link (should NOT match the file stat from above) + memset(&s, 0, sizeof(s)); + err = lstat("folder/file-link", &s); + assert(!err); + assert(s.st_dev); + assert(s.st_ino); + assert(S_ISLNK(s.st_mode)); + assert(s.st_nlink); + assert(s.st_rdev == 0); + assert(s.st_size == 4); // strlen("file") + assert(s.st_atime != 1200000000); // should NOT match the utime call we did for dir/file + assert(s.st_mtime != 1200000000); + assert(s.st_ctime); +#if EMSCRIPTEN + assert(s.st_blksize == 4096); + assert(s.st_blocks == 1); +#endif + + puts("success"); +} + +int main() { + atexit(cleanup); + signal(SIGABRT, cleanup); + setup(); + test(); + return EXIT_SUCCESS; +}
\ No newline at end of file diff --git a/tests/unistd/unlink.c b/tests/unistd/unlink.c index 3f7d84b6..87252da2 100644 --- a/tests/unistd/unlink.c +++ b/tests/unistd/unlink.c @@ -1,35 +1,138 @@ -#include <stdio.h> +#include <assert.h> #include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> #include <unistd.h> +#include <sys/stat.h> -int main() { - char* files[] = {"/device", "/file", "/file-forbidden", "/noexist"}; - char* folders[] = {"/empty", "/empty-forbidden", "/full"}; - int i; - - for (i = 0; i < sizeof files / sizeof files[0]; i++) { - printf("access(%s) before: %d\n", files[i], access(files[i], F_OK)); - rmdir(files[i]); - printf("errno: %d\n", errno); - errno = 0; - printf("access(%s) after rmdir: %d\n", files[i], access(files[i], F_OK)); - unlink(files[i]); - printf("errno: %d\n", errno); - errno = 0; - printf("access(%s) after unlink: %d\n\n", files[i], access(files[i], F_OK)); - } - - for (i = 0; i < sizeof folders / sizeof folders[0]; i++) { - printf("access(%s) before: %d\n", folders[i], access(folders[i], F_OK)); - unlink(folders[i]); - printf("errno: %d\n", errno); - errno = 0; - printf("access(%s) after unlink: %d\n", folders[i], access(folders[i], F_OK)); - rmdir(folders[i]); - printf("errno: %d\n", errno); - errno = 0; - printf("access(%s) after rmdir: %d\n\n", folders[i], access(folders[i], F_OK)); - } - - return 0; +static void create_file(const char *path, const char *buffer, int mode) { + int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, mode); + assert(fd >= 0); + + int err = write(fd, buffer, sizeof(char) * strlen(buffer)); + assert(err == (sizeof(char) * strlen(buffer))); + + close(fd); +} + +void setup() { + create_file("file", "test", 0777); + create_file("file1", "test", 0777); + symlink("file1", "file1-link"); + mkdir("dir-empty", 0777); + symlink("dir-empty", "dir-empty-link"); + mkdir("dir-readonly", 0777); + create_file("dir-readonly/anotherfile", "test", 0777); + mkdir("dir-readonly/anotherdir", 0777); + chmod("dir-readonly", 0555); + mkdir("dir-full", 0777); + create_file("dir-full/anotherfile", "test", 0777); +} + +void cleanup() { + unlink("file"); + unlink("file1"); + unlink("file1-link"); + rmdir("dir-empty"); + unlink("dir-empty-link"); + chmod("dir-readonly", 0777); + unlink("dir-readonly/anotherfile"); + rmdir("dir-readonly/anotherdir"); + rmdir("dir-readonly"); + unlink("dir-full/anotherfile"); + rmdir("dir-full"); +} + +void test() { + int err; + char buffer[512]; + + // + // test unlink + // + err = unlink("noexist"); + assert(err == -1); + assert(errno == ENOENT); + + err = unlink("dir-readonly"); + assert(err == -1); +#ifdef __linux__ + assert(errno == EISDIR); +#else + assert(errno == EPERM); +#endif + + err = unlink("dir-readonly/anotherfile"); + assert(err == -1); + assert(errno == EACCES); + + // try unlinking the symlink first to make sure + // we don't follow the link + err = unlink("file1-link"); + assert(!err); + err = access("file1", F_OK); + assert(!err); + err = access("file1-link", F_OK); + assert(err == -1); + + err = unlink("file"); + assert(!err); + err = access("file", F_OK); + assert(err == -1); + + // + // test rmdir + // + err = rmdir("noexist"); + assert(err == -1); + assert(errno == ENOENT); + + err = rmdir("file1"); + assert(err == -1); + assert(errno == ENOTDIR); + + err = rmdir("dir-readonly/anotherdir"); + assert(err == -1); + assert(errno == EACCES); + + err = rmdir("dir-full"); + assert(err == -1); + assert(errno == ENOTEMPTY); + + // test removing the cwd / root. The result isn't specified by + // POSIX, but Linux seems to set EBUSY in both cases. +#ifndef __APPLE__ + getcwd(buffer, sizeof(buffer)); + err = rmdir(buffer); + assert(err == -1); + assert(errno == EBUSY); +#endif + err = rmdir("/"); + assert(err == -1); +#ifdef __APPLE__ + assert(errno == EISDIR); +#else + assert(errno == EBUSY); +#endif + + err = rmdir("dir-empty-link"); + assert(err == -1); + assert(errno == ENOTDIR); + + err = rmdir("dir-empty"); + assert(!err); + err = access("dir-empty", F_OK); + assert(err == -1); + + puts("success"); } + +int main() { + atexit(cleanup); + signal(SIGABRT, cleanup); + setup(); + test(); + return EXIT_SUCCESS; +}
\ No newline at end of file diff --git a/tests/unistd/unlink.js b/tests/unistd/unlink.js deleted file mode 100644 index c2366080..00000000 --- a/tests/unistd/unlink.js +++ /dev/null @@ -1,7 +0,0 @@ -FS.createDevice('/', 'device', function() {}, function() {}); -FS.createDataFile('/', 'file', 'test', true, true); -FS.createDataFile('/', 'file-forbidden', 'test', true, false); -FS.createFolder('/', 'empty', true, true); -FS.createFolder('/', 'empty-forbidden', true, false); -FS.createFolder('/', 'full', true, true); -FS.createFolder('/full', 'junk', true, true); diff --git a/tests/unistd/unlink.out b/tests/unistd/unlink.out deleted file mode 100644 index f7a894cb..00000000 --- a/tests/unistd/unlink.out +++ /dev/null @@ -1,42 +0,0 @@ -access(/device) before: 0 -errno: 20 -access(/device) after rmdir: 0 -errno: 0 -access(/device) after unlink: -1 - -access(/file) before: 0 -errno: 20 -access(/file) after rmdir: 0 -errno: 0 -access(/file) after unlink: -1 - -access(/file-forbidden) before: 0 -errno: 13 -access(/file-forbidden) after rmdir: 0 -errno: 13 -access(/file-forbidden) after unlink: 0 - -access(/noexist) before: -1 -errno: 2 -access(/noexist) after rmdir: -1 -errno: 2 -access(/noexist) after unlink: -1 - -access(/empty) before: 0 -errno: 21 -access(/empty) after unlink: 0 -errno: 0 -access(/empty) after rmdir: -1 - -access(/empty-forbidden) before: 0 -errno: 21 -access(/empty-forbidden) after unlink: 0 -errno: 13 -access(/empty-forbidden) after rmdir: 0 - -access(/full) before: 0 -errno: 21 -access(/full) after unlink: 0 -errno: 90 -access(/full) after rmdir: 0 - diff --git a/tools/file_packager.py b/tools/file_packager.py index cc030f59..575e5957 100644 --- a/tools/file_packager.py +++ b/tools/file_packager.py @@ -330,15 +330,18 @@ for file_ in data_files: if file_['mode'] == 'embed': # Embed data = map(ord, open(file_['srcpath'], 'rb').read()) - str_data = '' - chunk_size = 10240 - while len(data) > 0: - chunk = data[:chunk_size] - data = data[chunk_size:] - if not str_data: - str_data = str(chunk) - else: - str_data += '.concat(' + str(chunk) + ')' + if not data: + str_data = '[]' + else: + str_data = '' + chunk_size = 10240 + while len(data) > 0: + chunk = data[:chunk_size] + data = data[chunk_size:] + if not str_data: + str_data = str(chunk) + else: + str_data += '.concat(' + str(chunk) + ')' code += '''Module['FS_createDataFile']('/%s', '%s', %s, true, true);\n''' % (os.path.dirname(filename), os.path.basename(filename), str_data) elif file_['mode'] == 'preload': # Preload 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 71a59921..a7f1e4e1 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -410,7 +410,7 @@ function removeUnneededLabelSettings(ast) { var USEFUL_BINARY_OPS = set('<<', '>>', '|', '&', '^'); var COMPARE_OPS = set('<', '<=', '>', '>=', '==', '===', '!=', '!=='); -function simplifyExpressionsPre(ast) { +function simplifyExpressions(ast) { // Simplify common expressions used to perform integer conversion operations // in cases where no conversion is needed. function simplifyIntegerConversions(ast) { @@ -793,6 +793,7 @@ function simplifyExpressionsPre(ast) { joinAdditions(func); // simplifyZeroComp(func); TODO: investigate performance if (asm) asmOpts(func); + simplifyNotComps(func); }); } @@ -1158,10 +1159,6 @@ function simplifyNotComps(ast) { simplifyNotCompsPass = false; } -function simplifyExpressionsPost(ast) { - simplifyNotComps(ast); -} - var NO_SIDE_EFFECTS = set('num', 'name'); function hasSideEffects(node) { // this is 99% incomplete! @@ -1540,24 +1537,22 @@ function detectAsmCoercion(node, asmInfo) { // for params, +x vs x|0, for vars, 0.0 vs 0 if (node[0] === 'num' && node[1].toString().indexOf('.') >= 0) return ASM_DOUBLE; if (node[0] === 'unary-prefix') return ASM_DOUBLE; - if (asmInfo && node[0] == 'name') { - if (node[1] in asmInfo.vars) return asmInfo.vars[node[1]]; - if (node[1] in asmInfo.params) return asmInfo.params[node[1]]; - } + if (asmInfo && node[0] == 'name') return getAsmType(node[1], asmInfo); return ASM_INT; } -function makeAsmParamCoercion(param, type) { - return type === ASM_INT ? ['binary', '|', ['name', param], ['num', 0]] : ['unary-prefix', '+', ['name', param]]; +function makeAsmCoercion(node, type) { + return type === ASM_INT ? ['binary', '|', node, ['num', 0]] : ['unary-prefix', '+', node]; } function makeAsmVarDef(v, type) { return [v, type === ASM_INT ? ['num', 0] : ['unary-prefix', '+', ['num', 0]]]; } -function getAsmType(asmInfo, name) { +function getAsmType(name, asmInfo) { if (name in asmInfo.vars) return asmInfo.vars[name]; - return asmInfo.params[name]; + if (name in asmInfo.params) return asmInfo.params[name]; + assert(false, 'unknown var ' + name); } function normalizeAsm(func) { @@ -1658,7 +1653,7 @@ function denormalizeAsm(func, data) { // add param coercions var next = 0; func[2].forEach(function(param) { - stats[next++] = ['stat', ['assign', true, ['name', param], makeAsmParamCoercion(param, data.params[param])]]; + stats[next++] = ['stat', ['assign', true, ['name', param], makeAsmCoercion(['name', param], data.params[param])]]; }); // add variable definitions var varDefs = []; @@ -1673,6 +1668,37 @@ function denormalizeAsm(func, data) { //printErr('denormalized \n\n' + astToSrc(func) + '\n\n'); } +function getFirstIndexInNormalized(func, data) { + // In a normalized asm function, return the index of the first element that is not not defs or annotation + var stats = func[3]; + var i = stats.length-1; + while (i >= 0) { + var stat = stats[i]; + if (stat[0] == 'var') break; + i--; + } + return i+1; +} + +function getStackBumpNode(ast) { + var found = null; + traverse(ast, function(node, type) { + if (type === 'assign' && node[2][0] === 'name' && node[2][1] === 'STACKTOP') { + var value = node[3]; + if (value[0] === 'name') return true; + assert(value[0] == 'binary' && value[1] == '|' && value[2][0] == 'binary' && value[2][1] == '+' && value[2][2][0] == 'name' && value[2][2][1] == 'STACKTOP' && value[2][3][0] == 'num'); + found = node; + return true; + } + }); + return found; +} + +function getStackBumpSize(ast) { + var node = getStackBumpNode(ast); + return node ? node[3][2][3][1] : 0; +} + // Very simple 'registerization', coalescing of variables into a smaller number, // as part of minification. Globals-level minification began in a previous pass, // we receive extraInfo which tells us how to rename globals. (Only in asm.js.) @@ -2981,32 +3007,37 @@ function outline(ast) { stack.push(name); } asmData.stackPos = {}; + var stackSize = getStackBumpSize(func); + if (stackSize % 8 === 0) stackSize += 8 - (stackSize % 8); for (var i = 0; i < stack.length; i++) { - asmData.stackPos[stack[i]] = i*8; - } - // Reserve an extra two spots: one for control flow var, the other for control flow data - asmData.stackSize = (stack.length + 2)*8; - asmData.controlStackPos = asmData.stackSize - 16; - asmData.controlDataStackPos = asmData.stackSize - 8; + asmData.stackPos[stack[i]] = stackSize + i*8; + } + // Reserve an extra two spots per possible outlining: 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.maxOutlinings = Math.round(1.5*measureSize(func)/sizeToOutline); + asmData.totalStackSize = stackSize + (stack.length + 2*asmData.maxOutlinings)*8; + asmData.controlStackPos = function(i) { return stackSize + (stack.length + i)*8 }; + asmData.controlDataStackPos = function(i) { return stackSize + (stack.length + i)*8 + 4 }; asmData.splitCounter = 0; } // Analyze uses - reads and writes - of variables in part of the AST of a function function analyzeCode(func, asmData, ast) { - var labels = {}; + var labels = {}; // labels defined in this code var labelCounter = 1; // 0 means no label traverse(ast, function(node, type) { - if ((type == 'label' || type in LOOP_FLOW) && node[1] && !(node[1] in labels)) { + if (type == 'label' && !(node[1] in labels)) { labels[node[1]] = labelCounter++; } }); 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. '0' is an unlabeled one + var continues = {}; // to (name -> id, just like labels) var breakCapturers = 0; var continueCapturers = 0; @@ -3014,27 +3045,32 @@ 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 if (label && (label in labels)) return; // label, and defined in this code, so captured - breaks[label || 0] = 0; + if (label) breaks[label] = labelCounter++; hasBreak = true; } else if (type == 'continue') { var label = node[1] || 0; if (!label && continueCapturers > 0) return; // no label, and captured if (label && (label in labels)) return; // label, and defined in this code, so captured - continues[label || 0] = 0; + if (label) continues[label] = labelCounter++; hasContinue = true; } else { if (type in BREAK_CAPTURERS) { @@ -3052,14 +3088,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, 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) { @@ -3076,76 +3113,106 @@ function outline(ast) { function makeComparison(left, comp, right) { return ['binary', comp, left, right]; } + function makeSwitch(value, cases) { + return ['switch', value, cases]; + } var CONTROL_BREAK = 1, CONTROL_BREAK_LABEL = 2, CONTROL_CONTINUE = 3, CONTROL_CONTINUE_LABEL = 4, CONTROL_RETURN_VOID = 5, CONTROL_RETURN_INT = 6, CONTROL_RETURN_DOUBLE = 7; var sizeToOutline = extraInfo.sizeToOutline; var level = 0; + var outliningParents = {}; // function name => parent it was outlined from + function doOutline(func, asmData, stats, start, end) { - printErr(' do outline ' + [func[1], level, 'range:', start, end, 'of', stats.length]); + if (asmData.splitCounter === asmData.maxOutlinings) return []; + if (!extraInfo.allowCostlyOutlines) var originalStats = copy(stats); var code = stats.slice(start, end+1); - var newIdent = func[1] + '$' + (asmData.splitCounter++); - // add spills and reads before and after the call to the outlined code, and in the outlined code itself + var funcSize = measureSize(func); + var outlineIndex = asmData.splitCounter++; + var newIdent = func[1] + '$' + outlineIndex; + // analyze variables, and find 'owned' variables - that only appear in the outlined code, and do not need any spill support var codeInfo = analyzeCode(func, asmData, code); + var allCodeInfo = analyzeCode(func, asmData, func); + //printErr(' do outline ' + [func[1], level, 'range:', start, end, 'of', stats.length, newIdent, measureSize(code), JSON.stringify(codeInfo.labels), JSON.stringify(codeInfo.breaks), JSON.stringify(codeInfo.continues)]); + var owned = { sp: 1 }; // sp is always owned, each has its own + keys(setUnion(codeInfo.reads, codeInfo.writes)).forEach(function(v) { + if (allCodeInfo.reads[v] === codeInfo.reads[v] && allCodeInfo.writes[v] === codeInfo.writes[v] && !(v in asmData.params)) { + owned[v] = 1; + } + }); var reps = []; - for (var v in codeInfo.reads) { - if (v != 'sp') { - reps.push(['stat', ['assign', true, ['sub', ['name', getAsmType(asmData, v) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]], ['name', v]]]); - code.unshift(['stat', ['assign', true, ['name', v], ['sub', ['name', getAsmType(asmData, v) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]]]]); + // wipe out control variable + reps.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', 0])]); + reps.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos(outlineIndex)), ['num', 0])]); // XXX not really needed + // add spills and reads before and after the call to the outlined code, and in the outlined code itself + keys(setUnion(codeInfo.reads, codeInfo.writes)).forEach(function(v) { + if (!(v in owned)) { + reps.push(['stat', ['assign', true, ['sub', ['name', getAsmType(v, asmData) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]], ['name', v]]]); } - } - reps.push(['stat', ['call', ['name', newIdent], [['name', 'sp']]]]); + }); + reps.push(['stat', ['assign', true, ['name', 'sp'], makeAsmCoercion(['call', ['name', newIdent], [['name', 'sp']]], ASM_INT)]]); for (var v in codeInfo.writes) { - reps.push(['stat', ['assign', true, ['name', v], ['sub', ['name', getAsmType(asmData, v) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]]]]); - code.push(['stat', ['assign', true, ['sub', ['name', getAsmType(asmData, v) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]], ['name', v]]]); + if (!(v in owned)) { + reps.push(['stat', ['assign', true, ['name', v], makeAsmCoercion(['sub', ['name', getAsmType(v, asmData) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]], getAsmType(v, asmData))]]); + } } // Generate new function - if (codeInfo.hasReturn || codeInfo.hasBreak || codeInfo.hasContinue) { + if (codeInfo.hasReturn || codeInfo.hasReturnInt || codeInfo.hasReturnDouble || codeInfo.hasBreak || codeInfo.hasContinue) { // we need to capture all control flow using a top-level labeled one-time loop in the outlined function - code = [['label', 'OL', ['do', ['num', 0], ['block', code]]]]; var breakCapturers = 0; var continueCapturers = 0; - traverse(code, function(node, type) { + traverse(['block', code], function(node, type) { // traverse on dummy block, so we get the toplevel statements // replace all break/continue/returns with code to break out of the main one-time loop, and set the control data - if (type == 'return') { - var ret = ['break', 'OL']; - if (!node[1]) { - ret = ['seq', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos), ['num', CONTROL_RETURN_VOID]), ret]; - } else { - var type = detectAsmCoercion(node[1], asmData); - ret = ['seq', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos), ['num', type == ASM_INT ? CONTROL_RETURN_INT : CONTROL_RETURN_DOUBLE]), ret]; - ret = ['seq', makeAssign(makeStackAccess(type, asmData.controlDataStackPos), node[1]), ret]; - } - return ret; - } else if (type == 'break') { - var label = node[1] || 0; - if (label == 'OL') return; // this was just added before us, it is new replacement code - if (!label && breakCapturers > 0) return; // no label, and captured - if (label && (label in codeInfo.labels)) return; // label, and defined in this code, so captured - var ret = ['break', 'OL']; - ret = ['seq', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos), ['num', label ? CONTROL_BREAK_LABEL : CONTROL_BREAK]), ret]; - if (label) { - assert(label in codeInfo.labels, label + ' in ' + keys(codeInfo.labels)); - ret = ['seq', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos), ['num', codeInfo.labels[label]]), ret]; - } - return ret; - } else if (type == 'continue') { - var label = node[1] || 0; - if (!label && continueCapturers > 0) return; // no label, and captured - if (label && (label in codeInfo.labels)) return; // label, and defined in this code, so captured - var ret = ['break', 'OL']; - ret = ['seq', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos), ['num', label ? CONTROL_CONTINUE_LABEL : CONTROL_CONTINUE]), ret]; - if (label) { - ret = ['seq', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos), ['num', codeInfo.labels[label]]), ret]; - } - return ret; - } else { - if (type in BREAK_CAPTURERS) { - breakCapturers++; - } - if (type in CONTINUE_CAPTURERS) { - continueCapturers++; + if (type in BREAK_CAPTURERS) { + breakCapturers++; + } + if (type in CONTINUE_CAPTURERS) { + continueCapturers++; + } + var stats = node === code ? node : getStatements(node); + if (stats) { + for (var i = 0; i < stats.length; i++) { + var node = stats[i]; // step all over node and type here, for convenience + if (node[0] == 'stat') node = node[1]; + var type = node[0]; + var ret = null; + if (type == 'return') { + ret = []; + if (!node[1]) { + ret.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', CONTROL_RETURN_VOID])]); + } else { + var type = detectAsmCoercion(node[1], asmData); + ret.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', type == ASM_INT ? CONTROL_RETURN_INT : CONTROL_RETURN_DOUBLE])]); + ret.push(['stat', makeAssign(makeStackAccess(type, asmData.controlDataStackPos(outlineIndex)), node[1])]); + } + ret.push(['stat', ['break', 'OL']]); + } else if (type == 'break') { + var label = node[1] || 0; + if (label == 'OL') continue; // this was just added before us, it is new replacement code + if (!label && breakCapturers > 0) continue; // no label, and captured + if (label && (label in codeInfo.labels)) continue; // label, and defined in this code, so captured + ret = [['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', label ? CONTROL_BREAK_LABEL : CONTROL_BREAK])]]; + if (label) { + assert(label in codeInfo.breaks); + ret.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos(outlineIndex)), ['num', codeInfo.breaks[label]])]); + } + ret.push(['stat', ['break', 'OL']]); + } else if (type == 'continue') { + var label = node[1] || 0; + if (!label && continueCapturers > 0) continue; // no label, and captured + if (label && (label in codeInfo.labels)) continue; // label, and defined in this code, so captured + ret = [['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', label ? CONTROL_CONTINUE_LABEL : CONTROL_CONTINUE])]]; + if (label) { + assert(label in codeInfo.continues); + ret.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos(outlineIndex)), ['num', codeInfo.continues[label]])]); + } + ret.push(['stat', ['break', 'OL']]); + } + if (ret) { + stats.splice.apply(stats, [i, 1].concat(ret)); + i += ret.length-1; + } } } }, function(node, type) { @@ -3156,70 +3223,167 @@ function outline(ast) { continueCapturers--; } }); - // read the control data at the callsite to the outlined function + code = [['label', 'OL', ['do', ['num', 0], ['block', code]]]]; // do this after processing, to not confuse breakCapturers etc. + // read the control data at the callsite to the outlined function, and clear the control values + reps.push(['stat', makeAssign( + ['name', 'tempValue'], + makeAsmCoercion(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ASM_INT) + )]); + reps.push(['stat', makeAssign( + ['name', 'tempInt'], + makeAsmCoercion(makeStackAccess(ASM_INT, asmData.controlDataStackPos(outlineIndex)), ASM_INT) + )]); + reps.push(['stat', makeAssign( + ['name', 'tempDouble'], + makeAsmCoercion(makeStackAccess(ASM_DOUBLE, asmData.controlDataStackPos(outlineIndex)), ASM_DOUBLE) + )]); + reps.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', 0])]); + reps.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos(outlineIndex)), ['num', 0])]); // XXX not really needed + // use the control data information if (codeInfo.hasReturn) { reps.push(makeIf( - makeComparison(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_RETURN_VOID]), + makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_RETURN_VOID]), [['stat', ['return']]] )); + } + if (codeInfo.hasReturnInt) { reps.push(makeIf( - makeComparison(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_RETURN_INT]), - [['stat', ['return', makeStackAccess(ASM_INT, asmData.controlDataStackPos)]]] + makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_RETURN_INT]), + [['stat', ['return', makeAsmCoercion(['name', 'tempInt'], ASM_INT)]]] )); + } + if (codeInfo.hasReturnDouble) { reps.push(makeIf( - makeComparison(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_RETURN_DOUBLE]), - [['stat', ['return', makeStackAccess(ASM_DOUBLE, asmData.controlDataStackPos)]]] + makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_RETURN_DOUBLE]), + [['stat', ['return', makeAsmCoercion(['name', 'tempDouble'], ASM_DOUBLE)]]] )); } if (codeInfo.hasBreak) { reps.push(makeIf( - makeComparison(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_BREAK]), - ['stat', ['break']] - )); - reps.push(makeIf( - makeComparison(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_BREAK_LABEL]), - ['stat', ['break', makeStackAccess(ASM_INT, asmData.controlDataStackPos)]] // XXX here and below, need a switch overall possible labels + makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_BREAK]), + [['stat', ['break']]] )); + if (keys(codeInfo.breaks).length > 0) { + reps.push(makeIf( + makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_BREAK_LABEL]), + [makeSwitch(makeAsmCoercion(['name', 'tempInt'], ASM_INT), keys(codeInfo.breaks).map(function(key) { + var id = codeInfo.breaks[key]; + return [['num', id], [['block', [['stat', ['break', key]]]]]]; + }))] + )); + } } if (codeInfo.hasContinue) { reps.push(makeIf( - makeComparison(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_CONTINUE]), - ['stat', ['break']] - )); - reps.push(makeIf( - makeComparison(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_CONTINUE_LABEL]), - ['stat', ['continue', makeStackAccess(ASM_INT, asmData.controlDataStackPos)]] // XXX + makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_CONTINUE]), + [['stat', ['continue']]] )); + if (keys(codeInfo.continues).length > 0) { + reps.push(makeIf( + makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_CONTINUE_LABEL]), + [makeSwitch(makeAsmCoercion(['name', 'tempInt'], ASM_INT), keys(codeInfo.continues).map(function(key) { + var id = codeInfo.continues[key]; + return [['num', id], [['block', [['stat', ['continue', key]]]]]]; + }))] + )); + } + } + } + // add spills and unspills in outlined code outside the OL loop + keys(setUnion(codeInfo.reads, codeInfo.writes)).forEach(function(v) { + if (!(v in owned)) { + code.unshift(['stat', ['assign', true, ['name', v], makeAsmCoercion(['sub', ['name', getAsmType(v, asmData) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]], getAsmType(v, asmData))]]); + } + }); + for (var v in codeInfo.writes) { + if (!(v in owned)) { + code.push(['stat', ['assign', true, ['sub', ['name', getAsmType(v, asmData) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]], ['name', v]]]); } } + // add final return of sp. the model is that we send sp as the single param, and get it back out + code.push(['stat', ['return', makeAsmCoercion(['name', 'sp'], ASM_INT)]]); + // finalize var newFunc = ['defun', newIdent, ['sp'], code]; var newAsmData = { params: { sp: ASM_INT }, vars: {} }; for (var v in codeInfo.reads) { - newAsmData.vars[v] = getAsmType(asmData, v); + if (v != 'sp') newAsmData.vars[v] = getAsmType(v, asmData); } for (var v in codeInfo.writes) { - newAsmData.vars[v] = getAsmType(asmData, v); + if (v != 'sp') newAsmData.vars[v] = getAsmType(v, asmData); } denormalizeAsm(newFunc, newAsmData); + // add outline call markers (we cannot do later outlinings that cut through an outlining call) + reps.unshift(['begin-outline-call', newIdent]); + reps.push(['end-outline-call', newIdent]); // replace in stats stats.splice.apply(stats, [start, end-start+1].concat(reps)); + // final evaluation and processing + if (!extraInfo.allowCostlyOutlines && (measureSize(func) >= funcSize || measureSize(newFunc) >= funcSize)) { + // abort, this was pointless + stats.length = originalStats.length; + for (var i = 0; i < stats.length; i++) stats[i] = originalStats[i]; + return []; + } + for (var v in owned) { + if (v != 'sp') delete asmData.vars[v]; // parent does not need these anymore + } + // if we just removed a final return from the original function, add one + var last = getStatements(func)[getStatements(func).length-1]; + if (last[0] === 'stat') last = last[1]; + if (last[0] !== 'return') { + if (allCodeInfo.hasReturnInt || allCodeInfo.hasReturnDouble) { + getStatements(func).push(['stat', ['return', makeAsmCoercion(['num', 0], allCodeInfo.hasReturnInt ? ASM_INT : ASM_DOUBLE)]]); + } + } + outliningParents[newIdent] = func[1]; return [newFunc]; } function outlineStatements(func, asmData, stats, maxSize) { level++; - if (measureSize(stats) < sizeToOutline) return; + printErr('outlineStatements: ' + [func[1], level, measureSize(func)]); + var lastSize = measureSize(stats); + if (lastSize < sizeToOutline) { level--; return } var ret = []; var sizeSeen = 0; var end = stats.length-1; var i = stats.length; - while (--i >= 0) { + var minIndex = stats == getStatements(func) ? getFirstIndexInNormalized(func, asmData) : 0; + var canRestart = false; + while (1) { + i--; + if (i < minIndex) { + // we might be done. but, if we have just outlined, do a further attempt from the beginning. + // (but only if the total costs are not extravagant) + var currSize = measureSize(stats); + var outlinedSize = measureSize(ret); + if (canRestart && currSize > 1.2*sizeToOutline && lastSize - currSize >= 0.75*sizeToOutline) { + printErr('restarting ' + func[1] + ' since ' + [currSize, outlinedSize, lastSize] + ' in level ' + level); + lastSize = currSize; + i = stats.length; + end = stats.length-1; + sizeSeen = 0; + canRestart = false; + continue; + } else { + break; + } + } var stat = stats[i]; + if (stat[0] === 'end-outline-call') { + // we cannot outline through an outline call, so include all of it + while (stats[i--][0] !== 'begin-outline-call') { + assert(i >= 0); + } + assert(i >= 0); + stat = stats[i]; + } var size = measureSize(stat); //printErr(level + ' size ' + [i, size]); if (size >= sizeToOutline) { // this by itself is big enough to inline, recurse into it and find statements to split on var subStatements = null; + var pre = ret.length; traverse(stat, function(node, type) { if (type == 'block') { if (measureSize(node) >= sizeToOutline) { @@ -3229,14 +3393,20 @@ function outline(ast) { return null; // do not recurse into children, outlineStatements will do so if necessary } }); - sizeSeen = 0; - continue; + if (ret.length > pre) { + // we outlined recursively, reset our state here + printErr('successful outline in recursion ' + func[1] + ' due to recursive in level ' + level); + end = i-1; + sizeSeen = 0; + canRestart = true; + continue; + } } sizeSeen += size; - // If this is big enough to outline, but no too big (if very close to the size of the full function, + // If this is big enough to outline, but not too big (if very close to the size of the full function, // outlining is pointless; remove stats from the end to try to achieve the good case), then outline. // Also, try to reduce the size if it is much larger than the hoped-for size - while ((sizeSeen > maxSize || sizeSeen > 2*sizeToOutline) && i < end) { + while ((sizeSeen > maxSize || sizeSeen > 2*sizeToOutline) && i < end && stats[i][0] !== 'end-outline-call') { sizeSeen -= measureSize(stats[end]); if (sizeSeen >= sizeToOutline) { end--; @@ -3247,8 +3417,11 @@ function outline(ast) { } if (sizeSeen >= sizeToOutline && sizeSeen <= maxSize) { ret.push.apply(ret, doOutline(func, asmData, stats, i, end)); // outline [i, .. ,end] inclusive + printErr('performed outline on ' + func[1] + ' of ' + sizeSeen + ', func is now size ' + measureSize(func)); sizeSeen = 0; end = i-1; + canRestart = true; + continue; } } level--; @@ -3274,29 +3447,78 @@ function outline(ast) { var asmData = normalizeAsm(func); var size = measureSize(func); if (size >= sizeToOutline) { + printErr('trying to reduce the size of ' + func[1] + ' which is ' + size + ' (>= ' + sizeToOutline + ')'); aggressiveVariableElimination(func, asmData); analyzeFunction(func, asmData); - var ret = outlineStatements(func, asmData, getStatements(func), 0.5*size); - if (ret && ret.length > 0) newFuncs.push.apply(newFuncs, ret); + var stats = getStatements(func); + var ret = outlineStatements(func, asmData, stats, 0.9*size); + assert(level == 0); + if (ret && ret.length > 0) { + newFuncs.push.apply(newFuncs, ret); + // We have outlined. Add stack support + if ('sp' in asmData.vars) { + // find stack bump (STACKTOP = STACKTOP + X | 0) and add the extra space + var stackBumpNode = getStackBumpNode(stats); + if (stackBumpNode) stackBumpNode[3][2][3][1] = asmData.totalStackSize; + } else if (!('sp' in asmData.params)) { // if sp is a param, then we are an outlined function, no need to add stack support for us + // add sp variable and stack bump + var index = getFirstIndexInNormalized(func, asmData); + stats.splice(index, 0, + ['stat', makeAssign(['name', 'sp'], ['name', 'STACKTOP'])], + ['stat', makeAssign(['name', 'STACKTOP'], ['binary', '|', ['binary', '+', ['name', 'STACKTOP'], ['num', asmData.totalStackSize]], ['num', 0]])] + ); + asmData.vars.sp = ASM_INT; // no need to add to vars, we are about to denormalize anyhow + // we added sp, so we must add stack popping + function makePop() { + return ['stat', makeAssign(['name', 'STACKTOP'], ['name', 'sp'])]; + } + traverse(func, function(node, type) { + var stats = getStatements(node); + if (!stats) return; + for (var i = 0; i < stats.length; i++) { + var subNode = stats[i]; + if (subNode[0] === 'stat') subNode = subNode[1]; + if (subNode[0] == 'return') { + stats.splice(i, 0, makePop()); + i++; + } + } + }); + // pop the stack at the end if there is not a return + var last = stats[stats.length-1]; + if (last[0] === 'stat') last = last[1]; + if (last[0] !== 'return') { + stats.push(makePop()); + } + } + } + printErr('... resulting size of ' + func[1] + ' is ' + measureSize(func)); } denormalizeAsm(func, asmData); }); + funcs = null; + // TODO: control flow: route returns and breaks. outlined code should have all breaks/continues/returns break into the outermost scope, // after setting a state variable, etc. if (newFuncs.length > 0) { - // We have outlined. Add stack support: header in which we allocate enough stack space TODO - // If sp was not present before, add it and before each return, pop the stack. also a final pop if not ending with a return TODO - // (none of this should be done in inner functions, of course, just the original) - // add new functions to the toplevel, or create a toplevel if there isn't one ast[1].push.apply(ast[1], newFuncs); - funcs = newFuncs; - more = true; + // TODO: check if in some cases we do need to outline new functions + //funcs = newFuncs.filter(function(newFunc) { + // // recursively outline if we have a large new function that did not come at a high cost + // return measureSize(newFunc) > sizeToOutline && costs[newFunc[1]] < 0.1*sizeToOutline; + //}); + //more = funcs.length > 0; } } + + // clear out markers + traverse(ast, function(node, type) { + if (type === 'begin-outline-call' || type === 'end-outline-call') return emptyNode(); + }); } // Last pass utilities @@ -3378,10 +3600,9 @@ var passes = { unGlobalize: unGlobalize, removeAssignsToUndefined: removeAssignsToUndefined, //removeUnneededLabelSettings: removeUnneededLabelSettings, - simplifyExpressionsPre: simplifyExpressionsPre, + simplifyExpressions: simplifyExpressions, optimizeShiftsConservative: optimizeShiftsConservative, optimizeShiftsAggressive: optimizeShiftsAggressive, - simplifyExpressionsPost: simplifyExpressionsPost, hoistMultiples: hoistMultiples, loopOptimizer: loopOptimizer, registerize: registerize, diff --git a/tools/js_optimizer.py b/tools/js_optimizer.py index a4e1ca6c..4e7d5474 100644 --- a/tools/js_optimizer.py +++ b/tools/js_optimizer.py @@ -102,16 +102,20 @@ start_asm_marker = '// EMSCRIPTEN_START_ASM\n' end_asm_marker = '// EMSCRIPTEN_END_ASM\n' def run_on_chunk(command): - filename = command[2] # XXX hackish - #print >> sys.stderr, 'running js optimizer command', ' '.join(command), '""""', open(filename).read() - output = subprocess.Popen(command, stdout=subprocess.PIPE).communicate()[0] - assert len(output) > 0 and not output.startswith('Assertion failed'), 'Error in js optimizer: ' + output - filename = temp_files.get(os.path.basename(filename) + '.jo.js').name - f = open(filename, 'w') - f.write(output) - f.close() - if DEBUG and not shared.WINDOWS: print >> sys.stderr, '.' # Skip debug progress indicator on Windows, since it doesn't buffer well with multiple threads printing to console. - return filename + try: + filename = command[2] # XXX hackish + #print >> sys.stderr, 'running js optimizer command', ' '.join(command), '""""', open(filename).read() + output = subprocess.Popen(command, stdout=subprocess.PIPE).communicate()[0] + assert len(output) > 0 and not output.startswith('Assertion failed'), 'Error in js optimizer: ' + output + filename = temp_files.get(os.path.basename(filename) + '.jo.js').name + f = open(filename, 'w') + f.write(output) + f.close() + if DEBUG and not shared.WINDOWS: print >> sys.stderr, '.' # Skip debug progress indicator on Windows, since it doesn't buffer well with multiple threads printing to console. + return filename + except KeyboardInterrupt: + # avoid throwing keyboard interrupts from a child process + raise Exception() def run_on_js(filename, passes, js_engine, jcache, source_map=False, extra_info=None): if isinstance(jcache, bool) and jcache: jcache = shared.JCache diff --git a/tools/shared.py b/tools/shared.py index 0351a736..007c2ee8 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -726,7 +726,7 @@ class Building: env['HOST_CXXFLAGS'] = "-W" #if set to nothing, CXXFLAGS is used, which we don't want env['PKG_CONFIG_LIBDIR'] = path_from_root('system', 'local', 'lib', 'pkgconfig') + os.path.pathsep + path_from_root('system', 'lib', 'pkgconfig') env['PKG_CONFIG_PATH'] = os.environ.get ('EM_PKG_CONFIG_PATH') or '' - env['EMSCRIPTEN'] = '1' + env['EMSCRIPTEN'] = path_from_root() return env @staticmethod diff --git a/tools/test-js-optimizer-asm-outline-output.js b/tools/test-js-optimizer-asm-outline-output.js deleted file mode 100644 index c54fc346..00000000 --- a/tools/test-js-optimizer-asm-outline-output.js +++ /dev/null @@ -1,570 +0,0 @@ -function linear() { - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); - cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); -} -function _free($mem) { - $mem = $mem | 0; - var $5 = 0, $10 = 0, $16 = 0, $21 = 0, $25 = 0, $26 = 0, $37 = 0, $40 = 0, $_pre_phi307 = 0, $69 = 0, $72 = 0, $75 = 0, $80 = 0, $95 = 0, $100 = 0, $RP_0 = 0, $R_0 = 0, $102 = 0, $103 = 0, $106 = 0, $107 = 0, $R_1 = 0, $120 = 0, $151 = 0, $164 = 0, $psize_0 = 0, $p_0 = 0, $189 = 0, $194 = 0, $204 = 0, $220 = 0, $227 = 0, $233 = 0, $236 = 0, $_pre_phi305 = 0, $267 = 0, $270 = 0, $273 = 0, $278 = 0, $294 = 0, $299 = 0, $RP9_0 = 0, $R7_0 = 0, $301 = 0, $302 = 0, $305 = 0, $306 = 0, $R7_1 = 0, $320 = 0, $351 = 0, $364 = 0, $psize_1 = 0, $390 = 0, $396 = 0, $404 = 0, $_pre_phi = 0, $F16_0 = 0, $414 = 0, $415 = 0, $428 = 0, $436 = 0, $I18_0 = 0, $443 = 0, $447 = 0, $448 = 0, $463 = 0, $T_0 = 0, $K19_0 = 0, $472 = 0, $473 = 0, $486 = 0, $487 = 0, $489 = 0, $501 = 0, $sp_0_in_i = 0, $sp_0_i = 0, label = 0, sp = 0; - sp = STACKTOP; - if (($mem | 0) == 0) { - STACKTOP = sp; - return; - } - $5 = HEAP32[24] | 0; - if (($mem - 8 | 0) >>> 0 < $5 >>> 0) { - _abort(); - } - $10 = HEAP32[$mem - 4 >> 2] | 0; - if (($10 & 3 | 0) == 1) { - _abort(); - } - $16 = $mem + (($10 & -8) - 8) | 0; - L621 : do { - if (($10 & 1 | 0) == 0) { - $21 = HEAP32[($mem - 8 | 0) >> 2] | 0; - if (($10 & 3 | 0) == 0) { - return; - } - $25 = $mem + (-8 - $21 | 0) | 0; - $26 = $21 + ($10 & -8) | 0; - if (($mem + (-8 - $21 | 0) | 0) >>> 0 < $5 >>> 0) { - _abort(); - } - if (($25 | 0) == (HEAP32[25] | 0)) { - if ((HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] & 3 | 0) != 3) { - $p_0 = $25; - $psize_0 = $26; - break; - } - HEAP32[22] = $26; - HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] = HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] & -2; - HEAP32[$mem + ((-8 - $21 | 0) + 4) >> 2] = $26 | 1; - HEAP32[($mem + (($10 & -8) - 8) | 0) >> 2] = $26; - return; - } - if ($21 >>> 0 < 256) { - $37 = HEAP32[$mem + ((-8 - $21 | 0) + 8) >> 2] | 0; - $40 = HEAP32[$mem + ((-8 - $21 | 0) + 12) >> 2] | 0; - do { - if (($37 | 0) != (120 + ($21 >>> 3 << 1 << 2) | 0 | 0)) { - if ($37 >>> 0 < $5 >>> 0) { - _abort(); - } - if ((HEAP32[$37 + 12 >> 2] | 0) == ($25 | 0)) { - break; - } - _abort(); - } - } while (0); - if (($40 | 0) == ($37 | 0)) { - HEAP32[20] = HEAP32[20] & (1 << ($21 >>> 3) ^ -1); - $p_0 = $25; - $psize_0 = $26; - break; - } - do { - if (($40 | 0) == (120 + ($21 >>> 3 << 1 << 2) | 0 | 0)) { - $_pre_phi307 = $40 + 8 | 0; - } else { - if ($40 >>> 0 < $5 >>> 0) { - _abort(); - } - if ((HEAP32[($40 + 8 | 0) >> 2] | 0) == ($25 | 0)) { - $_pre_phi307 = $40 + 8 | 0; - break; - } - _abort(); - } - } while (0); - HEAP32[$37 + 12 >> 2] = $40; - HEAP32[$_pre_phi307 >> 2] = $37; - $p_0 = $25; - $psize_0 = $26; - break; - } - $69 = $mem + (-8 - $21 | 0) | 0; - $72 = HEAP32[$mem + ((-8 - $21 | 0) + 24) >> 2] | 0; - $75 = HEAP32[$mem + ((-8 - $21 | 0) + 12) >> 2] | 0; - do { - if (($75 | 0) == ($69 | 0)) { - $95 = HEAP32[($mem + ((-8 - $21 | 0) + 20) | 0) >> 2] | 0; - if (($95 | 0) == 0) { - $100 = HEAP32[($mem + ((-8 - $21 | 0) + 16) | 0) >> 2] | 0; - if (($100 | 0) == 0) { - $R_1 = 0; - break; - } else { - $R_0 = $100; - $RP_0 = $mem + ((-8 - $21 | 0) + 16) | 0; - } - } else { - $R_0 = $95; - $RP_0 = $mem + ((-8 - $21 | 0) + 20) | 0; - } - while (1) { - $102 = $R_0 + 20 | 0; - $103 = HEAP32[$102 >> 2] | 0; - if (($103 | 0) != 0) { - $R_0 = $103; - $RP_0 = $102; - continue; - } - $106 = $R_0 + 16 | 0; - $107 = HEAP32[$106 >> 2] | 0; - if (($107 | 0) == 0) { - break; - } else { - $R_0 = $107; - $RP_0 = $106; - } - } - if ($RP_0 >>> 0 < $5 >>> 0) { - _abort(); - } else { - HEAP32[$RP_0 >> 2] = 0; - $R_1 = $R_0; - break; - } - } else { - $80 = HEAP32[$mem + ((-8 - $21 | 0) + 8) >> 2] | 0; - if ($80 >>> 0 < $5 >>> 0) { - _abort(); - } - if ((HEAP32[($80 + 12 | 0) >> 2] | 0) != ($69 | 0)) { - _abort(); - } - if ((HEAP32[($75 + 8 | 0) >> 2] | 0) == ($69 | 0)) { - HEAP32[($80 + 12 | 0) >> 2] = $75; - HEAP32[($75 + 8 | 0) >> 2] = $80; - $R_1 = $75; - break; - } else { - _abort(); - } - } - } while (0); - if (($72 | 0) == 0) { - $p_0 = $25; - $psize_0 = $26; - break; - } - $120 = 384 + (HEAP32[($mem + ((-8 - $21 | 0) + 28) | 0) >> 2] << 2) | 0; - do { - if (($69 | 0) == (HEAP32[$120 >> 2] | 0)) { - HEAP32[$120 >> 2] = $R_1; - if (($R_1 | 0) != 0) { - break; - } - HEAP32[21] = HEAP32[21] & (1 << HEAP32[($mem + ((-8 - $21 | 0) + 28) | 0) >> 2] ^ -1); - $p_0 = $25; - $psize_0 = $26; - break L621; - } else { - if ($72 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); - } - if ((HEAP32[($72 + 16 | 0) >> 2] | 0) == ($69 | 0)) { - HEAP32[($72 + 16 | 0) >> 2] = $R_1; - } else { - HEAP32[$72 + 20 >> 2] = $R_1; - } - if (($R_1 | 0) == 0) { - $p_0 = $25; - $psize_0 = $26; - break L621; - } - } - } while (0); - if ($R_1 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); - } - HEAP32[$R_1 + 24 >> 2] = $72; - $151 = HEAP32[$mem + ((-8 - $21 | 0) + 16) >> 2] | 0; - do { - if (($151 | 0) != 0) { - if ($151 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); - } else { - HEAP32[$R_1 + 16 >> 2] = $151; - HEAP32[$151 + 24 >> 2] = $R_1; - break; - } - } - } while (0); - $164 = HEAP32[$mem + ((-8 - $21 | 0) + 20) >> 2] | 0; - if (($164 | 0) == 0) { - $p_0 = $25; - $psize_0 = $26; - break; - } - if ($164 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); - } else { - HEAP32[$R_1 + 20 >> 2] = $164; - HEAP32[$164 + 24 >> 2] = $R_1; - $p_0 = $25; - $psize_0 = $26; - break; - } - } else { - $p_0 = $mem - 8 | 0; - $psize_0 = $10 & -8; - } - } while (0); - $189 = $p_0; - if ($189 >>> 0 >= ($mem + (($10 & -8) - 8) | 0) >>> 0) { - _abort(); - } - $194 = HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] | 0; - if (($194 & 1 | 0) == 0) { - _abort(); - } - do { - if (($194 & 2 | 0) == 0) { - if (($16 | 0) == (HEAP32[26] | 0)) { - $204 = (HEAP32[23] | 0) + $psize_0 | 0; - HEAP32[23] = $204; - HEAP32[26] = $p_0; - HEAP32[$p_0 + 4 >> 2] = $204 | 1; - if (($p_0 | 0) == (HEAP32[25] | 0)) { - HEAP32[25] = 0; - HEAP32[22] = 0; - } - if ($204 >>> 0 <= (HEAP32[27] | 0) >>> 0) { - return; - } - _sys_trim(0) | 0; - return; - } - if (($16 | 0) == (HEAP32[25] | 0)) { - $220 = (HEAP32[22] | 0) + $psize_0 | 0; - HEAP32[22] = $220; - HEAP32[25] = $p_0; - HEAP32[$p_0 + 4 >> 2] = $220 | 1; - HEAP32[$189 + $220 >> 2] = $220; - return; - } - $227 = ($194 & -8) + $psize_0 | 0; - L726 : do { - if ($194 >>> 0 < 256) { - $233 = HEAP32[$mem + ($10 & -8) >> 2] | 0; - $236 = HEAP32[$mem + ($10 & -8 | 4) >> 2] | 0; - do { - if (($233 | 0) != (120 + ($194 >>> 3 << 1 << 2) | 0 | 0)) { - if ($233 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); - } - if ((HEAP32[$233 + 12 >> 2] | 0) == ($16 | 0)) { - break; - } - _abort(); - } - } while (0); - if (($236 | 0) == ($233 | 0)) { - HEAP32[20] = HEAP32[20] & (1 << ($194 >>> 3) ^ -1); - break; - } - do { - if (($236 | 0) == (120 + ($194 >>> 3 << 1 << 2) | 0 | 0)) { - $_pre_phi305 = $236 + 8 | 0; - } else { - if ($236 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); - } - if ((HEAP32[($236 + 8 | 0) >> 2] | 0) == ($16 | 0)) { - $_pre_phi305 = $236 + 8 | 0; - break; - } - _abort(); - } - } while (0); - HEAP32[$233 + 12 >> 2] = $236; - HEAP32[$_pre_phi305 >> 2] = $233; - } else { - $267 = $mem + (($10 & -8) - 8) | 0; - $270 = HEAP32[$mem + (($10 & -8) + 16) >> 2] | 0; - $273 = HEAP32[$mem + ($10 & -8 | 4) >> 2] | 0; - do { - if (($273 | 0) == ($267 | 0)) { - $294 = HEAP32[($mem + (($10 & -8) + 12) | 0) >> 2] | 0; - if (($294 | 0) == 0) { - $299 = HEAP32[($mem + (($10 & -8) + 8) | 0) >> 2] | 0; - if (($299 | 0) == 0) { - $R7_1 = 0; - break; - } else { - $R7_0 = $299; - $RP9_0 = $mem + (($10 & -8) + 8) | 0; - } - } else { - $R7_0 = $294; - $RP9_0 = $mem + (($10 & -8) + 12) | 0; - } - while (1) { - $301 = $R7_0 + 20 | 0; - $302 = HEAP32[$301 >> 2] | 0; - if (($302 | 0) != 0) { - $R7_0 = $302; - $RP9_0 = $301; - continue; - } - $305 = $R7_0 + 16 | 0; - $306 = HEAP32[$305 >> 2] | 0; - if (($306 | 0) == 0) { - break; - } else { - $R7_0 = $306; - $RP9_0 = $305; - } - } - if ($RP9_0 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); - } else { - HEAP32[$RP9_0 >> 2] = 0; - $R7_1 = $R7_0; - break; - } - } else { - $278 = HEAP32[$mem + ($10 & -8) >> 2] | 0; - if ($278 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); - } - if ((HEAP32[($278 + 12 | 0) >> 2] | 0) != ($267 | 0)) { - _abort(); - } - if ((HEAP32[($273 + 8 | 0) >> 2] | 0) == ($267 | 0)) { - HEAP32[($278 + 12 | 0) >> 2] = $273; - HEAP32[($273 + 8 | 0) >> 2] = $278; - $R7_1 = $273; - break; - } else { - _abort(); - } - } - } while (0); - if (($270 | 0) == 0) { - break; - } - $320 = 384 + (HEAP32[($mem + (($10 & -8) + 20) | 0) >> 2] << 2) | 0; - do { - if (($267 | 0) == (HEAP32[$320 >> 2] | 0)) { - HEAP32[$320 >> 2] = $R7_1; - if (($R7_1 | 0) != 0) { - break; - } - HEAP32[21] = HEAP32[21] & (1 << HEAP32[($mem + (($10 & -8) + 20) | 0) >> 2] ^ -1); - break L726; - } else { - if ($270 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); - } - if ((HEAP32[($270 + 16 | 0) >> 2] | 0) == ($267 | 0)) { - HEAP32[($270 + 16 | 0) >> 2] = $R7_1; - } else { - HEAP32[$270 + 20 >> 2] = $R7_1; - } - if (($R7_1 | 0) == 0) { - break L726; - } - } - } while (0); - if ($R7_1 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); - } - HEAP32[$R7_1 + 24 >> 2] = $270; - $351 = HEAP32[$mem + (($10 & -8) + 8) >> 2] | 0; - do { - if (($351 | 0) != 0) { - if ($351 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); - } else { - HEAP32[$R7_1 + 16 >> 2] = $351; - HEAP32[$351 + 24 >> 2] = $R7_1; - break; - } - } - } while (0); - $364 = HEAP32[$mem + (($10 & -8) + 12) >> 2] | 0; - if (($364 | 0) == 0) { - break; - } - if ($364 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); - } else { - HEAP32[$R7_1 + 20 >> 2] = $364; - HEAP32[$364 + 24 >> 2] = $R7_1; - break; - } - } - } while (0); - HEAP32[$p_0 + 4 >> 2] = $227 | 1; - HEAP32[$189 + $227 >> 2] = $227; - if (($p_0 | 0) != (HEAP32[25] | 0)) { - $psize_1 = $227; - break; - } - HEAP32[22] = $227; - return; - } else { - HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] = $194 & -2; - HEAP32[$p_0 + 4 >> 2] = $psize_0 | 1; - HEAP32[$189 + $psize_0 >> 2] = $psize_0; - $psize_1 = $psize_0; - } - } while (0); - $390 = $psize_1 >>> 3; - if ($psize_1 >>> 0 < 256) { - $396 = HEAP32[20] | 0; - do { - if (($396 & 1 << $390 | 0) == 0) { - HEAP32[20] = $396 | 1 << $390; - $F16_0 = 120 + ($390 << 1 << 2) | 0; - $_pre_phi = 120 + (($390 << 1) + 2 << 2) | 0; - } else { - $404 = HEAP32[(120 + (($390 << 1) + 2 << 2) | 0) >> 2] | 0; - if ($404 >>> 0 >= (HEAP32[24] | 0) >>> 0) { - $F16_0 = $404; - $_pre_phi = 120 + (($390 << 1) + 2 << 2) | 0; - break; - } - _abort(); - } - } while (0); - HEAP32[$_pre_phi >> 2] = $p_0; - HEAP32[$F16_0 + 12 >> 2] = $p_0; - HEAP32[$p_0 + 8 >> 2] = $F16_0; - HEAP32[$p_0 + 12 >> 2] = 120 + ($390 << 1 << 2) | 0; - return; - } - $414 = $p_0; - $415 = $psize_1 >>> 8; - do { - if (($415 | 0) == 0) { - $I18_0 = 0; - } else { - if ($psize_1 >>> 0 > 16777215) { - $I18_0 = 31; - break; - } - $428 = $415 << (($415 + 1048320 | 0) >>> 16 & 8) << ((($415 << (($415 + 1048320 | 0) >>> 16 & 8)) + 520192 | 0) >>> 16 & 4); - $436 = 14 - ((($415 << (($415 + 1048320 | 0) >>> 16 & 8)) + 520192 | 0) >>> 16 & 4 | ($415 + 1048320 | 0) >>> 16 & 8 | ($428 + 245760 | 0) >>> 16 & 2) + ($428 << (($428 + 245760 | 0) >>> 16 & 2) >>> 15) | 0; - $I18_0 = $psize_1 >>> (($436 + 7 | 0) >>> 0) & 1 | $436 << 1; - } - } while (0); - $443 = 384 + ($I18_0 << 2) | 0; - HEAP32[$p_0 + 28 >> 2] = $I18_0; - HEAP32[$p_0 + 20 >> 2] = 0; - HEAP32[$p_0 + 16 >> 2] = 0; - $447 = HEAP32[21] | 0; - $448 = 1 << $I18_0; - do { - if (($447 & $448 | 0) == 0) { - HEAP32[21] = $447 | $448; - HEAP32[$443 >> 2] = $414; - HEAP32[$p_0 + 24 >> 2] = $443; - HEAP32[$p_0 + 12 >> 2] = $p_0; - HEAP32[$p_0 + 8 >> 2] = $p_0; - } else { - if (($I18_0 | 0) == 31) { - $463 = 0; - } else { - $463 = 25 - ($I18_0 >>> 1) | 0; - } - $K19_0 = $psize_1 << $463; - $T_0 = HEAP32[$443 >> 2] | 0; - while (1) { - if ((HEAP32[$T_0 + 4 >> 2] & -8 | 0) == ($psize_1 | 0)) { - break; - } - $472 = $T_0 + 16 + ($K19_0 >>> 31 << 2) | 0; - $473 = HEAP32[$472 >> 2] | 0; - if (($473 | 0) == 0) { - label = 569; - break; - } else { - $K19_0 = $K19_0 << 1; - $T_0 = $473; - } - } - if ((label | 0) == 569) { - if ($472 >>> 0 < (HEAP32[24] | 0) >>> 0) { - _abort(); - } else { - HEAP32[$472 >> 2] = $414; - HEAP32[$p_0 + 24 >> 2] = $T_0; - HEAP32[$p_0 + 12 >> 2] = $p_0; - HEAP32[$p_0 + 8 >> 2] = $p_0; - break; - } - } - $486 = $T_0 + 8 | 0; - $487 = HEAP32[$486 >> 2] | 0; - $489 = HEAP32[24] | 0; - if ($T_0 >>> 0 < $489 >>> 0) { - _abort(); - } - if ($487 >>> 0 < $489 >>> 0) { - _abort(); - } else { - HEAP32[$487 + 12 >> 2] = $414; - HEAP32[$486 >> 2] = $414; - HEAP32[$p_0 + 8 >> 2] = $487; - HEAP32[$p_0 + 12 >> 2] = $T_0; - HEAP32[$p_0 + 24 >> 2] = 0; - break; - } - } - } while (0); - $501 = (HEAP32[28] | 0) - 1 | 0; - HEAP32[28] = $501; - if (($501 | 0) == 0) { - $sp_0_in_i = 536; - } else { - return; - } - while (1) { - $sp_0_i = HEAP32[$sp_0_in_i >> 2] | 0; - if (($sp_0_i | 0) == 0) { - break; - } else { - $sp_0_in_i = $sp_0_i + 8 | 0; - } - } - HEAP32[28] = -1; - STACKTOP = sp; - return; -} - diff --git a/tools/test-js-optimizer-asm-outline1-output.js b/tools/test-js-optimizer-asm-outline1-output.js new file mode 100644 index 00000000..8d1071a9 --- /dev/null +++ b/tools/test-js-optimizer-asm-outline1-output.js @@ -0,0 +1,566 @@ +function lin() { + var sp = 0; + sp = STACKTOP; + STACKTOP = STACKTOP + 72 | 0; + c(1); + c(2); + c(3); + c(4); + HEAP32[sp + 16 >> 2] = 0; + HEAP32[sp + 20 >> 2] = 0; + sp = lin$1(sp) | 0; + HEAP32[sp + 8 >> 2] = 0; + HEAP32[sp + 12 >> 2] = 0; + sp = lin$0(sp) | 0; + STACKTOP = sp; +} +function lin2() { + var sp = 0; + sp = STACKTOP; + STACKTOP = STACKTOP + 72 | 0; + while (1) { + c(1); + c(2); + c(3); + c(4); + HEAP32[sp + 16 >> 2] = 0; + HEAP32[sp + 20 >> 2] = 0; + sp = lin2$1(sp) | 0; + HEAP32[sp + 8 >> 2] = 0; + HEAP32[sp + 12 >> 2] = 0; + sp = lin2$0(sp) | 0; + } + STACKTOP = sp; +} +function lin3() { + var sp = 0; + sp = STACKTOP; + STACKTOP = STACKTOP + 72 | 0; + while (1) { + c(1); + c(2); + c(3); + c(4); + c(5); + HEAP32[sp + 16 >> 2] = 0; + HEAP32[sp + 20 >> 2] = 0; + sp = lin3$1(sp) | 0; + HEAP32[sp + 8 >> 2] = 0; + HEAP32[sp + 12 >> 2] = 0; + sp = lin3$0(sp) | 0; + tempValue = HEAP32[sp + 8 >> 2] | 0; + tempInt = HEAP32[sp + 12 >> 2] | 0; + tempDouble = +HEAPF32[sp + 12 >> 2]; + HEAP32[sp + 8 >> 2] = 0; + HEAP32[sp + 12 >> 2] = 0; + if ((tempValue | 0) == 6) { + STACKTOP = sp; + return tempInt | 0; + } + } + STACKTOP = sp; + return 20; +} +function lin4() { + var sp = 0; + sp = STACKTOP; + STACKTOP = STACKTOP + 72 | 0; + while (1) { + c(1); + c(2); + c(3); + c(4); + HEAP32[sp + 16 >> 2] = 0; + HEAP32[sp + 20 >> 2] = 0; + sp = lin4$1(sp) | 0; + HEAP32[sp + 8 >> 2] = 0; + HEAP32[sp + 12 >> 2] = 0; + sp = lin4$0(sp) | 0; + tempValue = HEAP32[sp + 8 >> 2] | 0; + tempInt = HEAP32[sp + 12 >> 2] | 0; + tempDouble = +HEAPF32[sp + 12 >> 2]; + HEAP32[sp + 8 >> 2] = 0; + HEAP32[sp + 12 >> 2] = 0; + if ((tempValue | 0) == 1) { + break; + } + } + STACKTOP = sp; + return 20; +} +function lin5() { + var sp = 0; + sp = STACKTOP; + STACKTOP = STACKTOP + 72 | 0; + while (1) { + c(1); + c(2); + c(3); + c(4); + HEAP32[sp + 16 >> 2] = 0; + HEAP32[sp + 20 >> 2] = 0; + sp = lin5$1(sp) | 0; + HEAP32[sp + 8 >> 2] = 0; + HEAP32[sp + 12 >> 2] = 0; + sp = lin5$0(sp) | 0; + tempValue = HEAP32[sp + 8 >> 2] | 0; + tempInt = HEAP32[sp + 12 >> 2] | 0; + tempDouble = +HEAPF32[sp + 12 >> 2]; + HEAP32[sp + 8 >> 2] = 0; + HEAP32[sp + 12 >> 2] = 0; + if ((tempValue | 0) == 3) { + continue; + } + } + STACKTOP = sp; + return 20; +} +function mix() { + var sp = 0; + sp = STACKTOP; + STACKTOP = STACKTOP + 88 | 0; + main : while (1) { + c(1); + c(2); + c(3); + c(4); + c(5); + c(6); + c(7); + HEAP32[sp + 16 >> 2] = 0; + HEAP32[sp + 20 >> 2] = 0; + sp = mix$1(sp) | 0; + HEAP32[sp + 8 >> 2] = 0; + HEAP32[sp + 12 >> 2] = 0; + sp = mix$0(sp) | 0; + tempValue = HEAP32[sp + 8 >> 2] | 0; + tempInt = HEAP32[sp + 12 >> 2] | 0; + tempDouble = +HEAPF32[sp + 12 >> 2]; + HEAP32[sp + 8 >> 2] = 0; + HEAP32[sp + 12 >> 2] = 0; + if ((tempValue | 0) == 1) { + break; + } + if ((tempValue | 0) == 2) { + switch (tempInt | 0) { + case 2: + { + break main; + } + } + } + if ((tempValue | 0) == 3) { + continue; + } + if ((tempValue | 0) == 4) { + switch (tempInt | 0) { + case 3: + { + continue main; + } + } + } + } + STACKTOP = sp; + return 20; +} +function vars(x, y) { + x = x | 0; + y = +y; + var sp = 0; + sp = STACKTOP; + STACKTOP = STACKTOP + 88 | 0; + HEAP32[sp + 32 >> 2] = 0; + HEAP32[sp + 36 >> 2] = 0; + HEAP32[sp + 8 >> 2] = x; + HEAPF32[sp + 16 >> 2] = y; + sp = vars$1(sp) | 0; + HEAP32[sp + 24 >> 2] = 0; + HEAP32[sp + 28 >> 2] = 0; + HEAP32[sp + 8 >> 2] = x; + HEAPF32[sp + 16 >> 2] = y; + sp = vars$0(sp) | 0; + STACKTOP = sp; +} +function vars2(x, y) { + x = x | 0; + y = +y; + var a = 0, b = +0, sp = 0; + sp = STACKTOP; + STACKTOP = STACKTOP + 88 | 0; + a = x + y; + b = y * x; + a = c(1 + a); + b = c(2 + b); + HEAP32[sp + 40 >> 2] = 0; + HEAP32[sp + 44 >> 2] = 0; + HEAP32[sp + 24 >> 2] = a; + HEAPF32[sp + 32 >> 2] = b; + sp = vars2$0(sp) | 0; + a = HEAP32[sp + 24 >> 2] | 0; + b = +HEAPF32[sp + 32 >> 2]; + STACKTOP = sp; +} +function vars3(x, y) { + x = x | 0; + y = +y; + var a = 0, sp = 0; + sp = STACKTOP; + STACKTOP = STACKTOP + 96 | 0; + HEAP32[sp + 40 >> 2] = 0; + HEAP32[sp + 44 >> 2] = 0; + HEAP32[sp + 24 >> 2] = a; + HEAP32[sp + 8 >> 2] = x; + HEAPF32[sp + 16 >> 2] = y; + sp = vars3$1(sp) | 0; + a = HEAP32[sp + 24 >> 2] | 0; + HEAP32[sp + 32 >> 2] = 0; + HEAP32[sp + 36 >> 2] = 0; + HEAP32[sp + 24 >> 2] = a; + HEAPF32[sp + 16 >> 2] = y; + HEAP32[sp + 8 >> 2] = x; + sp = vars3$0(sp) | 0; + a = HEAP32[sp + 24 >> 2] | 0; + STACKTOP = sp; +} +function vars4(x, y) { + x = x | 0; + y = +y; + var a = 0, b = +0, sp = 0; + sp = STACKTOP; + STACKTOP = STACKTOP + 104 | 0; + a = x + y; + HEAP32[sp + 48 >> 2] = 0; + HEAP32[sp + 52 >> 2] = 0; + HEAPF32[sp + 16 >> 2] = y; + HEAP32[sp + 8 >> 2] = x; + HEAP32[sp + 24 >> 2] = a; + HEAPF32[sp + 32 >> 2] = b; + sp = vars4$1(sp) | 0; + b = +HEAPF32[sp + 32 >> 2]; + a = HEAP32[sp + 24 >> 2] | 0; + HEAP32[sp + 40 >> 2] = 0; + HEAP32[sp + 44 >> 2] = 0; + HEAP32[sp + 24 >> 2] = a; + HEAP32[sp + 8 >> 2] = x; + HEAPF32[sp + 32 >> 2] = b; + sp = vars4$0(sp) | 0; + a = HEAP32[sp + 24 >> 2] | 0; + b = +HEAPF32[sp + 32 >> 2]; + STACKTOP = sp; +} +function vars_w_stack(x, y) { + x = x | 0; + y = +y; + var a = 0, b = +0, sp = 0; + sp = STACKTOP; + STACKTOP = STACKTOP + 144 | 0; + a = x + y; + HEAP32[sp + 72 >> 2] = 0; + HEAP32[sp + 76 >> 2] = 0; + HEAPF32[sp + 32 >> 2] = y; + HEAP32[sp + 24 >> 2] = x; + HEAP32[sp + 40 >> 2] = a; + HEAPF32[sp + 48 >> 2] = b; + sp = vars_w_stack$1(sp) | 0; + b = +HEAPF32[sp + 48 >> 2]; + a = HEAP32[sp + 40 >> 2] | 0; + HEAP32[sp + 64 >> 2] = 0; + HEAP32[sp + 68 >> 2] = 0; + HEAP32[sp + 40 >> 2] = a; + HEAPF32[sp + 48 >> 2] = b; + sp = vars_w_stack$0(sp) | 0; + a = HEAP32[sp + 40 >> 2] | 0; + b = +HEAPF32[sp + 48 >> 2]; +} +function lin$0(sp) { + sp = sp | 0; + c(13); + c(14); + c(15); + c(16); + c(17); + c(18); + c(19); + c(20); + return sp | 0; +} +function lin$1(sp) { + sp = sp | 0; + c(5); + c(6); + c(7); + c(8); + c(9); + c(10); + c(11); + c(12); + return sp | 0; +} +function lin2$0(sp) { + sp = sp | 0; + c(13); + c(14); + c(15); + c(16); + c(17); + c(18); + c(19); + c(20); + return sp | 0; +} +function lin2$1(sp) { + sp = sp | 0; + c(5); + c(6); + c(7); + c(8); + c(9); + c(10); + c(11); + c(12); + return sp | 0; +} +function lin3$0(sp) { + sp = sp | 0; + OL : do { + c(14); + c(15); + c(16); + c(17); + c(18); + c(19); + c(20); + HEAP32[sp + 8 >> 2] = 6; + HEAP32[sp + 12 >> 2] = 10; + break OL; + } while (0); + return sp | 0; +} +function lin3$1(sp) { + sp = sp | 0; + c(6); + c(7); + c(8); + c(9); + c(10); + c(11); + c(12); + c(13); + return sp | 0; +} +function lin4$0(sp) { + sp = sp | 0; + OL : do { + c(13); + c(14); + c(15); + c(16); + c(17); + c(18); + c(19); + c(20); + HEAP32[sp + 8 >> 2] = 1; + break OL; + } while (0); + return sp | 0; +} +function lin4$1(sp) { + sp = sp | 0; + c(5); + c(6); + c(7); + c(8); + c(9); + c(10); + c(11); + c(12); + return sp | 0; +} +function lin5$0(sp) { + sp = sp | 0; + OL : do { + c(13); + c(14); + c(15); + c(16); + c(17); + c(18); + c(19); + c(20); + HEAP32[sp + 8 >> 2] = 3; + break OL; + } while (0); + return sp | 0; +} +function lin5$1(sp) { + sp = sp | 0; + c(5); + c(6); + c(7); + c(8); + c(9); + c(10); + c(11); + c(12); + return sp | 0; +} +function mix$0(sp) { + sp = sp | 0; + OL : do { + c(16); + c(17); + HEAP32[sp + 8 >> 2] = 2; + HEAP32[sp + 12 >> 2] = 2; + break OL; + c(18); + HEAP32[sp + 8 >> 2] = 1; + break OL; + while (1) { + break; + } + inner : while (1) { + break inner; + } + c(19); + HEAP32[sp + 8 >> 2] = 3; + break OL; + c(20); + HEAP32[sp + 8 >> 2] = 4; + HEAP32[sp + 12 >> 2] = 3; + break OL; + } while (0); + return sp | 0; +} +function mix$1(sp) { + sp = sp | 0; + c(8); + c(9); + c(10); + c(11); + c(12); + c(13); + c(14); + c(15); + return sp | 0; +} +function vars$0(sp) { + sp = sp | 0; + var x = 0, y = +0; + y = +HEAPF32[sp + 16 >> 2]; + x = HEAP32[sp + 8 >> 2] | 0; + c(5 + (x + y)); + c(6 + y * x); + c(7 + (x + y)); + c(8 + y * x); + return sp | 0; +} +function vars$1(sp) { + sp = sp | 0; + var x = 0, y = +0; + y = +HEAPF32[sp + 16 >> 2]; + x = HEAP32[sp + 8 >> 2] | 0; + c(1 + (x + y)); + c(2 + y * x); + c(3 + (x + y)); + c(4 + y * x); + return sp | 0; +} +function vars2$0(sp) { + sp = sp | 0; + var a = 0, b = +0; + b = +HEAPF32[sp + 32 >> 2]; + a = HEAP32[sp + 24 >> 2] | 0; + a = c(3 + a); + b = c(4 + b); + a = c(5 + a); + b = c(6 + b); + HEAP32[sp + 24 >> 2] = a; + HEAPF32[sp + 32 >> 2] = b; + return sp | 0; +} +function vars3$0(sp) { + sp = sp | 0; + var a = 0, y = +0, x = 0; + x = HEAP32[sp + 8 >> 2] | 0; + y = +HEAPF32[sp + 16 >> 2]; + a = HEAP32[sp + 24 >> 2] | 0; + a = c(4 + y * x); + a = c(5 + a); + a = c(6 + y * x); + a = c(7 + a); + HEAP32[sp + 24 >> 2] = a; + return sp | 0; +} +function vars3$1(sp) { + sp = sp | 0; + var a = 0, x = 0, y = +0; + y = +HEAPF32[sp + 16 >> 2]; + x = HEAP32[sp + 8 >> 2] | 0; + a = HEAP32[sp + 24 >> 2] | 0; + a = x + y; + a = c(1 + a); + a = c(2 + y * x); + a = c(3 + a); + HEAP32[sp + 24 >> 2] = a; + return sp | 0; +} +function vars4$0(sp) { + sp = sp | 0; + var a = 0, x = 0, b = +0; + b = +HEAPF32[sp + 32 >> 2]; + x = HEAP32[sp + 8 >> 2] | 0; + a = HEAP32[sp + 24 >> 2] | 0; + a = c(4 + a); + a = c(5 + a); + a = c(6 + a); + b = c(7 + a + x); + HEAP32[sp + 24 >> 2] = a; + HEAPF32[sp + 32 >> 2] = b; + return sp | 0; +} +function vars4$1(sp) { + sp = sp | 0; + var y = +0, x = 0, a = 0, b = +0; + b = +HEAPF32[sp + 32 >> 2]; + a = HEAP32[sp + 24 >> 2] | 0; + x = HEAP32[sp + 8 >> 2] | 0; + y = +HEAPF32[sp + 16 >> 2]; + b = y * x; + a = c(1 + a); + a = c(2 + a); + a = c(3 + a); + HEAPF32[sp + 32 >> 2] = b; + HEAP32[sp + 24 >> 2] = a; + return sp | 0; +} +function vars_w_stack$0(sp) { + sp = sp | 0; + var a = 0, b = +0; + b = +HEAPF32[sp + 48 >> 2]; + a = HEAP32[sp + 40 >> 2] | 0; + a = c(4 + a); + a = c(5 + a); + a = c(6 + a); + b = c(7 + a); + STACKTOP = sp; + HEAP32[sp + 40 >> 2] = a; + HEAPF32[sp + 48 >> 2] = b; + return sp | 0; +} +function vars_w_stack$1(sp) { + sp = sp | 0; + var y = +0, x = 0, a = 0, b = +0; + b = +HEAPF32[sp + 48 >> 2]; + a = HEAP32[sp + 40 >> 2] | 0; + x = HEAP32[sp + 24 >> 2] | 0; + y = +HEAPF32[sp + 32 >> 2]; + b = y * x; + a = c(1 + a); + a = c(2 + a); + a = c(3 + a); + HEAPF32[sp + 48 >> 2] = b; + HEAP32[sp + 40 >> 2] = a; + return sp | 0; +} + diff --git a/tools/test-js-optimizer-asm-outline1.js b/tools/test-js-optimizer-asm-outline1.js new file mode 100644 index 00000000..40026439 --- /dev/null +++ b/tools/test-js-optimizer-asm-outline1.js @@ -0,0 +1,234 @@ +function lin() { + c(1); + c(2); + c(3); + c(4); + c(5); + c(6); + c(7); + c(8); + c(9); + c(10); + c(11); + c(12); + c(13); + c(14); + c(15); + c(16); + c(17); + c(18); + c(19); + c(20); +} +function lin2() { + while (1) { + c(1); + c(2); + c(3); + c(4); + c(5); + c(6); + c(7); + c(8); + c(9); + c(10); + c(11); + c(12); + c(13); + c(14); + c(15); + c(16); + c(17); + c(18); + c(19); + c(20); + } +} +function lin3() { + while (1) { + c(1); + c(2); + c(3); + c(4); + c(5); + c(6); + c(7); + c(8); + c(9); + c(10); + c(11); + c(12); + c(13); + c(14); + c(15); + c(16); + c(17); + c(18); + c(19); + c(20); + return 10; + } + return 20; +} +function lin4() { + while (1) { + c(1); + c(2); + c(3); + c(4); + c(5); + c(6); + c(7); + c(8); + c(9); + c(10); + c(11); + c(12); + c(13); + c(14); + c(15); + c(16); + c(17); + c(18); + c(19); + c(20); + break; + } + return 20; +} +function lin5() { + while (1) { + c(1); + c(2); + c(3); + c(4); + c(5); + c(6); + c(7); + c(8); + c(9); + c(10); + c(11); + c(12); + c(13); + c(14); + c(15); + c(16); + c(17); + c(18); + c(19); + c(20); + continue; + } + return 20; +} +function mix() { + main: while (1) { + c(1); + c(2); + c(3); + c(4); + c(5); + c(6); + c(7); + c(8); + c(9); + c(10); + c(11); + c(12); + c(13); + c(14); + c(15); + c(16); + c(17); + break main; + c(18); + break; + while (1) { + break; // no need to forward + } + inner: while (1) { + break inner; // no need to forward + } + c(19); + continue; + c(20); + continue main; + } + return 20; +} +function vars(x, y) { + x = x | 0; + y = +y; + var a = 0, b = +0; + a = x+y; + b = y*x; + c(1+a); + c(2+b); + c(3+a); + c(4+b); + c(5+a); + c(6+b); + c(7+a); + c(8+b); +} +function vars2(x, y) { + x = x | 0; + y = +y; + var a = 0, b = +0; + a = x+y; + b = y*x; + a = c(1+a); + b = c(2+b); + a = c(3+a); + b = c(4+b); + a = c(5+a); + b = c(6+b); +} +function vars3(x, y) { + x = x | 0; + y = +y; + var a = 0, b = +0; + a = x+y; + b = y*x; + a = c(1+a); + a = c(2+b); + a = c(3+a); + a = c(4+b); + a = c(5+a); + a = c(6+b); + a = c(7+a); +} +function vars4(x, y) { + x = x | 0; + y = +y; + var a = 0, b = +0; + a = x+y; + b = y*x; + a = c(1+a); + a = c(2+a); + a = c(3+a); + a = c(4+a); + a = c(5+a); + a = c(6+a); + b = c(7+a+x); +} +function vars_w_stack(x, y) { + x = x | 0; + y = +y; + var a = 0, b = +0, sp = 0; + sp = STACKTOP; + STACKTOP = STACKTOP + 16 | 0; + a = x+y; + b = y*x; + a = c(1+a); + a = c(2+a); + a = c(3+a); + a = c(4+a); + a = c(5+a); + a = c(6+a); + b = c(7+a); + STACKTOP = sp; +} +// EMSCRIPTEN_GENERATED_FUNCTIONS +// EXTRA_INFO: { "sizeToOutline": 30, "allowCostlyOutlines": 1 } diff --git a/tools/test-js-optimizer-asm-outline2-output.js b/tools/test-js-optimizer-asm-outline2-output.js new file mode 100644 index 00000000..31bb7bf9 --- /dev/null +++ b/tools/test-js-optimizer-asm-outline2-output.js @@ -0,0 +1,728 @@ +function linear() { + var sp = 0; + sp = STACKTOP; + STACKTOP = STACKTOP + 72 | 0; + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + HEAP32[sp + 16 >> 2] = 0; + HEAP32[sp + 20 >> 2] = 0; + sp = linear$1(sp) | 0; + HEAP32[sp + 8 >> 2] = 0; + HEAP32[sp + 12 >> 2] = 0; + sp = linear$0(sp) | 0; + STACKTOP = sp; +} +function _free($mem) { + $mem = $mem | 0; + var $5 = 0, $10 = 0, $16 = 0, $21 = 0, $25 = 0, $26 = 0, $psize_0 = 0, $p_0 = 0, $189 = 0, $194 = 0, sp = 0; + sp = STACKTOP; + if (($mem | 0) == 0) { + STACKTOP = sp; + return; + } + $5 = HEAP32[24] | 0; + if (($mem - 8 | 0) >>> 0 < $5 >>> 0) { + _abort(); + } + $10 = HEAP32[$mem - 4 >> 2] | 0; + if (($10 & 3 | 0) == 1) { + _abort(); + } + $16 = $mem + (($10 & -8) - 8) | 0; + L621 : do { + if (($10 & 1 | 0) == 0) { + $21 = HEAP32[($mem - 8 | 0) >> 2] | 0; + if (($10 & 3 | 0) == 0) { + return; + } + $25 = $mem + (-8 - $21 | 0) | 0; + $26 = $21 + ($10 & -8) | 0; + if (($mem + (-8 - $21 | 0) | 0) >>> 0 < $5 >>> 0) { + _abort(); + } + HEAP32[sp + 648 >> 2] = 0; + HEAP32[sp + 652 >> 2] = 0; + HEAP32[sp + 48 >> 2] = $25; + HEAP32[sp + 8 >> 2] = $mem; + HEAP32[sp + 24 >> 2] = $10; + HEAP32[sp + 56 >> 2] = $26; + HEAP32[sp + 40 >> 2] = $21; + HEAP32[sp + 16 >> 2] = $5; + HEAP32[sp + 224 >> 2] = $p_0; + HEAP32[sp + 216 >> 2] = $psize_0; + sp = _free$1(sp) | 0; + $p_0 = HEAP32[sp + 224 >> 2] | 0; + $psize_0 = HEAP32[sp + 216 >> 2] | 0; + tempValue = HEAP32[sp + 648 >> 2] | 0; + tempInt = HEAP32[sp + 652 >> 2] | 0; + tempDouble = +HEAPF32[sp + 652 >> 2]; + HEAP32[sp + 648 >> 2] = 0; + HEAP32[sp + 652 >> 2] = 0; + if ((tempValue | 0) == 5) { + return; + } + if ((tempValue | 0) == 1) { + break; + } + if ((tempValue | 0) == 2) { + switch (tempInt | 0) { + case 2: + { + break L621; + } + } + } + } else { + $p_0 = $mem - 8 | 0; + $psize_0 = $10 & -8; + } + } while (0); + $189 = $p_0; + if ($189 >>> 0 >= ($mem + (($10 & -8) - 8) | 0) >>> 0) { + _abort(); + } + $194 = HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] | 0; + if (($194 & 1 | 0) == 0) { + _abort(); + } + HEAP32[sp + 656 >> 2] = 0; + HEAP32[sp + 660 >> 2] = 0; + HEAP32[sp + 240 >> 2] = $194; + HEAP32[sp + 32 >> 2] = $16; + HEAP32[sp + 216 >> 2] = $psize_0; + HEAP32[sp + 224 >> 2] = $p_0; + HEAP32[sp + 232 >> 2] = $189; + HEAP32[sp + 8 >> 2] = $mem; + HEAP32[sp + 24 >> 2] = $10; + sp = _free$2(sp) | 0; + tempValue = HEAP32[sp + 656 >> 2] | 0; + tempInt = HEAP32[sp + 660 >> 2] | 0; + tempDouble = +HEAPF32[sp + 660 >> 2]; + HEAP32[sp + 656 >> 2] = 0; + HEAP32[sp + 660 >> 2] = 0; + if ((tempValue | 0) == 5) { + return; + } +} +function linear$0(sp) { + sp = sp | 0; + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + return sp | 0; +} +function linear$1(sp) { + sp = sp | 0; + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + return sp | 0; +} +function _free$0(sp) { + sp = sp | 0; + var $16 = 0, $220 = 0, $psize_0 = 0, $p_0 = 0, $189 = 0, $227 = 0, $194 = 0, $233 = 0, $mem = 0, $10 = 0, $236 = 0, $_pre_phi305 = 0, $267 = 0, $270 = 0, $273 = 0, $294 = 0, $299 = 0, $R7_1 = 0, $R7_0 = 0, $RP9_0 = 0, $301 = 0, $302 = 0, $305 = 0, $306 = 0, $278 = 0, $320 = 0, $351 = 0, $364 = 0, $psize_1 = 0; + $psize_1 = HEAP32[sp + 424 >> 2] | 0; + $10 = HEAP32[sp + 24 >> 2] | 0; + $mem = HEAP32[sp + 8 >> 2] | 0; + $194 = HEAP32[sp + 240 >> 2] | 0; + $189 = HEAP32[sp + 232 >> 2] | 0; + $p_0 = HEAP32[sp + 224 >> 2] | 0; + $psize_0 = HEAP32[sp + 216 >> 2] | 0; + $16 = HEAP32[sp + 32 >> 2] | 0; + OL : do { + if (($16 | 0) == (HEAP32[25] | 0)) { + $220 = (HEAP32[22] | 0) + $psize_0 | 0; + HEAP32[22] = $220; + HEAP32[25] = $p_0; + HEAP32[$p_0 + 4 >> 2] = $220 | 1; + HEAP32[$189 + $220 >> 2] = $220; + HEAP32[sp + 640 >> 2] = 5; + break OL; + } + $227 = ($194 & -8) + $psize_0 | 0; + L726 : do { + if ($194 >>> 0 < 256) { + $233 = HEAP32[$mem + ($10 & -8) >> 2] | 0; + $236 = HEAP32[$mem + ($10 & -8 | 4) >> 2] | 0; + do { + if (($233 | 0) != (120 + ($194 >>> 3 << 1 << 2) | 0 | 0)) { + if ($233 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } + if ((HEAP32[$233 + 12 >> 2] | 0) == ($16 | 0)) { + break; + } + _abort(); + } + } while (0); + if (($236 | 0) == ($233 | 0)) { + HEAP32[20] = HEAP32[20] & (1 << ($194 >>> 3) ^ -1); + break; + } + do { + if (($236 | 0) == (120 + ($194 >>> 3 << 1 << 2) | 0 | 0)) { + $_pre_phi305 = $236 + 8 | 0; + } else { + if ($236 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } + if ((HEAP32[($236 + 8 | 0) >> 2] | 0) == ($16 | 0)) { + $_pre_phi305 = $236 + 8 | 0; + break; + } + _abort(); + } + } while (0); + HEAP32[$233 + 12 >> 2] = $236; + HEAP32[$_pre_phi305 >> 2] = $233; + } else { + $267 = $mem + (($10 & -8) - 8) | 0; + $270 = HEAP32[$mem + (($10 & -8) + 16) >> 2] | 0; + $273 = HEAP32[$mem + ($10 & -8 | 4) >> 2] | 0; + do { + if (($273 | 0) == ($267 | 0)) { + $294 = HEAP32[($mem + (($10 & -8) + 12) | 0) >> 2] | 0; + if (($294 | 0) == 0) { + $299 = HEAP32[($mem + (($10 & -8) + 8) | 0) >> 2] | 0; + if (($299 | 0) == 0) { + $R7_1 = 0; + break; + } else { + $R7_0 = $299; + $RP9_0 = $mem + (($10 & -8) + 8) | 0; + } + } else { + $R7_0 = $294; + $RP9_0 = $mem + (($10 & -8) + 12) | 0; + } + while (1) { + $301 = $R7_0 + 20 | 0; + $302 = HEAP32[$301 >> 2] | 0; + if (($302 | 0) != 0) { + $R7_0 = $302; + $RP9_0 = $301; + continue; + } + $305 = $R7_0 + 16 | 0; + $306 = HEAP32[$305 >> 2] | 0; + if (($306 | 0) == 0) { + break; + } else { + $R7_0 = $306; + $RP9_0 = $305; + } + } + if ($RP9_0 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } else { + HEAP32[$RP9_0 >> 2] = 0; + $R7_1 = $R7_0; + break; + } + } else { + $278 = HEAP32[$mem + ($10 & -8) >> 2] | 0; + if ($278 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } + if ((HEAP32[($278 + 12 | 0) >> 2] | 0) != ($267 | 0)) { + _abort(); + } + if ((HEAP32[($273 + 8 | 0) >> 2] | 0) == ($267 | 0)) { + HEAP32[($278 + 12 | 0) >> 2] = $273; + HEAP32[($273 + 8 | 0) >> 2] = $278; + $R7_1 = $273; + break; + } else { + _abort(); + } + } + } while (0); + if (($270 | 0) == 0) { + break; + } + $320 = 384 + (HEAP32[($mem + (($10 & -8) + 20) | 0) >> 2] << 2) | 0; + do { + if (($267 | 0) == (HEAP32[$320 >> 2] | 0)) { + HEAP32[$320 >> 2] = $R7_1; + if (($R7_1 | 0) != 0) { + break; + } + HEAP32[21] = HEAP32[21] & (1 << HEAP32[($mem + (($10 & -8) + 20) | 0) >> 2] ^ -1); + break L726; + } else { + if ($270 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } + if ((HEAP32[($270 + 16 | 0) >> 2] | 0) == ($267 | 0)) { + HEAP32[($270 + 16 | 0) >> 2] = $R7_1; + } else { + HEAP32[$270 + 20 >> 2] = $R7_1; + } + if (($R7_1 | 0) == 0) { + break L726; + } + } + } while (0); + if ($R7_1 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } + HEAP32[$R7_1 + 24 >> 2] = $270; + $351 = HEAP32[$mem + (($10 & -8) + 8) >> 2] | 0; + do { + if (($351 | 0) != 0) { + if ($351 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } else { + HEAP32[$R7_1 + 16 >> 2] = $351; + HEAP32[$351 + 24 >> 2] = $R7_1; + break; + } + } + } while (0); + $364 = HEAP32[$mem + (($10 & -8) + 12) >> 2] | 0; + if (($364 | 0) == 0) { + break; + } + if ($364 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } else { + HEAP32[$R7_1 + 20 >> 2] = $364; + HEAP32[$364 + 24 >> 2] = $R7_1; + break; + } + } + } while (0); + HEAP32[$p_0 + 4 >> 2] = $227 | 1; + HEAP32[$189 + $227 >> 2] = $227; + if (($p_0 | 0) != (HEAP32[25] | 0)) { + $psize_1 = $227; + HEAP32[sp + 640 >> 2] = 1; + break OL; + } + HEAP32[22] = $227; + HEAP32[sp + 640 >> 2] = 5; + break OL; + } while (0); + HEAP32[sp + 424 >> 2] = $psize_1; + return sp | 0; +} +function _free$1(sp) { + sp = sp | 0; + var $25 = 0, $mem = 0, $10 = 0, $26 = 0, $21 = 0, $37 = 0, $40 = 0, $5 = 0, $_pre_phi307 = 0, $69 = 0, $72 = 0, $75 = 0, $95 = 0, $100 = 0, $R_1 = 0, $R_0 = 0, $RP_0 = 0, $102 = 0, $103 = 0, $106 = 0, $107 = 0, $80 = 0, $120 = 0, $151 = 0, $164 = 0, $p_0 = 0, $psize_0 = 0; + $psize_0 = HEAP32[sp + 216 >> 2] | 0; + $p_0 = HEAP32[sp + 224 >> 2] | 0; + $5 = HEAP32[sp + 16 >> 2] | 0; + $21 = HEAP32[sp + 40 >> 2] | 0; + $26 = HEAP32[sp + 56 >> 2] | 0; + $10 = HEAP32[sp + 24 >> 2] | 0; + $mem = HEAP32[sp + 8 >> 2] | 0; + $25 = HEAP32[sp + 48 >> 2] | 0; + OL : do { + if (($25 | 0) == (HEAP32[25] | 0)) { + if ((HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] & 3 | 0) != 3) { + $p_0 = $25; + $psize_0 = $26; + HEAP32[sp + 648 >> 2] = 1; + break OL; + } + HEAP32[22] = $26; + HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] = HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] & -2; + HEAP32[$mem + ((-8 - $21 | 0) + 4) >> 2] = $26 | 1; + HEAP32[($mem + (($10 & -8) - 8) | 0) >> 2] = $26; + HEAP32[sp + 648 >> 2] = 5; + break OL; + } + if ($21 >>> 0 < 256) { + $37 = HEAP32[$mem + ((-8 - $21 | 0) + 8) >> 2] | 0; + $40 = HEAP32[$mem + ((-8 - $21 | 0) + 12) >> 2] | 0; + do { + if (($37 | 0) != (120 + ($21 >>> 3 << 1 << 2) | 0 | 0)) { + if ($37 >>> 0 < $5 >>> 0) { + _abort(); + } + if ((HEAP32[$37 + 12 >> 2] | 0) == ($25 | 0)) { + break; + } + _abort(); + } + } while (0); + if (($40 | 0) == ($37 | 0)) { + HEAP32[20] = HEAP32[20] & (1 << ($21 >>> 3) ^ -1); + $p_0 = $25; + $psize_0 = $26; + HEAP32[sp + 648 >> 2] = 1; + break OL; + } + do { + if (($40 | 0) == (120 + ($21 >>> 3 << 1 << 2) | 0 | 0)) { + $_pre_phi307 = $40 + 8 | 0; + } else { + if ($40 >>> 0 < $5 >>> 0) { + _abort(); + } + if ((HEAP32[($40 + 8 | 0) >> 2] | 0) == ($25 | 0)) { + $_pre_phi307 = $40 + 8 | 0; + break; + } + _abort(); + } + } while (0); + HEAP32[$37 + 12 >> 2] = $40; + HEAP32[$_pre_phi307 >> 2] = $37; + $p_0 = $25; + $psize_0 = $26; + HEAP32[sp + 648 >> 2] = 1; + break OL; + } + $69 = $mem + (-8 - $21 | 0) | 0; + $72 = HEAP32[$mem + ((-8 - $21 | 0) + 24) >> 2] | 0; + $75 = HEAP32[$mem + ((-8 - $21 | 0) + 12) >> 2] | 0; + do { + if (($75 | 0) == ($69 | 0)) { + $95 = HEAP32[($mem + ((-8 - $21 | 0) + 20) | 0) >> 2] | 0; + if (($95 | 0) == 0) { + $100 = HEAP32[($mem + ((-8 - $21 | 0) + 16) | 0) >> 2] | 0; + if (($100 | 0) == 0) { + $R_1 = 0; + break; + } else { + $R_0 = $100; + $RP_0 = $mem + ((-8 - $21 | 0) + 16) | 0; + } + } else { + $R_0 = $95; + $RP_0 = $mem + ((-8 - $21 | 0) + 20) | 0; + } + while (1) { + $102 = $R_0 + 20 | 0; + $103 = HEAP32[$102 >> 2] | 0; + if (($103 | 0) != 0) { + $R_0 = $103; + $RP_0 = $102; + continue; + } + $106 = $R_0 + 16 | 0; + $107 = HEAP32[$106 >> 2] | 0; + if (($107 | 0) == 0) { + break; + } else { + $R_0 = $107; + $RP_0 = $106; + } + } + if ($RP_0 >>> 0 < $5 >>> 0) { + _abort(); + } else { + HEAP32[$RP_0 >> 2] = 0; + $R_1 = $R_0; + break; + } + } else { + $80 = HEAP32[$mem + ((-8 - $21 | 0) + 8) >> 2] | 0; + if ($80 >>> 0 < $5 >>> 0) { + _abort(); + } + if ((HEAP32[($80 + 12 | 0) >> 2] | 0) != ($69 | 0)) { + _abort(); + } + if ((HEAP32[($75 + 8 | 0) >> 2] | 0) == ($69 | 0)) { + HEAP32[($80 + 12 | 0) >> 2] = $75; + HEAP32[($75 + 8 | 0) >> 2] = $80; + $R_1 = $75; + break; + } else { + _abort(); + } + } + } while (0); + if (($72 | 0) == 0) { + $p_0 = $25; + $psize_0 = $26; + HEAP32[sp + 648 >> 2] = 1; + break OL; + } + $120 = 384 + (HEAP32[($mem + ((-8 - $21 | 0) + 28) | 0) >> 2] << 2) | 0; + do { + if (($69 | 0) == (HEAP32[$120 >> 2] | 0)) { + HEAP32[$120 >> 2] = $R_1; + if (($R_1 | 0) != 0) { + break; + } + HEAP32[21] = HEAP32[21] & (1 << HEAP32[($mem + ((-8 - $21 | 0) + 28) | 0) >> 2] ^ -1); + $p_0 = $25; + $psize_0 = $26; + HEAP32[sp + 648 >> 2] = 2; + HEAP32[sp + 652 >> 2] = 2; + break OL; + } else { + if ($72 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } + if ((HEAP32[($72 + 16 | 0) >> 2] | 0) == ($69 | 0)) { + HEAP32[($72 + 16 | 0) >> 2] = $R_1; + } else { + HEAP32[$72 + 20 >> 2] = $R_1; + } + if (($R_1 | 0) == 0) { + $p_0 = $25; + $psize_0 = $26; + HEAP32[sp + 648 >> 2] = 2; + HEAP32[sp + 652 >> 2] = 2; + break OL; + } + } + } while (0); + if ($R_1 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } + HEAP32[$R_1 + 24 >> 2] = $72; + $151 = HEAP32[$mem + ((-8 - $21 | 0) + 16) >> 2] | 0; + do { + if (($151 | 0) != 0) { + if ($151 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } else { + HEAP32[$R_1 + 16 >> 2] = $151; + HEAP32[$151 + 24 >> 2] = $R_1; + break; + } + } + } while (0); + $164 = HEAP32[$mem + ((-8 - $21 | 0) + 20) >> 2] | 0; + if (($164 | 0) == 0) { + $p_0 = $25; + $psize_0 = $26; + HEAP32[sp + 648 >> 2] = 1; + break OL; + } + if ($164 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } else { + HEAP32[$R_1 + 20 >> 2] = $164; + HEAP32[$164 + 24 >> 2] = $R_1; + $p_0 = $25; + $psize_0 = $26; + HEAP32[sp + 648 >> 2] = 1; + break OL; + } + } while (0); + HEAP32[sp + 224 >> 2] = $p_0; + HEAP32[sp + 216 >> 2] = $psize_0; + return sp | 0; +} +function _free$2(sp) { + sp = sp | 0; + var $194 = 0, $16 = 0, $204 = 0, $psize_0 = 0, $p_0 = 0, $189 = 0, $mem = 0, $10 = 0, $psize_1 = 0, $390 = 0, $396 = 0, $F16_0 = 0, $_pre_phi = 0, $404 = 0, $414 = 0, $415 = 0, $I18_0 = 0, $428 = 0, $436 = 0, $443 = 0, $447 = 0, $448 = 0, $463 = 0, $K19_0 = 0, $T_0 = 0, $472 = 0, $473 = 0, label = 0, $486 = 0, $487 = 0, $489 = 0, $501 = 0, $sp_0_in_i = 0, $sp_0_i = 0; + $10 = HEAP32[sp + 24 >> 2] | 0; + $mem = HEAP32[sp + 8 >> 2] | 0; + $189 = HEAP32[sp + 232 >> 2] | 0; + $p_0 = HEAP32[sp + 224 >> 2] | 0; + $psize_0 = HEAP32[sp + 216 >> 2] | 0; + $16 = HEAP32[sp + 32 >> 2] | 0; + $194 = HEAP32[sp + 240 >> 2] | 0; + OL : do { + do { + if (($194 & 2 | 0) == 0) { + if (($16 | 0) == (HEAP32[26] | 0)) { + $204 = (HEAP32[23] | 0) + $psize_0 | 0; + HEAP32[23] = $204; + HEAP32[26] = $p_0; + HEAP32[$p_0 + 4 >> 2] = $204 | 1; + if (($p_0 | 0) == (HEAP32[25] | 0)) { + HEAP32[25] = 0; + HEAP32[22] = 0; + } + if ($204 >>> 0 <= (HEAP32[27] | 0) >>> 0) { + HEAP32[sp + 656 >> 2] = 5; + break OL; + } + _sys_trim(0) | 0; + HEAP32[sp + 656 >> 2] = 5; + break OL; + } + HEAP32[sp + 640 >> 2] = 0; + HEAP32[sp + 644 >> 2] = 0; + HEAP32[sp + 32 >> 2] = $16; + HEAP32[sp + 216 >> 2] = $psize_0; + HEAP32[sp + 224 >> 2] = $p_0; + HEAP32[sp + 232 >> 2] = $189; + HEAP32[sp + 240 >> 2] = $194; + HEAP32[sp + 8 >> 2] = $mem; + HEAP32[sp + 24 >> 2] = $10; + HEAP32[sp + 424 >> 2] = $psize_1; + sp = _free$0(sp) | 0; + $psize_1 = HEAP32[sp + 424 >> 2] | 0; + tempValue = HEAP32[sp + 640 >> 2] | 0; + tempInt = HEAP32[sp + 644 >> 2] | 0; + tempDouble = +HEAPF32[sp + 644 >> 2]; + HEAP32[sp + 640 >> 2] = 0; + HEAP32[sp + 644 >> 2] = 0; + if ((tempValue | 0) == 5) { + HEAP32[sp + 656 >> 2] = 5; + break OL; + } + if ((tempValue | 0) == 1) { + break; + } + } else { + HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] = $194 & -2; + HEAP32[$p_0 + 4 >> 2] = $psize_0 | 1; + HEAP32[$189 + $psize_0 >> 2] = $psize_0; + $psize_1 = $psize_0; + } + } while (0); + $390 = $psize_1 >>> 3; + if ($psize_1 >>> 0 < 256) { + $396 = HEAP32[20] | 0; + do { + if (($396 & 1 << $390 | 0) == 0) { + HEAP32[20] = $396 | 1 << $390; + $F16_0 = 120 + ($390 << 1 << 2) | 0; + $_pre_phi = 120 + (($390 << 1) + 2 << 2) | 0; + } else { + $404 = HEAP32[(120 + (($390 << 1) + 2 << 2) | 0) >> 2] | 0; + if ($404 >>> 0 >= (HEAP32[24] | 0) >>> 0) { + $F16_0 = $404; + $_pre_phi = 120 + (($390 << 1) + 2 << 2) | 0; + break; + } + _abort(); + } + } while (0); + HEAP32[$_pre_phi >> 2] = $p_0; + HEAP32[$F16_0 + 12 >> 2] = $p_0; + HEAP32[$p_0 + 8 >> 2] = $F16_0; + HEAP32[$p_0 + 12 >> 2] = 120 + ($390 << 1 << 2) | 0; + HEAP32[sp + 656 >> 2] = 5; + break OL; + } + $414 = $p_0; + $415 = $psize_1 >>> 8; + do { + if (($415 | 0) == 0) { + $I18_0 = 0; + } else { + if ($psize_1 >>> 0 > 16777215) { + $I18_0 = 31; + break; + } + $428 = $415 << (($415 + 1048320 | 0) >>> 16 & 8) << ((($415 << (($415 + 1048320 | 0) >>> 16 & 8)) + 520192 | 0) >>> 16 & 4); + $436 = 14 - ((($415 << (($415 + 1048320 | 0) >>> 16 & 8)) + 520192 | 0) >>> 16 & 4 | ($415 + 1048320 | 0) >>> 16 & 8 | ($428 + 245760 | 0) >>> 16 & 2) + ($428 << (($428 + 245760 | 0) >>> 16 & 2) >>> 15) | 0; + $I18_0 = $psize_1 >>> (($436 + 7 | 0) >>> 0) & 1 | $436 << 1; + } + } while (0); + $443 = 384 + ($I18_0 << 2) | 0; + HEAP32[$p_0 + 28 >> 2] = $I18_0; + HEAP32[$p_0 + 20 >> 2] = 0; + HEAP32[$p_0 + 16 >> 2] = 0; + $447 = HEAP32[21] | 0; + $448 = 1 << $I18_0; + do { + if (($447 & $448 | 0) == 0) { + HEAP32[21] = $447 | $448; + HEAP32[$443 >> 2] = $414; + HEAP32[$p_0 + 24 >> 2] = $443; + HEAP32[$p_0 + 12 >> 2] = $p_0; + HEAP32[$p_0 + 8 >> 2] = $p_0; + } else { + if (($I18_0 | 0) == 31) { + $463 = 0; + } else { + $463 = 25 - ($I18_0 >>> 1) | 0; + } + $K19_0 = $psize_1 << $463; + $T_0 = HEAP32[$443 >> 2] | 0; + while (1) { + if ((HEAP32[$T_0 + 4 >> 2] & -8 | 0) == ($psize_1 | 0)) { + break; + } + $472 = $T_0 + 16 + ($K19_0 >>> 31 << 2) | 0; + $473 = HEAP32[$472 >> 2] | 0; + if (($473 | 0) == 0) { + label = 569; + break; + } else { + $K19_0 = $K19_0 << 1; + $T_0 = $473; + } + } + if ((label | 0) == 569) { + if ($472 >>> 0 < (HEAP32[24] | 0) >>> 0) { + _abort(); + } else { + HEAP32[$472 >> 2] = $414; + HEAP32[$p_0 + 24 >> 2] = $T_0; + HEAP32[$p_0 + 12 >> 2] = $p_0; + HEAP32[$p_0 + 8 >> 2] = $p_0; + break; + } + } + $486 = $T_0 + 8 | 0; + $487 = HEAP32[$486 >> 2] | 0; + $489 = HEAP32[24] | 0; + if ($T_0 >>> 0 < $489 >>> 0) { + _abort(); + } + if ($487 >>> 0 < $489 >>> 0) { + _abort(); + } else { + HEAP32[$487 + 12 >> 2] = $414; + HEAP32[$486 >> 2] = $414; + HEAP32[$p_0 + 8 >> 2] = $487; + HEAP32[$p_0 + 12 >> 2] = $T_0; + HEAP32[$p_0 + 24 >> 2] = 0; + break; + } + } + } while (0); + $501 = (HEAP32[28] | 0) - 1 | 0; + HEAP32[28] = $501; + if (($501 | 0) == 0) { + $sp_0_in_i = 536; + } else { + HEAP32[sp + 656 >> 2] = 5; + break OL; + } + while (1) { + $sp_0_i = HEAP32[$sp_0_in_i >> 2] | 0; + if (($sp_0_i | 0) == 0) { + break; + } else { + $sp_0_in_i = $sp_0_i + 8 | 0; + } + } + HEAP32[28] = -1; + STACKTOP = sp; + HEAP32[sp + 656 >> 2] = 5; + break OL; + } while (0); + return sp | 0; +} + diff --git a/tools/test-js-optimizer-asm-outline.js b/tools/test-js-optimizer-asm-outline2.js index 153d7feb..5da6e04a 100644 --- a/tools/test-js-optimizer-asm-outline.js +++ b/tools/test-js-optimizer-asm-outline2.js @@ -603,4 +603,4 @@ function _free($mem) { return; } // EMSCRIPTEN_GENERATED_FUNCTIONS -// EXTRA_INFO: { "sizeToOutline": 1000 } +// EXTRA_INFO: { "sizeToOutline": 1000, "allowCostlyOutlines": 1 } diff --git a/tools/test-js-optimizer-asm-pre-output.js b/tools/test-js-optimizer-asm-pre-output.js index 8a5803d6..b31327f2 100644 --- a/tools/test-js-optimizer-asm-pre-output.js +++ b/tools/test-js-optimizer-asm-pre-output.js @@ -254,7 +254,7 @@ function _main($argc, $argv) { $j_08_i_i = 0; $i_09_i_i = 1; while (1) { - if (!(($j_08_i_i | 0) < 14)) { + if (($j_08_i_i | 0) >= 14) { label = 49; break; } @@ -361,7 +361,7 @@ function _main($argc, $argv) { $118 = $world + 102952 | 0; HEAP32[$116 + 96 >> 2] = HEAP32[$118 >> 2]; $121 = HEAP32[$118 >> 2] | 0; - if (!(($121 | 0) == 0)) { + if (($121 | 0) != 0) { HEAP32[$121 + 92 >> 2] = $116; } HEAP32[$118 >> 2] = $116; @@ -451,7 +451,7 @@ function _main($argc, $argv) { $y_sroa_0_0_insert_insert$1 = +$y_sroa_1_4_load293550; HEAPF32[$185 >> 2] = $y_sroa_0_0_load283451; HEAPF32[$185 + 4 >> 2] = $y_sroa_0_0_insert_insert$1; - if (!((HEAP32[$98 >> 2] & 2 | 0) == 0)) { + if ((HEAP32[$98 >> 2] & 2 | 0) != 0) { label = 65; break L82; } @@ -466,7 +466,7 @@ function _main($argc, $argv) { HEAP32[$209 + 92 >> 2] = 0; HEAP32[$209 + 96 >> 2] = HEAP32[$118 >> 2]; $213 = HEAP32[$118 >> 2] | 0; - if (!(($213 | 0) == 0)) { + if (($213 | 0) != 0) { HEAP32[$213 + 92 >> 2] = $209; } HEAP32[$118 >> 2] = $209; |