diff options
34 files changed, 2090 insertions, 975 deletions
@@ -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, diff --git a/emscripten.py b/emscripten.py index 3e3538e9..ab68fcaa 100755 --- a/emscripten.py +++ b/emscripten.py @@ -291,8 +291,9 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None, indexed_functions.add(key) if settings.get('ASM_JS'): export_bindings = settings['EXPORT_BINDINGS'] + export_all = settings['EXPORT_ALL'] for key in curr_forwarded_json['Functions']['implementedFunctions'].iterkeys(): - if key in all_exported_functions or (export_bindings and key.startswith('_emscripten_bind')): + if key in all_exported_functions or export_all or (export_bindings and key.startswith('_emscripten_bind')): exported_implemented_functions.add(key) for key, value in curr_forwarded_json['Functions']['unimplementedFunctions'].iteritems(): forwarded_json['Functions']['unimplementedFunctions'][key] = value diff --git a/src/analyzer.js b/src/analyzer.js index 1d32d7fc..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; diff --git a/src/jsifier.js b/src/jsifier.js index 30cea99b..b377202d 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -1211,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; @@ -1368,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!'); @@ -1433,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]) }); @@ -1522,7 +1522,8 @@ function JSify(data, functionsOnly, givenFunctions) { 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; @@ -1537,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 a41c8105..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) { @@ -2575,7 +2600,7 @@ LibraryManager.library = { if (maxx != sub) maxx = 0; } if (maxx) { - var argPtr = HEAP32[(varargs + argIndex)>>2]; + var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}}; argIndex += Runtime.getAlignSize('void*', null, true); fields++; for (var i = 0; i < maxx; i++) { @@ -6243,15 +6268,345 @@ 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'], + 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 @@ -6307,46 +6662,9 @@ LibraryManager.library = { }; 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 MONTH_DAYS_REGULAR = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; - var MONTH_DAYS_LEAP = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; 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}; - var isLeapYear = function(year) { - return year%4===0 && (year%100!==0 || year%400===0); - }; - - var arraySum = function(array, index) { - var sum = 0; - for (var i=0; i<=index; sum += array[i++]); - return sum; - }; - - var addDays = function(date, days) { - while(days>0) { - var leap = isLeapYear(date.getFullYear()); - var currentMonth = date.getMonth(); - var daysInCurrentMonth = (leap ? MONTH_DAYS_LEAP : MONTH_DAYS_REGULAR)[currentMonth]; - - if (days>daysInCurrentMonth-date.getDate()) { - // we spill over to next month - days -= (daysInCurrentMonth-date.getDate()+1); - date.setDate(1); - if (currentMonth<11) { - date.setMonth(currentMonth+1) - } else { - date.setMonth(0); - date.setFullYear(date.getFullYear()+1); - } - } else { - // we stay in current month - date.setDate(date.getDate()+days); - return date; - } - } - return date; - }; - for (var datePattern in DATE_PATTERNS) { pattern = pattern.replace(datePattern, '('+datePattern+DATE_PATTERNS[datePattern]+')'); } @@ -6446,10 +6764,10 @@ LibraryManager.library = { } else if ((value=getMatch('j'))) { // get day of month from day of year ... var day = parseInt(value); - var leapYear = isLeapYear(date.year); + 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]) { + 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; } } @@ -6468,10 +6786,10 @@ LibraryManager.library = { var endDate; if (janFirst.getDay() === 0) { // Jan 1st is a Sunday, and, hence in the 1st CW - endDate = addDays(janFirst, weekDayNumber+7*(weekNumber-1)); + 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)); + endDate = __addDays(janFirst, 7-janFirst.getDay()+weekDayNumber+7*(weekNumber-1)); } date.day = endDate.getDate(); date.month = endDate.getMonth(); @@ -6487,10 +6805,10 @@ LibraryManager.library = { var endDate; if (janFirst.getDay()===1) { // Jan 1st is a Monday, and, hence in the 1st CW - endDate = addDays(janFirst, weekDayNumber+7*(weekNumber-1)); + 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)); + endDate = __addDays(janFirst, 7-janFirst.getDay()+1+weekDayNumber+7*(weekNumber-1)); } date.day = endDate.getDate(); @@ -6518,7 +6836,7 @@ LibraryManager.library = { {{{ 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_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 @@ -6635,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_browser.js b/src/library_browser.js index 7f79b2bd..0db2cc44 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -70,18 +70,6 @@ mergeInto(LibraryManager.library, { // (possibly modified) data. For example, a plugin might decompress a file, or it // might create some side data structure for use later (like an Image element, etc.). - function getMimetype(name) { - return { - 'jpg': 'image/jpeg', - 'jpeg': 'image/jpeg', - 'png': 'image/png', - 'bmp': 'image/bmp', - 'ogg': 'audio/ogg', - 'wav': 'audio/wav', - 'mp3': 'audio/mpeg' - }[name.substr(name.lastIndexOf('.')+1)]; - } - var imagePlugin = {}; imagePlugin['canHandle'] = function(name) { return !Module.noImageDecoding && /\.(jpg|jpeg|png|bmp)$/i.test(name); @@ -90,10 +78,10 @@ mergeInto(LibraryManager.library, { var b = null; if (Browser.hasBlobConstructor) { try { - b = new Blob([byteArray], { type: getMimetype(name) }); + b = new Blob([byteArray], { type: Browser.getMimetype(name) }); if (b.size !== byteArray.length) { // Safari bug #118630 // Safari's Blob can only take an ArrayBuffer - b = new Blob([(new Uint8Array(byteArray)).buffer], { type: getMimetype(name) }); + b = new Blob([(new Uint8Array(byteArray)).buffer], { type: Browser.getMimetype(name) }); } } catch(e) { Runtime.warnOnce('Blob constructor present but fails: ' + e + '; falling back to blob builder'); @@ -148,7 +136,7 @@ mergeInto(LibraryManager.library, { } if (Browser.hasBlobConstructor) { try { - var b = new Blob([byteArray], { type: getMimetype(name) }); + var b = new Blob([byteArray], { type: Browser.getMimetype(name) }); } catch(e) { return fail(); } @@ -391,6 +379,18 @@ mergeInto(LibraryManager.library, { }, timeout); }, + getMimetype: function(name) { + return { + 'jpg': 'image/jpeg', + 'jpeg': 'image/jpeg', + 'png': 'image/png', + 'bmp': 'image/bmp', + 'ogg': 'audio/ogg', + 'wav': 'audio/wav', + 'mp3': 'audio/mpeg' + }[name.substr(name.lastIndexOf('.')+1)]; + }, + getUserMedia: function(func) { if(!window.getUserMedia) { window.getUserMedia = navigator['getUserMedia'] || @@ -399,6 +399,7 @@ mergeInto(LibraryManager.library, { window.getUserMedia(func); }, + getMovementX: function(event) { return event['movementX'] || event['mozMovementX'] || diff --git a/src/library_gl.js b/src/library_gl.js index 959773bc..d0f1a692 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -372,6 +372,12 @@ var LibraryGL = { case 0x8DF9: // GL_NUM_SHADER_BINARY_FORMATS {{{ makeSetValue('p', '0', '0', 'i32') }}}; return; + case 0x86A2: // GL_NUM_COMPRESSED_TEXTURE_FORMATS + // WebGL doesn't have GL_NUM_COMPRESSED_TEXTURE_FORMATS (it's obsolete since GL_COMPRESSED_TEXTURE_FORMATS returns a JS array that can be queried for length), + // so implement it ourselves to allow C++ GLES2 code get the length. + var formats = Module.ctx.getParameter(0x86A3 /*GL_COMPRESSED_TEXTURE_FORMATS*/); + {{{ makeSetValue('p', '0', 'formats.length', 'i32') }}}; + return; } var result = Module.ctx.getParameter(name_); switch (typeof(result)) { @@ -1076,7 +1082,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 9287bd3e..7078aa9d 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -28,6 +28,7 @@ var LibrarySDL = { // The currently preloaded audio elements ready to be played audios: [null], + rwops: [null], // The currently playing audio element. There's only one music track. music: { audio: null, @@ -1228,9 +1229,24 @@ var LibrarySDL = { return flags; // We support JPG, PNG, TIF because browsers do }, - IMG_Load__deps: ['SDL_LockSurface'], - IMG_Load: function(filename) { - filename = FS.standardizePath(Pointer_stringify(filename)); + IMG_Load_RW__deps: ['SDL_LockSurface'], + IMG_Load_RW: function(rwopsID, freesrc) { + var rwops = SDL.rwops[rwopsID]; + + if (rwops === undefined) { + return 0; + } + + var filename = rwops.filename; + + if (filename === undefined) { + Runtime.warnOnce('Only file names that have been preloaded are supported for IMG_Load_RW.'); + // TODO. Support loading image data from embedded files, similarly to Mix_LoadWAV_RW + // TODO. Support loading image data from byte arrays, similarly to Mix_LoadWAV_RW + return 0; + } + + filename = FS.standardizePath(filename); if (filename[0] == '/') { // Convert the path to relative filename = filename.substr(1); @@ -1262,8 +1278,14 @@ var LibrarySDL = { return surf; }, SDL_LoadBMP: 'IMG_Load', - SDL_LoadBMP_RW: 'IMG_Load', - IMG_Load_RW: 'IMG_Load', + SDL_LoadBMP_RW: 'IMG_Load_RW', + IMG_Load__deps: ['IMG_Load_RW', 'SDL_RWFromFile', 'SDL_FreeRW'], + IMG_Load: function(filename){ + var rwops = _SDL_RWFromFile(filename); + var result = _IMG_Load_RW(rwops); + _SDL_FreeRW(rwops); + return result; + }, // SDL_Audio @@ -1399,22 +1421,62 @@ var LibrarySDL = { return 0; // error }, - Mix_LoadWAV_RW: function(filename, freesrc) { - filename = FS.standardizePath(Pointer_stringify(filename)); - var raw = Module["preloadedAudios"][filename]; - if (!raw) { - if (raw === null) Module.printErr('Trying to reuse preloaded audio, but freePreloadedMediaOnUse is set!'); - Runtime.warnOnce('Cannot find preloaded audio ' + filename); + Mix_LoadWAV_RW: function(rwopsID, freesrc) { + var rwops = SDL.rwops[rwopsID]; + + if (rwops === undefined) + return 0; + + var filename = ''; + var audio; + var bytes; + + if (rwops.filename !== undefined) { + filename = rwops.filename; + filename = FS.standardizePath(filename); + var raw = Module["preloadedAudios"][filename]; + if (!raw) { + if (raw === null) Module.printErr('Trying to reuse preloaded audio, but freePreloadedMediaOnUse is set!'); + Runtime.warnOnce('Cannot find preloaded audio ' + filename); + + // see if we can read the file-contents from the in-memory FS + var fileObject = FS.findObject(filename); + + if (fileObject === null) Module.printErr('Couldn\'t find file for: ' + filename); + + // We found the file. Load the contents + if (fileObject && !fileObject.isFolder && fileObject.read) { + bytes = fileObject.contents + } else { + return 0; + } + } + if (Module['freePreloadedMediaOnUse']) { + Module["preloadedAudios"][filename] = null; + } + audio = raw; + } + else if (rwops.bytes !== undefined) { + bytes = HEAPU8.subarray(rwops.bytes, rwops.bytes + rwops.count); + } + else { return 0; } - if (Module['freePreloadedMediaOnUse']) { - Module["preloadedAudios"][filename] = null; + + // Here, we didn't find a preloaded audio but we either were passed a filepath for + // which we loaded bytes, or we were passed some bytes + if (audio === undefined && bytes) { + var blob = new Blob([new Uint8Array(bytes)], {type: rwops.mimetype}); + var url = URL.createObjectURL(blob); + audio = new Audio(); + audio.src = url; } + var id = SDL.audios.length; // Keep the loaded audio in the audio arrays, ready for playback SDL.audios.push({ source: filename, - audio: raw + audio: audio }); return id; }, @@ -1574,8 +1636,14 @@ var LibrarySDL = { return SDL.setGetVolume(SDL.music, volume); }, - Mix_LoadMUS: 'Mix_LoadWAV_RW', Mix_LoadMUS_RW: 'Mix_LoadWAV_RW', + Mix_LoadMUS__deps: ['Mix_LoadMUS_RW', 'SDL_RWFromFile', 'SDL_FreeRW'], + Mix_LoadMUS: function(filename) { + var rwops = _SDL_RWFromFile(filename); + var result = _Mix_LoadMUS_RW(rwops); + _SDL_FreeRW(rwops); + return result; + }, Mix_FreeMusic: 'Mix_FreeChunk', @@ -1978,9 +2046,24 @@ var LibrarySDL = { // Misc SDL_InitSubSystem: function(flags) { return 0 }, + SDL_RWFromConstMem: function(mem, size) { + var id = SDL.rwops.length; // TODO: recycle ids when they are null + SDL.rwops.push({ bytes: mem, count: size }); + return id; + }, - SDL_RWFromFile: function(filename, mode) { - return filename; // XXX We just forward the filename + SDL_RWFromFile: function(_name, mode) { + var id = SDL.rwops.length; // TODO: recycle ids when they are null + var name = Pointer_stringify(_name) + SDL.rwops.push({ filename: name, mimetype: Browser.getMimetype(name) }); + return id; + }, + + SDL_FreeRW: function(rwopsID) { + SDL.rwops[rwopsID] = null; + while (SDL.rwops.length > 0 && SDL.rwops[SDL.rwops.length-1] === null) { + SDL.rwops.pop(); + } }, SDL_EnableUNICODE: function(on) { @@ -2007,7 +2090,6 @@ var LibrarySDL = { SDL_GetThreadID: function() { throw 'SDL_GetThreadID' }, SDL_ThreadID: function() { throw 'SDL_ThreadID' }, SDL_AllocRW: function() { throw 'SDL_AllocRW: TODO' }, - SDL_FreeRW: function() { throw 'SDL_FreeRW: TODO' }, SDL_CondBroadcast: function() { throw 'SDL_CondBroadcast: TODO' }, SDL_CondWaitTimeout: function() { throw 'SDL_CondWaitTimeout: TODO' }, SDL_WM_IconifyWindow: function() { throw 'SDL_WM_IconifyWindow TODO' }, diff --git a/src/parseTools.js b/src/parseTools.js index a5785e27..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); @@ -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 ef34da6b..218e0388 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -784,7 +784,7 @@ Module['writeArrayToMemory'] = writeArrayToMemory; {{{ reSign }}} #if PRECISE_I32_MUL -if (!Math.imul) Math.imul = function(a, b) { +if (!Math['imul']) Math['imul'] = function(a, b) { var ah = a >>> 16; var al = a & 0xffff; var bh = b >>> 16; @@ -792,10 +792,11 @@ if (!Math.imul) Math.imul = function(a, b) { return (al*bl + ((ah*bl + al*bh) << 16))|0; }; #else -Math.imul = function(a, b) { +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/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 b33ea7b3..b7460cf2 100644 --- a/src/settings.js +++ b/src/settings.js @@ -243,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. @@ -412,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/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/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/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/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 e34fb63b..15f0070a 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -271,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 @@ -286,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: @@ -2967,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"] @@ -4029,6 +4062,11 @@ def process(filename): Settings.EXPORTED_FUNCTIONS = ['_main', '_save_me_aimee'] self.do_run(src, 'hello world!\n*100*\n*fivesix*\nmann\n', post_build=check) + # test EXPORT_ALL + Settings.EXPORTED_FUNCTIONS = [] + Settings.EXPORT_ALL = 1 + self.do_run(src, 'hello world!\n*100*\n*fivesix*\nmann\n', post_build=check) + def test_inlinejs(self): if Settings.ASM_JS: return self.skip('asm does not support random code, TODO: something that works in asm') src = r''' @@ -5111,6 +5149,162 @@ The current type of b is: 9 ''' 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''' @@ -6805,11 +6999,12 @@ Pass: 0.000012 0.000012''') 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); @@ -6889,7 +7084,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): @@ -6921,7 +7116,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''' @@ -7015,23 +7210,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 = ''' @@ -10860,40 +11051,53 @@ f.close() def measure_funcs(filename): i = 0 start = -1 - curr = '?' + curr = None ret = {} for line in open(filename): i += 1 if line.startswith('function '): start = i curr = line - elif line.startswith('}'): + elif line.startswith('}') and curr: size = i - start - if size > 100: ret[curr] = size + ret[curr] = size + curr = None return ret - for outlining_limit in [1000, 2000, 5000, 0]: - Popen([PYTHON, EMCC, src] + libs + ['-o', 'test.js', '-O2', '-g3', '-s', 'OUTLINING_LIMIT=%d' % outlining_limit] + args).communicate() - assert os.path.exists('test.js') - shutil.copyfile('test.js', '%d_test.js' % outlining_limit) - for engine in JS_ENGINES: - out = run_js('test.js', engine=engine, stderr=PIPE, full_output=True) - self.assertContained(expected, out) - if engine == SPIDERMONKEY_ENGINE: self.validate_asmjs(out) - low = expected_ranges[outlining_limit][0] - seen = max(measure_funcs('test.js').values()) - high = expected_ranges[outlining_limit][1] - print outlining_limit, ' ', low, '<=', seen, '<=', high - assert low <= seen <= high + for debug, outlining_limits in [ + ([], (1000,)), + (['-g1'], (1000,)), + (['-g2'], (1000,)), + (['-g'], (100, 250, 500, 1000, 2000, 5000, 0)) + ]: + for outlining_limit in outlining_limits: + print '\n', debug, outlining_limit, '\n' + # TODO: test without -g3, tell all sorts + Popen([PYTHON, EMCC, src] + libs + ['-o', 'test.js', '-O2'] + debug + ['-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) + if debug == ['-g']: + low = expected_ranges[outlining_limit][0] + seen = max(measure_funcs('test.js').values()) + high = expected_ranges[outlining_limit][1] + print outlining_limit, ' ', low, '<=', seen, '<=', high + assert low <= seen <= high test('zlib', path_from_root('tests', 'zlib', 'example.c'), self.get_library('zlib', os.path.join('libz.a'), make_args=['libz.a']), open(path_from_root('tests', 'zlib', 'ref.txt'), 'r').read(), { - 1000: (380, 390), - 2000: (395, 410), + 100: (190, 210), + 250: (300, 330), + 500: (250, 310), + 1000: (330, 400), + 2000: (450, 500), 5000: (800, 1100), - 0: (1500, 1800) + 0: (1500, 1800) }, args=['-I' + path_from_root('tests', 'zlib')], suffix='c') @@ -11651,6 +11855,8 @@ f.close() ['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']), + (path_from_root('tools', 'test-js-optimizer-asm-outline3.js'), open(path_from_root('tools', 'test-js-optimizer-asm-outline3-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] @@ -12811,11 +13017,13 @@ Press any key to continue.''' def test_sdl_audio(self): shutil.copyfile(path_from_root('tests', 'sounds', 'alarmvictory_1.ogg'), os.path.join(self.get_dir(), 'sound.ogg')) shutil.copyfile(path_from_root('tests', 'sounds', 'alarmcreatemiltaryfoot_1.wav'), os.path.join(self.get_dir(), 'sound2.wav')) + shutil.copyfile(path_from_root('tests', 'sounds', 'noise.ogg'), os.path.join(self.get_dir(), 'noise.ogg')) + shutil.copyfile(path_from_root('tests', 'sounds', 'the_entertainer.ogg'), os.path.join(self.get_dir(), 'the_entertainer.ogg')) open(os.path.join(self.get_dir(), 'bad.ogg'), 'w').write('I claim to be audio, but am lying') open(os.path.join(self.get_dir(), 'sdl_audio.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio.c')).read())) # use closure to check for a possible bug with closure minifying away newer Audio() attributes - Popen([PYTHON, EMCC, '-O2', '--closure', '1', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio.c'), '--preload-file', 'sound.ogg', '--preload-file', 'sound2.wav', '--preload-file', 'bad.ogg', '-o', 'page.html', '-s', 'EXPORTED_FUNCTIONS=["_main", "_play", "_play2"]']).communicate() + Popen([PYTHON, EMCC, '-O2', '--closure', '1', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio.c'), '--preload-file', 'sound.ogg', '--preload-file', 'sound2.wav', '--embed-file', 'the_entertainer.ogg', '--preload-file', 'noise.ogg', '--preload-file', 'bad.ogg', '-o', 'page.html', '-s', 'EXPORTED_FUNCTIONS=["_main", "_play", "_play2"]']).communicate() self.run_browser('page.html', '', '/report_result?1') def test_sdl_audio_mix_channels(self): @@ -13181,6 +13389,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_audio.c b/tests/sdl_audio.c index ae1b89e9..7373d220 100644 --- a/tests/sdl_audio.c +++ b/tests/sdl_audio.c @@ -1,11 +1,13 @@ #include <stdio.h> +#include <stdlib.h> #include <SDL/SDL.h> #include <SDL/SDL_mixer.h> #include <assert.h> #include <emscripten.h> +#include <sys/stat.h> -Mix_Chunk *sound, *sound2; - +Mix_Chunk *sound, *sound2, *sound3; +Mix_Music * music; int play2(); int play() { @@ -28,6 +30,9 @@ int play2() { int channel2 = Mix_PlayChannel(-1, sound2, 0); assert(channel2 == 1); + int channel3 = Mix_PlayChannel(-1, sound3, 0); + assert(channel3 == 2); + assert(Mix_PlayMusic(music, 1) == 0); return channel2; } @@ -39,6 +44,26 @@ int main(int argc, char **argv) { sound = Mix_LoadWAV("sound.ogg"); assert(sound); + + { + struct stat info; + int result = stat("noise.ogg", &info); + char * bytes = malloc( info.st_size ); + FILE * f = fopen( "noise.ogg", "rb" ); + fread( bytes, 1, info.st_size, f ); + fclose(f); + + SDL_RWops * ops = SDL_RWFromConstMem(bytes, info.st_size); + sound3 = Mix_LoadWAV_RW(ops, 0); + SDL_FreeRW(ops); + free(bytes); + } + + { + music = Mix_LoadMUS("the_entertainer.ogg"); + } + + sound2 = Mix_LoadWAV("sound2.wav"); assert(sound); 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/tools/find_bigfuncs.py b/tools/find_bigfuncs.py index 31825544..6fdec3a9 100644 --- a/tools/find_bigfuncs.py +++ b/tools/find_bigfuncs.py @@ -7,16 +7,17 @@ import os, sys, re filename = sys.argv[1] i = 0 start = -1 -curr = '?' +curr = None data = [] for line in open(filename): i += 1 if line.startswith('function '): start = i curr = line - elif line.startswith('}'): + elif line.startswith('}') and curr: size = i - start - data.append([curr, size]); + data.append([curr, size]) + curr = None 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 46b9c731..95d9b82f 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -1743,7 +1743,7 @@ function registerize(ast) { } }); vacuum(fun); - if (extraInfo) { + if (extraInfo && extraInfo.globals) { assert(asm); var usedGlobals = {}; var nextLocal = 0; @@ -1784,16 +1784,17 @@ function registerize(ast) { } } }); - assert(fun[1] in extraInfo.globals, fun[1]); - fun[1] = extraInfo.globals[fun[1]]; - assert(fun[1]); + if (fun[1] in extraInfo.globals) { // if fun was created by a previous optimization pass, it will not be here + fun[1] = extraInfo.globals[fun[1]]; + assert(fun[1]); + } var nextRegName = 0; } var regTypes = {}; function getNewRegName(num, name) { if (!asm) return 'r' + num; var type = asmData.vars[name]; - if (!extraInfo) { + if (!extraInfo || !extraInfo.globals) { var ret = (type ? 'd' : 'i') + num; regTypes[ret] = type; return ret; @@ -2872,6 +2873,7 @@ function outline(ast) { defs[name] = node; } else { if (name in asmData.params) { + assignments[name] = (assignments[name] || 1) + 1; // init to 1 for initial parameter assignment considered[name] = true; // this parameter is not ssa, it must be in a hand-optimized function, so it is not trivial } } @@ -2997,6 +2999,63 @@ function outline(ast) { }); } + // Try to flatten out code as much as possible, to make outlining more feasible. + function flatten(func, asmData) { + var minSize = sizeToOutline/3; + var helperId = 0; + function getHelper() { + while (1) { + var ret = 'helper$' + (helperId++); + if (!(ret in asmData.vars) && !(ret in asmData.params)) { + asmData.vars[ret] = ASM_INT; + return ret; + } + } + } + traverse(func, function(node) { + var stats = getStatements(node); + if (stats) { + for (var i = 0; i < stats.length; i++) { + var node = stats[i]; // step over param + var type = node[0]; + if (measureSize(node) >= minSize) { + if (type === 'if' && node[3]) { + var reps = []; + var helper = getHelper(); + // clear helper + reps.push(['stat', ['assign', true, ['name', helper], ['num', 1]]]); + // gather parts + var parts = []; + var curr = node; + while (1) { + parts.push({ condition: curr[1], body: curr[2] }); + curr = curr[3]; + if (!curr) break; + if (curr[0] != 'if') { + parts.push({ condition: null, body: curr }); + break; + } + } + // generate flattened code + parts.forEach(function(part) { + var condition = ['name', helper]; + if (part.condition) condition = ['conditional', condition, part.condition, ['num', 0]]; + assert(part.body[0] == 'block'); + reps.push(makeIf(condition, part.body[1])); + getStatements(part.body).unshift(['stat', ['assign', true, ['name', helper], ['num', 0]]]); + }); + // replace code and update i + stats.splice.apply(stats, [i, 1].concat(reps)); + i--; // negate loop increment + i += reps.length; + continue; + } + } + } + } + }); + } + // Prepares information for spilling of local variables function analyzeFunction(func, asmData) { var stack = []; // list of variables, each gets 8 bytes @@ -3008,15 +3067,17 @@ function outline(ast) { } 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]] = stackSize + i*8; } - // Reserve an extra two spots: one for control flow var, the other for control flow data + // 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.extraStackSize = (stack.length + 2)*8; - asmData.controlStackPos = stackSize + asmData.extraStackSize - 16; - asmData.controlDataStackPos = stackSize + asmData.extraStackSize - 8; + asmData.maxOutlinings = Math.round(3*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; } @@ -3120,13 +3181,15 @@ function outline(ast) { var sizeToOutline = extraInfo.sizeToOutline; var level = 0; - var costs = {}; // new function name => overhead cost of outlining + var outliningParents = {}; // function name => parent it was outlined from function doOutline(func, asmData, stats, start, end) { + if (asmData.splitCounter === asmData.maxOutlinings) return []; if (!extraInfo.allowCostlyOutlines) var originalStats = copy(stats); var code = stats.slice(start, end+1); var funcSize = measureSize(func); - var newIdent = func[1] + '$' + (asmData.splitCounter++); + 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); @@ -3139,15 +3202,15 @@ function outline(ast) { }); var reps = []; // wipe out control variable - reps.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos), ['num', 0])]); - reps.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos), ['num', 0])]); // XXX not really needed + 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', ['assign', true, ['name', 'sp'], makeAsmCoercion(['call', ['name', newIdent], [['name', 'sp']]], ASM_INT)]]); + reps.push(['stat', ['call', ['name', newIdent], [['name', 'sp']]]]); for (var v in codeInfo.writes) { 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))]]); @@ -3176,11 +3239,11 @@ function outline(ast) { if (type == 'return') { ret = []; if (!node[1]) { - ret.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos), ['num', CONTROL_RETURN_VOID])]); + 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), ['num', type == ASM_INT ? CONTROL_RETURN_INT : CONTROL_RETURN_DOUBLE])]); - ret.push(['stat', makeAssign(makeStackAccess(type, asmData.controlDataStackPos), node[1])]); + 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') { @@ -3188,20 +3251,20 @@ function outline(ast) { 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), ['num', label ? CONTROL_BREAK_LABEL : CONTROL_BREAK])]]; + 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), ['num', codeInfo.breaks[label]])]); + 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), ['num', label ? CONTROL_CONTINUE_LABEL : CONTROL_CONTINUE])]]; + 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), ['num', codeInfo.continues[label]])]); + ret.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos(outlineIndex)), ['num', codeInfo.continues[label]])]); } ret.push(['stat', ['break', 'OL']]); } @@ -3223,18 +3286,18 @@ function outline(ast) { // 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), ASM_INT) + makeAsmCoercion(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ASM_INT) )]); reps.push(['stat', makeAssign( ['name', 'tempInt'], - makeAsmCoercion(makeStackAccess(ASM_INT, asmData.controlDataStackPos), ASM_INT) + makeAsmCoercion(makeStackAccess(ASM_INT, asmData.controlDataStackPos(outlineIndex)), ASM_INT) )]); reps.push(['stat', makeAssign( ['name', 'tempDouble'], - makeAsmCoercion(makeStackAccess(ASM_DOUBLE, asmData.controlDataStackPos), ASM_DOUBLE) + makeAsmCoercion(makeStackAccess(ASM_DOUBLE, asmData.controlDataStackPos(outlineIndex)), ASM_DOUBLE) )]); - reps.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos), ['num', 0])]); - reps.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos), ['num', 0])]); // XXX not really needed + 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( @@ -3296,8 +3359,6 @@ function outline(ast) { 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: {} }; @@ -3305,11 +3366,16 @@ function outline(ast) { if (v != 'sp') newAsmData.vars[v] = getAsmType(v, asmData); } for (var v in codeInfo.writes) { - if (v != 'sp') newAsmData.vars[v] = getAsmType(v, asmData); + assert(v != 'sp'); // we send sp as a read-only parameter, cannot be written to in outlined code + 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; @@ -3327,33 +3393,41 @@ function outline(ast) { getStatements(func).push(['stat', ['return', makeAsmCoercion(['num', 0], allCodeInfo.hasReturnInt ? ASM_INT : ASM_DOUBLE)]]); } } - // the cost is the total size increase of all code, after the outlining operation. We also - // inherit the outlining cost of the parent function, if any, so the repeated outlining - // cannot infinitely recurse. - costs[newIdent] = measureSize(func) + measureSize(newFunc) - funcSize + (costs[func[1]] || 0); + outliningParents[newIdent] = func[1]; return [newFunc]; } function outlineStatements(func, asmData, stats, maxSize) { level++; - printErr('outlineStatements: ' + [func[1], level, measureSize(func)]); + //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; - var minIndex = stats == getStatements(func) ? getFirstIndexInNormalized(func, asmData) : 0; var canRestart = false; + var minIndex = 0; + function calcMinIndex() { + if (stats == getStatements(func)) { + minIndex = getFirstIndexInNormalized(func, asmData); + for (var i = minIndex; i < stats.length; i++) { + var stat = stats[i]; + if (stat[0] == 'stat') stat = stat[1]; + if (stat[0] == 'assign' && stat[2][0] == 'name' && stat[2][1] == 'sp') minIndex = i+1; // cannot outline |sp = | + } + } + } while (1) { i--; + calcMinIndex(); 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); + //printErr('restarting ' + func[1] + ' since ' + [currSize, outlinedSize, lastSize] + ' in level ' + level); lastSize = currSize; i = stats.length; end = stats.length-1; @@ -3364,7 +3438,17 @@ function outline(ast) { break; } } + var stat = stats[i]; + while (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 >= minIndex+1); + assert(stats[i][0] !== 'end-outline-call'); + } + stat = stats[i]; + } + var size = measureSize(stat); //printErr(level + ' size ' + [i, size]); if (size >= sizeToOutline) { @@ -3382,7 +3466,7 @@ function outline(ast) { }); if (ret.length > pre) { // we outlined recursively, reset our state here - printErr('successful outline in recursion ' + func[1] + ' due to recursive in level ' + level); + //printErr('successful outline in recursion ' + func[1] + ' due to recursive in level ' + level); end = i-1; sizeSeen = 0; canRestart = true; @@ -3390,10 +3474,10 @@ function outline(ast) { } } 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) && end > i+1 && stats[end][0] !== 'begin-outline-call' && stats[end][0] !== 'end-outline-call') { sizeSeen -= measureSize(stats[end]); if (sizeSeen >= sizeToOutline) { end--; @@ -3402,9 +3486,26 @@ function outline(ast) { break; } } + // verify we are not outlining through an outline call + var sum = 0; + stats.slice(i, end+1).forEach(function(stat) { + if (stat[0] == 'begin-outline-call') { + assert(sum == 0); + sum++; + } else if (stat[0] == 'end-outline-call') { + assert(sum == 1); + sum--; + } + }); + assert(sum == 0); + // final decision and action 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)); + assert(i >= minIndex); + var newFuncs = doOutline(func, asmData, stats, i, end); // outline [i, .. ,end] inclusive + if (newFuncs.length) { + ret.push.apply(ret, newFuncs); + printErr('performed outline on ' + func[1] + ' of ' + sizeSeen + ', func is now size ' + measureSize(func)); + } sizeSeen = 0; end = i-1; canRestart = true; @@ -3436,6 +3537,7 @@ function outline(ast) { if (size >= sizeToOutline) { printErr('trying to reduce the size of ' + func[1] + ' which is ' + size + ' (>= ' + sizeToOutline + ')'); aggressiveVariableElimination(func, asmData); + flatten(func, asmData); analyzeFunction(func, asmData); var stats = getStatements(func); var ret = outlineStatements(func, asmData, stats, 0.9*size); @@ -3443,17 +3545,16 @@ function outline(ast) { if (ret && ret.length > 0) { newFuncs.push.apply(newFuncs, ret); // We have outlined. Add stack support - var extraSpace = asmData.extraStackSize; 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] += extraSpace; + 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', extraSpace]], ['num', 0]])] + ['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 @@ -3502,6 +3603,11 @@ function outline(ast) { //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 diff --git a/tools/js_optimizer.py b/tools/js_optimizer.py index 4e7d5474..acb87460 100644 --- a/tools/js_optimizer.py +++ b/tools/js_optimizer.py @@ -74,7 +74,7 @@ class Minifier: f = open(temp_file, 'w') f.write(shell) f.write('\n') - f.write('// EXTRA_INFO:' + self.serialize()) + f.write('// EXTRA_INFO:' + json.dumps(self.serialize())) f.close() output = subprocess.Popen(self.js_engine + @@ -91,10 +91,10 @@ class Minifier: def serialize(self): - return json.dumps({ + return { 'names': self.names, 'globals': self.globs - }) + } start_funcs_marker = '// EMSCRIPTEN_START_FUNCS\n' end_funcs_marker = '// EMSCRIPTEN_END_FUNCS\n' @@ -256,9 +256,12 @@ EMSCRIPTEN_FUNCS(); f.write(chunk) f.write(suffix_marker) if minify_globals: - assert not extra_info + if extra_info: + for key, value in extra_info.iteritems(): + assert key not in minify_info or value == minify_info[key], [key, value, minify_info[key]] + minify_info[key] = value f.write('\n') - f.write('// EXTRA_INFO:' + minify_info) + f.write('// EXTRA_INFO:' + json.dumps(minify_info)) elif extra_info: f.write('\n') f.write('// EXTRA_INFO:' + json.dumps(extra_info)) diff --git a/tools/test-js-optimizer-asm-outline1-output.js b/tools/test-js-optimizer-asm-outline1-output.js index 6ef32f54..65fbd6a5 100644 --- a/tools/test-js-optimizer-asm-outline1-output.js +++ b/tools/test-js-optimizer-asm-outline1-output.js @@ -1,58 +1,58 @@ function lin() { var sp = 0; sp = STACKTOP; - STACKTOP = STACKTOP + 16 | 0; + STACKTOP = STACKTOP + 136 | 0; c(1); c(2); c(3); c(4); - HEAP32[sp + 0 >> 2] = 0; - HEAP32[sp + 8 >> 2] = 0; - sp = lin$1(sp) | 0; - HEAP32[sp + 0 >> 2] = 0; + HEAP32[sp + 16 >> 2] = 0; + HEAP32[sp + 20 >> 2] = 0; + lin$1(sp); HEAP32[sp + 8 >> 2] = 0; - sp = lin$0(sp) | 0; + HEAP32[sp + 12 >> 2] = 0; + lin$0(sp); STACKTOP = sp; } function lin2() { var sp = 0; sp = STACKTOP; - STACKTOP = STACKTOP + 16 | 0; + STACKTOP = STACKTOP + 136 | 0; while (1) { c(1); c(2); c(3); c(4); - HEAP32[sp + 0 >> 2] = 0; + HEAP32[sp + 16 >> 2] = 0; + HEAP32[sp + 20 >> 2] = 0; + lin2$1(sp); HEAP32[sp + 8 >> 2] = 0; - sp = lin2$1(sp) | 0; - HEAP32[sp + 0 >> 2] = 0; - HEAP32[sp + 8 >> 2] = 0; - sp = lin2$0(sp) | 0; + HEAP32[sp + 12 >> 2] = 0; + lin2$0(sp); } STACKTOP = sp; } function lin3() { var sp = 0; sp = STACKTOP; - STACKTOP = STACKTOP + 16 | 0; + STACKTOP = STACKTOP + 152 | 0; while (1) { c(1); c(2); c(3); c(4); c(5); - HEAP32[sp + 0 >> 2] = 0; - HEAP32[sp + 8 >> 2] = 0; - sp = lin3$1(sp) | 0; - HEAP32[sp + 0 >> 2] = 0; + HEAP32[sp + 16 >> 2] = 0; + HEAP32[sp + 20 >> 2] = 0; + lin3$1(sp); HEAP32[sp + 8 >> 2] = 0; - sp = lin3$0(sp) | 0; - tempValue = HEAP32[sp + 0 >> 2] | 0; - tempInt = HEAP32[sp + 8 >> 2] | 0; - tempDouble = +HEAPF32[sp + 8 >> 2]; - HEAP32[sp + 0 >> 2] = 0; + HEAP32[sp + 12 >> 2] = 0; + lin3$0(sp); + 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; @@ -64,23 +64,23 @@ function lin3() { function lin4() { var sp = 0; sp = STACKTOP; - STACKTOP = STACKTOP + 16 | 0; + STACKTOP = STACKTOP + 152 | 0; while (1) { c(1); c(2); c(3); c(4); - HEAP32[sp + 0 >> 2] = 0; + HEAP32[sp + 16 >> 2] = 0; + HEAP32[sp + 20 >> 2] = 0; + lin4$1(sp); HEAP32[sp + 8 >> 2] = 0; - sp = lin4$1(sp) | 0; - HEAP32[sp + 0 >> 2] = 0; - HEAP32[sp + 8 >> 2] = 0; - sp = lin4$0(sp) | 0; - tempValue = HEAP32[sp + 0 >> 2] | 0; - tempInt = HEAP32[sp + 8 >> 2] | 0; - tempDouble = +HEAPF32[sp + 8 >> 2]; - HEAP32[sp + 0 >> 2] = 0; + HEAP32[sp + 12 >> 2] = 0; + lin4$0(sp); + 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; } @@ -91,23 +91,23 @@ function lin4() { function lin5() { var sp = 0; sp = STACKTOP; - STACKTOP = STACKTOP + 16 | 0; + STACKTOP = STACKTOP + 152 | 0; while (1) { c(1); c(2); c(3); c(4); - HEAP32[sp + 0 >> 2] = 0; - HEAP32[sp + 8 >> 2] = 0; - sp = lin5$1(sp) | 0; - HEAP32[sp + 0 >> 2] = 0; + HEAP32[sp + 16 >> 2] = 0; + HEAP32[sp + 20 >> 2] = 0; + lin5$1(sp); HEAP32[sp + 8 >> 2] = 0; - sp = lin5$0(sp) | 0; - tempValue = HEAP32[sp + 0 >> 2] | 0; - tempInt = HEAP32[sp + 8 >> 2] | 0; - tempDouble = +HEAPF32[sp + 8 >> 2]; - HEAP32[sp + 0 >> 2] = 0; + HEAP32[sp + 12 >> 2] = 0; + lin5$0(sp); + 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; } @@ -118,7 +118,7 @@ function lin5() { function mix() { var sp = 0; sp = STACKTOP; - STACKTOP = STACKTOP + 16 | 0; + STACKTOP = STACKTOP + 168 | 0; main : while (1) { c(1); c(2); @@ -127,17 +127,17 @@ function mix() { c(5); c(6); c(7); - HEAP32[sp + 0 >> 2] = 0; - HEAP32[sp + 8 >> 2] = 0; - sp = mix$1(sp) | 0; - HEAP32[sp + 0 >> 2] = 0; + HEAP32[sp + 16 >> 2] = 0; + HEAP32[sp + 20 >> 2] = 0; + mix$1(sp); HEAP32[sp + 8 >> 2] = 0; - sp = mix$0(sp) | 0; - tempValue = HEAP32[sp + 0 >> 2] | 0; - tempInt = HEAP32[sp + 8 >> 2] | 0; - tempDouble = +HEAPF32[sp + 8 >> 2]; - HEAP32[sp + 0 >> 2] = 0; + HEAP32[sp + 12 >> 2] = 0; + mix$0(sp); + 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; } @@ -169,17 +169,17 @@ function vars(x, y) { y = +y; var sp = 0; sp = STACKTOP; - STACKTOP = STACKTOP + 32 | 0; - HEAP32[sp + 16 >> 2] = 0; - HEAP32[sp + 24 >> 2] = 0; - HEAP32[sp + 0 >> 2] = x; - HEAPF32[sp + 8 >> 2] = y; - sp = vars$1(sp) | 0; - HEAP32[sp + 16 >> 2] = 0; + STACKTOP = STACKTOP + 152 | 0; + HEAP32[sp + 32 >> 2] = 0; + HEAP32[sp + 36 >> 2] = 0; + HEAP32[sp + 8 >> 2] = x; + HEAPF32[sp + 16 >> 2] = y; + vars$1(sp); HEAP32[sp + 24 >> 2] = 0; - HEAP32[sp + 0 >> 2] = x; - HEAPF32[sp + 8 >> 2] = y; - sp = vars$0(sp) | 0; + HEAP32[sp + 28 >> 2] = 0; + HEAP32[sp + 8 >> 2] = x; + HEAPF32[sp + 16 >> 2] = y; + vars$0(sp); STACKTOP = sp; } function vars2(x, y) { @@ -187,18 +187,18 @@ function vars2(x, y) { y = +y; var a = 0, b = +0, sp = 0; sp = STACKTOP; - STACKTOP = STACKTOP + 48 | 0; + STACKTOP = STACKTOP + 152 | 0; a = x + y; b = y * x; a = c(1 + a); b = c(2 + b); - HEAP32[sp + 32 >> 2] = 0; HEAP32[sp + 40 >> 2] = 0; - HEAP32[sp + 16 >> 2] = a; - HEAPF32[sp + 24 >> 2] = b; - sp = vars2$0(sp) | 0; - a = HEAP32[sp + 16 >> 2] | 0; - b = +HEAPF32[sp + 24 >> 2]; + HEAP32[sp + 44 >> 2] = 0; + HEAP32[sp + 24 >> 2] = a; + HEAPF32[sp + 32 >> 2] = b; + vars2$0(sp); + a = HEAP32[sp + 24 >> 2] | 0; + b = +HEAPF32[sp + 32 >> 2]; STACKTOP = sp; } function vars3(x, y) { @@ -206,21 +206,21 @@ function vars3(x, y) { y = +y; var a = 0, sp = 0; sp = STACKTOP; - STACKTOP = STACKTOP + 40 | 0; - HEAP32[sp + 24 >> 2] = 0; - HEAP32[sp + 32 >> 2] = 0; - HEAP32[sp + 16 >> 2] = a; - HEAP32[sp + 0 >> 2] = x; - HEAPF32[sp + 8 >> 2] = y; - sp = vars3$1(sp) | 0; - a = HEAP32[sp + 16 >> 2] | 0; - HEAP32[sp + 24 >> 2] = 0; + STACKTOP = STACKTOP + 160 | 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; + vars3$1(sp); + a = HEAP32[sp + 24 >> 2] | 0; HEAP32[sp + 32 >> 2] = 0; - HEAP32[sp + 16 >> 2] = a; - HEAPF32[sp + 8 >> 2] = y; - HEAP32[sp + 0 >> 2] = x; - sp = vars3$0(sp) | 0; - a = HEAP32[sp + 16 >> 2] | 0; + HEAP32[sp + 36 >> 2] = 0; + HEAP32[sp + 24 >> 2] = a; + HEAPF32[sp + 16 >> 2] = y; + HEAP32[sp + 8 >> 2] = x; + vars3$0(sp); + a = HEAP32[sp + 24 >> 2] | 0; STACKTOP = sp; } function vars4(x, y) { @@ -228,25 +228,25 @@ function vars4(x, y) { y = +y; var a = 0, b = +0, sp = 0; sp = STACKTOP; - STACKTOP = STACKTOP + 48 | 0; + STACKTOP = STACKTOP + 168 | 0; a = x + y; - HEAP32[sp + 32 >> 2] = 0; + 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; + vars4$1(sp); + b = +HEAPF32[sp + 32 >> 2]; + a = HEAP32[sp + 24 >> 2] | 0; HEAP32[sp + 40 >> 2] = 0; - HEAPF32[sp + 8 >> 2] = y; - HEAP32[sp + 0 >> 2] = x; - HEAP32[sp + 16 >> 2] = a; - HEAPF32[sp + 24 >> 2] = b; - sp = vars4$1(sp) | 0; - b = +HEAPF32[sp + 24 >> 2]; - a = HEAP32[sp + 16 >> 2] | 0; - HEAP32[sp + 32 >> 2] = 0; - HEAP32[sp + 40 >> 2] = 0; - HEAP32[sp + 16 >> 2] = a; - HEAP32[sp + 0 >> 2] = x; - HEAPF32[sp + 24 >> 2] = b; - sp = vars4$0(sp) | 0; - a = HEAP32[sp + 16 >> 2] | 0; - b = +HEAPF32[sp + 24 >> 2]; + HEAP32[sp + 44 >> 2] = 0; + HEAP32[sp + 24 >> 2] = a; + HEAP32[sp + 8 >> 2] = x; + HEAPF32[sp + 32 >> 2] = b; + vars4$0(sp); + a = HEAP32[sp + 24 >> 2] | 0; + b = +HEAPF32[sp + 32 >> 2]; STACKTOP = sp; } function vars_w_stack(x, y) { @@ -254,24 +254,61 @@ function vars_w_stack(x, y) { y = +y; var a = 0, b = +0, sp = 0; sp = STACKTOP; - STACKTOP = STACKTOP + 72 | 0; + STACKTOP = STACKTOP + 208 | 0; a = x + y; - HEAP32[sp + 56 >> 2] = 0; + 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; + vars_w_stack$1(sp); + b = +HEAPF32[sp + 48 >> 2]; + a = HEAP32[sp + 40 >> 2] | 0; HEAP32[sp + 64 >> 2] = 0; - HEAPF32[sp + 24 >> 2] = y; - HEAP32[sp + 16 >> 2] = x; - HEAP32[sp + 32 >> 2] = a; - HEAPF32[sp + 40 >> 2] = b; - sp = vars_w_stack$1(sp) | 0; - b = +HEAPF32[sp + 40 >> 2]; - a = HEAP32[sp + 32 >> 2] | 0; + HEAP32[sp + 68 >> 2] = 0; + HEAP32[sp + 40 >> 2] = a; + HEAPF32[sp + 48 >> 2] = b; + vars_w_stack$0(sp); + a = HEAP32[sp + 40 >> 2] | 0; + b = +HEAPF32[sp + 48 >> 2]; +} +function chain() { + var helper$0 = 0, sp = 0; + sp = STACKTOP; + STACKTOP = STACKTOP + 352 | 0; + helper$0 = 1; HEAP32[sp + 56 >> 2] = 0; - HEAP32[sp + 64 >> 2] = 0; - HEAP32[sp + 32 >> 2] = a; - HEAPF32[sp + 40 >> 2] = b; - sp = vars_w_stack$0(sp) | 0; - a = HEAP32[sp + 32 >> 2] | 0; - b = +HEAPF32[sp + 40 >> 2]; + HEAP32[sp + 60 >> 2] = 0; + HEAP32[sp + 8 >> 2] = helper$0; + chain$5(sp); + helper$0 = HEAP32[sp + 8 >> 2] | 0; + HEAP32[sp + 48 >> 2] = 0; + HEAP32[sp + 52 >> 2] = 0; + HEAP32[sp + 8 >> 2] = helper$0; + chain$4(sp); + helper$0 = HEAP32[sp + 8 >> 2] | 0; + HEAP32[sp + 40 >> 2] = 0; + HEAP32[sp + 44 >> 2] = 0; + HEAP32[sp + 8 >> 2] = helper$0; + chain$3(sp); + helper$0 = HEAP32[sp + 8 >> 2] | 0; + HEAP32[sp + 32 >> 2] = 0; + HEAP32[sp + 36 >> 2] = 0; + HEAP32[sp + 8 >> 2] = helper$0; + chain$2(sp); + helper$0 = HEAP32[sp + 8 >> 2] | 0; + HEAP32[sp + 24 >> 2] = 0; + HEAP32[sp + 28 >> 2] = 0; + HEAP32[sp + 8 >> 2] = helper$0; + chain$1(sp); + helper$0 = HEAP32[sp + 8 >> 2] | 0; + HEAP32[sp + 16 >> 2] = 0; + HEAP32[sp + 20 >> 2] = 0; + HEAP32[sp + 8 >> 2] = helper$0; + chain$0(sp); + helper$0 = HEAP32[sp + 8 >> 2] | 0; + STACKTOP = sp; } function lin$0(sp) { sp = sp | 0; @@ -283,7 +320,6 @@ function lin$0(sp) { c(18); c(19); c(20); - return sp | 0; } function lin$1(sp) { sp = sp | 0; @@ -295,7 +331,6 @@ function lin$1(sp) { c(10); c(11); c(12); - return sp | 0; } function lin2$0(sp) { sp = sp | 0; @@ -307,7 +342,6 @@ function lin2$0(sp) { c(18); c(19); c(20); - return sp | 0; } function lin2$1(sp) { sp = sp | 0; @@ -319,7 +353,6 @@ function lin2$1(sp) { c(10); c(11); c(12); - return sp | 0; } function lin3$0(sp) { sp = sp | 0; @@ -331,11 +364,10 @@ function lin3$0(sp) { c(18); c(19); c(20); - HEAP32[sp + 0 >> 2] = 6; - HEAP32[sp + 8 >> 2] = 10; + HEAP32[sp + 8 >> 2] = 6; + HEAP32[sp + 12 >> 2] = 10; break OL; } while (0); - return sp | 0; } function lin3$1(sp) { sp = sp | 0; @@ -347,7 +379,6 @@ function lin3$1(sp) { c(11); c(12); c(13); - return sp | 0; } function lin4$0(sp) { sp = sp | 0; @@ -360,10 +391,9 @@ function lin4$0(sp) { c(18); c(19); c(20); - HEAP32[sp + 0 >> 2] = 1; + HEAP32[sp + 8 >> 2] = 1; break OL; } while (0); - return sp | 0; } function lin4$1(sp) { sp = sp | 0; @@ -375,7 +405,6 @@ function lin4$1(sp) { c(10); c(11); c(12); - return sp | 0; } function lin5$0(sp) { sp = sp | 0; @@ -388,10 +417,9 @@ function lin5$0(sp) { c(18); c(19); c(20); - HEAP32[sp + 0 >> 2] = 3; + HEAP32[sp + 8 >> 2] = 3; break OL; } while (0); - return sp | 0; } function lin5$1(sp) { sp = sp | 0; @@ -403,18 +431,17 @@ function lin5$1(sp) { c(10); c(11); c(12); - return sp | 0; } function mix$0(sp) { sp = sp | 0; OL : do { c(16); c(17); - HEAP32[sp + 0 >> 2] = 2; HEAP32[sp + 8 >> 2] = 2; + HEAP32[sp + 12 >> 2] = 2; break OL; c(18); - HEAP32[sp + 0 >> 2] = 1; + HEAP32[sp + 8 >> 2] = 1; break OL; while (1) { break; @@ -423,14 +450,13 @@ function mix$0(sp) { break inner; } c(19); - HEAP32[sp + 0 >> 2] = 3; + HEAP32[sp + 8 >> 2] = 3; break OL; c(20); - HEAP32[sp + 0 >> 2] = 4; - HEAP32[sp + 8 >> 2] = 3; + HEAP32[sp + 8 >> 2] = 4; + HEAP32[sp + 12 >> 2] = 3; break OL; } while (0); - return sp | 0; } function mix$1(sp) { sp = sp | 0; @@ -442,125 +468,203 @@ function mix$1(sp) { c(13); c(14); c(15); - return sp | 0; } function vars$0(sp) { sp = sp | 0; var x = 0, y = +0; - y = +HEAPF32[sp + 8 >> 2]; - x = HEAP32[sp + 0 >> 2] | 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 + 8 >> 2]; - x = HEAP32[sp + 0 >> 2] | 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 + 24 >> 2]; - a = HEAP32[sp + 16 >> 2] | 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 + 16 >> 2] = a; - HEAPF32[sp + 24 >> 2] = b; - return sp | 0; + HEAP32[sp + 24 >> 2] = a; + HEAPF32[sp + 32 >> 2] = b; } function vars3$0(sp) { sp = sp | 0; var a = 0, y = +0, x = 0; - x = HEAP32[sp + 0 >> 2] | 0; - y = +HEAPF32[sp + 8 >> 2]; - a = HEAP32[sp + 16 >> 2] | 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 + 16 >> 2] = a; - return sp | 0; + HEAP32[sp + 24 >> 2] = a; } function vars3$1(sp) { sp = sp | 0; var a = 0, x = 0, y = +0; - y = +HEAPF32[sp + 8 >> 2]; - x = HEAP32[sp + 0 >> 2] | 0; - a = HEAP32[sp + 16 >> 2] | 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 + 16 >> 2] = a; - return sp | 0; + HEAP32[sp + 24 >> 2] = a; } function vars4$0(sp) { sp = sp | 0; var a = 0, x = 0, b = +0; - b = +HEAPF32[sp + 24 >> 2]; - x = HEAP32[sp + 0 >> 2] | 0; - a = HEAP32[sp + 16 >> 2] | 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 + 16 >> 2] = a; - HEAPF32[sp + 24 >> 2] = b; - return sp | 0; + HEAP32[sp + 24 >> 2] = a; + HEAPF32[sp + 32 >> 2] = b; } function vars4$1(sp) { sp = sp | 0; var y = +0, x = 0, a = 0, b = +0; - b = +HEAPF32[sp + 24 >> 2]; - a = HEAP32[sp + 16 >> 2] | 0; - x = HEAP32[sp + 0 >> 2] | 0; - y = +HEAPF32[sp + 8 >> 2]; + 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 + 24 >> 2] = b; - HEAP32[sp + 16 >> 2] = a; - return sp | 0; + HEAPF32[sp + 32 >> 2] = b; + HEAP32[sp + 24 >> 2] = a; } function vars_w_stack$0(sp) { sp = sp | 0; var a = 0, b = +0; - b = +HEAPF32[sp + 40 >> 2]; - a = HEAP32[sp + 32 >> 2] | 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 + 32 >> 2] = a; - HEAPF32[sp + 40 >> 2] = b; - return sp | 0; + HEAP32[sp + 40 >> 2] = a; + HEAPF32[sp + 48 >> 2] = b; } function vars_w_stack$1(sp) { sp = sp | 0; var y = +0, x = 0, a = 0, b = +0; - b = +HEAPF32[sp + 40 >> 2]; - a = HEAP32[sp + 32 >> 2] | 0; - x = HEAP32[sp + 16 >> 2] | 0; - y = +HEAPF32[sp + 24 >> 2]; + 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 + 40 >> 2] = b; - HEAP32[sp + 32 >> 2] = a; - return sp | 0; + HEAPF32[sp + 48 >> 2] = b; + HEAP32[sp + 40 >> 2] = a; +} +function chain$0(sp) { + sp = sp | 0; + var helper$0 = 0; + helper$0 = HEAP32[sp + 8 >> 2] | 0; + if (helper$0 ? x == 11 : 0) { + helper$0 = 0; + print(11); + } + if (helper$0 ? x == 12 : 0) { + helper$0 = 0; + print(12); + } + if (helper$0) { + helper$0 = 0; + print(99); + } + HEAP32[sp + 8 >> 2] = helper$0; +} +function chain$1(sp) { + sp = sp | 0; + var helper$0 = 0; + helper$0 = HEAP32[sp + 8 >> 2] | 0; + if (helper$0 ? x == 9 : 0) { + helper$0 = 0; + print(9); + } + if (helper$0 ? x == 10 : 0) { + helper$0 = 0; + print(10); + } + HEAP32[sp + 8 >> 2] = helper$0; +} +function chain$2(sp) { + sp = sp | 0; + var helper$0 = 0; + helper$0 = HEAP32[sp + 8 >> 2] | 0; + if (helper$0 ? x == 7 : 0) { + helper$0 = 0; + print(7); + } + if (helper$0 ? x == 8 : 0) { + helper$0 = 0; + print(8); + } + HEAP32[sp + 8 >> 2] = helper$0; +} +function chain$3(sp) { + sp = sp | 0; + var helper$0 = 0; + helper$0 = HEAP32[sp + 8 >> 2] | 0; + if (helper$0 ? x == 5 : 0) { + helper$0 = 0; + print(5); + } + if (helper$0 ? x == 6 : 0) { + helper$0 = 0; + print(6); + } + HEAP32[sp + 8 >> 2] = helper$0; +} +function chain$4(sp) { + sp = sp | 0; + var helper$0 = 0; + helper$0 = HEAP32[sp + 8 >> 2] | 0; + if (helper$0 ? x == 3 : 0) { + helper$0 = 0; + print(3); + } + if (helper$0 ? x == 4 : 0) { + helper$0 = 0; + print(4); + } + HEAP32[sp + 8 >> 2] = helper$0; +} +function chain$5(sp) { + sp = sp | 0; + var helper$0 = 0; + helper$0 = HEAP32[sp + 8 >> 2] | 0; + if (helper$0 ? x == 1 : 0) { + helper$0 = 0; + print(1); + } + if (helper$0 ? x == 2 : 0) { + helper$0 = 0; + print(2); + } + HEAP32[sp + 8 >> 2] = helper$0; } diff --git a/tools/test-js-optimizer-asm-outline1.js b/tools/test-js-optimizer-asm-outline1.js index 40026439..311cb206 100644 --- a/tools/test-js-optimizer-asm-outline1.js +++ b/tools/test-js-optimizer-asm-outline1.js @@ -230,5 +230,34 @@ function vars_w_stack(x, y) { b = c(7+a); STACKTOP = sp; } +function chain() { + if (x == 1) { + print(1); + } else if (x == 2) { + print(2); + } else if (x == 3) { + print(3); + } else if (x == 4) { + print(4); + } else if (x == 5) { + print(5); + } else if (x == 6) { + print(6); + } else if (x == 7) { + print(7); + } else if (x == 8) { + print(8); + } else if (x == 9) { + print(9); + } else if (x == 10) { + print(10); + } else if (x == 11) { + print(11); + } else if (x == 12) { + print(12); + } else { + print(99); + } +} // 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 index 4b7bb6e2..abff54cb 100644 --- a/tools/test-js-optimizer-asm-outline2-output.js +++ b/tools/test-js-optimizer-asm-outline2-output.js @@ -1,7 +1,7 @@ function linear() { var sp = 0; sp = STACKTOP; - STACKTOP = STACKTOP + 16 | 0; + STACKTOP = STACKTOP + 152 | 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); @@ -11,17 +11,17 @@ 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); - HEAP32[sp + 0 >> 2] = 0; + HEAP32[sp + 16 >> 2] = 0; + HEAP32[sp + 20 >> 2] = 0; + linear$1(sp); HEAP32[sp + 8 >> 2] = 0; - sp = linear$1(sp) | 0; - HEAP32[sp + 0 >> 2] = 0; - HEAP32[sp + 8 >> 2] = 0; - sp = linear$0(sp) | 0; + HEAP32[sp + 12 >> 2] = 0; + linear$0(sp); 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; + 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, helper$0 = 0; sp = STACKTOP; if (($mem | 0) == 0) { STACKTOP = sp; @@ -37,7 +37,9 @@ function _free($mem) { } $16 = $mem + (($10 & -8) - 8) | 0; L621 : do { - if (($10 & 1 | 0) == 0) { + helper$0 = 1; + if (helper$0 ? ($10 & 1 | 0) == 0 : 0) { + helper$0 = 0; $21 = HEAP32[($mem - 8 | 0) >> 2] | 0; if (($10 & 3 | 0) == 0) { return; @@ -47,24 +49,24 @@ function _free($mem) { if (($mem + (-8 - $21 | 0) | 0) >>> 0 < $5 >>> 0) { _abort(); } - HEAP32[sp + 632 >> 2] = 0; - HEAP32[sp + 640 >> 2] = 0; - HEAP32[sp + 40 >> 2] = $25; - HEAP32[sp + 0 >> 2] = $mem; - HEAP32[sp + 16 >> 2] = $10; - HEAP32[sp + 48 >> 2] = $26; - HEAP32[sp + 32 >> 2] = $21; - HEAP32[sp + 8 >> 2] = $5; - HEAP32[sp + 216 >> 2] = $p_0; - HEAP32[sp + 208 >> 2] = $psize_0; - sp = _free$1(sp) | 0; - $p_0 = HEAP32[sp + 216 >> 2] | 0; - $psize_0 = HEAP32[sp + 208 >> 2] | 0; - tempValue = HEAP32[sp + 632 >> 2] | 0; - tempInt = HEAP32[sp + 640 >> 2] | 0; - tempDouble = +HEAPF32[sp + 640 >> 2]; - HEAP32[sp + 632 >> 2] = 0; - HEAP32[sp + 640 >> 2] = 0; + HEAP32[sp + 672 >> 2] = 0; + HEAP32[sp + 676 >> 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; + _free$1(sp); + $p_0 = HEAP32[sp + 224 >> 2] | 0; + $psize_0 = HEAP32[sp + 216 >> 2] | 0; + tempValue = HEAP32[sp + 672 >> 2] | 0; + tempInt = HEAP32[sp + 676 >> 2] | 0; + tempDouble = +HEAPF32[sp + 676 >> 2]; + HEAP32[sp + 672 >> 2] = 0; + HEAP32[sp + 676 >> 2] = 0; if ((tempValue | 0) == 5) { return; } @@ -79,7 +81,9 @@ function _free($mem) { } } } - } else { + } + if (helper$0) { + helper$0 = 0; $p_0 = $mem - 8 | 0; $psize_0 = $10 & -8; } @@ -92,21 +96,21 @@ function _free($mem) { if (($194 & 1 | 0) == 0) { _abort(); } - HEAP32[sp + 632 >> 2] = 0; - HEAP32[sp + 640 >> 2] = 0; - HEAP32[sp + 232 >> 2] = $194; - HEAP32[sp + 24 >> 2] = $16; - HEAP32[sp + 208 >> 2] = $psize_0; - HEAP32[sp + 216 >> 2] = $p_0; - HEAP32[sp + 224 >> 2] = $189; - HEAP32[sp + 0 >> 2] = $mem; - HEAP32[sp + 16 >> 2] = $10; - sp = _free$2(sp) | 0; - tempValue = HEAP32[sp + 632 >> 2] | 0; - tempInt = HEAP32[sp + 640 >> 2] | 0; - tempDouble = +HEAPF32[sp + 640 >> 2]; - HEAP32[sp + 632 >> 2] = 0; - HEAP32[sp + 640 >> 2] = 0; + HEAP32[sp + 680 >> 2] = 0; + HEAP32[sp + 684 >> 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; + _free$2(sp); + tempValue = HEAP32[sp + 680 >> 2] | 0; + tempInt = HEAP32[sp + 684 >> 2] | 0; + tempDouble = +HEAPF32[sp + 684 >> 2]; + HEAP32[sp + 680 >> 2] = 0; + HEAP32[sp + 684 >> 2] = 0; if ((tempValue | 0) == 5) { return; } @@ -126,7 +130,6 @@ function linear$0(sp) { 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; @@ -143,19 +146,18 @@ function linear$1(sp) { 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 + 416 >> 2] | 0; - $10 = HEAP32[sp + 16 >> 2] | 0; - $mem = HEAP32[sp + 0 >> 2] | 0; - $194 = HEAP32[sp + 232 >> 2] | 0; - $189 = HEAP32[sp + 224 >> 2] | 0; - $p_0 = HEAP32[sp + 216 >> 2] | 0; - $psize_0 = HEAP32[sp + 208 >> 2] | 0; - $16 = HEAP32[sp + 24 >> 2] | 0; + var $16 = 0, $220 = 0, $psize_0 = 0, $p_0 = 0, $189 = 0, $227 = 0, $194 = 0, helper$2 = 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; @@ -163,12 +165,14 @@ function _free$0(sp) { HEAP32[25] = $p_0; HEAP32[$p_0 + 4 >> 2] = $220 | 1; HEAP32[$189 + $220 >> 2] = $220; - HEAP32[sp + 632 >> 2] = 5; + HEAP32[sp + 664 >> 2] = 5; break OL; } $227 = ($194 & -8) + $psize_0 | 0; L726 : do { - if ($194 >>> 0 < 256) { + helper$2 = 1; + if (helper$2 ? $194 >>> 0 < 256 : 0) { + helper$2 = 0; $233 = HEAP32[$mem + ($10 & -8) >> 2] | 0; $236 = HEAP32[$mem + ($10 & -8 | 4) >> 2] | 0; do { @@ -202,7 +206,9 @@ function _free$0(sp) { } while (0); HEAP32[$233 + 12 >> 2] = $236; HEAP32[$_pre_phi305 >> 2] = $233; - } else { + } + if (helper$2) { + helper$2 = 0; $267 = $mem + (($10 & -8) - 8) | 0; $270 = HEAP32[$mem + (($10 & -8) + 16) >> 2] | 0; $273 = HEAP32[$mem + ($10 & -8 | 4) >> 2] | 0; @@ -323,40 +329,39 @@ function _free$0(sp) { HEAP32[$189 + $227 >> 2] = $227; if (($p_0 | 0) != (HEAP32[25] | 0)) { $psize_1 = $227; - HEAP32[sp + 632 >> 2] = 1; + HEAP32[sp + 664 >> 2] = 1; break OL; } HEAP32[22] = $227; - HEAP32[sp + 632 >> 2] = 5; + HEAP32[sp + 664 >> 2] = 5; break OL; } while (0); - HEAP32[sp + 416 >> 2] = $psize_1; - return sp | 0; + HEAP32[sp + 424 >> 2] = $psize_1; } 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 + 208 >> 2] | 0; - $p_0 = HEAP32[sp + 216 >> 2] | 0; - $5 = HEAP32[sp + 8 >> 2] | 0; - $21 = HEAP32[sp + 32 >> 2] | 0; - $26 = HEAP32[sp + 48 >> 2] | 0; - $10 = HEAP32[sp + 16 >> 2] | 0; - $mem = HEAP32[sp + 0 >> 2] | 0; - $25 = HEAP32[sp + 40 >> 2] | 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 + 632 >> 2] = 1; + HEAP32[sp + 672 >> 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 + 632 >> 2] = 5; + HEAP32[sp + 672 >> 2] = 5; break OL; } if ($21 >>> 0 < 256) { @@ -377,7 +382,7 @@ function _free$1(sp) { HEAP32[20] = HEAP32[20] & (1 << ($21 >>> 3) ^ -1); $p_0 = $25; $psize_0 = $26; - HEAP32[sp + 632 >> 2] = 1; + HEAP32[sp + 672 >> 2] = 1; break OL; } do { @@ -398,7 +403,7 @@ function _free$1(sp) { HEAP32[$_pre_phi307 >> 2] = $37; $p_0 = $25; $psize_0 = $26; - HEAP32[sp + 632 >> 2] = 1; + HEAP32[sp + 672 >> 2] = 1; break OL; } $69 = $mem + (-8 - $21 | 0) | 0; @@ -465,7 +470,7 @@ function _free$1(sp) { if (($72 | 0) == 0) { $p_0 = $25; $psize_0 = $26; - HEAP32[sp + 632 >> 2] = 1; + HEAP32[sp + 672 >> 2] = 1; break OL; } $120 = 384 + (HEAP32[($mem + ((-8 - $21 | 0) + 28) | 0) >> 2] << 2) | 0; @@ -478,8 +483,8 @@ function _free$1(sp) { HEAP32[21] = HEAP32[21] & (1 << HEAP32[($mem + ((-8 - $21 | 0) + 28) | 0) >> 2] ^ -1); $p_0 = $25; $psize_0 = $26; - HEAP32[sp + 632 >> 2] = 2; - HEAP32[sp + 640 >> 2] = 2; + HEAP32[sp + 672 >> 2] = 2; + HEAP32[sp + 676 >> 2] = 2; break OL; } else { if ($72 >>> 0 < (HEAP32[24] | 0) >>> 0) { @@ -493,8 +498,8 @@ function _free$1(sp) { if (($R_1 | 0) == 0) { $p_0 = $25; $psize_0 = $26; - HEAP32[sp + 632 >> 2] = 2; - HEAP32[sp + 640 >> 2] = 2; + HEAP32[sp + 672 >> 2] = 2; + HEAP32[sp + 676 >> 2] = 2; break OL; } } @@ -519,7 +524,7 @@ function _free$1(sp) { if (($164 | 0) == 0) { $p_0 = $25; $psize_0 = $26; - HEAP32[sp + 632 >> 2] = 1; + HEAP32[sp + 672 >> 2] = 1; break OL; } if ($164 >>> 0 < (HEAP32[24] | 0) >>> 0) { @@ -529,27 +534,28 @@ function _free$1(sp) { HEAP32[$164 + 24 >> 2] = $R_1; $p_0 = $25; $psize_0 = $26; - HEAP32[sp + 632 >> 2] = 1; + HEAP32[sp + 672 >> 2] = 1; break OL; } } while (0); - HEAP32[sp + 216 >> 2] = $p_0; - HEAP32[sp + 208 >> 2] = $psize_0; - return sp | 0; + HEAP32[sp + 224 >> 2] = $p_0; + HEAP32[sp + 216 >> 2] = $psize_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 + 16 >> 2] | 0; - $mem = HEAP32[sp + 0 >> 2] | 0; - $189 = HEAP32[sp + 224 >> 2] | 0; - $p_0 = HEAP32[sp + 216 >> 2] | 0; - $psize_0 = HEAP32[sp + 208 >> 2] | 0; - $16 = HEAP32[sp + 24 >> 2] | 0; - $194 = HEAP32[sp + 232 >> 2] | 0; + var helper$1 = 0, $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) { + helper$1 = 1; + if (helper$1 ? ($194 & 2 | 0) == 0 : 0) { + helper$1 = 0; if (($16 | 0) == (HEAP32[26] | 0)) { $204 = (HEAP32[23] | 0) + $psize_0 | 0; HEAP32[23] = $204; @@ -560,38 +566,40 @@ function _free$2(sp) { HEAP32[22] = 0; } if ($204 >>> 0 <= (HEAP32[27] | 0) >>> 0) { - HEAP32[sp + 632 >> 2] = 5; + HEAP32[sp + 680 >> 2] = 5; break OL; } _sys_trim(0) | 0; - HEAP32[sp + 632 >> 2] = 5; + HEAP32[sp + 680 >> 2] = 5; break OL; } - HEAP32[sp + 632 >> 2] = 0; - HEAP32[sp + 640 >> 2] = 0; - HEAP32[sp + 24 >> 2] = $16; - HEAP32[sp + 208 >> 2] = $psize_0; - HEAP32[sp + 216 >> 2] = $p_0; - HEAP32[sp + 224 >> 2] = $189; - HEAP32[sp + 232 >> 2] = $194; - HEAP32[sp + 0 >> 2] = $mem; - HEAP32[sp + 16 >> 2] = $10; - HEAP32[sp + 416 >> 2] = $psize_1; - sp = _free$0(sp) | 0; - $psize_1 = HEAP32[sp + 416 >> 2] | 0; - tempValue = HEAP32[sp + 632 >> 2] | 0; - tempInt = HEAP32[sp + 640 >> 2] | 0; - tempDouble = +HEAPF32[sp + 640 >> 2]; - HEAP32[sp + 632 >> 2] = 0; - HEAP32[sp + 640 >> 2] = 0; + HEAP32[sp + 664 >> 2] = 0; + HEAP32[sp + 668 >> 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; + _free$0(sp); + $psize_1 = HEAP32[sp + 424 >> 2] | 0; + tempValue = HEAP32[sp + 664 >> 2] | 0; + tempInt = HEAP32[sp + 668 >> 2] | 0; + tempDouble = +HEAPF32[sp + 668 >> 2]; + HEAP32[sp + 664 >> 2] = 0; + HEAP32[sp + 668 >> 2] = 0; if ((tempValue | 0) == 5) { - HEAP32[sp + 632 >> 2] = 5; + HEAP32[sp + 680 >> 2] = 5; break OL; } if ((tempValue | 0) == 1) { break; } - } else { + } + if (helper$1) { + helper$1 = 0; HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] = $194 & -2; HEAP32[$p_0 + 4 >> 2] = $psize_0 | 1; HEAP32[$189 + $psize_0 >> 2] = $psize_0; @@ -620,7 +628,7 @@ function _free$2(sp) { 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 + 632 >> 2] = 5; + HEAP32[sp + 680 >> 2] = 5; break OL; } $414 = $p_0; @@ -707,7 +715,7 @@ function _free$2(sp) { if (($501 | 0) == 0) { $sp_0_in_i = 536; } else { - HEAP32[sp + 632 >> 2] = 5; + HEAP32[sp + 680 >> 2] = 5; break OL; } while (1) { @@ -720,9 +728,8 @@ function _free$2(sp) { } HEAP32[28] = -1; STACKTOP = sp; - HEAP32[sp + 632 >> 2] = 5; + HEAP32[sp + 680 >> 2] = 5; break OL; } while (0); - return sp | 0; } diff --git a/tools/test-js-optimizer-asm-outline3-output.js b/tools/test-js-optimizer-asm-outline3-output.js new file mode 100644 index 00000000..5cc0f48d --- /dev/null +++ b/tools/test-js-optimizer-asm-outline3-output.js @@ -0,0 +1,28 @@ +function _memset(ptr, value, num) { + ptr = ptr | 0; + value = value | 0; + num = num | 0; + var stop = 0, value4 = 0, unaligned = 0; + stop = ptr + num | 0; + if ((num | 0) >= 20) { + value = value & 255; + unaligned = ptr & 3; + value4 = value | value << 8 | value << 16 | value << 24; + if (unaligned) { + unaligned = ptr + 4 - unaligned | 0; + while ((ptr | 0) < (unaligned | 0)) { + HEAP8[ptr] = value; + ptr = ptr + 1 | 0; + } + } + while ((ptr | 0) < (stop & ~3 | 0)) { + HEAP32[ptr >> 2] = value4; + ptr = ptr + 4 | 0; + } + } + while ((ptr | 0) < (stop | 0)) { + HEAP8[ptr] = value; + ptr = ptr + 1 | 0; + } +} + diff --git a/tools/test-js-optimizer-asm-outline3.js b/tools/test-js-optimizer-asm-outline3.js new file mode 100644 index 00000000..119447d8 --- /dev/null +++ b/tools/test-js-optimizer-asm-outline3.js @@ -0,0 +1,30 @@ +function _memset(ptr, value, num) { + ptr = ptr | 0; + value = value | 0; + num = num | 0; + var stop = 0, value4 = 0, stop4 = 0, unaligned = 0; + stop = ptr + num | 0; + if ((num | 0) >= 20) { + value = value & 255; + unaligned = ptr & 3; + value4 = value | value << 8 | value << 16 | value << 24; + stop4 = stop & ~3; + if (unaligned) { + unaligned = ptr + 4 - unaligned | 0; + while ((ptr | 0) < (unaligned | 0)) { + HEAP8[ptr] = value; + ptr = ptr + 1 | 0; + } + } + while ((ptr | 0) < (stop4 | 0)) { + HEAP32[ptr >> 2] = value4; + ptr = ptr + 4 | 0; + } + } + while ((ptr | 0) < (stop | 0)) { + HEAP8[ptr] = value; + ptr = ptr + 1 | 0; + } +} +// EMSCRIPTEN_GENERATED_FUNCTIONS +// EXTRA_INFO: { "sizeToOutline": 100 } |