diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/analyzer.js | 5 | ||||
-rw-r--r-- | src/compiler.js | 1 | ||||
-rw-r--r-- | src/experimental/simplifyGeneratedFunctionsDetection.diff | 336 | ||||
-rw-r--r-- | src/intertyper.js | 23 | ||||
-rw-r--r-- | src/jsifier.js | 18 | ||||
-rw-r--r-- | src/library_gl.js | 19 | ||||
-rw-r--r-- | src/library_sdl.js | 51 | ||||
-rw-r--r-- | src/modules.js | 4 | ||||
-rw-r--r-- | src/preamble.js | 17 | ||||
-rw-r--r-- | src/runtime.js | 2 | ||||
-rw-r--r-- | src/settings.js | 12 |
11 files changed, 435 insertions, 53 deletions
diff --git a/src/analyzer.js b/src/analyzer.js index 209e3140..926ac9d3 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -231,9 +231,10 @@ function analyzer(data, sidePass) { } if (isIllegalType(item.valueType) || isIllegalType(item.type)) { isIllegal = true; - } - if ((item.intertype == 'load' || item.intertype == 'store') && isStructType(item.valueType)) { + } else if ((item.intertype == 'load' || item.intertype == 'store') && isStructType(item.valueType)) { isIllegal = true; // storing an entire structure is illegal + } else if (item.intertype == 'mathop' && item.op == 'trunc' && isIllegalType(item.params[1].ident)) { // trunc stores target value in second ident + isIllegal = true; } }); if (!isIllegal) { diff --git a/src/compiler.js b/src/compiler.js index 3047daf1..447d34b7 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -163,6 +163,7 @@ if (SAFE_HEAP >= 2) { EXPORTED_FUNCTIONS = set(EXPORTED_FUNCTIONS); EXPORTED_GLOBALS = set(EXPORTED_GLOBALS); EXCEPTION_CATCHING_WHITELIST = set(EXCEPTION_CATCHING_WHITELIST); +DEAD_FUNCTIONS = set(DEAD_FUNCTIONS); RUNTIME_DEBUG = LIBRARY_DEBUG || GL_DEBUG; diff --git a/src/experimental/simplifyGeneratedFunctionsDetection.diff b/src/experimental/simplifyGeneratedFunctionsDetection.diff new file mode 100644 index 00000000..09e0ebcd --- /dev/null +++ b/src/experimental/simplifyGeneratedFunctionsDetection.diff @@ -0,0 +1,336 @@ +diff --git a/emscripten.py b/emscripten.py +index b698654..a7843c7 100755 +--- a/emscripten.py ++++ b/emscripten.py +@@ -294,8 +294,6 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None, + outfile.write(blockaddrsize(indexize(pre))) + pre = None + +- #if DEBUG: outfile.write('// funcs\n') +- + # forward + forwarded_data = json.dumps(forwarded_json) + forwarded_file = temp_files.get('.2.json').name +@@ -483,9 +481,14 @@ Runtime.stackRestore = function(top) { asm.stackRestore(top) }; + else: + function_tables_defs = '\n'.join([table for table in last_forwarded_json['Functions']['tables'].itervalues()]) + outfile.write(function_tables_defs) ++ ++ outfile.write('// EMSCRIPTEN_START_FUNCS\n') ++ + outfile.write(blockaddrsize(indexize(funcs_js))) + funcs_js = None + ++ outfile.write('// EMSCRIPTEN_END_FUNCS\n') ++ + outfile.write(indexize(post)) + if DEBUG: print >> sys.stderr, ' emscript: phase 3 took %s seconds' % (time.time() - t) + +diff --git a/src/jsifier.js b/src/jsifier.js +index ff58ece..d6aa110 100644 +--- a/src/jsifier.js ++++ b/src/jsifier.js +@@ -1587,12 +1587,6 @@ function JSify(data, functionsOnly, givenFunctions) { + + var shellParts = read(shellFile).split('{{BODY}}'); + print(shellParts[1]); +- // Print out some useful metadata (for additional optimizations later, like the eliminator) +- if (EMIT_GENERATED_FUNCTIONS) { +- print('// EMSCRIPTEN_GENERATED_FUNCTIONS: ' + JSON.stringify(keys(Functions.implementedFunctions).filter(function(func) { +- return IGNORED_FUNCTIONS.indexOf(func.ident) < 0; +- })) + '\n'); +- } + + PassManager.serialize(); + +diff --git a/src/settings.js b/src/settings.js +index 1bfcf92..99c83f4 100644 +--- a/src/settings.js ++++ b/src/settings.js +@@ -336,8 +336,6 @@ var EXPLICIT_ZEXT = 0; // If 1, generate an explicit conversion of zext i1 to i3 + + var NECESSARY_BLOCKADDRS = []; // List of (function, block) for all block addresses that are taken. + +-var EMIT_GENERATED_FUNCTIONS = 0; // whether to emit the list of generated functions, needed for external JS optimization passes +- + // Compiler debugging options + var DEBUG_TAGS_SHOWING = []; + // Some useful items: +diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js +index f2dc516..9fa038c 100644 +--- a/tools/js-optimizer.js ++++ b/tools/js-optimizer.js +@@ -140,16 +140,6 @@ var UNDEFINED_NODE = ['unary-prefix', 'void', ['num', 0]]; + var TRUE_NODE = ['unary-prefix', '!', ['num', 0]]; + var FALSE_NODE = ['unary-prefix', '!', ['num', 1]]; + +-var GENERATED_FUNCTIONS_MARKER = '// EMSCRIPTEN_GENERATED_FUNCTIONS:'; +-var generatedFunctions = null; +-function setGeneratedFunctions(metadata) { +- var start = metadata.indexOf(GENERATED_FUNCTIONS_MARKER); +- generatedFunctions = set(eval(metadata.substr(start + GENERATED_FUNCTIONS_MARKER.length))); +-} +-function isGenerated(ident) { +- return ident in generatedFunctions; +-} +- + function srcToAst(src) { + return uglify.parser.parse(src); + } +@@ -212,21 +202,9 @@ function traverse(node, pre, post, stack) { + return result; + } + +-// Only walk through the generated functions +-function traverseGenerated(ast, pre, post, stack) { +- assert(generatedFunctions); +- traverse(ast, function(node) { +- if (node[0] == 'defun' && isGenerated(node[1])) { +- traverse(node, pre, post, stack); +- return null; +- } +- }); +-} +- +-function traverseGeneratedFunctions(ast, callback) { +- assert(generatedFunctions); ++function traverseFunctions(ast, callback) { + traverse(ast, function(node) { +- if (node[0] == 'defun' && isGenerated(node[1])) { ++ if (node[0] == 'defun') { + callback(node); + return null; + } +@@ -418,7 +396,7 @@ function simplifyExpressionsPre(ast) { + var rerun = true; + while (rerun) { + rerun = false; +- traverseGenerated(ast, function process(node, type, stack) { ++ traverse(ast, function process(node, type, stack) { + if (type == 'binary' && node[1] == '|') { + if (node[2][0] == 'num' && node[3][0] == 'num') { + return ['num', node[2][1] | node[3][1]]; +@@ -455,7 +433,7 @@ function simplifyExpressionsPre(ast) { + } + + // &-related optimizations +- traverseGenerated(ast, function(node, type) { ++ traverse(ast, function(node, type) { + if (type == 'binary' && node[1] == '&' && node[3][0] == 'num') { + if (node[2][0] == 'num') return ['num', node[2][1] & node[3][1]]; + var input = node[2]; +@@ -489,7 +467,7 @@ function simplifyExpressionsPre(ast) { + + if (asm) { + // optimize num >> num, in asm we need this here since we do not run optimizeShifts +- traverseGenerated(ast, function(node, type) { ++ traverse(ast, function(node, type) { + if (type == 'binary' && node[1] == '>>' && node[2][0] == 'num' && node[3][0] == 'num') { + node[0] = 'num'; + node[1] = node[2][1] >> node[3][1]; +@@ -505,7 +483,7 @@ function simplifyExpressionsPre(ast) { + var rerun = true; + while (rerun) { + rerun = false; +- traverseGenerated(ast, function(node, type) { ++ traverse(ast, function(node, type) { + if (type == 'binary' && node[1] == '+') { + if (node[2][0] == 'num' && node[3][0] == 'num') { + rerun = true; +@@ -528,7 +506,7 @@ function simplifyExpressionsPre(ast) { + + // if (x == 0) can be if (!x), etc. + function simplifyZeroComp(ast) { +- traverseGenerated(ast, function(node, type) { ++ traverse(ast, function(node, type) { + var binary; + if (type == 'if' && (binary = node[1])[0] == 'binary') { + if ((binary[1] == '!=' || binary[1] == '!==') && binary[3][0] == 'num' && binary[3][1] == 0) { +@@ -554,7 +532,7 @@ function simplifyExpressionsPre(ast) { + // TODO: when shifting a variable, if there are other uses, keep an unshifted version too, to prevent slowdowns? + function optimizeShiftsInternal(ast, conservative) { + var MAX_SHIFTS = 3; +- traverseGeneratedFunctions(ast, function(fun) { ++ traverseFunctions(ast, function(fun) { + var funMore = true; + var funFinished = {}; + while (funMore) { +@@ -999,7 +977,7 @@ function vacuum(ast) { + } break; + } + } +- traverseGeneratedFunctions(ast, function(node) { ++ traverseFunctions(ast, function(node) { + vacuumInternal(node); + simplifyNotComps(node); + }); +@@ -1021,7 +999,7 @@ function getStatements(node) { + // if (condition) { label == x } else .. + // We can hoist the multiple block into the condition, thus removing code and one 'if' check + function hoistMultiples(ast) { +- traverseGeneratedFunctions(ast, function(node) { ++ traverseFunctions(ast, function(node) { + traverse(node, function(node, type) { + var statements = getStatements(node); + if (!statements) return; +@@ -1135,7 +1113,7 @@ function hoistMultiples(ast) { + // if (..) { .. break|continue } else { .. } + // to + // if (..) { .. break|continue } .. +- traverseGenerated(ast, function(container, type) { ++ traverse(ast, function(container, type) { + var statements = getStatements(container); + if (!statements) return; + for (var i = 0; i < statements.length; i++) { +@@ -1168,7 +1146,7 @@ function loopOptimizer(ast) { + function passTwo(ast) { + var neededDos = []; + // Find unneeded labels +- traverseGenerated(ast, function(node, type, stack) { ++ traverse(ast, function(node, type, stack) { + if (type == 'label' && node[2][0] in LOOP) { + // this is a labelled loop. we don't know if it's needed yet. Mark its label for removal for now now. + stack.push(node); +@@ -1212,7 +1190,7 @@ function loopOptimizer(ast) { + // We return whether another pass is necessary + var more = false; + // Remove unneeded labels +- traverseGenerated(ast, function(node, type) { ++ traverse(ast, function(node, type) { + if (type == 'label' && node[1][0] == '+') { + more = true; + var ident = node[1].substr(1); +@@ -1227,13 +1205,13 @@ function loopOptimizer(ast) { + }); + // Remove unneeded one-time loops. We need such loops if (1) they have a label, or (2) they have a direct break so they are in neededDos. + // First, add all labeled loops of this nature to neededDos +- traverseGenerated(ast, function(node, type) { ++ traverse(ast, function(node, type) { + if (type == 'label' && node[2][0] == 'do') { + neededDos.push(node[2]); + } + }); + // Remove unneeded dos, we know who they are now +- traverseGenerated(ast, function(node, type) { ++ traverse(ast, function(node, type) { + if (type == 'do' && neededDos.indexOf(node) < 0) { + assert(jsonCompare(node[1], ['num', 0]), 'Trying to remove a one-time do loop that is not one of our generated ones.;'); + more = true; +@@ -1407,7 +1385,7 @@ function denormalizeAsm(func, data) { + // we still need the eliminator? Closure? And in what order? Perhaps just + // closure simple? + function registerize(ast) { +- traverseGeneratedFunctions(ast, function(fun) { ++ traverseFunctions(ast, function(fun) { + if (asm) var asmData = normalizeAsm(fun); + // Add parameters as a first (fake) var (with assignment), so they get taken into consideration + var params = {}; // note: params are special, they can never share a register between them (see later) +@@ -1671,7 +1649,7 @@ var ABORTING_ELIMINATOR_SCAN_NODES = set('new', 'object', 'function', 'defun', ' + + function eliminate(ast, memSafe) { + // Find variables that have a single use, and if they can be eliminated, do so +- traverseGeneratedFunctions(ast, function(func, type) { ++ traverseFunctions(ast, function(func, type) { + if (asm) var asmData = normalizeAsm(func); + //printErr('eliminate in ' + func[1]); + +@@ -2218,9 +2196,6 @@ var passes = { + var src = read(arguments_[0]); + var ast = srcToAst(src); + //printErr(JSON.stringify(ast)); throw 1; +-var metadata = src.split('\n').filter(function(line) { return line.indexOf(GENERATED_FUNCTIONS_MARKER) >= 0 })[0]; +-//assert(metadata, 'Must have EMSCRIPTEN_GENERATED_FUNCTIONS metadata'); +-if (metadata) setGeneratedFunctions(metadata); + + arguments_.slice(1).forEach(function(arg) { + passes[arg](ast); +diff --git a/tools/js_optimizer.py b/tools/js_optimizer.py +index 2fd2211..a253afb 100644 +--- a/tools/js_optimizer.py ++++ b/tools/js_optimizer.py +@@ -41,17 +41,17 @@ def run_on_js(filename, passes, js_engine, jcache): + if os.linesep != '\n': + js = js.replace(os.linesep, '\n') # we assume \n in the splitting code + +- # Find suffix +- suffix_marker = '// EMSCRIPTEN_GENERATED_FUNCTIONS' +- suffix_start = js.find(suffix_marker) +- suffix = '' +- if suffix_start >= 0: +- suffix = js[suffix_start:js.find('\n', suffix_start)] + '\n' +- # if there is metadata, we will run only on the generated functions. If there isn't, we will run on everything. +- generated = set(eval(suffix[len(suffix_marker)+1:])) +- +- if not suffix and jcache: +- # JCache cannot be used without metadata, since it might reorder stuff, and that's dangerous since only generated can be reordered ++ # Find markers ++ start_marker = '// EMSCRIPTEN_START_FUNCS\n' ++ end_marker = '// EMSCRIPTEN_END_FUNCS\n' ++ start = js.find(start_marker) ++ end = js.find(end_marker) ++ assert (start >= 0) == (end >= 0), 'must have both markers or neither' ++ have_markers = start >= 0 ++ # if there are markers, we will run only on the generated functions. If there isn't, we will run on everything. ++ ++ if not have_markers and jcache: ++ # JCache cannot be used without markers, since it might reorder stuff, and that's dangerous since only generated can be reordered + # This means jcache does not work after closure compiler runs, for example. But you won't get much benefit from jcache with closure + # anyhow (since closure is likely the longest part of the build). + if DEBUG: print >>sys.stderr, 'js optimizer: no metadata, so disabling jcache' +@@ -59,27 +59,10 @@ def run_on_js(filename, passes, js_engine, jcache): + + # If we process only generated code, find that and save the rest on the side + func_sig = re.compile('( *)function (_[\w$]+)\(') +- if suffix: +- pos = 0 +- gen_start = 0 +- gen_end = 0 +- while 1: +- m = func_sig.search(js, pos) +- if not m: break +- pos = m.end() +- indent = m.group(1) +- ident = m.group(2) +- if ident in generated: +- if not gen_start: +- gen_start = m.start() +- assert gen_start +- gen_end = js.find('\n%s}\n' % indent, m.end()) + (3 + len(indent)) +- assert gen_end > gen_start +- pre = js[:gen_start] +- post = js[gen_end:] +- if 'last' in passes: +- post = post.replace(suffix, '') # no need to write out the metadata - nothing after us needs it +- js = js[gen_start:gen_end] ++ if have_markers: ++ pre = js[:start + len(start_marker)] # includes start marker ++ post = js[end:] # includes end marker ++ js = js[start + len(start_marker):end] + else: + pre = '' + post = '' +@@ -95,7 +78,7 @@ def run_on_js(filename, passes, js_engine, jcache): + if m: + ident = m.group(2) + else: +- if suffix: continue # ignore whitespace ++ if have_markers: continue # ignore whitespace + ident = 'anon_%d' % i + assert ident + funcs.append((ident, func)) +@@ -131,7 +114,6 @@ def run_on_js(filename, passes, js_engine, jcache): + temp_file = temp_files.get('.jsfunc_%d.js' % i).name + f = open(temp_file, 'w') + f.write(chunk) +- f.write(suffix) + f.close() + return temp_file + filenames = [write_chunk(chunks[i], i) for i in range(len(chunks))] +@@ -169,7 +151,6 @@ def run_on_js(filename, passes, js_engine, jcache): + f.write(cached); # TODO: preserve order + f.write('\n') + f.write(post); +- # No need to write suffix: if there was one, it is inside post which exists when suffix is there + f.write('\n') + f.close() + diff --git a/src/intertyper.js b/src/intertyper.js index 2103ecfa..57e3011d 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -122,16 +122,19 @@ function intertyper(data, sidePass, baseLineNums) { SKIP_STACK_IN_SMALL = 0; } - unparsedBundles.push({ - intertype: 'unparsedFunction', - // We need this early, to know basic function info - ident, params, varargs - ident: toNiceIdent(func.ident), - params: func.params, - returnType: func.returnType, - hasVarArgs: func.hasVarArgs, - lineNum: currFunctionLineNum, - lines: currFunctionLines - }); + var ident = toNiceIdent(func.ident); + if (!(ident in DEAD_FUNCTIONS)) { + unparsedBundles.push({ + intertype: 'unparsedFunction', + // We need this early, to know basic function info - ident, params, varargs + ident: ident, + params: func.params, + returnType: func.returnType, + hasVarArgs: func.hasVarArgs, + lineNum: currFunctionLineNum, + lines: currFunctionLines + }); + } currFunctionLines = []; } } diff --git a/src/jsifier.js b/src/jsifier.js index ff58ece2..1662d249 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -600,6 +600,10 @@ function JSify(data, functionsOnly, givenFunctions) { func.JS += 'function ' + func.ident + '(' + paramIdents.join(', ') + ') {\n'; + if (PGO) { + func.JS += ' PGOMonitor.called["' + func.ident + '"] = 1;\n'; + } + if (ASM_JS) { // spell out argument types func.params.forEach(function(param) { @@ -1587,11 +1591,17 @@ function JSify(data, functionsOnly, givenFunctions) { var shellParts = read(shellFile).split('{{BODY}}'); print(shellParts[1]); - // Print out some useful metadata (for additional optimizations later, like the eliminator) - if (EMIT_GENERATED_FUNCTIONS) { - print('// EMSCRIPTEN_GENERATED_FUNCTIONS: ' + JSON.stringify(keys(Functions.implementedFunctions).filter(function(func) { + // Print out some useful metadata + if (EMIT_GENERATED_FUNCTIONS || PGO) { + var generatedFunctions = JSON.stringify(keys(Functions.implementedFunctions).filter(function(func) { return IGNORED_FUNCTIONS.indexOf(func.ident) < 0; - })) + '\n'); + })); + if (PGO) { + print('PGOMonitor.allGenerated = ' + generatedFunctions + ';\nremoveRunDependency("pgo");\n'); + } + if (EMIT_GENERATED_FUNCTIONS) { + print('// EMSCRIPTEN_GENERATED_FUNCTIONS: ' + generatedFunctions + '\n'); + } } PassManager.serialize(); diff --git a/src/library_gl.js b/src/library_gl.js index c6007809..9e12e4ee 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -276,7 +276,7 @@ var LibraryGL = { // TODO: initial pass to detect ranges we need to upload, might not need an upload per attrib for (var i = 0; i < GL.maxVertexAttribs; ++i) { var cb = GL.clientBuffers[i]; - if (!cb.enabled) continue; + if (!cb.clientside || !cb.enabled) continue; GL.resetBufferBinding = true; @@ -291,9 +291,9 @@ var LibraryGL = { } while (used.indexOf(buf) >= 0); used.push(buf); Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, buf); - Module.ctx.bufferData(Module.ctx.ARRAY_BUFFER, - HEAPU8.subarray(cb.ptr, cb.ptr + size), - Module.ctx.DYNAMIC_DRAW); + Module.ctx.bufferSubData(Module.ctx.ARRAY_BUFFER, + 0, + HEAPU8.subarray(cb.ptr, cb.ptr + size)); Module.ctx.vertexAttribPointer(i, cb.size, cb.type, cb.normalized, cb.stride, 0); } }, @@ -314,7 +314,7 @@ var LibraryGL = { GL.maxVertexAttribs = Module.ctx.getParameter(Module.ctx.MAX_VERTEX_ATTRIBS); #if FULL_ES2 for (var i = 0; i < GL.maxVertexAttribs; i++) { - GL.clientBuffers[i] = { enabled: false, size: 0, type: 0, normalized: 0, stride: 0, ptr: 0 }; + GL.clientBuffers[i] = { enabled: false, clientside: false, size: 0, type: 0, normalized: 0, stride: 0, ptr: 0 }; } GL.generateTempBuffers(); @@ -2888,9 +2888,10 @@ var LibraryGL = { cb.normalized = normalized; cb.stride = stride; cb.ptr = ptr; + cb.clientside = true; return; } - cb.enabled = false; + cb.clientside = false; #endif Module.ctx.vertexAttribPointer(index, size, type, normalized, stride, ptr); }, @@ -2939,9 +2940,9 @@ var LibraryGL = { var size = GL.calcBufLength(1, type, 0, count); buf = GL.tempIndexBuffers[GL.tempBufferIndexLookup[size]]; Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, buf); - Module.ctx.bufferData(Module.ctx.ELEMENT_ARRAY_BUFFER, - HEAPU8.subarray(indices, indices + size), - Module.ctx.DYNAMIC_DRAW); + Module.ctx.bufferSubData(Module.ctx.ELEMENT_ARRAY_BUFFER, + 0, + HEAPU8.subarray(indices, indices + size)); // the index is now 0 indices = 0; } diff --git a/src/library_sdl.js b/src/library_sdl.js index 5033b27e..3dbb9a33 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -1235,6 +1235,7 @@ var LibrarySDL = { Mix_Init: function(flags) { if (!flags) return 0; + SDL.channelMinimumNumber = 0; return 8; /* MIX_INIT_OGG */ }, Mix_Quit: function(){}, @@ -1317,7 +1318,9 @@ var LibrarySDL = { Mix_FreeChunk: function(id) { SDL.audios[id] = null; }, - + Mix_ReserveChannels: function(num) { + SDL.channelMinimumNumber = num; + }, Mix_PlayChannel: function(channel, id, loops) { // TODO: handle loops @@ -1330,8 +1333,8 @@ var LibrarySDL = { // If the user asks us to allocate a channel automatically, get the first // free one. if (channel == -1) { - channel = 0; - for (var i = 0; i < SDL.numChannels; i++) { + channel = SDL.channelMinimumNumber; + for (var i = SDL.channelMinimumNumber; i < SDL.numChannels; i++) { if (!SDL.channels[i].audio) { channel = i; break; @@ -1403,6 +1406,7 @@ var LibrarySDL = { audio.play(); } audio.volume = channelInfo.volume; + audio.paused = false; return channel; }, Mix_PlayChannelTimed: 'Mix_PlayChannel', // XXX ignore Timing @@ -1493,44 +1497,45 @@ var LibrarySDL = { // http://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer_38.html#SEC38 // "Note: Does not check if the channel has been paused." - Mix_Playing: function(id) { - if (id === -1) { + Mix_Playing: function(channel) { + if (channel === -1) { var count = 0; - for (var i = 0; i < SDL.audios.length; i++) { - count += SDL.Mix_Playing(i); + for (var i = 0; i < SDL.channels.length; i++) { + count += _Mix_Playing(i); } return count; } - var info = SDL.audios[id]; + var info = SDL.channels[channel]; if (info && info.audio && !info.audio.paused) { return 1; } return 0; }, - Mix_Pause: function(id) { - if (id === -1) { - for (var i = 0; i<SDL.audios.length;i++) { - SDL.Mix_Pause(i); + Mix_Pause: function(channel) { + if (channel === -1) { + for (var i = 0; i<SDL.channels.length;i++) { + _Mix_Pause(i); } return; } - var info = SDL.audios[id]; + var info = SDL.channels[channel]; if (info && info.audio) { info.audio.pause(); + info.audio.paused = true; } }, // http://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer_39.html#SEC39 - Mix_Paused: function(id) { - if (id === -1) { + Mix_Paused: function(channel) { + if (channel === -1) { var pausedCount = 0; - for (var i = 0; i<SDL.audios.length;i++) { - pausedCount += SDL.Mix_Paused(i); + for (var i = 0; i<SDL.channels.length;i++) { + pausedCount += _Mix_Paused(i); } return pausedCount; } - var info = SDL.audios[id]; + var info = SDL.channels[channel]; if (info && info.audio && info.audio.paused) { return 1; } @@ -1542,14 +1547,14 @@ var LibrarySDL = { }, // http://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer_33.html#SEC33 - Mix_Resume: function(id) { - if (id === -1) { - for (var i = 0; i<SDL.audios.length;i++) { - SDL.Mix_Resume(i); + Mix_Resume: function(channel) { + if (channel === -1) { + for (var i = 0; i<SDL.channels.length;i++) { + _Mix_Resume(i); } return; } - var info = SDL.audios[id]; + var info = SDL.channels[channel]; if (info && info.audio) { info.audio.play(); } diff --git a/src/modules.js b/src/modules.js index 797d4d83..65b8d437 100644 --- a/src/modules.js +++ b/src/modules.js @@ -291,7 +291,7 @@ var Functions = { var sig = ASM_JS ? Functions.implementedFunctions[ident] || Functions.unimplementedFunctions[ident] || LibraryManager.library[ident.substr(1) + '__sig'] : 'x'; assert(sig, ident); if (!tables[sig]) tables[sig] = emptyTable(sig); // TODO: make them compact - tables[sig][this.indexedFunctions[ident]] = ident; + tables[sig][this.indexedFunctions[ident]] = ident in DEAD_FUNCTIONS ? '0' : ident; } var generated = false; var wrapped = {}; @@ -315,7 +315,7 @@ var Functions = { } if (ASM_JS) { var curr = table[i]; - if (curr && !Functions.implementedFunctions[curr]) { + if (curr && curr != '0' && !Functions.implementedFunctions[curr]) { // This is a library function, we can't just put it in the function table, need a wrapper if (!wrapped[curr]) { var args = '', arg_coercions = '', call = curr + '(', retPre = '', retPost = ''; diff --git a/src/preamble.js b/src/preamble.js index 9bc68d8f..7538b19c 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -848,5 +848,22 @@ Module['removeRunDependency'] = removeRunDependency; Module["preloadedImages"] = {}; // maps url to image data Module["preloadedAudios"] = {}; // maps url to audio data +#if PGO +var PGOMonitor = { + called: {}, + dump: function() { + var dead = []; + for (var i = 0; i < this.allGenerated.length; i++) { + var func = this.allGenerated[i]; + if (!this.called[func]) dead.push(func); + } + Module.print('-s DEAD_FUNCTIONS=\'' + JSON.stringify(dead) + '\'\n'); + } +}; +__ATEXIT__.push({ func: function() { PGOMonitor.dump() } }); +if (!Module.preRun) Module.preRun = []; +Module.preRun.push(function() { addRunDependency('pgo') }); +#endif + // === Body === diff --git a/src/runtime.js b/src/runtime.js index dc604a8d..8352ade1 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -87,7 +87,7 @@ var RuntimeGenerator = { }; function unInline(name_, params) { - var src = '(function ' + name_ + '(' + params + ') { var ret = ' + RuntimeGenerator[name_].apply(null, params) + '; return ret; })'; + var src = '(function(' + params + ') { var ret = ' + RuntimeGenerator[name_].apply(null, params) + '; return ret; })'; var ret = eval(src); return ret; } diff --git a/src/settings.js b/src/settings.js index 1bfcf92a..101c403c 100644 --- a/src/settings.js +++ b/src/settings.js @@ -15,7 +15,7 @@ var QUANTUM_SIZE = 4; // This is the size of an individual field in a structure. // the normal value of 4 means all fields take 4 memory addresses, // as per the norm on a 32-bit machine. // - // 1 is somewhat faster than 4, but dangerous. + // Changing this from the default of 4 is deprecated. var CORRECT_SIGNS = 1; // Whether we make sure to convert unsigned values to signed values. // Decreases performance with additional runtime checks. Might not be @@ -63,7 +63,7 @@ var RELOOPER = 'relooper.js'; // Loads the relooper from this path relative to c var USE_TYPED_ARRAYS = 2; // Use typed arrays for the heap. See https://github.com/kripken/emscripten/wiki/Code-Generation-Modes/ // 0 means no typed arrays are used. // 1 has two heaps, IHEAP (int32) and FHEAP (double), - // and addresses there are a match for normal addresses. + // and addresses there are a match for normal addresses. This is deprecated. // 2 is a single heap, accessible through views as int8, int32, etc. This is // the recommended mode both for performance and for compatibility. var USE_FHEAP = 1; // Relevant in USE_TYPED_ARRAYS == 1. If this is disabled, only IHEAP will be used, and FHEAP @@ -332,6 +332,14 @@ var ASM_JS = 0; // If 1, generate code in asm.js format. XXX This is highly expe // and will not work on most codebases yet. It is NOT recommended that you // try this yet. +var PGO = 0; // Enables profile-guided optimization in the form of runtime checks for + // which functions are actually called. Emits a list during shutdown that you + // can pass to DEAD_FUNCTIONS (you can also emit the list manually by + // calling PGOMonitor.dump()); +var DEAD_FUNCTIONS = []; // A list of functions that no code will be emitted for, and + // a runtime abort will happen if they are called + // TODO: options to lazily load such functions + var EXPLICIT_ZEXT = 0; // If 1, generate an explicit conversion of zext i1 to i32, using ?: var NECESSARY_BLOCKADDRS = []; // List of (function, block) for all block addresses that are taken. |