diff options
-rw-r--r-- | src/library_sdl.js | 273 | ||||
-rw-r--r-- | src/relooper/Relooper.cpp | 9 | ||||
-rw-r--r-- | src/relooper/fuzzer.py | 4 | ||||
-rw-r--r-- | src/relooper/test.cpp | 31 | ||||
-rw-r--r-- | src/relooper/test.txt | 153 | ||||
-rw-r--r-- | tests/life.c | 2 | ||||
-rw-r--r-- | tests/sdl_audio_mix.c | 2 | ||||
-rw-r--r-- | tools/js-optimizer.js | 34 | ||||
-rw-r--r-- | tools/test-js-optimizer-asm-regs-harder-output.js | 5 | ||||
-rw-r--r-- | tools/test-js-optimizer-asm-regs-harder.js | 8 |
10 files changed, 388 insertions, 133 deletions
diff --git a/src/library_sdl.js b/src/library_sdl.js index eabfe3e5..d9639907 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -943,11 +943,67 @@ var LibrarySDL = { var ret = info.volume * 128; // MIX_MAX_VOLUME if (volume != -1) { info.volume = volume / 128; - if (info.audio) info.audio.volume = info.volume; + if (info.audio) { + info.audio.volume = info.volume; // For <audio> element + if (info.audio.webAudioGainNode) info.audio.webAudioGainNode['gain']['value'] = info.volume; // For WebAudio playback + } } return ret; }, + // Plays out an SDL audio resource that was loaded with the Mix_Load APIs, when using Web Audio.. + playWebAudio: function(audio) { + if (!audio) return; + if (audio.webAudioNode) return; // This instance is already playing, don't start again. + if (!SDL.webAudioAvailable()) return; + var webAudio = audio.resource.webAudio; + audio.paused = false; + if (!webAudio.decodedBuffer) { + if (webAudio.onDecodeComplete === undefined) abort("Cannot play back audio object that was not loaded"); + webAudio.onDecodeComplete.push(function() { if (!audio.paused) SDL.playWebAudio(audio); }); + return; + } + audio.webAudioNode = SDL.audioContext['createBufferSource'](); + audio.webAudioNode['buffer'] = webAudio.decodedBuffer; + audio.webAudioNode['loop'] = audio.loop; + audio.webAudioNode['onended'] = function() { audio.onended(); } // For <media> element compatibility, route the onended signal to the instance. + + // Add an intermediate gain node to control volume. + audio.webAudioGainNode = SDL.audioContext['createGain'](); + audio.webAudioGainNode['gain']['value'] = audio.volume; + audio.webAudioNode['connect'](audio.webAudioGainNode); + audio.webAudioGainNode['connect'](SDL.audioContext['destination']); + audio.webAudioNode['start'](0, audio.currentPosition); + audio.startTime = SDL.audioContext['currentTime'] - audio.currentPosition; + }, + + // Pausea an SDL audio resource that was played with Web Audio.. + pauseWebAudio: function(audio) { + if (!audio) return; + if (audio.webAudioNode) { + // Remember where we left off, so that if/when we resume, we can restart the playback at a proper place. + audio.currentPosition = (SDL.audioContext['currentTime'] - audio.startTime) % audio.resource.webAudio.decodedBuffer.duration; + // Important: When we reach here, the audio playback is stopped by the user. But when calling .stop() below, the Web Audio + // graph will send the onended signal, but we don't want to process that, since pausing should not clear/destroy the audio + // channel. + audio.webAudioNode['onended'] = undefined; + audio.webAudioNode.stop(); + audio.webAudioNode = undefined; + } + audio.paused = true; + }, + + openAudioContext: function() { + // Initialize Web Audio API if we haven't done so yet. Note: Only initialize Web Audio context ever once on the web page, + // since initializing multiple times fails on Chrome saying 'audio resources have been exhausted'. + if (!SDL.audioContext) { + if (typeof(AudioContext) !== 'undefined') SDL.audioContext = new AudioContext(); + else if (typeof(webkitAudioContext) !== 'undefined') SDL.audioContext = new webkitAudioContext(); + } + }, + + webAudioAvailable: function() { return !!SDL.audioContext; }, + fillWebAudioBufferFromHeap: function(heapPtr, sizeSamplesPerChannel, dstAudioBuffer) { // The input audio data is interleaved across the channels, i.e. [L, R, L, R, L, R, ...] and is either 8-bit or 16-bit as // supported by the SDL API. The output audio wave data for Web Audio API must be in planar buffers of [-1,1]-normalized Float32 data, @@ -1250,11 +1306,11 @@ var LibrarySDL = { for (var i = 0; i < SDL.numChannels; ++i) { if (SDL.channels[i].audio) { SDL.channels[i].audio.pause(); + SDL.channels[i].audio = undefined; } } - if (SDL.music.audio) { - SDL.music.audio.pause(); - } + if (SDL.music.audio) SDL.music.audio.pause(); + SDL.music.audio = undefined; Module.print('SDL_Quit called (and ignored)'); }, @@ -2004,15 +2060,8 @@ var LibrarySDL = { } else { // Initialize Web Audio API if we haven't done so yet. Note: Only initialize Web Audio context ever once on the web page, // since initializing multiple times fails on Chrome saying 'audio resources have been exhausted'. - if (!SDL.audioContext) { - if (typeof(AudioContext) !== 'undefined') { - SDL.audioContext = new AudioContext(); - } else if (typeof(webkitAudioContext) !== 'undefined') { - SDL.audioContext = new webkitAudioContext(); - } else { - throw 'Web Audio API is not available!'; - } - } + SDL.openAudioContext(); + if (!SDL.audioContext) throw 'Web Audio API is not available!'; SDL.audio.soundSource = new Array(); // Use an array of sound sources as a ring buffer to queue blocks of synthesized audio to Web Audio API. SDL.audio.nextSoundSource = 0; // Index of the next sound buffer in the ring buffer queue to play. SDL.audio.nextPlayTime = 0; // Time in seconds when the next audio block is due to start. @@ -2195,6 +2244,7 @@ var LibrarySDL = { Mix_Quit: function(){}, Mix_OpenAudio: function(frequency, format, channels, chunksize) { + SDL.openAudioContext(); SDL.allocateChannels(32); // Just record the values for a later call to Mix_QuickLoad_RAW SDL.mixerFrequency = frequency; @@ -2237,6 +2287,7 @@ var LibrarySDL = { var filename = ''; var audio; + var webAudio; var bytes; if (rwops.filename !== undefined) { @@ -2244,7 +2295,7 @@ var LibrarySDL = { 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); + if (!Module.noAudioDecoding) Runtime.warnOnce('Cannot find preloaded audio ' + filename); // see if we can read the file-contents from the in-memory FS try { @@ -2260,15 +2311,33 @@ var LibrarySDL = { audio = raw; } else if (rwops.bytes !== undefined) { - bytes = HEAPU8.subarray(rwops.bytes, rwops.bytes + rwops.count); + // For Web Audio context buffer decoding, we must make a clone of the audio data, but for <media> element, + // a view to existing data is sufficient. + if (SDL.webAudioAvailable()) bytes = HEAPU8.buffer.slice(rwops.bytes, rwops.bytes + rwops.count); + else bytes = HEAPU8.subarray(rwops.bytes, rwops.bytes + rwops.count); } else { return 0; } - // 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) { + if (bytes !== undefined && SDL.webAudioAvailable()) { + audio = undefined; + webAudio = {}; + // The audio decoding process is asynchronous, which gives trouble if user code plays the audio data back immediately + // after loading. Therefore prepare an array of callback handlers to run when this audio decoding is complete, which + // will then start the playback (with some delay). + webAudio.onDecodeComplete = []; // While this member array exists, decoding hasn't finished yet. + function onDecodeComplete(data) { + webAudio.decodedBuffer = data; + // Call all handlers that were waiting for this decode to finish, and clear the handler list. + webAudio.onDecodeComplete.forEach(function(e) { e(); }); + webAudio.onDecodeComplete = undefined; // Don't allow more callback handlers since audio has finished decoding. + } + + SDL.audioContext['decodeAudioData'](bytes.buffer || bytes, onDecodeComplete); + } else if (audio === undefined && bytes) { + // 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 var blob = new Blob([bytes], {type: rwops.mimetype}); var url = URL.createObjectURL(blob); audio = new Audio(); @@ -2280,27 +2349,39 @@ var LibrarySDL = { // Keep the loaded audio in the audio arrays, ready for playback SDL.audios.push({ source: filename, - audio: audio + audio: audio, // Points to the <audio> element, if loaded + webAudio: webAudio // Points to a Web Audio -specific resource object, if loaded }); return id; }, Mix_QuickLoad_RAW: function(mem, len) { - var audio = new Audio(); - audio.mozAudioChannelType = 'content'; // bugzilla 910340 - // Record the number of channels and frequency for later usage - audio.numChannels = SDL.mixerNumChannels; - audio.frequency = SDL.mixerFrequency; + var audio; + var webAudio; + var numSamples = len >> 1; // len is the length in bytes, and the array contains 16-bit PCM values var buffer = new Float32Array(numSamples); for (var i = 0; i < numSamples; ++i) { buffer[i] = ({{{ makeGetValue('mem', 'i*2', 'i16', 0, 0) }}}) / 0x8000; // hardcoded 16-bit audio, signed (TODO: reSign if not ta2?) } - // FIXME: doesn't make sense to keep the audio element in the buffer + + if (SDL.webAudioAvailable()) { + webAudio = {}; + webAudio.decodedBuffer = buffer; + } else { + var audio = new Audio(); + audio.mozAudioChannelType = 'content'; // bugzilla 910340 + // Record the number of channels and frequency for later usage + audio.numChannels = SDL.mixerNumChannels; + audio.frequency = SDL.mixerFrequency; + // FIXME: doesn't make sense to keep the audio element in the buffer + } + var id = SDL.audios.length; SDL.audios.push({ source: '', audio: audio, + webAudio: webAudio, buffer: buffer }); return id; @@ -2313,13 +2394,12 @@ var LibrarySDL = { SDL.channelMinimumNumber = num; }, Mix_PlayChannel: function(channel, id, loops) { - // TODO: handle loops + // TODO: handle fixed amount of N loops. Currently loops either 0 or infinite times. // Get the audio element associated with the ID var info = SDL.audios[id]; if (!info) return -1; - var audio = info.audio; - if (!audio) return -1; + if (!info.audio && !info.webAudio) return -1; // If the user asks us to allocate a channel automatically, get the first // free one. @@ -2335,73 +2415,33 @@ var LibrarySDL = { return -1; } } - // We clone the audio node to utilize the preloaded audio buffer, since - // the browser has already preloaded the audio file. var channelInfo = SDL.channels[channel]; - channelInfo.audio = audio = audio.cloneNode(true); - audio.numChannels = info.audio.numChannels; - audio.frequency = info.audio.frequency; - // TODO: handle N loops. Behavior matches Mix_PlayMusic - audio.loop = loops != 0; - audio['onended'] = function SDL_audio_onended() { // TODO: cache these - channelInfo.audio = null; - if (SDL.channelFinished) { - Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel); - } - } - // Either play the element, or load the dynamic data into it - if (info.buffer) { - var contextCtor = null; - if (audio && ('mozSetup' in audio)) { // Audio Data API - try { - audio['mozSetup'](audio.numChannels, audio.frequency); - audio["mozWriteAudio"](info.buffer); - } catch (e) { - // Workaround for Firefox bug 783052 - // ignore this exception! - } - /* - } else if (contextCtor = (window.AudioContext || // WebAudio API - window.webkitAudioContext)) { - var currentIndex = 0; - var numChannels = parseInt(audio.numChannels); - var context = new contextCtor(); - var source = context.createBufferSource(); - source.loop = false; - source.buffer = context.createBuffer(numChannels, 1, audio.frequency); - var jsNode = context.createJavaScriptNode(2048, numChannels, numChannels); - jsNode.onaudioprocess = function jsNode_onaudioprocess(event) { - var buffers = new Array(numChannels); - for (var i = 0; i < numChannels; ++i) { - buffers[i] = event.outputBuffer.getChannelData(i); - } - var remaining = info.buffer.length - currentIndex; - if (remaining > 2048) { - remaining = 2048; - } - for (var i = 0; i < remaining;) { - for (var j = 0; j < numChannels; ++j) { - buffers[j][i] = info.buffer[currentIndex + i + j] * audio.volume; - } - i += j; - } - currentIndex += remaining * numChannels; - for (var i = remaining; i < 2048;) { - for (var j = 0; j < numChannels; ++j) { - buffers[j][i] = 0; // silence - } - i += j; - } - }; - source.connect(jsNode); - jsNode.connect(context.destination); - source.noteOn(0); - */ - } + var audio; + if (info.webAudio) { + // Create an instance of the WebAudio object. + audio = {}; + audio.resource = info; // This new object is an instance that refers to this existing resource. + audio.paused = false; + audio.currentPosition = 0; + // Make our instance look similar to the instance of a <media> to make api simple. + audio.play = function() { SDL.playWebAudio(this); } + audio.pause = function() { SDL.pauseWebAudio(this); } } else { - audio.play(); + // We clone the audio node to utilize the preloaded audio buffer, since + // the browser has already preloaded the audio file. + audio = info.audio.cloneNode(true); + audio.numChannels = info.audio.numChannels; + audio.frequency = info.audio.frequency; + } + audio['onended'] = function SDL_audio_onended() { // TODO: cache these + if (channelInfo.audio == this) { channelInfo.audio.paused = true; channelInfo.audio = null; } + if (SDL.channelFinished) Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel); } + channelInfo.audio = audio; + // TODO: handle N loops. Behavior matches Mix_PlayMusic + audio.loop = loops != 0; audio.volume = channelInfo.volume; + audio.play(); return channel; }, Mix_PlayChannelTimed: 'Mix_PlayChannel', // XXX ignore Timing @@ -2454,46 +2494,51 @@ var LibrarySDL = { Mix_PlayMusic__deps: ['Mix_HaltMusic'], Mix_PlayMusic: function(id, loops) { - loops = Math.max(loops, 1); - var audio = SDL.audios[id].audio; - if (!audio) return 0; - audio.loop = loops != 0; // TODO: handle N loops for finite N - if (SDL.audios[id].buffer) { - audio["mozWriteAudio"](SDL.audios[id].buffer); - } else { - audio.play(); - } - audio.volume = SDL.music.volume; - audio['onended'] = _Mix_HaltMusic; // will send callback + // Pause old music if it exists. if (SDL.music.audio) { - if (!SDL.music.audio.paused) { - Module.printErr('Music is already playing. ' + SDL.music.source); - } + if (!SDL.music.audio.paused) Module.printErr('Music is already playing. ' + SDL.music.source); SDL.music.audio.pause(); } + var info = SDL.audios[id]; + var audio; + if (info.webAudio) { // Play via Web Audio API + // Create an instance of the WebAudio object. + audio = {}; + audio.resource = info; // This new webAudio object is an instance that refers to this existing resource. + audio.paused = false; + audio.currentPosition = 0; + audio.play = function() { SDL.playWebAudio(this); } + audio.pause = function() { SDL.pauseWebAudio(this); } + } else if (info.audio) { // Play via the <audio> element + audio = info.audio; + } + audio['onended'] = function() { if (SDL.music.audio == this) _Mix_HaltMusic(); } // will send callback + audio.loop = loops != 0; // TODO: handle N loops for finite N + audio.volume = SDL.music.volume; SDL.music.audio = audio; + audio.play(); return 0; }, Mix_PauseMusic: function() { var audio = SDL.music.audio; - if (!audio) return 0; - audio.pause(); + if (audio) audio.pause(); return 0; }, Mix_ResumeMusic: function() { var audio = SDL.music.audio; - if (!audio) return 0; - audio.play(); + if (audio) audio.play(); return 0; }, Mix_HaltMusic: function() { var audio = SDL.music.audio; - if (!audio) return 0; - audio.src = audio.src; // rewind - audio.pause(); + if (audio) { + audio.src = audio.src; // rewind <media> element + audio.currentPosition = 0; // rewind Web Audio graph playback. + audio.pause(); + } SDL.music.audio = null; if (SDL.hookMusicFinished) { Runtime.dynCall('v', SDL.hookMusicFinished); @@ -2570,9 +2615,7 @@ var LibrarySDL = { return; } var info = SDL.channels[channel]; - if (info && info.audio) { - info.audio.play(); - } + if (info && info.audio) info.audio.play(); }, // SDL TTF diff --git a/src/relooper/Relooper.cpp b/src/relooper/Relooper.cpp index 568dd381..9e469ec4 100644 --- a/src/relooper/Relooper.cpp +++ b/src/relooper/Relooper.cpp @@ -1097,7 +1097,7 @@ void Relooper::Calculate(Block *Entry) { // Remove unneeded breaks and continues. // A flow operation is trivially unneeded if the shape we naturally get to by normal code // execution is the same as the flow forces us to. - void RemoveUnneededFlows(Shape *Root, Shape *Natural=NULL, LoopShape *LastLoop=NULL) { + void RemoveUnneededFlows(Shape *Root, Shape *Natural=NULL, LoopShape *LastLoop=NULL, unsigned Depth=0) { BlockSet NaturalBlocks; FollowNaturalFlow(Natural, NaturalBlocks); Shape *Next = Root; @@ -1108,7 +1108,7 @@ void Relooper::Calculate(Block *Entry) { if (Simple->Inner->BranchVar) LastLoop = NULL; // a switch clears out the loop (TODO: only for breaks, not continue) if (Simple->Next) { - if (!Simple->Inner->BranchVar && Simple->Inner->ProcessedBranchesOut.size() == 2) { + if (!Simple->Inner->BranchVar && Simple->Inner->ProcessedBranchesOut.size() == 2 && Depth < 20) { // If there is a next block, we already know at Simple creation time to make direct branches, // and we can do nothing more in general. But, we try to optimize the case of a break and // a direct: This would normally be if (break?) { break; } .. but if we @@ -1144,6 +1144,7 @@ void Relooper::Calculate(Block *Entry) { } } } + Depth++; // this optimization increases depth, for us and all our next chain (i.e., until this call returns) } Next = Simple->Next; } else { @@ -1168,11 +1169,11 @@ void Relooper::Calculate(Block *Entry) { } }, { for (IdShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Multiple->InnerMap.end(); iter++) { - RemoveUnneededFlows(iter->second, Multiple->Next, Multiple->Breaks ? NULL : LastLoop); + RemoveUnneededFlows(iter->second, Multiple->Next, Multiple->Breaks ? NULL : LastLoop, Depth+1); } Next = Multiple->Next; }, { - RemoveUnneededFlows(Loop->Inner, Loop->Inner, Loop); + RemoveUnneededFlows(Loop->Inner, Loop->Inner, Loop, Depth+1); Next = Loop->Next; }); } diff --git a/src/relooper/fuzzer.py b/src/relooper/fuzzer.py index 18db997e..156adb76 100644 --- a/src/relooper/fuzzer.py +++ b/src/relooper/fuzzer.py @@ -123,14 +123,14 @@ int main() { open('fuzz.slow.js', 'w').write(slow) open('fuzz.cpp', 'w').write(fast) print '_' - slow_out = subprocess.Popen(['mozjs', '-m', '-n', 'fuzz.slow.js'], stdout=subprocess.PIPE).communicate()[0] + slow_out = subprocess.Popen(['mozjs', 'fuzz.slow.js'], stdout=subprocess.PIPE).communicate()[0] print '.' subprocess.call(['g++', 'fuzz.cpp', 'Relooper.o', '-o', 'fuzz', '-g']) print '*' subprocess.call(['./fuzz'], stdout=open('fuzz.fast.js', 'w')) print '-' - fast_out = subprocess.Popen(['mozjs', '-m', '-n', 'fuzz.fast.js'], stdout=subprocess.PIPE).communicate()[0] + fast_out = subprocess.Popen(['mozjs', 'fuzz.fast.js'], stdout=subprocess.PIPE).communicate()[0] print if slow_out != fast_out: diff --git a/src/relooper/test.cpp b/src/relooper/test.cpp index 9f3ddceb..f319757b 100644 --- a/src/relooper/test.cpp +++ b/src/relooper/test.cpp @@ -1,4 +1,6 @@ +#include <vector> + #include "Relooper.h" int main() { @@ -435,5 +437,34 @@ int main() { puts(r.GetOutputBuffer()); } + + if (1) { + Relooper::MakeOutputBuffer(10); + + printf("\n\n-- lots of exits to an unwind block, possible nesting --\n\n"); + + const int DEPTH = 40; + + std::vector<Block*> blocks; + for (int i = 0; i < DEPTH; i++) blocks.push_back(new Block("// block\n", NULL)); + Block *last = new Block("// last\nreturn;\n", NULL); + Block *UW = new Block("// UW\nresumeException();\n\n", NULL); + + for (int i = 0; i < DEPTH; i++) { + Block *b = blocks[i]; + b->AddBranchTo(i+1 < DEPTH ? blocks[i+1] : last, "check()", NULL); + b->AddBranchTo(UW, NULL, NULL); + } + + Relooper r; + for (int i = 0; i < DEPTH; i++) r.AddBlock(blocks[i]); + r.AddBlock(last); + r.AddBlock(UW); + + r.Calculate(blocks[0]); + r.Render(); + + puts(r.GetOutputBuffer()); + } } diff --git a/src/relooper/test.txt b/src/relooper/test.txt index d53aeeb1..2ae331c0 100644 --- a/src/relooper/test.txt +++ b/src/relooper/test.txt @@ -417,3 +417,156 @@ } // block D + + +-- lots of exits to an unwind block, possible nesting -- + + // block + do { + if (check()) { + // block + if (check()) { + // block + if (check()) { + // block + if (check()) { + // block + if (check()) { + // block + if (check()) { + // block + if (check()) { + // block + if (check()) { + // block + if (check()) { + // block + if (check()) { + // block + if (check()) { + // block + if (check()) { + // block + if (check()) { + // block + if (check()) { + // block + if (check()) { + // block + if (check()) { + // block + if (check()) { + // block + if (check()) { + // block + if (check()) { + // block + if (!(check())) { + break; + } + // block + if (!(check())) { + break; + } + // block + if (!(check())) { + break; + } + // block + if (!(check())) { + break; + } + // block + if (!(check())) { + break; + } + // block + if (!(check())) { + break; + } + // block + if (!(check())) { + break; + } + // block + if (!(check())) { + break; + } + // block + if (!(check())) { + break; + } + // block + if (!(check())) { + break; + } + // block + if (!(check())) { + break; + } + // block + if (!(check())) { + break; + } + // block + if (!(check())) { + break; + } + // block + if (!(check())) { + break; + } + // block + if (!(check())) { + break; + } + // block + if (!(check())) { + break; + } + // block + if (!(check())) { + break; + } + // block + if (!(check())) { + break; + } + // block + if (!(check())) { + break; + } + // block + if (!(check())) { + break; + } + // block + if (!(check())) { + break; + } + // last + return; + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } while(0); + // UW + resumeException(); + + diff --git a/tests/life.c b/tests/life.c index 4ce8d385..263fa0e6 100644 --- a/tests/life.c +++ b/tests/life.c @@ -67,7 +67,9 @@ void game(int w, int h, int i) i--; nudge(univ, w, h); // keep it interesting for benchmark } else { +#if !__EMSCRIPTEN__ usleep(20000); +#endif show(univ, w, h); } } diff --git a/tests/sdl_audio_mix.c b/tests/sdl_audio_mix.c index a1c0485d..422fc122 100644 --- a/tests/sdl_audio_mix.c +++ b/tests/sdl_audio_mix.c @@ -58,7 +58,9 @@ void one_iter() { Mix_HaltChannel(soundChannel); Mix_HaltMusic(); int result = 1; +#ifdef REPORT_RESULT REPORT_RESULT(); +#endif break; }; } diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 088c4f0f..2004efda 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -2237,18 +2237,30 @@ function registerizeHarder(ast) { break; case 'conditional': isInExpr++; - buildFlowGraph(node[1]); - var jEnter = markJunction(); - var jExit = addJunction(); - if (node[2]) { - buildFlowGraph(node[2]); - } - joinJunction(jExit); - setJunction(jEnter); - if (node[3]) { - buildFlowGraph(node[3]); + // If the conditional has no side-effects, we can treat it as a single + // block, which might open up opportunities to remove it entirely. + if (!hasSideEffects(node)) { + buildFlowGraph(node[1]); + if (node[2]) { + buildFlowGraph(node[2]); + } + if (node[3]) { + buildFlowGraph(node[3]); + } + } else { + buildFlowGraph(node[1]); + var jEnter = markJunction(); + var jExit = addJunction(); + if (node[2]) { + buildFlowGraph(node[2]); + } + joinJunction(jExit); + setJunction(jEnter); + if (node[3]) { + buildFlowGraph(node[3]); + } + joinJunction(jExit); } - joinJunction(jExit); isInExpr--; break; case 'while': diff --git a/tools/test-js-optimizer-asm-regs-harder-output.js b/tools/test-js-optimizer-asm-regs-harder-output.js index e1df42cb..c3b326f6 100644 --- a/tools/test-js-optimizer-asm-regs-harder-output.js +++ b/tools/test-js-optimizer-asm-regs-harder-output.js @@ -129,4 +129,9 @@ function linkedVars() { } return i2 + i1; } +function deadCondExpr(i2) { + i2 = i2 | 0; + var i1 = 0; + return i1 | 0; +} diff --git a/tools/test-js-optimizer-asm-regs-harder.js b/tools/test-js-optimizer-asm-regs-harder.js index 0231a215..fa72aab8 100644 --- a/tools/test-js-optimizer-asm-regs-harder.js +++ b/tools/test-js-optimizer-asm-regs-harder.js @@ -149,5 +149,11 @@ function linkedVars() { } return outer1 + outer2; } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "_doit", "stackRestore", "switchey", "switchey2", "iffey", "labelledJump", "linkedVars'] +function deadCondExpr(input) { + input = input|0; + var dead = 0, temp = 0; + dead = (!input ? -1 : input)|0; + return temp|0; +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "_doit", "stackRestore", "switchey", "switchey2", "iffey", "labelledJump", "linkedVars", "deadCondExpr"] |