diff options
55 files changed, 4190 insertions, 1504 deletions
@@ -87,3 +87,7 @@ a license to everyone to use it as detailed in LICENSE.) * Manfred Manik Nerurkar <nerurkar*at*made-apps.biz> (copyright owned by MADE, GmbH) * Joseph Gentle <me@josephg.com> * Douglas T. Crosher <dtc-moz@scieneer.com> (copyright owned by Mozilla Founcation) +* Soeren Balko <soeren.balko@gmail.com> +* Ryan Kelly (ryan@rfk.id.au) +* Michael Lelli <toadking@toadking.com> + @@ -302,6 +302,9 @@ Options that are modified or new in %s include: your main compiled code (or run it before in some other way). + For more docs on the options --preload-file + accepts, see https://github.com/kripken/emscripten/wiki/Filesystem-Guide + --compression <codec> Compress both the compiled code and embedded/ preloaded files. <codec> should be a triple, @@ -1046,7 +1049,7 @@ try: if shared.Settings.CORRECT_SIGNS >= 2 or shared.Settings.CORRECT_OVERFLOWS >= 2 or shared.Settings.CORRECT_ROUNDINGS >= 2: debug_level = 4 # must keep debug info to do line-by-line operations - if debug_level > 0 and closure: + if debug_level > 1 and closure: logging.warning('disabling closure because debug info was requested') closure = False @@ -1077,6 +1080,9 @@ try: shared.Settings.LINKABLE = 1 # TODO: add FORCE_DCE option for the brave people that do want to dce here and in side modules debug_level = max(debug_level, 2) + if shared.Settings.DLOPEN_SUPPORT: + shared.Settings.LINKABLE = 1 + ## Compile source code to bitcode logging.debug('compiling to bitcode') @@ -1535,14 +1541,17 @@ try: # It is useful to run several js optimizer passes together, to save on unneeded unparsing/reparsing js_optimizer_queue = [] + js_optimizer_extra_info = {} def flush_js_optimizer_queue(): - global final, js_optimizer_queue + global final, js_optimizer_queue, js_optimizer_extra_info + if len(js_optimizer_extra_info) == 0: + js_optimizer_extra_info = None if len(js_optimizer_queue) > 0 and not(len(js_optimizer_queue) == 1 and js_optimizer_queue[0] == 'last'): if DEBUG != '2': if shared.Settings.ASM_JS: js_optimizer_queue = ['asm'] + js_optimizer_queue logging.debug('applying js optimization passes: %s', js_optimizer_queue) - final = shared.Building.js_optimizer(final, js_optimizer_queue, jcache, debug_level >= 4) + final = shared.Building.js_optimizer(final, js_optimizer_queue, jcache, debug_level >= 4, js_optimizer_extra_info) js_transform_tempfiles.append(final) if DEBUG: save_intermediate('js_opts') else: @@ -1551,10 +1560,11 @@ try: if shared.Settings.ASM_JS: passes = ['asm'] + passes logging.debug('applying js optimization pass: %s', passes) - final = shared.Building.js_optimizer(final, passes, jcache, debug_level >= 4) + final = shared.Building.js_optimizer(final, passes, jcache, debug_level >= 4, js_optimizer_extra_info) js_transform_tempfiles.append(final) save_intermediate(name) js_optimizer_queue = [] + js_optimizer_extra_info = {} if opt_level >= 1: logging.debug('running pre-closure post-opts') @@ -1571,7 +1581,7 @@ try: else: return 'eliminate' - js_optimizer_queue += [get_eliminate(), 'simplifyExpressionsPre'] + js_optimizer_queue += [get_eliminate(), 'simplifyExpressions'] if shared.Settings.RELOOP and not shared.Settings.ASM_JS: js_optimizer_queue += ['optimizeShiftsAggressive', get_eliminate()] # aggressive shifts optimization requires loops, it breaks on switches @@ -1585,9 +1595,9 @@ try: final = shared.Building.closure_compiler(final) if DEBUG: save_intermediate('closure') - if opt_level >= 1: - logging.debug('running post-closure post-opts') - js_optimizer_queue += ['simplifyExpressionsPost'] + if shared.Settings.OUTLINING_LIMIT > 0: + js_optimizer_queue += ['outline'] + js_optimizer_extra_info['sizeToOutline'] = shared.Settings.OUTLINING_LIMIT if (not closure or shared.Settings.ASM_JS) and shared.Settings.RELOOP and debug_level < 3: js_optimizer_queue += ['registerize'] diff --git a/emscripten.py b/emscripten.py index d9367566..3e3538e9 100755 --- a/emscripten.py +++ b/emscripten.py @@ -9,7 +9,7 @@ header files (so that the JS compiler can see the constants in those headers, for the libc implementation in JS). ''' -import os, sys, json, optparse, subprocess, re, time, multiprocessing, functools +import os, sys, json, optparse, subprocess, re, time, multiprocessing, string from tools import shared from tools import jsrun, cache as cache_module, tempfiles @@ -44,20 +44,25 @@ MIN_CHUNK_SIZE = 1024*1024 MAX_CHUNK_SIZE = float(os.environ.get('EMSCRIPT_MAX_CHUNK_SIZE') or 'inf') # configuring this is just for debugging purposes def process_funcs((i, funcs, meta, settings_file, compiler, forwarded_file, libraries, compiler_engine, temp_files, DEBUG)): - funcs_file = temp_files.get('.func_%d.ll' % i).name - f = open(funcs_file, 'w') - f.write(funcs) - funcs = None - f.write('\n') - f.write(meta) - f.close() - out = jsrun.run_js( - compiler, - engine=compiler_engine, - args=[settings_file, funcs_file, 'funcs', forwarded_file] + libraries, - stdout=subprocess.PIPE, - cwd=path_from_root('src')) - tempfiles.try_delete(funcs_file) + try: + funcs_file = temp_files.get('.func_%d.ll' % i).name + f = open(funcs_file, 'w') + f.write(funcs) + funcs = None + f.write('\n') + f.write(meta) + f.close() + out = jsrun.run_js( + compiler, + engine=compiler_engine, + args=[settings_file, funcs_file, 'funcs', forwarded_file] + libraries, + stdout=subprocess.PIPE, + cwd=path_from_root('src')) + except KeyboardInterrupt: + # Python 2.7 seems to lock up when a child process throws KeyboardInterrupt + raise Exception() + finally: + tempfiles.try_delete(funcs_file) if DEBUG: print >> sys.stderr, '.' return out @@ -620,6 +625,18 @@ Runtime.stackRestore = function(top) { asm['stackRestore'](top) }; // EMSCRIPTEN_END_FUNCS '''] + # Create symbol table for self-dlopen + if settings.get('DLOPEN_SUPPORT'): + symbol_table = { k:v+forwarded_json['Runtime']['GLOBAL_BASE'] + for k,v in forwarded_json['Variables']['indexedGlobals'].iteritems() + if forwarded_json['Variables']['globals'][k]['named'] } + for raw in last_forwarded_json['Functions']['tables'].itervalues(): + if raw == '': continue + table = map(string.strip, raw[raw.find('[')+1:raw.find(']')].split(",")) + symbol_table.update(map(lambda x: (x[1], x[0]), + filter(lambda x: x[1] != '0', enumerate(table)))) + outfile.write("var SYMBOL_TABLE = %s;" % json.dumps(symbol_table)) + for funcs_js_item in funcs_js: # do this loop carefully to save memory funcs_js_item = indexize(funcs_js_item) funcs_js_item = blockaddrsize(funcs_js_item) diff --git a/src/analyzer.js b/src/analyzer.js index de9a7940..b1f0b585 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -502,18 +502,38 @@ function analyzer(data, sidePass) { { intertype: 'value', ident: j.toString(), type: 'i32' } ] }); - var actualSizeType = 'i' + element.bits; // The last one may be smaller than 32 bits - toAdd.push({ + var newItem = { intertype: 'load', assignTo: element.ident, - pointerType: actualSizeType + '*', - valueType: actualSizeType, - type: actualSizeType, // XXX why is this missing from intertyper? - pointer: { intertype: 'value', ident: tempVar, type: actualSizeType + '*' }, + pointerType: 'i32*', + valueType: 'i32', + type: 'i32', + pointer: { intertype: 'value', ident: tempVar, type: 'i32*' }, ident: tempVar, - pointerType: actualSizeType + '*', align: value.align - }); + }; + var newItem2 = null; + // The last one may be smaller than 32 bits + if (element.bits < 32) { + newItem.assignTo += '$preadd$'; + newItem2 = { + intertype: 'mathop', + op: 'and', + assignTo: element.ident, + type: 'i32', + params: [{ + intertype: 'value', + type: 'i32', + ident: newItem.assignTo + }, { + intertype: 'value', + type: 'i32', + ident: (0xffffffff >>> (32 - element.bits)).toString() + }], + }; + } + toAdd.push(newItem); + if (newItem2) toAdd.push(newItem2); j++; }); Types.needAnalysis['[0 x i32]'] = 0; @@ -1433,15 +1453,14 @@ function analyzer(data, sidePass) { func.labelsDict = {}; func.labelIds = {}; func.labelIdsInverse = {}; - func.labelIds[toNiceIdent('%0')] = 1; - func.labelIdsInverse[0] = toNiceIdent('%0'); - func.labelIdCounter = 2; + func.labelIdCounter = 1; func.labels.forEach(function(label) { if (!(label.ident in func.labelIds)) { func.labelIds[label.ident] = func.labelIdCounter++; func.labelIdsInverse[func.labelIdCounter-1] = label.ident; } }); + var entryIdent = func.labels[0].ident; // Minify label ids to numeric ids. func.labels.forEach(function(label) { @@ -1478,7 +1497,7 @@ function analyzer(data, sidePass) { function getActualLabelId(labelId) { if (func.labelsDict[labelId]) return labelId; // If not present, it must be a surprisingly-named entry (or undefined behavior, in which case, still ok to use the entry) - labelId = func.labelIds[ENTRY_IDENT]; + labelId = func.labelIds[entryIdent]; assert(func.labelsDict[labelId]); return labelId; } diff --git a/src/embind/embind.js b/src/embind/embind.js index 91386c69..f0cd0c74 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -5,9 +5,9 @@ /*global __emval_register, _emval_handle_array, __emval_decref*/ /*global ___getTypeName*/ /*jslint sub:true*/ /* The symbols 'fromWireType' and 'toWireType' must be accessed via array notation to be closure-safe since craftInvokerFunction crafts functions as strings that can't be closured. */ -var InternalError = Module.InternalError = extendError(Error, 'InternalError'); -var BindingError = Module.BindingError = extendError(Error, 'BindingError'); -var UnboundTypeError = Module.UnboundTypeError = extendError(BindingError, 'UnboundTypeError'); +var InternalError = Module['InternalError'] = extendError(Error, 'InternalError'); +var BindingError = Module['BindingError'] = extendError(Error, 'BindingError'); +var UnboundTypeError = Module['UnboundTypeError'] = extendError(BindingError, 'UnboundTypeError'); function throwInternalError(message) { throw new InternalError(message); @@ -638,7 +638,7 @@ function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker, var tupleRegistrations = {}; -function __embind_register_tuple(rawType, name, rawConstructor, rawDestructor) { +function __embind_register_value_array(rawType, name, rawConstructor, rawDestructor) { tupleRegistrations[rawType] = { name: readLatin1String(name), rawConstructor: FUNCTION_TABLE[rawConstructor], @@ -647,7 +647,7 @@ function __embind_register_tuple(rawType, name, rawConstructor, rawDestructor) { }; } -function __embind_register_tuple_element( +function __embind_register_value_array_element( rawTupleType, getterReturnType, getter, @@ -666,7 +666,7 @@ function __embind_register_tuple_element( }); } -function __embind_finalize_tuple(rawTupleType) { +function __embind_finalize_value_array(rawTupleType) { var reg = tupleRegistrations[rawTupleType]; delete tupleRegistrations[rawTupleType]; var elements = reg.elements; @@ -725,7 +725,7 @@ function __embind_finalize_tuple(rawTupleType) { var structRegistrations = {}; -function __embind_register_struct( +function __embind_register_value_object( rawType, name, rawConstructor, @@ -739,7 +739,7 @@ function __embind_register_struct( }; } -function __embind_register_struct_field( +function __embind_register_value_object_field( structType, fieldName, getterReturnType, @@ -760,7 +760,7 @@ function __embind_register_struct_field( }); } -function __embind_finalize_struct(structType) { +function __embind_finalize_value_object(structType) { var reg = structRegistrations[structType]; delete structRegistrations[structType]; @@ -879,11 +879,11 @@ var genericPointerToWireType = function(destructors, handle) { if (handle.$$.smartPtrType === this) { ptr = handle.$$.smartPtr; } else { - var clonedHandle = handle.clone(); + var clonedHandle = handle['clone'](); ptr = this.rawShare( ptr, __emval_register(function() { - clonedHandle.delete(); + clonedHandle['delete'](); }) ); if (destructors !== null) { @@ -1088,7 +1088,7 @@ function getInstanceTypeName(handle) { return handle.$$.ptrType.registeredClass.name; } -ClassHandle.prototype.isAliasOf = function(other) { +ClassHandle.prototype['isAliasOf'] = function(other) { if (!(this instanceof ClassHandle)) { return false; } @@ -1118,7 +1118,7 @@ function throwInstanceAlreadyDeleted(obj) { throwBindingError(getInstanceTypeName(obj) + ' instance already deleted'); } -ClassHandle.prototype.clone = function() { +ClassHandle.prototype['clone'] = function() { if (!this.$$.ptr) { throwInstanceAlreadyDeleted(this); } diff --git a/src/embind/emval.js b/src/embind/emval.js index 77270597..0d075188 100644 --- a/src/embind/emval.js +++ b/src/embind/emval.js @@ -4,6 +4,7 @@ /*global createNamedFunction*/ /*global readLatin1String, writeStringToMemory*/ /*global requireRegisteredType, throwBindingError*/ +/*jslint sub:true*/ /* The symbols 'fromWireType' and 'toWireType' must be accessed via array notation to be closure-safe since craftInvokerFunction crafts functions as strings that can't be closured. */ var Module = Module || {}; @@ -100,7 +101,7 @@ function __emval_new_cstring(v) { function __emval_take_value(type, v) { type = requireRegisteredType(type, '_emval_take_value'); - v = type.fromWireType(v); + v = type['fromWireType'](v); return __emval_register(v); } @@ -203,7 +204,7 @@ function __emval_as(handle, returnType) { returnType = requireRegisteredType(returnType, 'emval::as'); var destructors = []; // caller owns destructing - return returnType.toWireType(destructors, _emval_handle_array[handle].value); + return returnType['toWireType'](destructors, _emval_handle_array[handle].value); } function parseParameters(argCount, argTypes, argWireTypes) { @@ -212,7 +213,7 @@ function parseParameters(argCount, argTypes, argWireTypes) { var argType = requireRegisteredType( HEAP32[(argTypes >> 2) + i], "parameter " + i); - a[i] = argType.fromWireType(argWireTypes[i]); + a[i] = argType['fromWireType'](argWireTypes[i]); } return a; } @@ -223,7 +224,7 @@ function __emval_call(handle, argCount, argTypes) { var args = new Array(argCount); for (var i = 0; i < argCount; ++i) { - args[i] = types[i].fromWireType(arguments[3 + i]); + args[i] = types[i]['fromWireType'](arguments[3 + i]); } var fn = _emval_handle_array[handle].value; @@ -247,8 +248,8 @@ function __emval_get_method_caller(argCount, argTypes) { var retType = types[0]; var signatureName = retType.name + "_$" + types.slice(1).map(function (t) { return t.name; }).join("_") + "$"; - var args1 = ["Runtime", "createNamedFunction", "requireHandle", "getStringOrSymbol", "_emval_handle_array", "retType"]; - var args2 = [Runtime, createNamedFunction, requireHandle, getStringOrSymbol, _emval_handle_array, retType]; + var args1 = ["addFunction", "createNamedFunction", "requireHandle", "getStringOrSymbol", "_emval_handle_array", "retType"]; + var args2 = [Runtime.addFunction, createNamedFunction, requireHandle, getStringOrSymbol, _emval_handle_array, retType]; var argsList = ""; // 'arg0, arg1, arg2, ... , argN' var argsListWired = ""; // 'arg0Wired, ..., argNWired' @@ -260,7 +261,7 @@ function __emval_get_method_caller(argCount, argTypes) { } var invokerFnBody = - "return Runtime.addFunction(createNamedFunction('" + signatureName + "', function (handle, name" + argsListWired + ") {\n" + + "return addFunction(createNamedFunction('" + signatureName + "', function (handle, name" + argsListWired + ") {\n" + "requireHandle(handle);\n" + "name = getStringOrSymbol(name);\n"; diff --git a/src/intertyper.js b/src/intertyper.js index 94d937e1..abfbdacb 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -503,6 +503,7 @@ function intertyper(data, sidePass, baseLineNums) { // variable var ident = item.tokens[0].text; var private_ = findTokenText(item, 'private') >= 0 || findTokenText(item, 'internal') >= 0; + var named = findTokenText(item, 'unnamed_addr') < 0; cleanOutTokens(LLVM.GLOBAL_MODIFIERS, item.tokens, [2, 3]); var external = false; if (item.tokens[2].text === 'external') { @@ -516,6 +517,7 @@ function intertyper(data, sidePass, baseLineNums) { type: item.tokens[2].text, external: external, private_: private_, + named: named, lineNum: item.lineNum }; if (!NAMED_GLOBALS) { diff --git a/src/jsifier.js b/src/jsifier.js index 82b78d0a..b377202d 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -286,6 +286,8 @@ function JSify(data, functionsOnly, givenFunctions) { allocator = 'ALLOC_NONE'; } + Variables.globals[item.ident].named = item.named; + if (ASM_JS && (MAIN_MODULE || SIDE_MODULE) && !item.private_ && !NAMED_GLOBALS && isIndexableGlobal(item.ident)) { // We need this to be named (and it normally would not be), so that it can be linked to and used from other modules Variables.globals[item.ident].linkable = 1; @@ -602,6 +604,8 @@ function JSify(data, functionsOnly, givenFunctions) { var associatedSourceFile = "NO_SOURCE"; } + if (DLOPEN_SUPPORT) Functions.getIndex(func.ident); + func.JS += 'function ' + func.ident + '(' + paramIdents.join(', ') + ') {\n'; if (PGO) { @@ -1207,7 +1211,7 @@ function JSify(data, functionsOnly, givenFunctions) { // in an assignment var disabled = DISABLE_EXCEPTION_CATCHING == 2 && !(item.funcData.ident in EXCEPTION_CATCHING_WHITELIST); var phiSets = calcPhiSets(item); - var call_ = makeFunctionCall(item.ident, item.params, item.funcData, item.type, ASM_JS && !disabled, !!item.assignTo || !item.standalone); + var call_ = makeFunctionCall(item.ident, item.params, item.funcData, item.type, ASM_JS && !disabled, !!item.assignTo || !item.standalone, true); var ret; @@ -1364,7 +1368,7 @@ function JSify(data, functionsOnly, givenFunctions) { return ret; }); - function makeFunctionCall(ident, params, funcData, type, forceByPointer, hasReturn) { + function makeFunctionCall(ident, params, funcData, type, forceByPointer, hasReturn, invoke) { // We cannot compile assembly. See comment in intertyper.js:'Call' assert(ident != 'asm', 'Inline assembly cannot be compiled to JavaScript!'); @@ -1429,7 +1433,7 @@ function JSify(data, functionsOnly, givenFunctions) { args = args.map(function(arg, i) { return indexizeFunctions(arg, argsTypes[i]) }); if (ASM_JS) { - if (shortident in Functions.libraryFunctions || simpleIdent in Functions.libraryFunctions || byPointerForced || funcData.setjmpTable) { + if (shortident in Functions.libraryFunctions || simpleIdent in Functions.libraryFunctions || byPointerForced || invoke || funcData.setjmpTable) { args = args.map(function(arg, i) { return asmCoercion(arg, argsTypes[i]) }); } else { args = args.map(function(arg, i) { return asmEnsureFloat(arg, argsTypes[i]) }); @@ -1470,7 +1474,6 @@ function JSify(data, functionsOnly, givenFunctions) { } args = args.concat(varargs); - var argsText = args.join(', '); // Inline if either we inline whenever we can (and we can), or if there is no noninlined version var inline = LibraryManager.library[simpleIdent + '__inline']; @@ -1492,16 +1495,35 @@ function JSify(data, functionsOnly, givenFunctions) { } } |